From 458ab037e395f872fece67403a2a46a3ad480112 Mon Sep 17 00:00:00 2001 From: Alexandre Rostovtsev Date: Wed, 6 Feb 2019 15:43:21 -0500 Subject: [PATCH] Update bundled gRPC to 1.18.0 and nanopb to 0.3.6 And take the opportunity to align third_party/grpc/BUILD closer with gRPC upstream's BUILD file (by adopting the same intermediate targets) to make importing easier next time. The MSYS2 patch is no longer needed judging by discussion in https://groups.google.com/forum/#!msg/grpc-io/gd6sIuo6rjQ/eyEFarhABgAJ Fixes #2804. --- third_party/grpc/BUILD | 2057 ++++++++--- third_party/grpc/README.bazel.md | 10 +- third_party/grpc/build_defs.bzl | 192 +- third_party/grpc/grpc.patch | 134 - third_party/grpc/include/grpc++/alarm.h | 71 +- third_party/grpc/include/grpc++/channel.h | 90 +- .../grpc/include/grpc++/client_context.h | 54 +- .../grpc/include/grpc++/completion_queue.h | 41 +- .../grpc/include/grpc++/create_channel.h | 73 +- .../include/grpc++/create_channel_posix.h | 28 + ...alth_check_service_server_builder_option.h | 28 + .../ext/proto_server_reflection_plugin.h | 28 + .../grpc++/generic/async_generic_service.h | 78 +- .../include/grpc++/generic/generic_stub.h | 66 +- third_party/grpc/include/grpc++/grpc++.h | 64 +- .../grpc++/health_check_service_interface.h | 28 + third_party/grpc/include/grpc++/impl/call.h | 41 +- .../grpc++/impl/channel_argument_option.h | 28 + .../include/grpc++/impl/client_unary_call.h | 41 +- .../grpc++/impl/codegen/async_stream.h | 464 +-- .../grpc++/impl/codegen/async_unary_call.h | 168 +- .../include/grpc++/impl/codegen/byte_buffer.h | 28 + .../grpc/include/grpc++/impl/codegen/call.h | 598 +--- .../include/grpc++/impl/codegen/call_hook.h | 53 +- .../grpc++/impl/codegen/channel_interface.h | 125 +- .../grpc++/impl/codegen/client_context.h | 354 +- .../grpc++/impl/codegen/client_unary_call.h | 77 +- .../grpc++/impl/codegen/completion_queue.h | 204 +- .../impl/codegen/completion_queue_tag.h | 58 +- .../grpc/include/grpc++/impl/codegen/config.h | 118 +- .../grpc++/impl/codegen/config_protobuf.h | 74 +- .../grpc++/impl/codegen/core_codegen.h | 28 + .../impl/codegen/core_codegen_interface.h | 28 + .../grpc++/impl/codegen/create_auth_context.h | 28 + .../grpc++/impl/codegen/grpc_library.h | 71 +- .../grpc++/impl/codegen/metadata_map.h | 28 + .../grpc++/impl/codegen/method_handler_impl.h | 205 +- .../include/grpc++/impl/codegen/proto_utils.h | 78 +- .../include/grpc++/impl/codegen/rpc_method.h | 75 +- .../grpc++/impl/codegen/rpc_service_method.h | 94 +- .../impl/codegen/security/auth_context.h | 112 +- .../impl/codegen/serialization_traits.h | 70 +- .../grpc++/impl/codegen/server_context.h | 216 +- .../grpc++/impl/codegen/server_interface.h | 256 +- .../grpc++/impl/codegen/service_type.h | 163 +- .../grpc/include/grpc++/impl/codegen/slice.h | 28 + .../grpc/include/grpc++/impl/codegen/status.h | 78 +- .../grpc++/impl/codegen/status_code_enum.h | 154 +- .../include/grpc++/impl/codegen/string_ref.h | 125 +- .../grpc++/impl/codegen/stub_options.h | 45 +- .../grpc/include/grpc++/impl/codegen/sync.h | 45 - .../include/grpc++/impl/codegen/sync_cxx11.h | 49 - .../grpc++/impl/codegen/sync_no_cxx11.h | 105 - .../include/grpc++/impl/codegen/sync_stream.h | 417 +-- .../grpc/include/grpc++/impl/codegen/time.h | 113 +- .../grpc/include/grpc++/impl/grpc_library.h | 69 +- .../include/grpc++/impl/method_handler_impl.h | 41 +- .../grpc/include/grpc++/impl/proto_utils.h | 39 - .../grpc/include/grpc++/impl/rpc_method.h | 41 +- .../include/grpc++/impl/rpc_service_method.h | 41 +- .../grpc++/impl/serialization_traits.h | 41 +- .../grpc++/impl/server_builder_option.h | 53 +- .../grpc++/impl/server_builder_plugin.h | 28 + .../include/grpc++/impl/server_initializer.h | 28 + .../grpc/include/grpc++/impl/service_type.h | 41 +- third_party/grpc/include/grpc++/impl/sync.h | 39 - .../grpc/include/grpc++/impl/sync_cxx11.h | 41 +- .../grpc/include/grpc++/impl/sync_no_cxx11.h | 41 +- third_party/grpc/include/grpc++/impl/thd.h | 45 - .../grpc/include/grpc++/impl/thd_cxx11.h | 45 - .../grpc/include/grpc++/impl/thd_no_cxx11.h | 117 - .../grpc/include/grpc++/resource_quota.h | 28 + .../include/grpc++/security/auth_context.h | 47 +- .../grpc++/security/auth_metadata_processor.h | 79 +- .../include/grpc++/security/credentials.h | 238 +- .../grpc++/security/server_credentials.h | 98 +- third_party/grpc/include/grpc++/server.h | 191 +- .../grpc/include/grpc++/server_builder.h | 140 +- .../grpc/include/grpc++/server_context.h | 41 +- .../grpc/include/grpc++/server_posix.h | 28 + .../include/grpc++/support/async_stream.h | 41 +- .../include/grpc++/support/async_unary_call.h | 41 +- .../grpc/include/grpc++/support/byte_buffer.h | 112 +- .../grpc++/support/channel_arguments.h | 106 +- .../grpc/include/grpc++/support/config.h | 41 +- .../include/grpc++/support/config_protobuf.h | 39 - .../include/grpc++/support/error_details.h | 28 + .../grpc/include/grpc++/support/slice.h | 90 +- .../grpc/include/grpc++/support/status.h | 41 +- .../include/grpc++/support/status_code_enum.h | 41 +- .../grpc/include/grpc++/support/string_ref.h | 41 +- .../include/grpc++/support/stub_options.h | 41 +- .../grpc/include/grpc++/support/sync_stream.h | 41 +- .../grpc/include/grpc++/support/time.h | 41 +- .../grpc/include/grpc++/test/mock_stream.h | 28 + .../grpc++/test/server_context_test_spouse.h | 28 + third_party/grpc/include/grpc/byte_buffer.h | 38 +- .../grpc/include/grpc/byte_buffer_reader.h | 56 +- third_party/grpc/include/grpc/census.h | 548 +-- third_party/grpc/include/grpc/compression.h | 87 +- third_party/grpc/include/grpc/fork.h | 26 + third_party/grpc/include/grpc/grpc.h | 451 ++- third_party/grpc/include/grpc/grpc_cronet.h | 38 + third_party/grpc/include/grpc/grpc_posix.h | 59 + third_party/grpc/include/grpc/grpc_security.h | 634 ++-- .../include/grpc/grpc_security_constants.h | 118 + .../grpc/include/grpc/grpc_zookeeper.h | 59 - .../grpc/include/grpc/impl/codegen/alloc.h | 74 - .../grpc/include/grpc/impl/codegen/atm.h | 59 +- .../grpc/impl/codegen/atm_gcc_atomic.h | 91 +- .../include/grpc/impl/codegen/atm_gcc_sync.h | 56 +- .../include/grpc/impl/codegen/atm_win32.h | 125 - .../include/grpc/impl/codegen/atm_windows.h | 128 + .../include/grpc/impl/codegen/byte_buffer.h | 97 +- .../grpc/impl/codegen/byte_buffer_reader.h | 42 + .../grpc/impl/codegen/compression_types.h | 97 +- .../grpc/impl/codegen/connectivity_state.h | 37 +- .../grpc/include/grpc/impl/codegen/fork.h | 48 + .../include/grpc/impl/codegen/gpr_slice.h | 69 + .../include/grpc/impl/codegen/gpr_types.h | 59 + .../include/grpc/impl/codegen/grpc_types.h | 595 ++- .../grpc/include/grpc/impl/codegen/log.h | 96 +- .../include/grpc/impl/codegen/port_platform.h | 464 ++- .../grpc/impl/codegen/propagation_bits.h | 45 +- .../grpc/include/grpc/impl/codegen/slice.h | 223 +- .../include/grpc/impl/codegen/slice_buffer.h | 105 - .../grpc/include/grpc/impl/codegen/status.h | 76 +- .../grpc/include/grpc/impl/codegen/sync.h | 295 +- .../include/grpc/impl/codegen/sync_custom.h | 38 + .../include/grpc/impl/codegen/sync_generic.h | 49 +- .../include/grpc/impl/codegen/sync_posix.h | 37 +- .../include/grpc/impl/codegen/sync_win32.h | 49 - .../include/grpc/impl/codegen/sync_windows.h | 36 + .../grpc/include/grpc/impl/codegen/time.h | 132 - .../grpc/include/grpc/load_reporting.h | 48 + .../grpc/include/grpc/module.modulemap | 76 + third_party/grpc/include/grpc/slice.h | 172 + third_party/grpc/include/grpc/slice_buffer.h | 84 + third_party/grpc/include/grpc/status.h | 37 +- third_party/grpc/include/grpc/support/alloc.h | 81 +- third_party/grpc/include/grpc/support/atm.h | 37 +- .../include/grpc/support/atm_gcc_atomic.h | 43 +- .../grpc/include/grpc/support/atm_gcc_sync.h | 37 +- .../grpc/include/grpc/support/atm_win32.h | 39 - .../grpc/include/grpc/support/atm_windows.h | 26 + third_party/grpc/include/grpc/support/avl.h | 91 - .../grpc/include/grpc/support/cmdline.h | 103 - third_party/grpc/include/grpc/support/cpu.h | 45 +- .../grpc/include/grpc/support/histogram.h | 79 - .../grpc/include/grpc/support/host_port.h | 66 - third_party/grpc/include/grpc/support/log.h | 37 +- .../grpc/include/grpc/support/log_win32.h | 51 - .../grpc/include/grpc/support/log_windows.h | 38 + .../grpc/include/grpc/support/port_platform.h | 35 +- third_party/grpc/include/grpc/support/slice.h | 39 - .../grpc/include/grpc/support/slice_buffer.h | 39 - .../grpc/include/grpc/support/string_util.h | 50 +- .../grpc/include/grpc/support/subprocess.h | 59 - third_party/grpc/include/grpc/support/sync.h | 293 +- .../grpc/include/grpc/support/sync_custom.h | 26 + .../grpc/include/grpc/support/sync_generic.h | 37 +- .../grpc/include/grpc/support/sync_posix.h | 37 +- .../grpc/include/grpc/support/sync_win32.h | 39 - .../grpc/include/grpc/support/sync_windows.h | 26 + third_party/grpc/include/grpc/support/thd.h | 91 - .../grpc/include/grpc/support/thd_id.h | 44 + third_party/grpc/include/grpc/support/time.h | 105 +- third_party/grpc/include/grpc/support/tls.h | 77 - .../grpc/include/grpc/support/tls_gcc.h | 56 - .../grpc/include/grpc/support/tls_msvc.h | 56 - .../grpc/include/grpc/support/tls_pthread.h | 60 - .../grpc/include/grpc/support/useful.h | 75 - .../include/grpc/support/workaround_list.h | 31 + third_party/grpc/include/grpcpp/alarm.h | 29 + third_party/grpc/include/grpcpp/alarm_impl.h | 116 + third_party/grpc/include/grpcpp/channel.h | 114 + .../grpc/include/grpcpp/client_context.h | 39 + .../grpc/include/grpcpp/completion_queue.h | 24 + .../grpc/include/grpcpp/create_channel.h | 79 + .../include/grpcpp/create_channel_posix.h | 69 + .../grpcpp/ext/channelz_service_plugin.h | 41 + ...alth_check_service_server_builder_option.h | 47 + .../ext/proto_server_reflection_plugin.h | 54 + .../grpcpp/ext/server_load_reporting.h | 53 + .../grpcpp/generic/async_generic_service.h | 24 + .../include/grpcpp/generic/generic_stub.h | 99 + third_party/grpc/include/grpcpp/grpcpp.h | 68 + .../grpcpp/health_check_service_interface.h | 58 + .../include/{grpc++ => grpcpp}/impl/README.md | 0 third_party/grpc/include/grpcpp/impl/call.h | 24 + .../grpcpp/impl/channel_argument_option.h | 37 + .../include/grpcpp/impl/client_unary_call.h | 24 + .../impl/codegen/async_generic_service.h | 81 + .../grpcpp/impl/codegen/async_stream.h | 1134 ++++++ .../grpcpp/impl/codegen/async_unary_call.h | 317 ++ .../include/grpcpp/impl/codegen/byte_buffer.h | 224 ++ .../grpc/include/grpcpp/impl/codegen/call.h | 94 + .../include/grpcpp/impl/codegen/call_hook.h | 39 + .../include/grpcpp/impl/codegen/call_op_set.h | 923 +++++ .../impl/codegen/call_op_set_interface.h | 59 + .../grpcpp/impl/codegen/callback_common.h | 217 ++ .../grpcpp/impl/codegen/channel_interface.h | 164 + .../grpcpp/impl/codegen/client_callback.h | 750 ++++ .../grpcpp/impl/codegen/client_context.h | 485 +++ .../grpcpp/impl/codegen/client_interceptor.h | 189 + .../grpcpp/impl/codegen/client_unary_call.h | 94 + .../grpcpp/impl/codegen/completion_queue.h | 403 +++ .../impl/codegen/completion_queue_tag.h | 54 + .../grpc/include/grpcpp/impl/codegen/config.h | 41 + .../grpcpp/impl/codegen/config_protobuf.h | 107 + .../grpcpp/impl/codegen/core_codegen.h | 123 + .../impl/codegen/core_codegen_interface.h | 159 + .../grpcpp/impl/codegen/create_auth_context.h | 33 + .../grpcpp/impl/codegen/grpc_library.h | 64 + .../grpcpp/impl/codegen/intercepted_channel.h | 80 + .../include/grpcpp/impl/codegen/interceptor.h | 175 + .../grpcpp/impl/codegen/interceptor_common.h | 458 +++ .../grpcpp/impl/codegen/metadata_map.h | 105 + .../grpcpp/impl/codegen/method_handler_impl.h | 347 ++ .../grpcpp/impl/codegen/proto_buffer_reader.h | 151 + .../grpcpp/impl/codegen/proto_buffer_writer.h | 167 + .../include/grpcpp/impl/codegen/proto_utils.h | 121 + .../include/grpcpp/impl/codegen/rpc_method.h | 61 + .../grpcpp/impl/codegen/rpc_service_method.h | 150 + .../impl/codegen/security/auth_context.h | 95 + .../impl/codegen/serialization_traits.h | 62 + .../grpcpp/impl/codegen/server_callback.h | 890 +++++ .../grpcpp/impl/codegen/server_context.h | 370 ++ .../grpcpp/impl/codegen/server_interceptor.h | 143 + .../grpcpp/impl/codegen/server_interface.h | 362 ++ .../grpcpp/impl/codegen/service_type.h | 239 ++ .../grpc/include/grpcpp/impl/codegen/slice.h | 143 + .../grpc/include/grpcpp/impl/codegen/status.h | 133 + .../grpcpp/impl/codegen/status_code_enum.h | 142 + .../include/grpcpp/impl/codegen/string_ref.h | 146 + .../grpcpp/impl/codegen/stub_options.h | 29 + .../include/grpcpp/impl/codegen/sync_stream.h | 934 +++++ .../grpc/include/grpcpp/impl/codegen/time.h | 89 + .../grpc/include/grpcpp/impl/grpc_library.h | 61 + .../include/grpcpp/impl/method_handler_impl.h | 24 + .../grpc/include/grpcpp/impl/rpc_method.h | 24 + .../include/grpcpp/impl/rpc_service_method.h | 24 + .../grpcpp/impl/serialization_traits.h | 24 + .../grpcpp/impl/server_builder_option.h | 43 + .../grpcpp/impl/server_builder_plugin.h | 66 + .../include/grpcpp/impl/server_initializer.h | 55 + .../grpc/include/grpcpp/impl/service_type.h | 24 + .../grpc/include/grpcpp/impl/sync_cxx11.h | 24 + .../grpc/include/grpcpp/impl/sync_no_cxx11.h | 24 + third_party/grpc/include/grpcpp/opencensus.h | 48 + .../grpc/include/grpcpp/resource_quota.h | 68 + .../include/grpcpp/security/auth_context.h | 24 + .../grpcpp/security/auth_metadata_processor.h | 61 + .../include/grpcpp/security/credentials.h | 277 ++ .../grpcpp/security/server_credentials.h | 107 + third_party/grpc/include/grpcpp/server.h | 288 ++ .../grpc/include/grpcpp/server_builder.h | 330 ++ .../grpc/include/grpcpp/server_context.h | 24 + .../grpc/include/grpcpp/server_posix.h | 42 + .../include/grpcpp/support/async_stream.h | 24 + .../include/grpcpp/support/async_unary_call.h | 24 + .../grpc/include/grpcpp/support/byte_buffer.h | 31 + .../grpcpp/support/channel_arguments.h | 148 + .../include/grpcpp/support/client_callback.h | 24 + .../grpcpp/support/client_interceptor.h | 24 + .../grpc/include/grpcpp/support/config.h | 24 + .../include/grpcpp/support/error_details.h | 46 + .../grpc/include/grpcpp/support/interceptor.h | 24 + .../grpcpp/support/proto_buffer_reader.h | 24 + .../grpcpp/support/proto_buffer_writer.h | 24 + .../include/grpcpp/support/server_callback.h | 24 + .../grpcpp/support/server_interceptor.h | 24 + .../grpc/include/grpcpp/support/slice.h | 26 + .../grpc/include/grpcpp/support/status.h | 24 + .../include/grpcpp/support/status_code_enum.h | 24 + .../grpc/include/grpcpp/support/string_ref.h | 24 + .../include/grpcpp/support/stub_options.h | 24 + .../grpc/include/grpcpp/support/sync_stream.h | 24 + .../grpc/include/grpcpp/support/time.h | 24 + .../grpc/include/grpcpp/test/mock_stream.h | 148 + .../grpcpp/test/server_context_test_spouse.h | 65 + third_party/grpc/src/compiler/README.md | 4 + third_party/grpc/src/compiler/config.h | 78 +- .../grpc/src/compiler/cpp_generator.cc | 2055 ++++++++--- third_party/grpc/src/compiler/cpp_generator.h | 128 +- .../grpc/src/compiler/cpp_generator_helpers.h | 53 +- third_party/grpc/src/compiler/cpp_plugin.cc | 124 +- .../grpc/src/compiler/csharp_generator.cc | 652 ++-- .../grpc/src/compiler/csharp_generator.h | 39 +- .../src/compiler/csharp_generator_helpers.h | 50 +- .../grpc/src/compiler/csharp_plugin.cc | 67 +- .../grpc/src/compiler/generator_helpers.h | 181 +- .../grpc/src/compiler/node_generator.cc | 278 ++ .../grpc/src/compiler/node_generator.h | 37 + .../src/compiler/node_generator_helpers.h | 42 + third_party/grpc/src/compiler/node_plugin.cc | 78 + .../src/compiler/objective_c_generator.cc | 236 +- .../grpc/src/compiler/objective_c_generator.h | 52 +- .../compiler/objective_c_generator_helpers.h | 84 +- .../grpc/src/compiler/objective_c_plugin.cc | 159 +- .../grpc/src/compiler/php_generator.cc | 184 + third_party/grpc/src/compiler/php_generator.h | 32 + .../grpc/src/compiler/php_generator_helpers.h | 76 + third_party/grpc/src/compiler/php_plugin.cc | 77 + .../grpc/src/compiler/protobuf_plugin.h | 196 + .../grpc/src/compiler/python_generator.cc | 916 +++-- .../grpc/src/compiler/python_generator.h | 46 +- .../src/compiler/python_generator_helpers.h | 127 + .../grpc/src/compiler/python_plugin.cc | 37 +- .../src/compiler/python_private_generator.h | 84 + .../grpc/src/compiler/ruby_generator.cc | 159 +- .../grpc/src/compiler/ruby_generator.h | 37 +- .../src/compiler/ruby_generator_helpers-inl.h | 53 +- .../src/compiler/ruby_generator_map-inl.h | 42 +- .../src/compiler/ruby_generator_string-inl.h | 78 +- third_party/grpc/src/compiler/ruby_plugin.cc | 45 +- .../grpc/src/compiler/schema_interface.h | 112 + third_party/grpc/src/core/README.md | 8 +- third_party/grpc/src/core/census/README.md | 76 - .../grpc/src/core/census/aggregation.h | 66 - third_party/grpc/src/core/census/context.c | 530 --- .../grpc/src/core/census/grpc_context.c | 53 - .../grpc/src/core/census/grpc_filter.c | 184 - .../grpc/src/core/census/grpc_filter.h | 44 - third_party/grpc/src/core/census/initialize.c | 54 - third_party/grpc/src/core/census/operation.c | 63 - .../grpc/src/core/census/placeholders.c | 109 - .../grpc/src/core/census/rpc_metric_id.h | 51 - third_party/grpc/src/core/census/tracing.c | 45 - .../grpc/src/core/channel/channel_args.c | 210 -- .../grpc/src/core/channel/channel_args.h | 88 - .../grpc/src/core/channel/channel_stack.c | 262 -- .../grpc/src/core/channel/channel_stack.h | 260 -- .../grpc/src/core/channel/client_channel.c | 528 --- .../grpc/src/core/channel/client_channel.h | 63 - .../grpc/src/core/channel/client_uchannel.c | 243 -- .../grpc/src/core/channel/client_uchannel.h | 60 - .../grpc/src/core/channel/compress_filter.c | 297 -- .../grpc/src/core/channel/compress_filter.h | 65 - .../grpc/src/core/channel/connected_channel.c | 167 - .../grpc/src/core/channel/connected_channel.h | 51 - third_party/grpc/src/core/channel/context.h | 49 - .../src/core/channel/http_client_filter.c | 248 -- .../src/core/channel/http_client_filter.h | 44 - .../src/core/channel/http_server_filter.c | 233 -- .../src/core/channel/http_server_filter.h | 42 - .../src/core/channel/subchannel_call_holder.c | 261 -- .../src/core/channel/subchannel_call_holder.h | 98 - .../src/core/client_config/client_config.c | 72 - .../src/core/client_config/client_config.h | 53 - .../grpc/src/core/client_config/connector.c | 54 - .../grpc/src/core/client_config/connector.h | 95 - .../default_initial_connect_string.c | 39 - .../client_config/initial_connect_string.c | 53 - .../client_config/initial_connect_string.h | 50 - .../client_config/lb_policies/pick_first.c | 416 --- .../client_config/lb_policies/pick_first.h | 43 - .../client_config/lb_policies/round_robin.c | 537 --- .../client_config/lb_policies/round_robin.h | 46 - .../grpc/src/core/client_config/lb_policy.c | 134 - .../grpc/src/core/client_config/lb_policy.h | 143 - .../core/client_config/lb_policy_factory.c | 48 - .../core/client_config/lb_policy_factory.h | 73 - .../core/client_config/lb_policy_registry.c | 88 - .../core/client_config/lb_policy_registry.h | 54 - .../grpc/src/core/client_config/resolver.c | 82 - .../grpc/src/core/client_config/resolver.h | 94 - .../src/core/client_config/resolver_factory.c | 55 - .../src/core/client_config/resolver_factory.h | 82 - .../core/client_config/resolver_registry.c | 137 - .../core/client_config/resolver_registry.h | 65 - .../client_config/resolvers/dns_resolver.c | 257 -- .../client_config/resolvers/dns_resolver.h | 42 - .../resolvers/sockaddr_resolver.c | 391 -- .../resolvers/sockaddr_resolver.h | 50 - .../resolvers/zookeeper_resolver.c | 520 --- .../resolvers/zookeeper_resolver.h | 42 - .../grpc/src/core/client_config/subchannel.c | 708 ---- .../grpc/src/core/client_config/subchannel.h | 165 - .../core/client_config/subchannel_factory.c | 49 - .../core/client_config/subchannel_factory.h | 66 - .../grpc/src/core/client_config/uri_parser.c | 242 -- .../grpc/src/core/client_config/uri_parser.h | 51 - .../grpc/src/core/compression/algorithm.c | 166 - .../src/core/compression/algorithm_metadata.h | 53 - .../src/core/compression/message_compress.c | 198 - .../src/core/compression/message_compress.h | 52 - third_party/grpc/src/core/debug/trace.c | 136 - third_party/grpc/src/core/debug/trace.h | 43 - third_party/grpc/src/core/ext/README.md | 5 + .../core/ext/filters/census/grpc_context.cc | 38 + .../core/ext/filters/client_channel/OWNERS | 4 + .../filters/client_channel}/README.md | 41 +- .../filters/client_channel/backup_poller.cc | 174 + .../filters/client_channel/backup_poller.h | 35 + .../client_channel/channel_connectivity.cc | 248 ++ .../filters/client_channel/client_channel.cc | 2730 ++++++++++++++ .../filters/client_channel/client_channel.h | 66 + .../client_channel/client_channel_channelz.cc | 186 + .../client_channel/client_channel_channelz.h | 98 + .../client_channel/client_channel_factory.cc | 67 + .../client_channel/client_channel_factory.h | 74 + .../client_channel/client_channel_plugin.cc | 71 + .../ext/filters/client_channel/connector.cc | 41 + .../ext/filters/client_channel/connector.h | 76 + .../filters/client_channel/health/health.pb.c | 23 + .../filters/client_channel/health/health.pb.h | 73 + .../health/health_check_client.cc | 650 ++++ .../health/health_check_client.h | 172 + .../client_channel/http_connect_handshaker.cc | 371 ++ .../client_channel/http_connect_handshaker.h | 34 + .../ext/filters/client_channel/http_proxy.cc | 212 ++ .../ext/filters/client_channel/http_proxy.h | 24 + .../ext/filters/client_channel/lb_policy.cc | 59 + .../ext/filters/client_channel/lb_policy.h | 215 ++ .../grpclb/client_load_reporting_filter.cc | 144 + .../grpclb/client_load_reporting_filter.h | 29 + .../client_channel/lb_policy/grpclb/grpclb.cc | 1867 ++++++++++ .../client_channel/lb_policy/grpclb/grpclb.h | 36 + .../lb_policy/grpclb/grpclb_channel.cc | 26 + .../lb_policy/grpclb/grpclb_channel.h | 36 + .../lb_policy/grpclb/grpclb_channel_secure.cc | 105 + .../lb_policy/grpclb/grpclb_client_stats.cc | 86 + .../lb_policy/grpclb/grpclb_client_stats.h | 72 + .../lb_policy/grpclb/load_balancer_api.cc | 307 ++ .../lb_policy/grpclb/load_balancer_api.h | 90 + .../grpc/lb/v1/google/protobuf/duration.pb.c | 19 + .../grpc/lb/v1/google/protobuf/duration.pb.h | 54 + .../grpc/lb/v1/google/protobuf/timestamp.pb.c | 19 + .../grpc/lb/v1/google/protobuf/timestamp.pb.h | 54 + .../proto/grpc/lb/v1/load_balancer.pb.c | 89 + .../proto/grpc/lb/v1/load_balancer.pb.h | 164 + .../lb_policy/pick_first/pick_first.cc | 643 ++++ .../lb_policy/round_robin/round_robin.cc | 721 ++++ .../lb_policy/subchannel_list.h | 593 +++ .../client_channel/lb_policy/xds/xds.cc | 1676 +++++++++ .../client_channel/lb_policy/xds/xds.h | 36 + .../lb_policy/xds/xds_channel.cc | 26 + .../lb_policy/xds/xds_channel.h | 36 + .../lb_policy/xds/xds_channel_secure.cc | 104 + .../lb_policy/xds/xds_client_stats.cc | 85 + .../lb_policy/xds/xds_client_stats.h | 72 + .../lb_policy/xds/xds_load_balancer_api.cc | 307 ++ .../lb_policy/xds/xds_load_balancer_api.h | 89 + .../client_channel/lb_policy_factory.h | 47 + .../client_channel/lb_policy_registry.cc | 102 + .../client_channel/lb_policy_registry.h | 58 + .../filters/client_channel/parse_address.cc | 215 ++ .../filters/client_channel/parse_address.h | 53 + .../filters/client_channel/proxy_mapper.cc | 48 + .../ext/filters/client_channel/proxy_mapper.h | 74 + .../client_channel/proxy_mapper_registry.cc | 122 + .../client_channel/proxy_mapper_registry.h | 44 + .../filters/client_channel/request_routing.cc | 936 +++++ .../filters/client_channel/request_routing.h | 177 + .../ext/filters/client_channel/resolver.cc | 35 + .../ext/filters/client_channel/resolver.h | 133 + .../filters/client_channel/resolver/README.md | 4 + .../resolver/dns/c_ares/dns_resolver_ares.cc | 516 +++ .../dns/c_ares/grpc_ares_ev_driver.cc | 368 ++ .../resolver/dns/c_ares/grpc_ares_ev_driver.h | 107 + .../dns/c_ares/grpc_ares_ev_driver_posix.cc | 106 + .../dns/c_ares/grpc_ares_ev_driver_windows.cc | 537 +++ .../resolver/dns/c_ares/grpc_ares_wrapper.cc | 702 ++++ .../resolver/dns/c_ares/grpc_ares_wrapper.h | 101 + .../dns/c_ares/grpc_ares_wrapper_fallback.cc | 65 + .../dns/c_ares/grpc_ares_wrapper_posix.cc | 35 + .../dns/c_ares/grpc_ares_wrapper_windows.cc | 97 + .../resolver/dns/native/README.md | 2 + .../resolver/dns/native/dns_resolver.cc | 343 ++ .../resolver/fake/fake_resolver.cc | 297 ++ .../resolver/fake/fake_resolver.h | 87 + .../resolver/sockaddr/README.md | 1 + .../resolver/sockaddr/sockaddr_resolver.cc | 207 ++ .../filters/client_channel/resolver_factory.h | 71 + .../client_channel/resolver_registry.cc | 178 + .../client_channel/resolver_registry.h | 83 + .../client_channel/resolver_result_parsing.cc | 377 ++ .../client_channel/resolver_result_parsing.h | 142 + .../filters/client_channel/retry_throttle.cc | 191 + .../filters/client_channel/retry_throttle.h | 81 + .../filters/client_channel/server_address.cc | 103 + .../filters/client_channel/server_address.h | 108 + .../ext/filters/client_channel/subchannel.cc | 1156 ++++++ .../ext/filters/client_channel/subchannel.h | 218 ++ .../client_channel/subchannel_index.cc | 248 ++ .../filters/client_channel/subchannel_index.h | 76 + .../ext/filters/deadline/deadline_filter.cc | 385 ++ .../ext/filters/deadline/deadline_filter.h | 89 + .../filters/http/client/http_client_filter.cc | 598 ++++ .../filters/http/client/http_client_filter.h | 31 + .../filters/http/client_authority_filter.cc | 156 + .../filters/http/client_authority_filter.h | 34 + .../ext/filters/http/http_filters_plugin.cc | 89 + .../message_compress_filter.cc | 511 +++ .../message_compress_filter.h | 53 + .../filters/http/server/http_server_filter.cc | 495 +++ .../filters/http/server/http_server_filter.h | 29 + .../registered_opencensus_objects.h | 117 + .../server_load_reporting_filter.cc | 363 ++ .../server_load_reporting_filter.h | 120 + .../ext/filters/max_age/max_age_filter.cc | 542 +++ .../core/ext/filters/max_age/max_age_filter.h | 26 + .../message_size/message_size_filter.cc | 385 ++ .../message_size/message_size_filter.h | 26 + .../workaround_cronet_compression_filter.cc | 208 ++ .../workaround_cronet_compression_filter.h | 27 + .../filters/workarounds/workaround_utils.cc | 53 + .../filters/workarounds/workaround_utils.h | 39 + .../grpc/src/core/ext/transport/README.md | 1 + .../src/core/ext/transport/chttp2/README.md | 1 + .../core/ext/transport/chttp2/alpn/alpn.cc | 44 + .../src/core/ext/transport/chttp2/alpn/alpn.h | 36 + .../ext/transport/chttp2/client/authority.cc | 42 + .../ext/transport/chttp2/client/authority.h | 36 + .../chttp2/client/chttp2_connector.cc | 239 ++ .../chttp2/client/chttp2_connector.h | 28 + .../chttp2/client/insecure/README.md | 1 + .../chttp2/client/insecure/channel_create.cc | 110 + .../client/insecure/channel_create_posix.cc | 79 + .../transport/chttp2/client/secure/README.md | 1 + .../client/secure/secure_channel_create.cc | 229 ++ .../transport/chttp2/server/chttp2_server.cc | 403 +++ .../transport/chttp2/server/chttp2_server.h | 33 + .../chttp2/server/insecure/README.md | 1 + .../chttp2/server/insecure/server_chttp2.cc | 45 + .../server/insecure/server_chttp2_posix.cc | 75 + .../transport/chttp2/server/secure/README.md | 1 + .../server/secure/server_secure_chttp2.cc | 86 + .../ext/transport/chttp2/transport/README.md | 4 + .../transport/chttp2/transport/bin_decoder.cc | 250 ++ .../transport/chttp2/transport/bin_decoder.h | 56 + .../transport/chttp2/transport/bin_encoder.cc | 231 ++ .../transport/chttp2/transport/bin_encoder.h | 41 + .../chttp2/transport/chttp2_plugin.cc | 35 + .../chttp2/transport/chttp2_transport.cc | 3181 +++++++++++++++++ .../chttp2/transport/chttp2_transport.h | 50 + .../chttp2/transport/context_list.cc | 67 + .../transport/chttp2/transport/context_list.h | 53 + .../chttp2/transport/flow_control.cc | 408 +++ .../transport/chttp2/transport/flow_control.h | 482 +++ .../ext/transport/chttp2/transport/frame.h | 47 + .../transport/chttp2/transport/frame_data.cc | 312 ++ .../transport/chttp2/transport/frame_data.h | 82 + .../chttp2/transport/frame_goaway.cc | 186 + .../transport/chttp2/transport/frame_goaway.h | 62 + .../transport/chttp2/transport/frame_ping.cc | 131 + .../transport/chttp2/transport/frame_ping.h | 45 + .../chttp2/transport/frame_rst_stream.cc | 112 + .../chttp2/transport/frame_rst_stream.h | 43 + .../chttp2/transport/frame_settings.cc | 238 ++ .../chttp2/transport/frame_settings.h | 60 + .../chttp2/transport/frame_window_update.cc | 122 + .../chttp2/transport/frame_window_update.h | 45 + .../chttp2/transport/hpack_encoder.cc | 717 ++++ .../chttp2/transport/hpack_encoder.h | 96 + .../chttp2/transport/hpack_parser.cc} | 1043 +++--- .../transport/chttp2/transport/hpack_parser.h | 109 + .../chttp2/transport/hpack_table.cc} | 190 +- .../transport/chttp2/transport/hpack_table.h | 104 + .../chttp2/transport}/hpack_tables.txt | 0 .../chttp2/transport/http2_settings.cc | 62 + .../chttp2/transport/http2_settings.h | 62 + .../transport/chttp2/transport/huffsyms.cc | 92 + .../ext/transport/chttp2/transport/huffsyms.h | 33 + .../chttp2/transport/incoming_metadata.cc | 61 + .../chttp2/transport/incoming_metadata.h | 52 + .../ext/transport/chttp2/transport/internal.h | 851 +++++ .../ext/transport/chttp2/transport/parsing.cc | 780 ++++ .../chttp2/transport/stream_lists.cc | 216 ++ .../transport/chttp2/transport/stream_map.cc | 167 + .../transport/chttp2/transport/stream_map.h | 68 + .../ext/transport/chttp2/transport/varint.cc | 56 + .../transport/chttp2/transport}/varint.h | 41 +- .../ext/transport/chttp2/transport/writing.cc | 657 ++++ .../client/secure/cronet_channel_create.cc | 54 + .../cronet/transport/cronet_api_dummy.cc | 82 + .../cronet/transport/cronet_transport.cc | 1491 ++++++++ .../cronet/transport/cronet_transport.h | 30 + .../ext/transport/inproc/inproc_plugin.cc | 28 + .../ext/transport/inproc/inproc_transport.cc | 1258 +++++++ .../ext/transport/inproc/inproc_transport.h | 35 + .../grpc/src/core/httpcli/format_request.c | 120 - .../grpc/src/core/httpcli/format_request.h | 45 - third_party/grpc/src/core/httpcli/httpcli.c | 286 -- third_party/grpc/src/core/httpcli/httpcli.h | 162 - .../core/httpcli/httpcli_security_connector.c | 188 - third_party/grpc/src/core/httpcli/parser.c | 211 -- third_party/grpc/src/core/httpcli/parser.h | 64 - third_party/grpc/src/core/iomgr/closure.c | 98 - third_party/grpc/src/core/iomgr/closure.h | 98 - third_party/grpc/src/core/iomgr/endpoint.c | 67 - third_party/grpc/src/core/iomgr/endpoint.h | 102 - .../grpc/src/core/iomgr/endpoint_pair.h | 47 - .../grpc/src/core/iomgr/endpoint_pair_posix.c | 82 - .../src/core/iomgr/endpoint_pair_windows.c | 97 - third_party/grpc/src/core/iomgr/exec_ctx.c | 76 - third_party/grpc/src/core/iomgr/exec_ctx.h | 85 - third_party/grpc/src/core/iomgr/executor.c | 143 - third_party/grpc/src/core/iomgr/executor.h | 53 - third_party/grpc/src/core/iomgr/fd_posix.c | 449 --- third_party/grpc/src/core/iomgr/fd_posix.h | 192 - .../grpc/src/core/iomgr/iocp_windows.c | 204 -- .../grpc/src/core/iomgr/iocp_windows.h | 56 - third_party/grpc/src/core/iomgr/iomgr.c | 156 - third_party/grpc/src/core/iomgr/iomgr.h | 43 - .../grpc/src/core/iomgr/iomgr_internal.h | 58 - third_party/grpc/src/core/iomgr/iomgr_posix.c | 52 - third_party/grpc/src/core/iomgr/iomgr_posix.h | 39 - .../grpc/src/core/iomgr/iomgr_windows.c | 73 - third_party/grpc/src/core/iomgr/pollset.h | 95 - .../iomgr/pollset_multipoller_with_epoll.c | 323 -- .../pollset_multipoller_with_poll_posix.c | 229 -- .../grpc/src/core/iomgr/pollset_posix.c | 638 ---- .../grpc/src/core/iomgr/pollset_posix.h | 149 - third_party/grpc/src/core/iomgr/pollset_set.h | 67 - .../grpc/src/core/iomgr/pollset_set_posix.c | 182 - .../grpc/src/core/iomgr/pollset_set_posix.h | 61 - .../grpc/src/core/iomgr/pollset_set_windows.c | 60 - .../grpc/src/core/iomgr/pollset_set_windows.h | 39 - .../grpc/src/core/iomgr/pollset_windows.c | 237 -- .../grpc/src/core/iomgr/pollset_windows.h | 79 - .../grpc/src/core/iomgr/resolve_address.h | 72 - .../src/core/iomgr/resolve_address_posix.c | 183 - .../src/core/iomgr/resolve_address_windows.c | 166 - third_party/grpc/src/core/iomgr/sockaddr.h | 47 - .../grpc/src/core/iomgr/sockaddr_posix.h | 44 - .../grpc/src/core/iomgr/sockaddr_utils.c | 234 -- .../grpc/src/core/iomgr/sockaddr_utils.h | 89 - .../grpc/src/core/iomgr/sockaddr_win32.h | 41 - .../core/iomgr/socket_utils_common_posix.c | 208 -- .../grpc/src/core/iomgr/socket_utils_linux.c | 51 - .../grpc/src/core/iomgr/socket_utils_posix.c | 70 - .../grpc/src/core/iomgr/socket_utils_posix.h | 113 - .../grpc/src/core/iomgr/socket_windows.c | 98 - third_party/grpc/src/core/iomgr/tcp_client.h | 53 - .../grpc/src/core/iomgr/tcp_client_posix.c | 304 -- .../grpc/src/core/iomgr/tcp_client_windows.c | 221 -- third_party/grpc/src/core/iomgr/tcp_posix.c | 491 --- third_party/grpc/src/core/iomgr/tcp_posix.h | 71 - third_party/grpc/src/core/iomgr/tcp_server.h | 103 - .../grpc/src/core/iomgr/tcp_server_posix.c | 644 ---- .../grpc/src/core/iomgr/tcp_server_windows.c | 558 --- third_party/grpc/src/core/iomgr/tcp_windows.c | 402 --- third_party/grpc/src/core/iomgr/tcp_windows.h | 57 - third_party/grpc/src/core/iomgr/timer.c | 345 -- third_party/grpc/src/core/iomgr/timer.h | 89 - third_party/grpc/src/core/iomgr/timer_heap.c | 148 - third_party/grpc/src/core/iomgr/timer_heap.h | 57 - .../grpc/src/core/iomgr/timer_internal.h | 61 - third_party/grpc/src/core/iomgr/udp_server.c | 429 --- third_party/grpc/src/core/iomgr/udp_server.h | 75 - .../grpc/src/core/iomgr/wakeup_fd_eventfd.c | 85 - .../grpc/src/core/iomgr/wakeup_fd_nospecial.c | 51 - .../grpc/src/core/iomgr/wakeup_fd_pipe.c | 97 - .../grpc/src/core/iomgr/wakeup_fd_pipe.h | 41 - .../grpc/src/core/iomgr/wakeup_fd_posix.c | 72 - third_party/grpc/src/core/iomgr/workqueue.h | 83 - .../grpc/src/core/iomgr/workqueue_posix.c | 143 - .../grpc/src/core/iomgr/workqueue_posix.h | 51 - .../grpc/src/core/iomgr/workqueue_windows.c | 40 - .../grpc/src/core/iomgr/workqueue_windows.h | 37 - third_party/grpc/src/core/json/json.c | 64 - third_party/grpc/src/core/json/json.h | 88 - third_party/grpc/src/core/json/json_common.h | 49 - third_party/grpc/src/core/json/json_writer.h | 97 - third_party/grpc/src/core/lib/README.md | 6 + third_party/grpc/src/core/lib/avl/avl.cc | 306 ++ third_party/grpc/src/core/lib/avl/avl.h | 94 + .../grpc/src/core/lib/backoff/backoff.cc | 78 + .../grpc/src/core/lib/backoff/backoff.h | 89 + .../grpc/src/core/lib/channel/README.md | 4 + .../grpc/src/core/lib/channel/channel_args.cc | 441 +++ .../grpc/src/core/lib/channel/channel_args.h | 131 + .../src/core/lib/channel/channel_stack.cc | 252 ++ .../grpc/src/core/lib/channel/channel_stack.h | 280 ++ .../core/lib/channel/channel_stack_builder.cc | 323 ++ .../core/lib/channel/channel_stack_builder.h | 166 + .../src/core/lib/channel/channel_trace.cc | 203 ++ .../grpc/src/core/lib/channel/channel_trace.h | 134 + .../grpc/src/core/lib/channel/channelz.cc | 460 +++ .../grpc/src/core/lib/channel/channelz.h | 296 ++ .../src/core/lib/channel/channelz_registry.cc | 324 ++ .../src/core/lib/channel/channelz_registry.h | 115 + .../src/core/lib/channel/connected_channel.cc | 243 ++ .../src/core/lib/channel/connected_channel.h | 34 + .../grpc/src/core/lib/channel/context.h | 49 + .../grpc/src/core/lib/channel/handshaker.cc | 331 ++ .../grpc/src/core/lib/channel/handshaker.h | 169 + .../core/lib/channel/handshaker_factory.cc | 42 + .../src/core/lib/channel/handshaker_factory.h | 52 + .../core/lib/channel/handshaker_registry.cc | 101 + .../core/lib/channel/handshaker_registry.h | 49 + .../grpc/src/core/lib/channel/status_util.cc | 100 + .../grpc/src/core/lib/channel/status_util.h | 58 + .../core/lib/compression/algorithm_metadata.h | 61 + .../src/core/lib/compression/compression.cc | 174 + .../lib/compression/compression_internal.cc | 276 ++ .../lib/compression/compression_internal.h | 88 + .../core/lib/compression/message_compress.cc | 187 + .../core/lib/compression/message_compress.h | 40 + .../lib/compression/stream_compression.cc | 79 + .../core/lib/compression/stream_compression.h | 116 + .../compression/stream_compression_gzip.cc | 230 ++ .../lib/compression/stream_compression_gzip.h | 28 + .../stream_compression_identity.cc | 94 + .../compression/stream_compression_identity.h | 29 + third_party/grpc/src/core/lib/debug/stats.cc | 178 + third_party/grpc/src/core/lib/debug/stats.h | 68 + .../grpc/src/core/lib/debug/stats_data.cc | 687 ++++ .../grpc/src/core/lib/debug/stats_data.h | 555 +++ .../grpc/src/core/lib/debug/stats_data.yaml | 300 ++ .../core/lib/debug/stats_data_bq_schema.sql | 98 + third_party/grpc/src/core/lib/debug/trace.cc | 148 + third_party/grpc/src/core/lib/debug/trace.h | 117 + third_party/grpc/src/core/lib/gpr/README.md | 8 + third_party/grpc/src/core/lib/gpr/alloc.cc | 99 + third_party/grpc/src/core/lib/gpr/alloc.h | 28 + third_party/grpc/src/core/lib/gpr/arena.cc | 192 + third_party/grpc/src/core/lib/gpr/arena.h | 43 + third_party/grpc/src/core/lib/gpr/atm.cc | 35 + .../grpc/src/core/lib/gpr/cpu_iphone.cc | 36 + .../grpc/src/core/lib/gpr/cpu_linux.cc | 82 + .../grpc/src/core/lib/gpr/cpu_posix.cc | 81 + .../grpc/src/core/lib/gpr/cpu_windows.cc | 33 + third_party/grpc/src/core/lib/gpr/env.h | 43 + .../grpc/src/core/lib/gpr/env_linux.cc | 82 + .../grpc/src/core/lib/gpr/env_posix.cc | 47 + .../grpc/src/core/lib/gpr/env_windows.cc | 72 + .../grpc/src/core/lib/gpr/host_port.cc | 98 + third_party/grpc/src/core/lib/gpr/host_port.h | 43 + third_party/grpc/src/core/lib/gpr/log.cc | 102 + .../grpc/src/core/lib/gpr/log_android.cc | 76 + .../grpc/src/core/lib/gpr/log_linux.cc | 97 + .../grpc/src/core/lib/gpr/log_posix.cc | 94 + .../grpc/src/core/lib/gpr/log_windows.cc | 102 + third_party/grpc/src/core/lib/gpr/mpscq.cc | 117 + third_party/grpc/src/core/lib/gpr/mpscq.h | 88 + .../grpc/src/core/lib/gpr/murmur_hash.cc | 80 + .../grpc/src/core/lib/gpr/murmur_hash.h | 29 + third_party/grpc/src/core/lib/gpr/spinlock.h | 46 + third_party/grpc/src/core/lib/gpr/string.cc | 347 ++ third_party/grpc/src/core/lib/gpr/string.h | 119 + .../grpc/src/core/lib/gpr/string_posix.cc | 72 + .../src/core/lib/gpr/string_util_windows.cc | 82 + .../grpc/src/core/lib/gpr/string_windows.cc | 69 + .../grpc/src/core/lib/gpr/string_windows.h | 32 + third_party/grpc/src/core/lib/gpr/sync.cc | 124 + .../grpc/src/core/lib/gpr/sync_posix.cc | 241 ++ .../grpc/src/core/lib/gpr/sync_windows.cc | 118 + .../core/{support/time.c => lib/gpr/time.cc} | 181 +- .../time_posix.c => lib/gpr/time_posix.cc} | 77 +- .../grpc/src/core/lib/gpr/time_precise.cc | 78 + .../grpc/src/core/lib/gpr/time_precise.h | 29 + .../grpc/src/core/lib/gpr/time_windows.cc | 98 + third_party/grpc/src/core/lib/gpr/tls.h | 68 + third_party/grpc/src/core/lib/gpr/tls_gcc.h | 52 + third_party/grpc/src/core/lib/gpr/tls_msvc.h | 52 + .../grpc/src/core/lib/gpr/tls_pthread.cc | 30 + .../grpc/src/core/lib/gpr/tls_pthread.h | 56 + third_party/grpc/src/core/lib/gpr/tmpfile.h | 32 + .../grpc/src/core/lib/gpr/tmpfile_msys.cc | 58 + .../grpc/src/core/lib/gpr/tmpfile_posix.cc | 70 + .../grpc/src/core/lib/gpr/tmpfile_windows.cc | 69 + third_party/grpc/src/core/lib/gpr/useful.h | 65 + .../grpc/src/core/lib/gpr/wrap_memcpy.cc | 42 + third_party/grpc/src/core/lib/gprpp/README.md | 16 + .../grpc/src/core/lib/gprpp/abstract.h | 37 + third_party/grpc/src/core/lib/gprpp/atomic.h | 30 + .../grpc/src/core/lib/gprpp/atomic_with_atm.h | 57 + .../grpc/src/core/lib/gprpp/atomic_with_std.h | 35 + .../grpc/src/core/lib/gprpp/debug_location.h | 52 + third_party/grpc/src/core/lib/gprpp/fork.cc | 268 ++ third_party/grpc/src/core/lib/gprpp/fork.h | 88 + .../grpc/src/core/lib/gprpp/inlined_vector.h | 200 ++ .../src/core/lib/gprpp/manual_constructor.h | 213 ++ third_party/grpc/src/core/lib/gprpp/memory.h | 114 + .../grpc/src/core/lib/gprpp/mutex_lock.h | 42 + .../grpc/src/core/lib/gprpp/orphanable.h | 133 + .../grpc/src/core/lib/gprpp/ref_counted.h | 245 ++ .../grpc/src/core/lib/gprpp/ref_counted_ptr.h | 185 + third_party/grpc/src/core/lib/gprpp/thd.h | 132 + .../grpc/src/core/lib/gprpp/thd_posix.cc | 160 + .../grpc/src/core/lib/gprpp/thd_windows.cc | 155 + .../grpc/src/core/lib/http/format_request.cc | 122 + .../grpc/src/core/lib/http/format_request.h | 34 + third_party/grpc/src/core/lib/http/httpcli.cc | 303 ++ third_party/grpc/src/core/lib/http/httpcli.h | 127 + .../lib/http/httpcli_security_connector.cc | 210 ++ third_party/grpc/src/core/lib/http/parser.cc | 371 ++ third_party/grpc/src/core/lib/http/parser.h | 113 + third_party/grpc/src/core/lib/iomgr/README.md | 6 + .../grpc/src/core/lib/iomgr/block_annotate.h | 57 + .../grpc/src/core/lib/iomgr/buffer_list.cc | 145 + .../grpc/src/core/lib/iomgr/buffer_list.h | 103 + .../grpc/src/core/lib/iomgr/call_combiner.cc | 262 ++ .../grpc/src/core/lib/iomgr/call_combiner.h | 222 ++ .../src/core/lib/iomgr/cfstream_handle.cc | 185 + .../grpc/src/core/lib/iomgr/cfstream_handle.h | 80 + third_party/grpc/src/core/lib/iomgr/closure.h | 353 ++ .../grpc/src/core/lib/iomgr/combiner.cc | 375 ++ .../grpc/src/core/lib/iomgr/combiner.h | 66 + .../src/core/lib/iomgr/dynamic_annotations.h | 67 + .../grpc/src/core/lib/iomgr/endpoint.cc | 67 + .../grpc/src/core/lib/iomgr/endpoint.h | 105 + .../src/core/lib/iomgr/endpoint_cfstream.cc | 375 ++ .../src/core/lib/iomgr/endpoint_cfstream.h | 49 + .../grpc/src/core/lib/iomgr/endpoint_pair.h | 34 + .../src/core/lib/iomgr/endpoint_pair_posix.cc | 73 + .../src/core/lib/iomgr/endpoint_pair_uv.cc | 40 + .../core/lib/iomgr/endpoint_pair_windows.cc | 87 + third_party/grpc/src/core/lib/iomgr/error.cc | 801 +++++ third_party/grpc/src/core/lib/iomgr/error.h | 245 ++ .../grpc/src/core/lib/iomgr/error_cfstream.cc | 52 + .../grpc/src/core/lib/iomgr/error_cfstream.h | 31 + .../grpc/src/core/lib/iomgr/error_internal.h | 61 + .../src/core/lib/iomgr/ev_epoll1_linux.cc | 1347 +++++++ .../grpc/src/core/lib/iomgr/ev_epoll1_linux.h | 31 + .../src/core/lib/iomgr/ev_epollex_linux.cc | 1685 +++++++++ .../src/core/lib/iomgr/ev_epollex_linux.h | 30 + .../grpc/src/core/lib/iomgr/ev_poll_posix.cc | 1883 ++++++++++ .../grpc/src/core/lib/iomgr/ev_poll_posix.h | 29 + .../grpc/src/core/lib/iomgr/ev_posix.cc | 406 +++ .../grpc/src/core/lib/iomgr/ev_posix.h | 191 + .../grpc/src/core/lib/iomgr/ev_windows.cc | 30 + .../grpc/src/core/lib/iomgr/exec_ctx.cc | 166 + .../grpc/src/core/lib/iomgr/exec_ctx.h | 231 ++ .../grpc/src/core/lib/iomgr/executor.cc | 433 +++ .../grpc/src/core/lib/iomgr/executor.h | 116 + .../grpc/src/core/lib/iomgr/fork_posix.cc | 112 + .../grpc/src/core/lib/iomgr/fork_windows.cc | 41 + .../grpc/src/core/lib/iomgr/gethostname.h | 26 + .../core/lib/iomgr/gethostname_fallback.cc | 30 + .../lib/iomgr/gethostname_host_name_max.cc | 40 + .../src/core/lib/iomgr/gethostname_sysconf.cc | 40 + .../grpc/src/core/lib/iomgr/gevent_util.h | 46 + .../src/core/lib/iomgr/internal_errqueue.cc | 69 + .../src/core/lib/iomgr/internal_errqueue.h | 89 + .../grpc/src/core/lib/iomgr/iocp_windows.cc | 157 + .../grpc/src/core/lib/iomgr/iocp_windows.h | 48 + third_party/grpc/src/core/lib/iomgr/iomgr.cc | 187 + third_party/grpc/src/core/lib/iomgr/iomgr.h | 45 + .../grpc/src/core/lib/iomgr/iomgr_custom.cc | 65 + .../grpc/src/core/lib/iomgr/iomgr_custom.h | 47 + .../grpc/src/core/lib/iomgr/iomgr_internal.cc | 47 + .../grpc/src/core/lib/iomgr/iomgr_internal.h | 61 + .../grpc/src/core/lib/iomgr/iomgr_posix.cc | 72 + .../grpc/src/core/lib/iomgr/iomgr_posix.h | 26 + .../core/lib/iomgr/iomgr_posix_cfstream.cc | 80 + .../grpc/src/core/lib/iomgr/iomgr_uv.cc | 40 + .../grpc/src/core/lib/iomgr/iomgr_windows.cc | 90 + .../lib/iomgr/is_epollexclusive_available.cc | 105 + .../lib/iomgr/is_epollexclusive_available.h | 36 + .../grpc/src/core/lib/iomgr/load_file.cc | 80 + .../grpc/src/core/lib/iomgr/load_file.h | 35 + .../grpc/src/core/lib/iomgr/lockfree_event.cc | 254 ++ .../grpc/src/core/lib/iomgr/lockfree_event.h | 72 + third_party/grpc/src/core/lib/iomgr/nameser.h | 106 + .../core/lib/iomgr/network_status_tracker.cc | 36 + .../core/lib/iomgr/network_status_tracker.h | 32 + .../grpc/src/core/lib/iomgr/polling_entity.cc | 96 + .../grpc/src/core/lib/iomgr/polling_entity.h | 68 + .../grpc/src/core/lib/iomgr/pollset.cc | 56 + third_party/grpc/src/core/lib/iomgr/pollset.h | 99 + .../grpc/src/core/lib/iomgr/pollset_custom.cc | 106 + .../grpc/src/core/lib/iomgr/pollset_custom.h | 35 + .../grpc/src/core/lib/iomgr/pollset_set.cc | 55 + .../grpc/src/core/lib/iomgr/pollset_set.h | 55 + .../src/core/lib/iomgr/pollset_set_custom.cc | 48 + .../src/core/lib/iomgr/pollset_set_custom.h | 26 + .../src/core/lib/iomgr/pollset_set_windows.cc | 51 + .../src/core/lib/iomgr/pollset_set_windows.h | 26 + .../grpc/src/core/lib/iomgr/pollset_uv.cc | 93 + .../grpc/src/core/lib/iomgr/pollset_uv.h | 32 + .../src/core/lib/iomgr/pollset_windows.cc | 229 ++ .../grpc/src/core/lib/iomgr/pollset_windows.h | 70 + third_party/grpc/src/core/lib/iomgr/port.h | 205 ++ .../src/core/lib/iomgr/resolve_address.cc | 50 + .../grpc/src/core/lib/iomgr/resolve_address.h | 83 + .../core/lib/iomgr/resolve_address_custom.cc | 187 + .../core/lib/iomgr/resolve_address_custom.h | 43 + .../core/lib/iomgr/resolve_address_posix.cc | 181 + .../core/lib/iomgr/resolve_address_windows.cc | 166 + .../grpc/src/core/lib/iomgr/resource_quota.cc | 1003 ++++++ .../grpc/src/core/lib/iomgr/resource_quota.h | 173 + .../grpc/src/core/lib/iomgr/sockaddr.h | 32 + .../grpc/src/core/lib/iomgr/sockaddr_custom.h | 54 + .../grpc/src/core/lib/iomgr/sockaddr_posix.h | 55 + .../grpc/src/core/lib/iomgr/sockaddr_utils.cc | 299 ++ .../grpc/src/core/lib/iomgr/sockaddr_utils.h | 84 + .../src/core/lib/iomgr/sockaddr_windows.h | 55 + .../core/lib/iomgr/socket_factory_posix.cc | 94 + .../src/core/lib/iomgr/socket_factory_posix.h | 69 + .../grpc/src/core/lib/iomgr/socket_mutator.cc | 83 + .../grpc/src/core/lib/iomgr/socket_mutator.h | 61 + .../grpc/src/core/lib/iomgr/socket_utils.h | 47 + .../lib/iomgr/socket_utils_common_posix.cc | 451 +++ .../src/core/lib/iomgr/socket_utils_linux.cc | 42 + .../src/core/lib/iomgr/socket_utils_posix.cc | 58 + .../src/core/lib/iomgr/socket_utils_posix.h | 144 + .../src/core/lib/iomgr/socket_utils_uv.cc | 49 + .../core/lib/iomgr/socket_utils_windows.cc | 47 + .../grpc/src/core/lib/iomgr/socket_windows.cc | 184 + .../src/core/{ => lib}/iomgr/socket_windows.h | 78 +- .../src/core/lib/iomgr/sys_epoll_wrapper.h | 30 + .../grpc/src/core/lib/iomgr/tcp_client.cc | 36 + .../grpc/src/core/lib/iomgr/tcp_client.h | 52 + .../src/core/lib/iomgr/tcp_client_cfstream.cc | 216 ++ .../src/core/lib/iomgr/tcp_client_custom.cc | 162 + .../src/core/lib/iomgr/tcp_client_posix.cc | 363 ++ .../src/core/lib/iomgr/tcp_client_posix.h | 68 + .../src/core/lib/iomgr/tcp_client_windows.cc | 231 ++ .../grpc/src/core/lib/iomgr/tcp_custom.cc | 368 ++ .../grpc/src/core/lib/iomgr/tcp_custom.h | 81 + .../grpc/src/core/lib/iomgr/tcp_posix.cc | 1182 ++++++ .../grpc/src/core/lib/iomgr/tcp_posix.h | 60 + .../grpc/src/core/lib/iomgr/tcp_server.cc | 73 + .../grpc/src/core/lib/iomgr/tcp_server.h | 122 + .../src/core/lib/iomgr/tcp_server_custom.cc | 472 +++ .../src/core/lib/iomgr/tcp_server_posix.cc | 562 +++ .../core/lib/iomgr/tcp_server_utils_posix.h | 122 + .../iomgr/tcp_server_utils_posix_common.cc | 223 ++ .../iomgr/tcp_server_utils_posix_ifaddrs.cc | 184 + .../iomgr/tcp_server_utils_posix_noifaddrs.cc | 36 + .../src/core/lib/iomgr/tcp_server_windows.cc | 560 +++ third_party/grpc/src/core/lib/iomgr/tcp_uv.cc | 420 +++ .../grpc/src/core/lib/iomgr/tcp_windows.cc | 536 +++ .../grpc/src/core/lib/iomgr/tcp_windows.h | 53 + .../iomgr/time_averaged_stats.cc} | 39 +- .../{ => lib}/iomgr/time_averaged_stats.h | 41 +- third_party/grpc/src/core/lib/iomgr/timer.cc | 45 + third_party/grpc/src/core/lib/iomgr/timer.h | 126 + .../grpc/src/core/lib/iomgr/timer_custom.cc | 93 + .../grpc/src/core/lib/iomgr/timer_custom.h | 43 + .../grpc/src/core/lib/iomgr/timer_generic.cc | 746 ++++ .../grpc/src/core/lib/iomgr/timer_generic.h | 39 + .../grpc/src/core/lib/iomgr/timer_heap.cc | 135 + .../grpc/src/core/lib/iomgr/timer_heap.h | 44 + .../grpc/src/core/lib/iomgr/timer_manager.cc | 376 ++ .../grpc/src/core/lib/iomgr/timer_manager.h | 39 + .../grpc/src/core/lib/iomgr/timer_uv.cc | 66 + .../grpc/src/core/lib/iomgr/udp_server.cc | 746 ++++ .../grpc/src/core/lib/iomgr/udp_server.h | 107 + .../src/core/lib/iomgr/unix_sockets_posix.cc | 104 + .../src/core/lib/iomgr/unix_sockets_posix.h | 43 + .../core/lib/iomgr/unix_sockets_posix_noop.cc | 49 + .../grpc/src/core/lib/iomgr/wakeup_fd_cv.cc | 107 + .../grpc/src/core/lib/iomgr/wakeup_fd_cv.h | 69 + .../src/core/lib/iomgr/wakeup_fd_eventfd.cc | 82 + .../src/core/lib/iomgr/wakeup_fd_nospecial.cc | 38 + .../grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc | 100 + .../grpc/src/core/lib/iomgr/wakeup_fd_pipe.h | 28 + .../src/core/lib/iomgr/wakeup_fd_posix.cc | 87 + .../core/{ => lib}/iomgr/wakeup_fd_posix.h | 65 +- third_party/grpc/src/core/lib/json/json.cc | 97 + third_party/grpc/src/core/lib/json/json.h | 99 + .../grpc/src/core/lib/json/json_common.h | 34 + .../json_reader.c => lib/json/json_reader.cc} | 118 +- .../src/core/{ => lib}/json/json_reader.h | 80 +- .../json_string.c => lib/json/json_string.cc} | 162 +- .../json_writer.c => lib/json/json_writer.cc} | 99 +- .../grpc/src/core/lib/json/json_writer.h | 84 + .../profiling/basic_timers.cc} | 159 +- .../core/{ => lib}/profiling/stap_probes.d | 0 .../src/core/lib/profiling/stap_timers.cc | 50 + .../grpc/src/core/lib/profiling/timers.h | 94 + .../lib/security/context/security_context.cc | 322 ++ .../lib/security/context/security_context.h | 151 + .../credentials/alts/alts_credentials.cc | 107 + .../credentials/alts/alts_credentials.h | 109 + .../credentials/alts/check_gcp_environment.cc | 72 + .../credentials/alts/check_gcp_environment.h | 57 + .../alts/check_gcp_environment_linux.cc | 68 + .../alts/check_gcp_environment_no_op.cc | 33 + .../alts/check_gcp_environment_windows.cc | 114 + .../grpc_alts_credentials_client_options.cc | 127 + .../alts/grpc_alts_credentials_options.cc | 46 + .../alts/grpc_alts_credentials_options.h | 75 + .../grpc_alts_credentials_server_options.cc | 59 + .../composite/composite_credentials.cc | 209 ++ .../composite/composite_credentials.h | 95 + .../lib/security/credentials/credentials.cc | 162 + .../lib/security/credentials/credentials.h | 271 ++ .../credentials/credentials_metadata.cc | 62 + .../credentials/fake/fake_credentials.cc | 112 + .../credentials/fake/fake_credentials.h | 82 + .../google_default/credentials_generic.cc | 41 + .../google_default_credentials.cc | 374 ++ .../google_default_credentials.h | 85 + .../credentials/iam/iam_credentials.cc | 78 + .../credentials/iam/iam_credentials.h | 45 + .../security/credentials/jwt/json_token.cc | 314 ++ .../lib/security/credentials/jwt/json_token.h | 77 + .../credentials/jwt/jwt_credentials.cc | 181 + .../credentials/jwt/jwt_credentials.h | 68 + .../security/credentials/jwt/jwt_verifier.cc | 938 +++++ .../security/credentials/jwt/jwt_verifier.h | 123 + .../credentials/local/local_credentials.cc | 64 + .../credentials/local/local_credentials.h | 61 + .../credentials/oauth2/oauth2_credentials.cc | 517 +++ .../credentials/oauth2/oauth2_credentials.h | 151 + .../credentials/plugin/plugin_credentials.cc | 252 ++ .../credentials/plugin/plugin_credentials.h | 69 + .../credentials/ssl/ssl_credentials.cc | 346 ++ .../credentials/ssl/ssl_credentials.h | 99 + .../alts/alts_security_connector.cc | 264 ++ .../alts/alts_security_connector.h | 71 + .../fake/fake_security_connector.cc | 305 ++ .../fake/fake_security_connector.h | 45 + .../security_connector/load_system_roots.h | 29 + .../load_system_roots_fallback.cc | 32 + .../load_system_roots_linux.cc | 165 + .../load_system_roots_linux.h | 44 + .../local/local_security_connector.cc | 253 ++ .../local/local_security_connector.h | 59 + .../security_connector/security_connector.cc | 129 + .../security_connector/security_connector.h | 174 + .../ssl/ssl_security_connector.cc | 452 +++ .../ssl/ssl_security_connector.h | 79 + .../security/security_connector/ssl_utils.cc | 349 ++ .../security/security_connector/ssl_utils.h | 95 + .../lib/security/transport/auth_filters.h | 37 + .../security/transport/client_auth_filter.cc | 429 +++ .../lib/security/transport/secure_endpoint.cc | 445 +++ .../lib/security/transport/secure_endpoint.h | 41 + .../security/transport/security_handshaker.cc | 546 +++ .../security/transport/security_handshaker.h | 34 + .../security/transport/server_auth_filter.cc | 325 ++ .../transport/target_authority_table.cc | 75 + .../transport/target_authority_table.h | 40 + .../core/lib/security/transport/tsi_error.cc | 29 + .../core/lib/security/transport/tsi_error.h | 29 + .../src/core/lib/security/util/json_util.cc | 52 + .../src/core/lib/security/util/json_util.h | 42 + .../{security/base64.c => lib/slice/b64.cc} | 129 +- third_party/grpc/src/core/lib/slice/b64.h | 51 + .../src/core/lib/slice/percent_encoding.cc | 169 + .../src/core/lib/slice/percent_encoding.h | 65 + third_party/grpc/src/core/lib/slice/slice.cc | 501 +++ .../grpc/src/core/lib/slice/slice_buffer.cc | 379 ++ .../src/core/lib/slice/slice_hash_table.h | 205 ++ .../grpc/src/core/lib/slice/slice_intern.cc | 332 ++ .../grpc/src/core/lib/slice/slice_internal.h | 54 + .../core/lib/slice/slice_string_helpers.cc | 118 + .../src/core/lib/slice/slice_string_helpers.h | 47 + .../grpc/src/core/lib/slice/slice_traits.h | 31 + .../core/lib/slice/slice_weak_hash_table.h | 109 + .../grpc/src/core/lib/surface/README.md | 4 + .../grpc/src/core/lib/surface/api_trace.cc | 24 + .../grpc/src/core/lib/surface/api_trace.h | 52 + .../grpc/src/core/lib/surface/byte_buffer.cc | 92 + .../core/lib/surface/byte_buffer_reader.cc | 129 + third_party/grpc/src/core/lib/surface/call.cc | 1979 ++++++++++ third_party/grpc/src/core/lib/surface/call.h | 121 + .../grpc/src/core/lib/surface/call_details.cc | 42 + .../src/core/lib/surface/call_log_batch.cc | 120 + .../src/core/lib/surface/call_test_only.h | 43 + .../grpc/src/core/lib/surface/channel.cc | 500 +++ .../grpc/src/core/lib/surface/channel.h | 88 + .../grpc/src/core/lib/surface/channel_init.cc | 109 + .../grpc/src/core/lib/surface/channel_init.h | 73 + .../grpc/src/core/lib/surface/channel_ping.cc | 65 + .../core/lib/surface/channel_stack_type.cc | 58 + .../src/core/lib/surface/channel_stack_type.h | 47 + .../src/core/lib/surface/completion_queue.cc | 1408 ++++++++ .../src/core/lib/surface/completion_queue.h | 94 + .../lib/surface/completion_queue_factory.cc | 88 + .../lib/surface/completion_queue_factory.h | 38 + .../grpc/src/core/lib/surface/event_string.cc | 68 + .../grpc/src/core/lib/surface/event_string.h | 29 + third_party/grpc/src/core/lib/surface/init.cc | 197 + third_party/grpc/src/core/lib/surface/init.h | 26 + .../grpc/src/core/lib/surface/init_secure.cc | 81 + .../src/core/lib/surface/init_unsecure.cc | 27 + .../grpc/src/core/lib/surface/lame_client.cc | 180 + .../grpc/src/core/lib/surface/lame_client.h | 28 + .../src/core/lib/surface/metadata_array.cc | 36 + .../grpc/src/core/lib/surface/server.cc | 1601 +++++++++ .../grpc/src/core/lib/surface/server.h | 76 + .../src/core/lib/surface/validate_metadata.cc | 95 + .../src/core/lib/surface/validate_metadata.h | 30 + .../grpc/src/core/lib/surface/version.cc | 28 + .../grpc/src/core/lib/transport/README.md | 7 + .../src/core/lib/transport/bdp_estimator.cc | 87 + .../src/core/lib/transport/bdp_estimator.h | 94 + .../src/core/lib/transport/byte_stream.cc | 160 + .../grpc/src/core/lib/transport/byte_stream.h | 164 + .../core/lib/transport/connectivity_state.cc | 195 + .../core/lib/transport/connectivity_state.h | 87 + .../src/core/lib/transport/error_utils.cc | 120 + .../grpc/src/core/lib/transport/error_utils.h | 46 + .../src/core/lib/transport/http2_errors.h | 41 + .../grpc/src/core/lib/transport/metadata.cc | 523 +++ .../grpc/src/core/lib/transport/metadata.h | 163 + .../src/core/lib/transport/metadata_batch.cc | 330 ++ .../src/core/lib/transport/metadata_batch.h | 154 + .../src/core/lib/transport/pid_controller.cc | 51 + .../src/core/lib/transport/pid_controller.h | 116 + .../src/core/lib/transport/service_config.cc | 106 + .../src/core/lib/transport/service_config.h | 249 ++ .../src/core/lib/transport/static_metadata.cc | 583 +++ .../src/core/lib/transport/static_metadata.h | 608 ++++ .../core/lib/transport/status_conversion.cc | 100 + .../core/lib/transport/status_conversion.h | 38 + .../src/core/lib/transport/status_metadata.cc | 54 + .../src/core/lib/transport/status_metadata.h | 30 + .../core/lib/transport/timeout_encoding.cc | 144 + .../src/core/lib/transport/timeout_encoding.h | 37 + .../grpc/src/core/lib/transport/transport.cc | 291 ++ .../grpc/src/core/lib/transport/transport.h | 400 +++ .../src/core/lib/transport/transport_impl.h | 71 + .../core/lib/transport/transport_op_string.cc | 207 ++ .../grpc/src/core/lib/uri/uri_parser.cc | 314 ++ .../grpc/src/core/lib/uri/uri_parser.h | 50 + .../grpc_cronet_plugin_registry.cc | 41 + .../plugin_registry/grpc_plugin_registry.cc | 93 + .../grpc_unsecure_plugin_registry.cc | 93 + .../grpc/src/core/profiling/stap_timers.c | 65 - third_party/grpc/src/core/profiling/timers.h | 119 - .../grpc/src/core/security/auth_filters.h | 42 - third_party/grpc/src/core/security/base64.h | 52 - .../src/core/security/client_auth_filter.c | 337 -- .../grpc/src/core/security/credentials.c | 1273 ------- .../grpc/src/core/security/credentials.h | 376 -- .../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 | 260 -- .../grpc/src/core/security/handshake.c | 328 -- .../grpc/src/core/security/handshake.h | 50 - .../grpc/src/core/security/json_token.c | 411 --- .../grpc/src/core/security/json_token.h | 118 - .../grpc/src/core/security/jwt_verifier.c | 843 ----- .../grpc/src/core/security/jwt_verifier.h | 136 - .../grpc/src/core/security/secure_endpoint.c | 383 -- .../grpc/src/core/security/secure_endpoint.h | 49 - .../src/core/security/security_connector.c | 786 ---- .../src/core/security/security_connector.h | 246 -- .../grpc/src/core/security/security_context.c | 342 -- .../grpc/src/core/security/security_context.h | 114 - .../src/core/security/server_auth_filter.c | 264 -- .../src/core/security/server_secure_chttp2.c | 267 -- .../grpc/src/core/statistics/census_init.c | 48 - .../src/core/statistics/census_interface.h | 76 - .../grpc/src/core/statistics/census_log.c | 603 ---- .../grpc/src/core/statistics/census_log.h | 91 - .../src/core/statistics/census_rpc_stats.c | 253 -- .../src/core/statistics/census_rpc_stats.h | 101 - .../grpc/src/core/statistics/census_tracing.c | 238 -- .../grpc/src/core/statistics/census_tracing.h | 96 - .../grpc/src/core/statistics/hash_table.c | 303 -- .../grpc/src/core/statistics/hash_table.h | 131 - .../grpc/src/core/statistics/window_stats.c | 316 -- .../grpc/src/core/statistics/window_stats.h | 173 - third_party/grpc/src/core/support/alloc.c | 90 - third_party/grpc/src/core/support/avl.c | 288 -- .../grpc/src/core/support/block_annotate.h | 48 - third_party/grpc/src/core/support/cmdline.c | 347 -- .../grpc/src/core/support/cpu_iphone.c | 49 - third_party/grpc/src/core/support/cpu_linux.c | 78 - third_party/grpc/src/core/support/cpu_posix.c | 77 - .../grpc/src/core/support/cpu_windows.c | 47 - third_party/grpc/src/core/support/env.h | 60 - third_party/grpc/src/core/support/env_linux.c | 72 - third_party/grpc/src/core/support/env_posix.c | 57 - third_party/grpc/src/core/support/env_win32.c | 73 - third_party/grpc/src/core/support/file.c | 91 - third_party/grpc/src/core/support/file.h | 63 - .../grpc/src/core/support/file_posix.c | 85 - .../grpc/src/core/support/file_win32.c | 84 - third_party/grpc/src/core/support/histogram.c | 244 -- third_party/grpc/src/core/support/host_port.c | 110 - third_party/grpc/src/core/support/log.c | 66 - .../grpc/src/core/support/log_android.c | 87 - third_party/grpc/src/core/support/log_linux.c | 104 - third_party/grpc/src/core/support/log_posix.c | 102 - third_party/grpc/src/core/support/log_win32.c | 126 - .../grpc/src/core/support/murmur_hash.c | 96 - .../grpc/src/core/support/murmur_hash.h | 44 - third_party/grpc/src/core/support/slice.c | 343 -- .../grpc/src/core/support/slice_buffer.c | 282 -- .../grpc/src/core/support/stack_lockfree.c | 185 - .../grpc/src/core/support/stack_lockfree.h | 53 - third_party/grpc/src/core/support/string.c | 296 -- third_party/grpc/src/core/support/string.h | 121 - .../grpc/src/core/support/string_posix.c | 86 - .../grpc/src/core/support/string_win32.c | 110 - .../grpc/src/core/support/string_win32.h | 47 - .../grpc/src/core/support/subprocess_posix.c | 112 - .../src/core/support/subprocess_windows.c | 141 - third_party/grpc/src/core/support/sync.c | 122 - .../grpc/src/core/support/sync_posix.c | 104 - .../grpc/src/core/support/sync_win32.c | 133 - third_party/grpc/src/core/support/thd.c | 64 - .../grpc/src/core/support/thd_internal.h | 39 - third_party/grpc/src/core/support/thd_posix.c | 94 - third_party/grpc/src/core/support/thd_win32.c | 117 - .../grpc/src/core/support/time_precise.c | 89 - .../grpc/src/core/support/time_precise.h | 42 - .../grpc/src/core/support/time_win32.c | 110 - .../grpc/src/core/support/tls_pthread.c | 45 - .../grpc/src/core/support/wrap_memcpy.c | 53 - third_party/grpc/src/core/surface/alarm.c | 83 - third_party/grpc/src/core/surface/api_trace.c | 36 - third_party/grpc/src/core/surface/api_trace.h | 65 - .../grpc/src/core/surface/byte_buffer.c | 97 - .../src/core/surface/byte_buffer_reader.c | 123 - third_party/grpc/src/core/surface/call.c | 1433 -------- third_party/grpc/src/core/surface/call.h | 109 - .../grpc/src/core/surface/call_details.c | 50 - .../grpc/src/core/surface/call_log_batch.c | 118 - .../grpc/src/core/surface/call_test_only.h | 64 - third_party/grpc/src/core/surface/channel.c | 327 -- third_party/grpc/src/core/surface/channel.h | 74 - .../src/core/surface/channel_connectivity.c | 220 -- .../grpc/src/core/surface/channel_create.c | 235 -- .../grpc/src/core/surface/channel_ping.c | 79 - .../grpc/src/core/surface/completion_queue.c | 480 --- .../grpc/src/core/surface/completion_queue.h | 91 - .../grpc/src/core/surface/event_string.c | 81 - .../grpc/src/core/surface/event_string.h | 42 - third_party/grpc/src/core/surface/init.c | 170 - third_party/grpc/src/core/surface/init.h | 40 - .../grpc/src/core/surface/init_secure.c | 42 - .../grpc/src/core/surface/init_unsecure.c | 36 - .../grpc/src/core/surface/lame_client.c | 149 - .../grpc/src/core/surface/metadata_array.c | 49 - .../src/core/surface/secure_channel_create.c | 336 -- third_party/grpc/src/core/surface/server.c | 1343 ------- third_party/grpc/src/core/surface/server.h | 67 - .../grpc/src/core/surface/server_chttp2.c | 149 - .../grpc/src/core/surface/server_create.c | 48 - .../grpc/src/core/surface/surface_trace.h | 48 - .../grpc/src/core/surface/validate_metadata.c | 73 - third_party/grpc/src/core/surface/version.c | 39 - .../grpc/src/core/transport/byte_stream.c | 78 - .../grpc/src/core/transport/byte_stream.h | 89 - .../grpc/src/core/transport/chttp2/alpn.c | 56 - .../grpc/src/core/transport/chttp2/alpn.h | 49 - .../src/core/transport/chttp2/bin_encoder.c | 285 -- .../src/core/transport/chttp2/bin_encoder.h | 54 - .../grpc/src/core/transport/chttp2/frame.h | 69 - .../src/core/transport/chttp2/frame_data.c | 248 -- .../src/core/transport/chttp2/frame_data.h | 101 - .../src/core/transport/chttp2/frame_goaway.c | 193 - .../src/core/transport/chttp2/frame_goaway.h | 77 - .../src/core/transport/chttp2/frame_ping.c | 97 - .../src/core/transport/chttp2/frame_ping.h | 56 - .../core/transport/chttp2/frame_rst_stream.c | 99 - .../core/transport/chttp2/frame_rst_stream.h | 55 - .../core/transport/chttp2/frame_settings.c | 259 -- .../core/transport/chttp2/frame_settings.h | 103 - .../transport/chttp2/frame_window_update.c | 113 - .../transport/chttp2/frame_window_update.h | 56 - .../src/core/transport/chttp2/hpack_encoder.c | 568 --- .../src/core/transport/chttp2/hpack_encoder.h | 95 - .../src/core/transport/chttp2/hpack_parser.h | 116 - .../src/core/transport/chttp2/hpack_table.h | 108 - .../src/core/transport/chttp2/http2_errors.h | 56 - .../grpc/src/core/transport/chttp2/huffsyms.c | 297 -- .../grpc/src/core/transport/chttp2/huffsyms.h | 48 - .../core/transport/chttp2/incoming_metadata.c | 96 - .../core/transport/chttp2/incoming_metadata.h | 60 - .../grpc/src/core/transport/chttp2/internal.h | 768 ---- .../grpc/src/core/transport/chttp2/parsing.c | 866 ----- .../core/transport/chttp2/status_conversion.c | 109 - .../core/transport/chttp2/status_conversion.h | 50 - .../src/core/transport/chttp2/stream_lists.c | 424 --- .../src/core/transport/chttp2/stream_map.c | 197 - .../src/core/transport/chttp2/stream_map.h | 84 - .../core/transport/chttp2/timeout_encoding.c | 188 - .../core/transport/chttp2/timeout_encoding.h | 47 - .../grpc/src/core/transport/chttp2/varint.c | 65 - .../grpc/src/core/transport/chttp2/writing.c | 359 -- .../src/core/transport/chttp2_transport.c | 1733 --------- .../src/core/transport/chttp2_transport.h | 51 - .../src/core/transport/connectivity_state.c | 164 - .../src/core/transport/connectivity_state.h | 85 - .../grpc/src/core/transport/metadata.c | 690 ---- .../grpc/src/core/transport/metadata.h | 156 - .../grpc/src/core/transport/metadata_batch.c | 194 - .../grpc/src/core/transport/metadata_batch.h | 125 - .../grpc/src/core/transport/static_metadata.c | 89 - .../grpc/src/core/transport/static_metadata.h | 408 --- .../grpc/src/core/transport/transport.c | 183 - .../grpc/src/core/transport/transport.h | 235 -- .../grpc/src/core/transport/transport_impl.h | 78 - .../src/core/transport/transport_op_string.c | 140 - third_party/grpc/src/core/tsi/README.md | 2 + .../grpc/src/core/tsi/alts/crypt/aes_gcm.cc | 689 ++++ .../grpc/src/core/tsi/alts/crypt/gsec.cc | 189 + .../grpc/src/core/tsi/alts/crypt/gsec.h | 454 +++ .../tsi/alts/frame_protector/alts_counter.cc | 118 + .../tsi/alts/frame_protector/alts_counter.h | 98 + .../tsi/alts/frame_protector/alts_crypter.cc | 66 + .../tsi/alts/frame_protector/alts_crypter.h | 255 ++ .../frame_protector/alts_frame_protector.cc | 407 +++ .../frame_protector/alts_frame_protector.h | 55 + .../alts_record_protocol_crypter_common.cc | 114 + .../alts_record_protocol_crypter_common.h | 114 + .../alts_seal_privacy_integrity_crypter.cc | 105 + .../alts_unseal_privacy_integrity_crypter.cc | 103 + .../tsi/alts/frame_protector/frame_handler.cc | 218 ++ .../tsi/alts/frame_protector/frame_handler.h | 236 ++ .../alts/handshaker/alts_handshaker_client.cc | 615 ++++ .../alts/handshaker/alts_handshaker_client.h | 157 + .../handshaker/alts_handshaker_service_api.cc | 520 +++ .../handshaker/alts_handshaker_service_api.h | 323 ++ .../alts_handshaker_service_api_util.cc | 145 + .../alts_handshaker_service_api_util.h | 149 + .../alts/handshaker/alts_shared_resource.cc | 83 + .../alts/handshaker/alts_shared_resource.h | 73 + .../alts/handshaker/alts_tsi_handshaker.cc | 466 +++ .../tsi/alts/handshaker/alts_tsi_handshaker.h | 94 + .../handshaker/alts_tsi_handshaker_private.h | 83 + .../tsi/alts/handshaker/alts_tsi_utils.cc | 60 + .../core/tsi/alts/handshaker/alts_tsi_utils.h | 52 + .../core/tsi/alts/handshaker/altscontext.pb.c | 47 + .../core/tsi/alts/handshaker/altscontext.pb.h | 63 + .../core/tsi/alts/handshaker/handshaker.pb.c | 122 + .../core/tsi/alts/handshaker/handshaker.pb.h | 254 ++ .../alts/handshaker/proto/altscontext.proto | 41 + .../alts/handshaker/proto/handshaker.options | 2 + .../alts/handshaker/proto/handshaker.proto | 224 ++ .../proto/transport_security_common.proto | 40 + .../handshaker/transport_security_common.pb.c | 49 + .../handshaker/transport_security_common.pb.h | 78 + .../transport_security_common_api.cc | 196 + .../transport_security_common_api.h | 163 + ...lts_grpc_integrity_only_record_protocol.cc | 226 ++ ...alts_grpc_integrity_only_record_protocol.h | 54 + ..._grpc_privacy_integrity_record_protocol.cc | 144 + ...s_grpc_privacy_integrity_record_protocol.h | 49 + .../alts_grpc_record_protocol.h | 91 + .../alts_grpc_record_protocol_common.cc | 174 + .../alts_grpc_record_protocol_common.h | 100 + .../alts_iovec_record_protocol.cc | 476 +++ .../alts_iovec_record_protocol.h | 199 ++ .../alts_zero_copy_grpc_protector.cc | 297 ++ .../alts_zero_copy_grpc_protector.h | 57 + .../src/core/tsi/fake_transport_security.c | 525 --- .../src/core/tsi/fake_transport_security.cc | 788 ++++ .../src/core/tsi/fake_transport_security.h | 60 +- .../grpc/src/core/tsi/grpc_shadow_boringssl.h | 3006 ++++++++++++++++ .../src/core/tsi/local_transport_security.cc | 209 ++ .../src/core/tsi/local_transport_security.h | 51 + .../core/tsi/ssl/session_cache/ssl_session.h | 75 + .../session_cache/ssl_session_boringssl.cc | 58 + .../ssl/session_cache/ssl_session_cache.cc | 212 ++ .../tsi/ssl/session_cache/ssl_session_cache.h | 99 + .../ssl/session_cache/ssl_session_openssl.cc | 76 + .../src/core/tsi/ssl_transport_security.c | 1469 -------- .../src/core/tsi/ssl_transport_security.cc | 1971 ++++++++++ .../src/core/tsi/ssl_transport_security.h | 365 +- third_party/grpc/src/core/tsi/ssl_types.h | 45 +- .../grpc/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 - .../grpc/src/core/tsi/test_creds/ca.key | 16 - .../grpc/src/core/tsi/test_creds/ca.pem | 15 - .../grpc/src/core/tsi/test_creds/client.key | 16 - .../grpc/src/core/tsi/test_creds/client.pem | 14 - .../grpc/src/core/tsi/test_creds/server0.key | 16 - .../grpc/src/core/tsi/test_creds/server0.pem | 14 - .../core/tsi/test_creds/server1-openssl.cnf | 26 - .../grpc/src/core/tsi/test_creds/server1.key | 16 - .../grpc/src/core/tsi/test_creds/server1.pem | 16 - .../grpc/src/core/tsi/transport_security.c | 284 -- .../grpc/src/core/tsi/transport_security.cc | 357 ++ .../grpc/src/core/tsi/transport_security.h | 171 +- .../src/core/tsi/transport_security_grpc.cc | 66 + .../src/core/tsi/transport_security_grpc.h | 74 + .../core/tsi/transport_security_interface.h | 374 +- third_party/grpc/src/cpp/README.md | 83 +- third_party/grpc/src/cpp/client/channel.cc | 144 - third_party/grpc/src/cpp/client/channel_cc.cc | 255 ++ .../grpc/src/cpp/client/client_context.cc | 105 +- .../grpc/src/cpp/client/client_interceptor.cc | 34 + .../grpc/src/cpp/client/create_channel.cc | 103 +- .../src/cpp/client/create_channel_internal.cc | 47 +- .../src/cpp/client/create_channel_internal.h | 45 +- .../src/cpp/client/create_channel_posix.cc | 75 + .../grpc/src/cpp/client/credentials.cc | 48 - .../grpc/src/cpp/client/credentials_cc.cc | 33 + .../grpc/src/cpp/client/cronet_credentials.cc | 64 + .../grpc/src/cpp/client/generic_stub.cc | 94 +- .../src/cpp/client/insecure_credentials.cc | 65 +- .../grpc/src/cpp/client/secure_credentials.cc | 216 +- .../grpc/src/cpp/client/secure_credentials.h | 85 +- .../grpc/src/cpp/codegen/codegen_init.cc | 30 + .../grpc/src/cpp/codegen/grpc_library.cc | 40 - third_party/grpc/src/cpp/common/alarm.cc | 171 +- .../src/cpp/common/auth_property_iterator.cc | 37 +- third_party/grpc/src/cpp/common/call.cc | 92 - .../grpc/src/cpp/common/channel_arguments.cc | 162 +- .../grpc/src/cpp/common/channel_filter.cc | 99 + .../grpc/src/cpp/common/channel_filter.h | 397 ++ .../grpc/src/cpp/common/completion_queue.cc | 99 - .../src/cpp/common/completion_queue_cc.cc | 99 + .../grpc/src/cpp/common/core_codegen.cc | 228 ++ .../grpc/src/cpp/common/create_auth_context.h | 42 - .../common/insecure_create_auth_context.cc | 37 +- .../grpc/src/cpp/common/resource_quota_cc.cc | 40 + third_party/grpc/src/cpp/common/rpc_method.cc | 37 +- .../src/cpp/common/secure_auth_context.cc | 73 +- .../grpc/src/cpp/common/secure_auth_context.h | 67 +- .../cpp/common/secure_channel_arguments.cc | 39 +- .../cpp/common/secure_create_auth_context.cc | 42 +- third_party/grpc/src/cpp/common/version_cc.cc | 26 + .../cpp/ext/filters/census/channel_filter.cc | 30 + .../cpp/ext/filters/census/channel_filter.h | 36 + .../cpp/ext/filters/census/client_filter.cc | 165 + .../cpp/ext/filters/census/client_filter.h | 104 + .../src/cpp/ext/filters/census/context.cc | 144 + .../grpc/src/cpp/ext/filters/census/context.h | 126 + .../src/cpp/ext/filters/census/grpc_plugin.cc | 130 + .../src/cpp/ext/filters/census/grpc_plugin.h | 107 + .../src/cpp/ext/filters/census/measures.cc | 129 + .../src/cpp/ext/filters/census/measures.h | 46 + .../cpp/ext/filters/census/rpc_encoding.cc | 39 + .../src/cpp/ext/filters/census/rpc_encoding.h | 284 ++ .../cpp/ext/filters/census/server_filter.cc | 198 + .../cpp/ext/filters/census/server_filter.h | 101 + .../grpc/src/cpp/ext/filters/census/views.cc | 491 +++ .../src/cpp/ext/proto_server_reflection.cc | 212 ++ .../src/cpp/ext/proto_server_reflection.h | 80 + .../cpp/ext/proto_server_reflection_plugin.cc | 82 + third_party/grpc/src/cpp/proto/proto_utils.cc | 206 -- .../src/cpp/server/async_generic_service.cc | 39 +- .../src/cpp/server/channel_argument_option.cc | 63 + .../cpp/server/channelz/channelz_service.cc | 146 + .../cpp/server/channelz/channelz_service.h | 64 + .../channelz/channelz_service_plugin.cc | 79 + .../cpp/server/create_default_thread_pool.cc | 46 +- .../src/cpp/server/dynamic_thread_pool.cc | 67 +- .../grpc/src/cpp/server/dynamic_thread_pool.h | 54 +- .../src/cpp/server/fixed_size_thread_pool.cc | 85 - .../src/cpp/server/fixed_size_thread_pool.h | 67 - .../health/default_health_check_service.cc | 499 +++ .../health/default_health_check_service.h | 284 ++ .../cpp/server/health/health_check_service.cc | 34 + ...lth_check_service_server_builder_option.cc | 35 + .../cpp/server/insecure_server_credentials.cc | 44 +- .../src/cpp/server/load_reporter/constants.h | 81 + .../cpp/server/load_reporter/get_cpu_stats.h | 36 + .../load_reporter/get_cpu_stats_linux.cc | 45 + .../load_reporter/get_cpu_stats_macos.cc | 45 + .../get_cpu_stats_unsupported.cc | 40 + .../load_reporter/get_cpu_stats_windows.cc | 55 + .../server/load_reporter/load_data_store.cc | 338 ++ .../server/load_reporter/load_data_store.h | 348 ++ .../cpp/server/load_reporter/load_reporter.cc | 509 +++ .../cpp/server/load_reporter/load_reporter.h | 228 ++ .../load_reporter_async_service_impl.cc | 370 ++ .../load_reporter_async_service_impl.h | 194 + ...reporting_service_server_builder_option.cc | 41 + ...reporting_service_server_builder_plugin.cc | 60 + ..._reporting_service_server_builder_plugin.h | 62 + .../grpc/src/cpp/server/load_reporter/util.cc | 47 + .../cpp/server/secure_server_credentials.cc | 95 +- .../cpp/server/secure_server_credentials.h | 48 +- third_party/grpc/src/cpp/server/server.cc | 588 --- .../grpc/src/cpp/server/server_builder.cc | 376 +- third_party/grpc/src/cpp/server/server_cc.cc | 1166 ++++++ .../grpc/src/cpp/server/server_context.cc | 386 +- .../grpc/src/cpp/server/server_credentials.cc | 37 +- .../grpc/src/cpp/server/server_posix.cc | 33 + .../src/cpp/server/thread_pool_interface.h | 39 +- .../src/cpp/thread_manager/thread_manager.cc | 252 ++ .../src/cpp/thread_manager/thread_manager.h | 178 + third_party/grpc/src/cpp/util/byte_buffer.cc | 94 - .../grpc/src/cpp/util/byte_buffer_cc.cc | 60 + third_party/grpc/src/cpp/util/core_stats.cc | 90 + third_party/grpc/src/cpp/util/core_stats.h | 33 + .../grpc/src/cpp/util/error_details.cc | 48 + third_party/grpc/src/cpp/util/slice.cc | 48 - third_party/grpc/src/cpp/util/status.cc | 37 +- third_party/grpc/src/cpp/util/string_ref.cc | 101 +- third_party/grpc/src/cpp/util/time.cc | 95 - third_party/grpc/src/cpp/util/time_cc.cc | 75 + third_party/nanopb/CHANGELOG.txt | 24 + third_party/nanopb/CONTRIBUTING.md | 32 + third_party/nanopb/README.bazel.md | 10 +- third_party/nanopb/docs/concepts.rst | 55 + third_party/nanopb/docs/index.rst | 9 +- third_party/nanopb/docs/migration.rst | 18 + third_party/nanopb/docs/reference.rst | 88 +- third_party/nanopb/docs/security.rst | 7 +- third_party/nanopb/extra/pb_syshdr.h | 8 + .../nanopb/generator/nanopb_generator.py | 100 +- .../nanopb/generator/proto/nanopb.proto | 3 + third_party/nanopb/pb.h | 61 +- third_party/nanopb/pb_decode.c | 203 +- third_party/nanopb/pb_decode.h | 6 +- third_party/nanopb/pb_encode.c | 125 +- third_party/nanopb/pb_encode.h | 8 +- .../nanopb/tools/make_windows_package.sh | 4 +- third_party/protobuf/BUILD | 2 + 1501 files changed, 168140 insertions(+), 75048 deletions(-) create mode 100644 third_party/grpc/include/grpc++/create_channel_posix.h create mode 100644 third_party/grpc/include/grpc++/ext/health_check_service_server_builder_option.h create mode 100644 third_party/grpc/include/grpc++/ext/proto_server_reflection_plugin.h create mode 100644 third_party/grpc/include/grpc++/health_check_service_interface.h create mode 100644 third_party/grpc/include/grpc++/impl/channel_argument_option.h create mode 100644 third_party/grpc/include/grpc++/impl/codegen/byte_buffer.h create mode 100644 third_party/grpc/include/grpc++/impl/codegen/core_codegen.h create mode 100644 third_party/grpc/include/grpc++/impl/codegen/core_codegen_interface.h create mode 100644 third_party/grpc/include/grpc++/impl/codegen/create_auth_context.h create mode 100644 third_party/grpc/include/grpc++/impl/codegen/metadata_map.h create mode 100644 third_party/grpc/include/grpc++/impl/codegen/slice.h delete mode 100644 third_party/grpc/include/grpc++/impl/codegen/sync.h delete mode 100644 third_party/grpc/include/grpc++/impl/codegen/sync_cxx11.h delete mode 100644 third_party/grpc/include/grpc++/impl/codegen/sync_no_cxx11.h delete mode 100644 third_party/grpc/include/grpc++/impl/proto_utils.h create mode 100644 third_party/grpc/include/grpc++/impl/server_builder_plugin.h create mode 100644 third_party/grpc/include/grpc++/impl/server_initializer.h delete mode 100644 third_party/grpc/include/grpc++/impl/sync.h delete mode 100644 third_party/grpc/include/grpc++/impl/thd.h delete mode 100644 third_party/grpc/include/grpc++/impl/thd_cxx11.h delete mode 100644 third_party/grpc/include/grpc++/impl/thd_no_cxx11.h create mode 100644 third_party/grpc/include/grpc++/resource_quota.h create mode 100644 third_party/grpc/include/grpc++/server_posix.h delete mode 100644 third_party/grpc/include/grpc++/support/config_protobuf.h create mode 100644 third_party/grpc/include/grpc++/support/error_details.h create mode 100644 third_party/grpc/include/grpc++/test/mock_stream.h create mode 100644 third_party/grpc/include/grpc++/test/server_context_test_spouse.h create mode 100644 third_party/grpc/include/grpc/fork.h create mode 100644 third_party/grpc/include/grpc/grpc_cronet.h create mode 100644 third_party/grpc/include/grpc/grpc_posix.h create mode 100644 third_party/grpc/include/grpc/grpc_security_constants.h delete mode 100644 third_party/grpc/include/grpc/grpc_zookeeper.h delete mode 100644 third_party/grpc/include/grpc/impl/codegen/alloc.h delete mode 100644 third_party/grpc/include/grpc/impl/codegen/atm_win32.h create mode 100644 third_party/grpc/include/grpc/impl/codegen/atm_windows.h create mode 100644 third_party/grpc/include/grpc/impl/codegen/byte_buffer_reader.h create mode 100644 third_party/grpc/include/grpc/impl/codegen/fork.h create mode 100644 third_party/grpc/include/grpc/impl/codegen/gpr_slice.h create mode 100644 third_party/grpc/include/grpc/impl/codegen/gpr_types.h delete mode 100644 third_party/grpc/include/grpc/impl/codegen/slice_buffer.h create mode 100644 third_party/grpc/include/grpc/impl/codegen/sync_custom.h delete mode 100644 third_party/grpc/include/grpc/impl/codegen/sync_win32.h create mode 100644 third_party/grpc/include/grpc/impl/codegen/sync_windows.h delete mode 100644 third_party/grpc/include/grpc/impl/codegen/time.h create mode 100644 third_party/grpc/include/grpc/load_reporting.h create mode 100644 third_party/grpc/include/grpc/module.modulemap create mode 100644 third_party/grpc/include/grpc/slice.h create mode 100644 third_party/grpc/include/grpc/slice_buffer.h delete mode 100644 third_party/grpc/include/grpc/support/atm_win32.h create mode 100644 third_party/grpc/include/grpc/support/atm_windows.h delete mode 100644 third_party/grpc/include/grpc/support/avl.h delete mode 100644 third_party/grpc/include/grpc/support/cmdline.h delete mode 100644 third_party/grpc/include/grpc/support/histogram.h delete mode 100644 third_party/grpc/include/grpc/support/host_port.h delete mode 100644 third_party/grpc/include/grpc/support/log_win32.h create mode 100644 third_party/grpc/include/grpc/support/log_windows.h delete mode 100644 third_party/grpc/include/grpc/support/slice.h delete mode 100644 third_party/grpc/include/grpc/support/slice_buffer.h delete mode 100644 third_party/grpc/include/grpc/support/subprocess.h create mode 100644 third_party/grpc/include/grpc/support/sync_custom.h delete mode 100644 third_party/grpc/include/grpc/support/sync_win32.h create mode 100644 third_party/grpc/include/grpc/support/sync_windows.h delete mode 100644 third_party/grpc/include/grpc/support/thd.h create mode 100644 third_party/grpc/include/grpc/support/thd_id.h delete mode 100644 third_party/grpc/include/grpc/support/tls.h delete mode 100644 third_party/grpc/include/grpc/support/tls_gcc.h delete mode 100644 third_party/grpc/include/grpc/support/tls_msvc.h delete mode 100644 third_party/grpc/include/grpc/support/tls_pthread.h delete mode 100644 third_party/grpc/include/grpc/support/useful.h create mode 100644 third_party/grpc/include/grpc/support/workaround_list.h create mode 100644 third_party/grpc/include/grpcpp/alarm.h create mode 100644 third_party/grpc/include/grpcpp/alarm_impl.h create mode 100644 third_party/grpc/include/grpcpp/channel.h create mode 100644 third_party/grpc/include/grpcpp/client_context.h create mode 100644 third_party/grpc/include/grpcpp/completion_queue.h create mode 100644 third_party/grpc/include/grpcpp/create_channel.h create mode 100644 third_party/grpc/include/grpcpp/create_channel_posix.h create mode 100644 third_party/grpc/include/grpcpp/ext/channelz_service_plugin.h create mode 100644 third_party/grpc/include/grpcpp/ext/health_check_service_server_builder_option.h create mode 100644 third_party/grpc/include/grpcpp/ext/proto_server_reflection_plugin.h create mode 100644 third_party/grpc/include/grpcpp/ext/server_load_reporting.h create mode 100644 third_party/grpc/include/grpcpp/generic/async_generic_service.h create mode 100644 third_party/grpc/include/grpcpp/generic/generic_stub.h create mode 100644 third_party/grpc/include/grpcpp/grpcpp.h create mode 100644 third_party/grpc/include/grpcpp/health_check_service_interface.h rename third_party/grpc/include/{grpc++ => grpcpp}/impl/README.md (100%) create mode 100644 third_party/grpc/include/grpcpp/impl/call.h create mode 100644 third_party/grpc/include/grpcpp/impl/channel_argument_option.h create mode 100644 third_party/grpc/include/grpcpp/impl/client_unary_call.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/async_generic_service.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/async_stream.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/async_unary_call.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/byte_buffer.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/call.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/call_hook.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/call_op_set.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/call_op_set_interface.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/callback_common.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/channel_interface.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/client_callback.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/client_context.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/client_interceptor.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/client_unary_call.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/completion_queue.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/completion_queue_tag.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/config.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/config_protobuf.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/core_codegen.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/core_codegen_interface.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/create_auth_context.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/grpc_library.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/intercepted_channel.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/interceptor.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/interceptor_common.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/metadata_map.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/method_handler_impl.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_reader.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_writer.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/proto_utils.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/rpc_method.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/rpc_service_method.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/security/auth_context.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/serialization_traits.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/server_callback.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/server_context.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/server_interceptor.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/server_interface.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/service_type.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/slice.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/status.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/status_code_enum.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/string_ref.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/stub_options.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/sync_stream.h create mode 100644 third_party/grpc/include/grpcpp/impl/codegen/time.h create mode 100644 third_party/grpc/include/grpcpp/impl/grpc_library.h create mode 100644 third_party/grpc/include/grpcpp/impl/method_handler_impl.h create mode 100644 third_party/grpc/include/grpcpp/impl/rpc_method.h create mode 100644 third_party/grpc/include/grpcpp/impl/rpc_service_method.h create mode 100644 third_party/grpc/include/grpcpp/impl/serialization_traits.h create mode 100644 third_party/grpc/include/grpcpp/impl/server_builder_option.h create mode 100644 third_party/grpc/include/grpcpp/impl/server_builder_plugin.h create mode 100644 third_party/grpc/include/grpcpp/impl/server_initializer.h create mode 100644 third_party/grpc/include/grpcpp/impl/service_type.h create mode 100644 third_party/grpc/include/grpcpp/impl/sync_cxx11.h create mode 100644 third_party/grpc/include/grpcpp/impl/sync_no_cxx11.h create mode 100644 third_party/grpc/include/grpcpp/opencensus.h create mode 100644 third_party/grpc/include/grpcpp/resource_quota.h create mode 100644 third_party/grpc/include/grpcpp/security/auth_context.h create mode 100644 third_party/grpc/include/grpcpp/security/auth_metadata_processor.h create mode 100644 third_party/grpc/include/grpcpp/security/credentials.h create mode 100644 third_party/grpc/include/grpcpp/security/server_credentials.h create mode 100644 third_party/grpc/include/grpcpp/server.h create mode 100644 third_party/grpc/include/grpcpp/server_builder.h create mode 100644 third_party/grpc/include/grpcpp/server_context.h create mode 100644 third_party/grpc/include/grpcpp/server_posix.h create mode 100644 third_party/grpc/include/grpcpp/support/async_stream.h create mode 100644 third_party/grpc/include/grpcpp/support/async_unary_call.h create mode 100644 third_party/grpc/include/grpcpp/support/byte_buffer.h create mode 100644 third_party/grpc/include/grpcpp/support/channel_arguments.h create mode 100644 third_party/grpc/include/grpcpp/support/client_callback.h create mode 100644 third_party/grpc/include/grpcpp/support/client_interceptor.h create mode 100644 third_party/grpc/include/grpcpp/support/config.h create mode 100644 third_party/grpc/include/grpcpp/support/error_details.h create mode 100644 third_party/grpc/include/grpcpp/support/interceptor.h create mode 100644 third_party/grpc/include/grpcpp/support/proto_buffer_reader.h create mode 100644 third_party/grpc/include/grpcpp/support/proto_buffer_writer.h create mode 100644 third_party/grpc/include/grpcpp/support/server_callback.h create mode 100644 third_party/grpc/include/grpcpp/support/server_interceptor.h create mode 100644 third_party/grpc/include/grpcpp/support/slice.h create mode 100644 third_party/grpc/include/grpcpp/support/status.h create mode 100644 third_party/grpc/include/grpcpp/support/status_code_enum.h create mode 100644 third_party/grpc/include/grpcpp/support/string_ref.h create mode 100644 third_party/grpc/include/grpcpp/support/stub_options.h create mode 100644 third_party/grpc/include/grpcpp/support/sync_stream.h create mode 100644 third_party/grpc/include/grpcpp/support/time.h create mode 100644 third_party/grpc/include/grpcpp/test/mock_stream.h create mode 100644 third_party/grpc/include/grpcpp/test/server_context_test_spouse.h create mode 100644 third_party/grpc/src/compiler/README.md create mode 100644 third_party/grpc/src/compiler/node_generator.cc create mode 100644 third_party/grpc/src/compiler/node_generator.h create mode 100644 third_party/grpc/src/compiler/node_generator_helpers.h create mode 100644 third_party/grpc/src/compiler/node_plugin.cc create mode 100644 third_party/grpc/src/compiler/php_generator.cc create mode 100644 third_party/grpc/src/compiler/php_generator.h create mode 100644 third_party/grpc/src/compiler/php_generator_helpers.h create mode 100644 third_party/grpc/src/compiler/php_plugin.cc create mode 100644 third_party/grpc/src/compiler/protobuf_plugin.h create mode 100644 third_party/grpc/src/compiler/python_generator_helpers.h create mode 100644 third_party/grpc/src/compiler/python_private_generator.h create mode 100644 third_party/grpc/src/compiler/schema_interface.h delete mode 100644 third_party/grpc/src/core/census/README.md delete mode 100644 third_party/grpc/src/core/census/aggregation.h delete mode 100644 third_party/grpc/src/core/census/context.c delete mode 100644 third_party/grpc/src/core/census/grpc_context.c delete mode 100644 third_party/grpc/src/core/census/grpc_filter.c delete mode 100644 third_party/grpc/src/core/census/grpc_filter.h delete mode 100644 third_party/grpc/src/core/census/initialize.c delete mode 100644 third_party/grpc/src/core/census/operation.c delete mode 100644 third_party/grpc/src/core/census/placeholders.c delete mode 100644 third_party/grpc/src/core/census/rpc_metric_id.h delete mode 100644 third_party/grpc/src/core/census/tracing.c delete mode 100644 third_party/grpc/src/core/channel/channel_args.c delete mode 100644 third_party/grpc/src/core/channel/channel_args.h delete mode 100644 third_party/grpc/src/core/channel/channel_stack.c delete mode 100644 third_party/grpc/src/core/channel/channel_stack.h delete mode 100644 third_party/grpc/src/core/channel/client_channel.c delete mode 100644 third_party/grpc/src/core/channel/client_channel.h delete mode 100644 third_party/grpc/src/core/channel/client_uchannel.c delete mode 100644 third_party/grpc/src/core/channel/client_uchannel.h delete mode 100644 third_party/grpc/src/core/channel/compress_filter.c delete mode 100644 third_party/grpc/src/core/channel/compress_filter.h delete mode 100644 third_party/grpc/src/core/channel/connected_channel.c delete mode 100644 third_party/grpc/src/core/channel/connected_channel.h delete mode 100644 third_party/grpc/src/core/channel/context.h delete mode 100644 third_party/grpc/src/core/channel/http_client_filter.c delete mode 100644 third_party/grpc/src/core/channel/http_client_filter.h delete mode 100644 third_party/grpc/src/core/channel/http_server_filter.c delete mode 100644 third_party/grpc/src/core/channel/http_server_filter.h delete mode 100644 third_party/grpc/src/core/channel/subchannel_call_holder.c delete mode 100644 third_party/grpc/src/core/channel/subchannel_call_holder.h delete mode 100644 third_party/grpc/src/core/client_config/client_config.c delete mode 100644 third_party/grpc/src/core/client_config/client_config.h delete mode 100644 third_party/grpc/src/core/client_config/connector.c delete mode 100644 third_party/grpc/src/core/client_config/connector.h delete mode 100644 third_party/grpc/src/core/client_config/default_initial_connect_string.c delete mode 100644 third_party/grpc/src/core/client_config/initial_connect_string.c delete mode 100644 third_party/grpc/src/core/client_config/initial_connect_string.h delete mode 100644 third_party/grpc/src/core/client_config/lb_policies/pick_first.c delete mode 100644 third_party/grpc/src/core/client_config/lb_policies/pick_first.h delete mode 100644 third_party/grpc/src/core/client_config/lb_policies/round_robin.c delete mode 100644 third_party/grpc/src/core/client_config/lb_policies/round_robin.h delete mode 100644 third_party/grpc/src/core/client_config/lb_policy.c delete mode 100644 third_party/grpc/src/core/client_config/lb_policy.h delete mode 100644 third_party/grpc/src/core/client_config/lb_policy_factory.c delete mode 100644 third_party/grpc/src/core/client_config/lb_policy_factory.h delete mode 100644 third_party/grpc/src/core/client_config/lb_policy_registry.c delete mode 100644 third_party/grpc/src/core/client_config/lb_policy_registry.h delete mode 100644 third_party/grpc/src/core/client_config/resolver.c delete mode 100644 third_party/grpc/src/core/client_config/resolver.h delete mode 100644 third_party/grpc/src/core/client_config/resolver_factory.c delete mode 100644 third_party/grpc/src/core/client_config/resolver_factory.h delete mode 100644 third_party/grpc/src/core/client_config/resolver_registry.c delete mode 100644 third_party/grpc/src/core/client_config/resolver_registry.h delete mode 100644 third_party/grpc/src/core/client_config/resolvers/dns_resolver.c delete mode 100644 third_party/grpc/src/core/client_config/resolvers/dns_resolver.h delete mode 100644 third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.c delete mode 100644 third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.h delete mode 100644 third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.c delete mode 100644 third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.h delete mode 100644 third_party/grpc/src/core/client_config/subchannel.c delete mode 100644 third_party/grpc/src/core/client_config/subchannel.h delete mode 100644 third_party/grpc/src/core/client_config/subchannel_factory.c delete mode 100644 third_party/grpc/src/core/client_config/subchannel_factory.h delete mode 100644 third_party/grpc/src/core/client_config/uri_parser.c delete mode 100644 third_party/grpc/src/core/client_config/uri_parser.h delete mode 100644 third_party/grpc/src/core/compression/algorithm.c delete mode 100644 third_party/grpc/src/core/compression/algorithm_metadata.h delete mode 100644 third_party/grpc/src/core/compression/message_compress.c delete mode 100644 third_party/grpc/src/core/compression/message_compress.h delete mode 100644 third_party/grpc/src/core/debug/trace.c delete mode 100644 third_party/grpc/src/core/debug/trace.h create mode 100644 third_party/grpc/src/core/ext/README.md create mode 100644 third_party/grpc/src/core/ext/filters/census/grpc_context.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/OWNERS rename third_party/grpc/src/core/{client_config => ext/filters/client_channel}/README.md (50%) create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/backup_poller.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/backup_poller.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/channel_connectivity.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/client_channel.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/client_channel.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/client_channel_plugin.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/connector.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/connector.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.c create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/http_proxy.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/http_proxy.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_secure.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy_factory.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/parse_address.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/parse_address.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/request_routing.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/request_routing.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/README.md create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/README.md create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/README.md create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver_factory.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/server_address.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/server_address.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/subchannel.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/subchannel.h create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.cc create mode 100644 third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.h create mode 100644 third_party/grpc/src/core/ext/filters/deadline/deadline_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/deadline/deadline_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/http/client/http_client_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/http/client/http_client_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/http/client_authority_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/http/client_authority_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/http/http_filters_plugin.cc create mode 100644 third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/http/server/http_server_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/http/server/http_server_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/load_reporting/registered_opencensus_objects.h create mode 100644 third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/max_age/max_age_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/max_age/max_age_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/message_size/message_size_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/message_size/message_size_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc create mode 100644 third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h create mode 100644 third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.cc create mode 100644 third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.h create mode 100644 third_party/grpc/src/core/ext/transport/README.md create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/README.md create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/authority.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/authority.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/insecure/README.md create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/secure/README.md create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/server/insecure/README.md create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/server/secure/README.md create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/README.md create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.h rename third_party/grpc/src/core/{transport/chttp2/hpack_parser.c => ext/transport/chttp2/transport/hpack_parser.cc} (59%) create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.h rename third_party/grpc/src/core/{transport/chttp2/hpack_table.c => ext/transport/chttp2/transport/hpack_table.cc} (56%) create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.h rename third_party/grpc/src/core/{transport/chttp2 => ext/transport/chttp2/transport}/hpack_tables.txt (100%) create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/internal.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/parsing.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/stream_lists.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.cc create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.h create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/varint.cc rename third_party/grpc/src/core/{transport/chttp2 => ext/transport/chttp2/transport}/varint.h (52%) create mode 100644 third_party/grpc/src/core/ext/transport/chttp2/transport/writing.cc create mode 100644 third_party/grpc/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc create mode 100644 third_party/grpc/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc create mode 100644 third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.cc create mode 100644 third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.h create mode 100644 third_party/grpc/src/core/ext/transport/inproc/inproc_plugin.cc create mode 100644 third_party/grpc/src/core/ext/transport/inproc/inproc_transport.cc create mode 100644 third_party/grpc/src/core/ext/transport/inproc/inproc_transport.h delete mode 100644 third_party/grpc/src/core/httpcli/format_request.c delete mode 100644 third_party/grpc/src/core/httpcli/format_request.h delete mode 100644 third_party/grpc/src/core/httpcli/httpcli.c delete mode 100644 third_party/grpc/src/core/httpcli/httpcli.h delete mode 100644 third_party/grpc/src/core/httpcli/httpcli_security_connector.c delete mode 100644 third_party/grpc/src/core/httpcli/parser.c delete mode 100644 third_party/grpc/src/core/httpcli/parser.h delete mode 100644 third_party/grpc/src/core/iomgr/closure.c delete mode 100644 third_party/grpc/src/core/iomgr/closure.h delete mode 100644 third_party/grpc/src/core/iomgr/endpoint.c delete mode 100644 third_party/grpc/src/core/iomgr/endpoint.h delete mode 100644 third_party/grpc/src/core/iomgr/endpoint_pair.h delete mode 100644 third_party/grpc/src/core/iomgr/endpoint_pair_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/endpoint_pair_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/exec_ctx.c delete mode 100644 third_party/grpc/src/core/iomgr/exec_ctx.h delete mode 100644 third_party/grpc/src/core/iomgr/executor.c delete mode 100644 third_party/grpc/src/core/iomgr/executor.h delete mode 100644 third_party/grpc/src/core/iomgr/fd_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/fd_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/iocp_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/iocp_windows.h delete mode 100644 third_party/grpc/src/core/iomgr/iomgr.c delete mode 100644 third_party/grpc/src/core/iomgr/iomgr.h delete mode 100644 third_party/grpc/src/core/iomgr/iomgr_internal.h delete mode 100644 third_party/grpc/src/core/iomgr/iomgr_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/iomgr_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/iomgr_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/pollset.h delete mode 100644 third_party/grpc/src/core/iomgr/pollset_multipoller_with_epoll.c delete mode 100644 third_party/grpc/src/core/iomgr/pollset_multipoller_with_poll_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/pollset_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/pollset_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/pollset_set.h delete mode 100644 third_party/grpc/src/core/iomgr/pollset_set_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/pollset_set_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/pollset_set_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/pollset_set_windows.h delete mode 100644 third_party/grpc/src/core/iomgr/pollset_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/pollset_windows.h delete mode 100644 third_party/grpc/src/core/iomgr/resolve_address.h delete mode 100644 third_party/grpc/src/core/iomgr/resolve_address_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/resolve_address_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/sockaddr.h delete mode 100644 third_party/grpc/src/core/iomgr/sockaddr_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/sockaddr_utils.c delete mode 100644 third_party/grpc/src/core/iomgr/sockaddr_utils.h delete mode 100644 third_party/grpc/src/core/iomgr/sockaddr_win32.h delete mode 100644 third_party/grpc/src/core/iomgr/socket_utils_common_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/socket_utils_linux.c delete mode 100644 third_party/grpc/src/core/iomgr/socket_utils_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/socket_utils_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/socket_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/tcp_client.h delete mode 100644 third_party/grpc/src/core/iomgr/tcp_client_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/tcp_client_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/tcp_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/tcp_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/tcp_server.h delete mode 100644 third_party/grpc/src/core/iomgr/tcp_server_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/tcp_server_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/tcp_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/tcp_windows.h delete mode 100644 third_party/grpc/src/core/iomgr/timer.c delete mode 100644 third_party/grpc/src/core/iomgr/timer.h delete mode 100644 third_party/grpc/src/core/iomgr/timer_heap.c delete mode 100644 third_party/grpc/src/core/iomgr/timer_heap.h delete mode 100644 third_party/grpc/src/core/iomgr/timer_internal.h delete mode 100644 third_party/grpc/src/core/iomgr/udp_server.c delete mode 100644 third_party/grpc/src/core/iomgr/udp_server.h delete mode 100644 third_party/grpc/src/core/iomgr/wakeup_fd_eventfd.c delete mode 100644 third_party/grpc/src/core/iomgr/wakeup_fd_nospecial.c delete mode 100644 third_party/grpc/src/core/iomgr/wakeup_fd_pipe.c delete mode 100644 third_party/grpc/src/core/iomgr/wakeup_fd_pipe.h delete mode 100644 third_party/grpc/src/core/iomgr/wakeup_fd_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/workqueue.h delete mode 100644 third_party/grpc/src/core/iomgr/workqueue_posix.c delete mode 100644 third_party/grpc/src/core/iomgr/workqueue_posix.h delete mode 100644 third_party/grpc/src/core/iomgr/workqueue_windows.c delete mode 100644 third_party/grpc/src/core/iomgr/workqueue_windows.h delete mode 100644 third_party/grpc/src/core/json/json.c delete mode 100644 third_party/grpc/src/core/json/json.h delete mode 100644 third_party/grpc/src/core/json/json_common.h delete mode 100644 third_party/grpc/src/core/json/json_writer.h create mode 100644 third_party/grpc/src/core/lib/README.md create mode 100644 third_party/grpc/src/core/lib/avl/avl.cc create mode 100644 third_party/grpc/src/core/lib/avl/avl.h create mode 100644 third_party/grpc/src/core/lib/backoff/backoff.cc create mode 100644 third_party/grpc/src/core/lib/backoff/backoff.h create mode 100644 third_party/grpc/src/core/lib/channel/README.md create mode 100644 third_party/grpc/src/core/lib/channel/channel_args.cc create mode 100644 third_party/grpc/src/core/lib/channel/channel_args.h create mode 100644 third_party/grpc/src/core/lib/channel/channel_stack.cc create mode 100644 third_party/grpc/src/core/lib/channel/channel_stack.h create mode 100644 third_party/grpc/src/core/lib/channel/channel_stack_builder.cc create mode 100644 third_party/grpc/src/core/lib/channel/channel_stack_builder.h create mode 100644 third_party/grpc/src/core/lib/channel/channel_trace.cc create mode 100644 third_party/grpc/src/core/lib/channel/channel_trace.h create mode 100644 third_party/grpc/src/core/lib/channel/channelz.cc create mode 100644 third_party/grpc/src/core/lib/channel/channelz.h create mode 100644 third_party/grpc/src/core/lib/channel/channelz_registry.cc create mode 100644 third_party/grpc/src/core/lib/channel/channelz_registry.h create mode 100644 third_party/grpc/src/core/lib/channel/connected_channel.cc create mode 100644 third_party/grpc/src/core/lib/channel/connected_channel.h create mode 100644 third_party/grpc/src/core/lib/channel/context.h create mode 100644 third_party/grpc/src/core/lib/channel/handshaker.cc create mode 100644 third_party/grpc/src/core/lib/channel/handshaker.h create mode 100644 third_party/grpc/src/core/lib/channel/handshaker_factory.cc create mode 100644 third_party/grpc/src/core/lib/channel/handshaker_factory.h create mode 100644 third_party/grpc/src/core/lib/channel/handshaker_registry.cc create mode 100644 third_party/grpc/src/core/lib/channel/handshaker_registry.h create mode 100644 third_party/grpc/src/core/lib/channel/status_util.cc create mode 100644 third_party/grpc/src/core/lib/channel/status_util.h create mode 100644 third_party/grpc/src/core/lib/compression/algorithm_metadata.h create mode 100644 third_party/grpc/src/core/lib/compression/compression.cc create mode 100644 third_party/grpc/src/core/lib/compression/compression_internal.cc create mode 100644 third_party/grpc/src/core/lib/compression/compression_internal.h create mode 100644 third_party/grpc/src/core/lib/compression/message_compress.cc create mode 100644 third_party/grpc/src/core/lib/compression/message_compress.h create mode 100644 third_party/grpc/src/core/lib/compression/stream_compression.cc create mode 100644 third_party/grpc/src/core/lib/compression/stream_compression.h create mode 100644 third_party/grpc/src/core/lib/compression/stream_compression_gzip.cc create mode 100644 third_party/grpc/src/core/lib/compression/stream_compression_gzip.h create mode 100644 third_party/grpc/src/core/lib/compression/stream_compression_identity.cc create mode 100644 third_party/grpc/src/core/lib/compression/stream_compression_identity.h create mode 100644 third_party/grpc/src/core/lib/debug/stats.cc create mode 100644 third_party/grpc/src/core/lib/debug/stats.h create mode 100644 third_party/grpc/src/core/lib/debug/stats_data.cc create mode 100644 third_party/grpc/src/core/lib/debug/stats_data.h create mode 100644 third_party/grpc/src/core/lib/debug/stats_data.yaml create mode 100644 third_party/grpc/src/core/lib/debug/stats_data_bq_schema.sql create mode 100644 third_party/grpc/src/core/lib/debug/trace.cc create mode 100644 third_party/grpc/src/core/lib/debug/trace.h create mode 100644 third_party/grpc/src/core/lib/gpr/README.md create mode 100644 third_party/grpc/src/core/lib/gpr/alloc.cc create mode 100644 third_party/grpc/src/core/lib/gpr/alloc.h create mode 100644 third_party/grpc/src/core/lib/gpr/arena.cc create mode 100644 third_party/grpc/src/core/lib/gpr/arena.h create mode 100644 third_party/grpc/src/core/lib/gpr/atm.cc create mode 100644 third_party/grpc/src/core/lib/gpr/cpu_iphone.cc create mode 100644 third_party/grpc/src/core/lib/gpr/cpu_linux.cc create mode 100644 third_party/grpc/src/core/lib/gpr/cpu_posix.cc create mode 100644 third_party/grpc/src/core/lib/gpr/cpu_windows.cc create mode 100644 third_party/grpc/src/core/lib/gpr/env.h create mode 100644 third_party/grpc/src/core/lib/gpr/env_linux.cc create mode 100644 third_party/grpc/src/core/lib/gpr/env_posix.cc create mode 100644 third_party/grpc/src/core/lib/gpr/env_windows.cc create mode 100644 third_party/grpc/src/core/lib/gpr/host_port.cc create mode 100644 third_party/grpc/src/core/lib/gpr/host_port.h create mode 100644 third_party/grpc/src/core/lib/gpr/log.cc create mode 100644 third_party/grpc/src/core/lib/gpr/log_android.cc create mode 100644 third_party/grpc/src/core/lib/gpr/log_linux.cc create mode 100644 third_party/grpc/src/core/lib/gpr/log_posix.cc create mode 100644 third_party/grpc/src/core/lib/gpr/log_windows.cc create mode 100644 third_party/grpc/src/core/lib/gpr/mpscq.cc create mode 100644 third_party/grpc/src/core/lib/gpr/mpscq.h create mode 100644 third_party/grpc/src/core/lib/gpr/murmur_hash.cc create mode 100644 third_party/grpc/src/core/lib/gpr/murmur_hash.h create mode 100644 third_party/grpc/src/core/lib/gpr/spinlock.h create mode 100644 third_party/grpc/src/core/lib/gpr/string.cc create mode 100644 third_party/grpc/src/core/lib/gpr/string.h create mode 100644 third_party/grpc/src/core/lib/gpr/string_posix.cc create mode 100644 third_party/grpc/src/core/lib/gpr/string_util_windows.cc create mode 100644 third_party/grpc/src/core/lib/gpr/string_windows.cc create mode 100644 third_party/grpc/src/core/lib/gpr/string_windows.h create mode 100644 third_party/grpc/src/core/lib/gpr/sync.cc create mode 100644 third_party/grpc/src/core/lib/gpr/sync_posix.cc create mode 100644 third_party/grpc/src/core/lib/gpr/sync_windows.cc rename third_party/grpc/src/core/{support/time.c => lib/gpr/time.cc} (51%) rename third_party/grpc/src/core/{support/time_posix.c => lib/gpr/time_posix.cc} (59%) create mode 100644 third_party/grpc/src/core/lib/gpr/time_precise.cc create mode 100644 third_party/grpc/src/core/lib/gpr/time_precise.h create mode 100644 third_party/grpc/src/core/lib/gpr/time_windows.cc create mode 100644 third_party/grpc/src/core/lib/gpr/tls.h create mode 100644 third_party/grpc/src/core/lib/gpr/tls_gcc.h create mode 100644 third_party/grpc/src/core/lib/gpr/tls_msvc.h create mode 100644 third_party/grpc/src/core/lib/gpr/tls_pthread.cc create mode 100644 third_party/grpc/src/core/lib/gpr/tls_pthread.h create mode 100644 third_party/grpc/src/core/lib/gpr/tmpfile.h create mode 100644 third_party/grpc/src/core/lib/gpr/tmpfile_msys.cc create mode 100644 third_party/grpc/src/core/lib/gpr/tmpfile_posix.cc create mode 100644 third_party/grpc/src/core/lib/gpr/tmpfile_windows.cc create mode 100644 third_party/grpc/src/core/lib/gpr/useful.h create mode 100644 third_party/grpc/src/core/lib/gpr/wrap_memcpy.cc create mode 100644 third_party/grpc/src/core/lib/gprpp/README.md create mode 100644 third_party/grpc/src/core/lib/gprpp/abstract.h create mode 100644 third_party/grpc/src/core/lib/gprpp/atomic.h create mode 100644 third_party/grpc/src/core/lib/gprpp/atomic_with_atm.h create mode 100644 third_party/grpc/src/core/lib/gprpp/atomic_with_std.h create mode 100644 third_party/grpc/src/core/lib/gprpp/debug_location.h create mode 100644 third_party/grpc/src/core/lib/gprpp/fork.cc create mode 100644 third_party/grpc/src/core/lib/gprpp/fork.h create mode 100644 third_party/grpc/src/core/lib/gprpp/inlined_vector.h create mode 100644 third_party/grpc/src/core/lib/gprpp/manual_constructor.h create mode 100644 third_party/grpc/src/core/lib/gprpp/memory.h create mode 100644 third_party/grpc/src/core/lib/gprpp/mutex_lock.h create mode 100644 third_party/grpc/src/core/lib/gprpp/orphanable.h create mode 100644 third_party/grpc/src/core/lib/gprpp/ref_counted.h create mode 100644 third_party/grpc/src/core/lib/gprpp/ref_counted_ptr.h create mode 100644 third_party/grpc/src/core/lib/gprpp/thd.h create mode 100644 third_party/grpc/src/core/lib/gprpp/thd_posix.cc create mode 100644 third_party/grpc/src/core/lib/gprpp/thd_windows.cc create mode 100644 third_party/grpc/src/core/lib/http/format_request.cc create mode 100644 third_party/grpc/src/core/lib/http/format_request.h create mode 100644 third_party/grpc/src/core/lib/http/httpcli.cc create mode 100644 third_party/grpc/src/core/lib/http/httpcli.h create mode 100644 third_party/grpc/src/core/lib/http/httpcli_security_connector.cc create mode 100644 third_party/grpc/src/core/lib/http/parser.cc create mode 100644 third_party/grpc/src/core/lib/http/parser.h create mode 100644 third_party/grpc/src/core/lib/iomgr/README.md create mode 100644 third_party/grpc/src/core/lib/iomgr/block_annotate.h create mode 100644 third_party/grpc/src/core/lib/iomgr/buffer_list.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/buffer_list.h create mode 100644 third_party/grpc/src/core/lib/iomgr/call_combiner.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/call_combiner.h create mode 100644 third_party/grpc/src/core/lib/iomgr/cfstream_handle.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/cfstream_handle.h create mode 100644 third_party/grpc/src/core/lib/iomgr/closure.h create mode 100644 third_party/grpc/src/core/lib/iomgr/combiner.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/combiner.h create mode 100644 third_party/grpc/src/core/lib/iomgr/dynamic_annotations.h create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint.h create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.h create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint_pair.h create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint_pair_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint_pair_uv.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/endpoint_pair_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/error.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/error.h create mode 100644 third_party/grpc/src/core/lib/iomgr/error_cfstream.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/error_cfstream.h create mode 100644 third_party/grpc/src/core/lib/iomgr/error_internal.h create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.h create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.h create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_poll_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_poll_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/ev_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/exec_ctx.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/exec_ctx.h create mode 100644 third_party/grpc/src/core/lib/iomgr/executor.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/executor.h create mode 100644 third_party/grpc/src/core/lib/iomgr/fork_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/fork_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/gethostname.h create mode 100644 third_party/grpc/src/core/lib/iomgr/gethostname_fallback.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/gethostname_host_name_max.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/gethostname_sysconf.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/gevent_util.h create mode 100644 third_party/grpc/src/core/lib/iomgr/internal_errqueue.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/internal_errqueue.h create mode 100644 third_party/grpc/src/core/lib/iomgr/iocp_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/iocp_windows.h create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr.h create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_custom.h create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_internal.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_internal.h create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_posix_cfstream.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_uv.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/iomgr_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.h create mode 100644 third_party/grpc/src/core/lib/iomgr/load_file.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/load_file.h create mode 100644 third_party/grpc/src/core/lib/iomgr/lockfree_event.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/lockfree_event.h create mode 100644 third_party/grpc/src/core/lib/iomgr/nameser.h create mode 100644 third_party/grpc/src/core/lib/iomgr/network_status_tracker.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/network_status_tracker.h create mode 100644 third_party/grpc/src/core/lib/iomgr/polling_entity.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/polling_entity.h create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset.h create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_custom.h create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_set.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_set.h create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_set_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_set_custom.h create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_set_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_set_windows.h create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_uv.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_uv.h create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/pollset_windows.h create mode 100644 third_party/grpc/src/core/lib/iomgr/port.h create mode 100644 third_party/grpc/src/core/lib/iomgr/resolve_address.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/resolve_address.h create mode 100644 third_party/grpc/src/core/lib/iomgr/resolve_address_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/resolve_address_custom.h create mode 100644 third_party/grpc/src/core/lib/iomgr/resolve_address_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/resolve_address_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/resource_quota.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/resource_quota.h create mode 100644 third_party/grpc/src/core/lib/iomgr/sockaddr.h create mode 100644 third_party/grpc/src/core/lib/iomgr/sockaddr_custom.h create mode 100644 third_party/grpc/src/core/lib/iomgr/sockaddr_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/sockaddr_utils.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/sockaddr_utils.h create mode 100644 third_party/grpc/src/core/lib/iomgr/sockaddr_windows.h create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_factory_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_factory_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_mutator.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_mutator.h create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_utils.h create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_utils_linux.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_utils_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_utils_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_utils_uv.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_utils_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/socket_windows.cc rename third_party/grpc/src/core/{ => lib}/iomgr/socket_windows.h (60%) create mode 100644 third_party/grpc/src/core/lib/iomgr/sys_epoll_wrapper.h create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_client.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_client.h create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_client_cfstream.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_client_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_client_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_client_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_client_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_custom.h create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server.h create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_common.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_server_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_uv.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_windows.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/tcp_windows.h rename third_party/grpc/src/core/{iomgr/time_averaged_stats.c => lib/iomgr/time_averaged_stats.cc} (51%) rename third_party/grpc/src/core/{ => lib}/iomgr/time_averaged_stats.h (61%) create mode 100644 third_party/grpc/src/core/lib/iomgr/timer.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/timer.h create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_custom.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_custom.h create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_generic.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_generic.h create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_heap.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_heap.h create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_manager.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_manager.h create mode 100644 third_party/grpc/src/core/lib/iomgr/timer_uv.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/udp_server.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/udp_server.h create mode 100644 third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.h create mode 100644 third_party/grpc/src/core/lib/iomgr/unix_sockets_posix_noop.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.h create mode 100644 third_party/grpc/src/core/lib/iomgr/wakeup_fd_eventfd.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/wakeup_fd_nospecial.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc create mode 100644 third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.h create mode 100644 third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.cc rename third_party/grpc/src/core/{ => lib}/iomgr/wakeup_fd_posix.h (51%) create mode 100644 third_party/grpc/src/core/lib/json/json.cc create mode 100644 third_party/grpc/src/core/lib/json/json.h create mode 100644 third_party/grpc/src/core/lib/json/json_common.h rename third_party/grpc/src/core/{json/json_reader.c => lib/json/json_reader.cc} (85%) rename third_party/grpc/src/core/{ => lib}/json/json_reader.h (61%) rename third_party/grpc/src/core/{json/json_string.c => lib/json/json_string.cc} (65%) rename third_party/grpc/src/core/{json/json_writer.c => lib/json/json_writer.cc} (64%) create mode 100644 third_party/grpc/src/core/lib/json/json_writer.h rename third_party/grpc/src/core/{profiling/basic_timers.c => lib/profiling/basic_timers.cc} (54%) rename third_party/grpc/src/core/{ => lib}/profiling/stap_probes.d (100%) create mode 100644 third_party/grpc/src/core/lib/profiling/stap_timers.cc create mode 100644 third_party/grpc/src/core/lib/profiling/timers.h create mode 100644 third_party/grpc/src/core/lib/security/context/security_context.cc create mode 100644 third_party/grpc/src/core/lib/security/context/security_context.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/credentials_metadata.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/google_default/credentials_generic.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/jwt/json_token.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/jwt/json_token.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/local/local_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/local/local_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.cc create mode 100644 third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/load_system_roots.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/load_system_roots_fallback.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/security_connector.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/security_connector.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.h create mode 100644 third_party/grpc/src/core/lib/security/security_connector/ssl_utils.cc create mode 100644 third_party/grpc/src/core/lib/security/security_connector/ssl_utils.h create mode 100644 third_party/grpc/src/core/lib/security/transport/auth_filters.h create mode 100644 third_party/grpc/src/core/lib/security/transport/client_auth_filter.cc create mode 100644 third_party/grpc/src/core/lib/security/transport/secure_endpoint.cc create mode 100644 third_party/grpc/src/core/lib/security/transport/secure_endpoint.h create mode 100644 third_party/grpc/src/core/lib/security/transport/security_handshaker.cc create mode 100644 third_party/grpc/src/core/lib/security/transport/security_handshaker.h create mode 100644 third_party/grpc/src/core/lib/security/transport/server_auth_filter.cc create mode 100644 third_party/grpc/src/core/lib/security/transport/target_authority_table.cc create mode 100644 third_party/grpc/src/core/lib/security/transport/target_authority_table.h create mode 100644 third_party/grpc/src/core/lib/security/transport/tsi_error.cc create mode 100644 third_party/grpc/src/core/lib/security/transport/tsi_error.h create mode 100644 third_party/grpc/src/core/lib/security/util/json_util.cc create mode 100644 third_party/grpc/src/core/lib/security/util/json_util.h rename third_party/grpc/src/core/{security/base64.c => lib/slice/b64.cc} (59%) create mode 100644 third_party/grpc/src/core/lib/slice/b64.h create mode 100644 third_party/grpc/src/core/lib/slice/percent_encoding.cc create mode 100644 third_party/grpc/src/core/lib/slice/percent_encoding.h create mode 100644 third_party/grpc/src/core/lib/slice/slice.cc create mode 100644 third_party/grpc/src/core/lib/slice/slice_buffer.cc create mode 100644 third_party/grpc/src/core/lib/slice/slice_hash_table.h create mode 100644 third_party/grpc/src/core/lib/slice/slice_intern.cc create mode 100644 third_party/grpc/src/core/lib/slice/slice_internal.h create mode 100644 third_party/grpc/src/core/lib/slice/slice_string_helpers.cc create mode 100644 third_party/grpc/src/core/lib/slice/slice_string_helpers.h create mode 100644 third_party/grpc/src/core/lib/slice/slice_traits.h create mode 100644 third_party/grpc/src/core/lib/slice/slice_weak_hash_table.h create mode 100644 third_party/grpc/src/core/lib/surface/README.md create mode 100644 third_party/grpc/src/core/lib/surface/api_trace.cc create mode 100644 third_party/grpc/src/core/lib/surface/api_trace.h create mode 100644 third_party/grpc/src/core/lib/surface/byte_buffer.cc create mode 100644 third_party/grpc/src/core/lib/surface/byte_buffer_reader.cc create mode 100644 third_party/grpc/src/core/lib/surface/call.cc create mode 100644 third_party/grpc/src/core/lib/surface/call.h create mode 100644 third_party/grpc/src/core/lib/surface/call_details.cc create mode 100644 third_party/grpc/src/core/lib/surface/call_log_batch.cc create mode 100644 third_party/grpc/src/core/lib/surface/call_test_only.h create mode 100644 third_party/grpc/src/core/lib/surface/channel.cc create mode 100644 third_party/grpc/src/core/lib/surface/channel.h create mode 100644 third_party/grpc/src/core/lib/surface/channel_init.cc create mode 100644 third_party/grpc/src/core/lib/surface/channel_init.h create mode 100644 third_party/grpc/src/core/lib/surface/channel_ping.cc create mode 100644 third_party/grpc/src/core/lib/surface/channel_stack_type.cc create mode 100644 third_party/grpc/src/core/lib/surface/channel_stack_type.h create mode 100644 third_party/grpc/src/core/lib/surface/completion_queue.cc create mode 100644 third_party/grpc/src/core/lib/surface/completion_queue.h create mode 100644 third_party/grpc/src/core/lib/surface/completion_queue_factory.cc create mode 100644 third_party/grpc/src/core/lib/surface/completion_queue_factory.h create mode 100644 third_party/grpc/src/core/lib/surface/event_string.cc create mode 100644 third_party/grpc/src/core/lib/surface/event_string.h create mode 100644 third_party/grpc/src/core/lib/surface/init.cc create mode 100644 third_party/grpc/src/core/lib/surface/init.h create mode 100644 third_party/grpc/src/core/lib/surface/init_secure.cc create mode 100644 third_party/grpc/src/core/lib/surface/init_unsecure.cc create mode 100644 third_party/grpc/src/core/lib/surface/lame_client.cc create mode 100644 third_party/grpc/src/core/lib/surface/lame_client.h create mode 100644 third_party/grpc/src/core/lib/surface/metadata_array.cc create mode 100644 third_party/grpc/src/core/lib/surface/server.cc create mode 100644 third_party/grpc/src/core/lib/surface/server.h create mode 100644 third_party/grpc/src/core/lib/surface/validate_metadata.cc create mode 100644 third_party/grpc/src/core/lib/surface/validate_metadata.h create mode 100644 third_party/grpc/src/core/lib/surface/version.cc create mode 100644 third_party/grpc/src/core/lib/transport/README.md create mode 100644 third_party/grpc/src/core/lib/transport/bdp_estimator.cc create mode 100644 third_party/grpc/src/core/lib/transport/bdp_estimator.h create mode 100644 third_party/grpc/src/core/lib/transport/byte_stream.cc create mode 100644 third_party/grpc/src/core/lib/transport/byte_stream.h create mode 100644 third_party/grpc/src/core/lib/transport/connectivity_state.cc create mode 100644 third_party/grpc/src/core/lib/transport/connectivity_state.h create mode 100644 third_party/grpc/src/core/lib/transport/error_utils.cc create mode 100644 third_party/grpc/src/core/lib/transport/error_utils.h create mode 100644 third_party/grpc/src/core/lib/transport/http2_errors.h create mode 100644 third_party/grpc/src/core/lib/transport/metadata.cc create mode 100644 third_party/grpc/src/core/lib/transport/metadata.h create mode 100644 third_party/grpc/src/core/lib/transport/metadata_batch.cc create mode 100644 third_party/grpc/src/core/lib/transport/metadata_batch.h create mode 100644 third_party/grpc/src/core/lib/transport/pid_controller.cc create mode 100644 third_party/grpc/src/core/lib/transport/pid_controller.h create mode 100644 third_party/grpc/src/core/lib/transport/service_config.cc create mode 100644 third_party/grpc/src/core/lib/transport/service_config.h create mode 100644 third_party/grpc/src/core/lib/transport/static_metadata.cc create mode 100644 third_party/grpc/src/core/lib/transport/static_metadata.h create mode 100644 third_party/grpc/src/core/lib/transport/status_conversion.cc create mode 100644 third_party/grpc/src/core/lib/transport/status_conversion.h create mode 100644 third_party/grpc/src/core/lib/transport/status_metadata.cc create mode 100644 third_party/grpc/src/core/lib/transport/status_metadata.h create mode 100644 third_party/grpc/src/core/lib/transport/timeout_encoding.cc create mode 100644 third_party/grpc/src/core/lib/transport/timeout_encoding.h create mode 100644 third_party/grpc/src/core/lib/transport/transport.cc create mode 100644 third_party/grpc/src/core/lib/transport/transport.h create mode 100644 third_party/grpc/src/core/lib/transport/transport_impl.h create mode 100644 third_party/grpc/src/core/lib/transport/transport_op_string.cc create mode 100644 third_party/grpc/src/core/lib/uri/uri_parser.cc create mode 100644 third_party/grpc/src/core/lib/uri/uri_parser.h create mode 100644 third_party/grpc/src/core/plugin_registry/grpc_cronet_plugin_registry.cc create mode 100644 third_party/grpc/src/core/plugin_registry/grpc_plugin_registry.cc create mode 100644 third_party/grpc/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc delete mode 100644 third_party/grpc/src/core/profiling/stap_timers.c delete mode 100644 third_party/grpc/src/core/profiling/timers.h delete mode 100644 third_party/grpc/src/core/security/auth_filters.h delete mode 100644 third_party/grpc/src/core/security/base64.h delete mode 100644 third_party/grpc/src/core/security/client_auth_filter.c delete mode 100644 third_party/grpc/src/core/security/credentials.c delete mode 100644 third_party/grpc/src/core/security/credentials.h delete mode 100644 third_party/grpc/src/core/security/credentials_metadata.c delete mode 100644 third_party/grpc/src/core/security/credentials_posix.c delete mode 100644 third_party/grpc/src/core/security/credentials_win32.c delete mode 100644 third_party/grpc/src/core/security/google_default_credentials.c delete mode 100644 third_party/grpc/src/core/security/handshake.c delete mode 100644 third_party/grpc/src/core/security/handshake.h delete mode 100644 third_party/grpc/src/core/security/json_token.c delete mode 100644 third_party/grpc/src/core/security/json_token.h delete mode 100644 third_party/grpc/src/core/security/jwt_verifier.c delete mode 100644 third_party/grpc/src/core/security/jwt_verifier.h delete mode 100644 third_party/grpc/src/core/security/secure_endpoint.c delete mode 100644 third_party/grpc/src/core/security/secure_endpoint.h delete mode 100644 third_party/grpc/src/core/security/security_connector.c delete mode 100644 third_party/grpc/src/core/security/security_connector.h delete mode 100644 third_party/grpc/src/core/security/security_context.c delete mode 100644 third_party/grpc/src/core/security/security_context.h delete mode 100644 third_party/grpc/src/core/security/server_auth_filter.c delete mode 100644 third_party/grpc/src/core/security/server_secure_chttp2.c delete mode 100644 third_party/grpc/src/core/statistics/census_init.c delete mode 100644 third_party/grpc/src/core/statistics/census_interface.h delete mode 100644 third_party/grpc/src/core/statistics/census_log.c delete mode 100644 third_party/grpc/src/core/statistics/census_log.h delete mode 100644 third_party/grpc/src/core/statistics/census_rpc_stats.c delete mode 100644 third_party/grpc/src/core/statistics/census_rpc_stats.h delete mode 100644 third_party/grpc/src/core/statistics/census_tracing.c delete mode 100644 third_party/grpc/src/core/statistics/census_tracing.h delete mode 100644 third_party/grpc/src/core/statistics/hash_table.c delete mode 100644 third_party/grpc/src/core/statistics/hash_table.h delete mode 100644 third_party/grpc/src/core/statistics/window_stats.c delete mode 100644 third_party/grpc/src/core/statistics/window_stats.h delete mode 100644 third_party/grpc/src/core/support/alloc.c delete mode 100644 third_party/grpc/src/core/support/avl.c delete mode 100644 third_party/grpc/src/core/support/block_annotate.h delete mode 100644 third_party/grpc/src/core/support/cmdline.c delete mode 100644 third_party/grpc/src/core/support/cpu_iphone.c delete mode 100644 third_party/grpc/src/core/support/cpu_linux.c delete mode 100644 third_party/grpc/src/core/support/cpu_posix.c delete mode 100644 third_party/grpc/src/core/support/cpu_windows.c delete mode 100644 third_party/grpc/src/core/support/env.h delete mode 100644 third_party/grpc/src/core/support/env_linux.c delete mode 100644 third_party/grpc/src/core/support/env_posix.c delete mode 100644 third_party/grpc/src/core/support/env_win32.c delete mode 100644 third_party/grpc/src/core/support/file.c delete mode 100644 third_party/grpc/src/core/support/file.h delete mode 100644 third_party/grpc/src/core/support/file_posix.c delete mode 100644 third_party/grpc/src/core/support/file_win32.c delete mode 100644 third_party/grpc/src/core/support/histogram.c delete mode 100644 third_party/grpc/src/core/support/host_port.c delete mode 100644 third_party/grpc/src/core/support/log.c delete mode 100644 third_party/grpc/src/core/support/log_android.c delete mode 100644 third_party/grpc/src/core/support/log_linux.c delete mode 100644 third_party/grpc/src/core/support/log_posix.c delete mode 100644 third_party/grpc/src/core/support/log_win32.c delete mode 100644 third_party/grpc/src/core/support/murmur_hash.c delete mode 100644 third_party/grpc/src/core/support/murmur_hash.h delete mode 100644 third_party/grpc/src/core/support/slice.c delete mode 100644 third_party/grpc/src/core/support/slice_buffer.c delete mode 100644 third_party/grpc/src/core/support/stack_lockfree.c delete mode 100644 third_party/grpc/src/core/support/stack_lockfree.h delete mode 100644 third_party/grpc/src/core/support/string.c delete mode 100644 third_party/grpc/src/core/support/string.h delete mode 100644 third_party/grpc/src/core/support/string_posix.c delete mode 100644 third_party/grpc/src/core/support/string_win32.c delete mode 100644 third_party/grpc/src/core/support/string_win32.h delete mode 100644 third_party/grpc/src/core/support/subprocess_posix.c delete mode 100644 third_party/grpc/src/core/support/subprocess_windows.c delete mode 100644 third_party/grpc/src/core/support/sync.c delete mode 100644 third_party/grpc/src/core/support/sync_posix.c delete mode 100644 third_party/grpc/src/core/support/sync_win32.c delete mode 100644 third_party/grpc/src/core/support/thd.c delete mode 100644 third_party/grpc/src/core/support/thd_internal.h delete mode 100644 third_party/grpc/src/core/support/thd_posix.c delete mode 100644 third_party/grpc/src/core/support/thd_win32.c delete mode 100644 third_party/grpc/src/core/support/time_precise.c delete mode 100644 third_party/grpc/src/core/support/time_precise.h delete mode 100644 third_party/grpc/src/core/support/time_win32.c delete mode 100644 third_party/grpc/src/core/support/tls_pthread.c delete mode 100644 third_party/grpc/src/core/support/wrap_memcpy.c delete mode 100644 third_party/grpc/src/core/surface/alarm.c delete mode 100644 third_party/grpc/src/core/surface/api_trace.c delete mode 100644 third_party/grpc/src/core/surface/api_trace.h delete mode 100644 third_party/grpc/src/core/surface/byte_buffer.c delete mode 100644 third_party/grpc/src/core/surface/byte_buffer_reader.c delete mode 100644 third_party/grpc/src/core/surface/call.c delete mode 100644 third_party/grpc/src/core/surface/call.h delete mode 100644 third_party/grpc/src/core/surface/call_details.c delete mode 100644 third_party/grpc/src/core/surface/call_log_batch.c delete mode 100644 third_party/grpc/src/core/surface/call_test_only.h delete mode 100644 third_party/grpc/src/core/surface/channel.c delete mode 100644 third_party/grpc/src/core/surface/channel.h delete mode 100644 third_party/grpc/src/core/surface/channel_connectivity.c delete mode 100644 third_party/grpc/src/core/surface/channel_create.c delete mode 100644 third_party/grpc/src/core/surface/channel_ping.c delete mode 100644 third_party/grpc/src/core/surface/completion_queue.c delete mode 100644 third_party/grpc/src/core/surface/completion_queue.h delete mode 100644 third_party/grpc/src/core/surface/event_string.c delete mode 100644 third_party/grpc/src/core/surface/event_string.h delete mode 100644 third_party/grpc/src/core/surface/init.c delete mode 100644 third_party/grpc/src/core/surface/init.h delete mode 100644 third_party/grpc/src/core/surface/init_secure.c delete mode 100644 third_party/grpc/src/core/surface/init_unsecure.c delete mode 100644 third_party/grpc/src/core/surface/lame_client.c delete mode 100644 third_party/grpc/src/core/surface/metadata_array.c delete mode 100644 third_party/grpc/src/core/surface/secure_channel_create.c delete mode 100644 third_party/grpc/src/core/surface/server.c delete mode 100644 third_party/grpc/src/core/surface/server.h delete mode 100644 third_party/grpc/src/core/surface/server_chttp2.c delete mode 100644 third_party/grpc/src/core/surface/server_create.c delete mode 100644 third_party/grpc/src/core/surface/surface_trace.h delete mode 100644 third_party/grpc/src/core/surface/validate_metadata.c delete mode 100644 third_party/grpc/src/core/surface/version.c delete mode 100644 third_party/grpc/src/core/transport/byte_stream.c delete mode 100644 third_party/grpc/src/core/transport/byte_stream.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/alpn.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/alpn.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/bin_encoder.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/bin_encoder.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_data.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_data.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_goaway.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_goaway.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_ping.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_ping.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_rst_stream.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_rst_stream.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_settings.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_settings.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_window_update.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/frame_window_update.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/hpack_encoder.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/hpack_encoder.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/hpack_parser.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/hpack_table.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/http2_errors.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/huffsyms.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/huffsyms.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/incoming_metadata.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/incoming_metadata.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/internal.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/parsing.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/status_conversion.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/status_conversion.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/stream_lists.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/stream_map.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/stream_map.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/timeout_encoding.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/timeout_encoding.h delete mode 100644 third_party/grpc/src/core/transport/chttp2/varint.c delete mode 100644 third_party/grpc/src/core/transport/chttp2/writing.c delete mode 100644 third_party/grpc/src/core/transport/chttp2_transport.c delete mode 100644 third_party/grpc/src/core/transport/chttp2_transport.h delete mode 100644 third_party/grpc/src/core/transport/connectivity_state.c delete mode 100644 third_party/grpc/src/core/transport/connectivity_state.h delete mode 100644 third_party/grpc/src/core/transport/metadata.c delete mode 100644 third_party/grpc/src/core/transport/metadata.h delete mode 100644 third_party/grpc/src/core/transport/metadata_batch.c delete mode 100644 third_party/grpc/src/core/transport/metadata_batch.h delete mode 100644 third_party/grpc/src/core/transport/static_metadata.c delete mode 100644 third_party/grpc/src/core/transport/static_metadata.h delete mode 100644 third_party/grpc/src/core/transport/transport.c delete mode 100644 third_party/grpc/src/core/transport/transport.h delete mode 100644 third_party/grpc/src/core/transport/transport_impl.h delete mode 100644 third_party/grpc/src/core/transport/transport_op_string.c create mode 100644 third_party/grpc/src/core/tsi/README.md create mode 100644 third_party/grpc/src/core/tsi/alts/crypt/aes_gcm.cc create mode 100644 third_party/grpc/src/core/tsi/alts/crypt/gsec.cc create mode 100644 third_party/grpc/src/core/tsi/alts/crypt/gsec.h create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.cc create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.h create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.cc create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.h create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.cc create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.h create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.cc create mode 100644 third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.cc create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.cc create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.cc create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.c create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.c create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/proto/altscontext.proto create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.options create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.proto create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/proto/transport_security_common.proto create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.c create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.h create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.cc create mode 100644 third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.h create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc create mode 100644 third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h delete mode 100644 third_party/grpc/src/core/tsi/fake_transport_security.c create mode 100644 third_party/grpc/src/core/tsi/fake_transport_security.cc create mode 100644 third_party/grpc/src/core/tsi/grpc_shadow_boringssl.h create mode 100644 third_party/grpc/src/core/tsi/local_transport_security.cc create mode 100644 third_party/grpc/src/core/tsi/local_transport_security.h create mode 100644 third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session.h create mode 100644 third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc create mode 100644 third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.cc create mode 100644 third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.h create mode 100644 third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc delete mode 100644 third_party/grpc/src/core/tsi/ssl_transport_security.c create mode 100644 third_party/grpc/src/core/tsi/ssl_transport_security.cc delete mode 100644 third_party/grpc/src/core/tsi/test_creds/README delete mode 100644 third_party/grpc/src/core/tsi/test_creds/badclient.key delete mode 100644 third_party/grpc/src/core/tsi/test_creds/badclient.pem delete mode 100644 third_party/grpc/src/core/tsi/test_creds/badserver.key delete mode 100644 third_party/grpc/src/core/tsi/test_creds/badserver.pem delete mode 100644 third_party/grpc/src/core/tsi/test_creds/ca-openssl.cnf delete mode 100644 third_party/grpc/src/core/tsi/test_creds/ca.key delete mode 100644 third_party/grpc/src/core/tsi/test_creds/ca.pem delete mode 100644 third_party/grpc/src/core/tsi/test_creds/client.key delete mode 100644 third_party/grpc/src/core/tsi/test_creds/client.pem delete mode 100644 third_party/grpc/src/core/tsi/test_creds/server0.key delete mode 100644 third_party/grpc/src/core/tsi/test_creds/server0.pem delete mode 100644 third_party/grpc/src/core/tsi/test_creds/server1-openssl.cnf delete mode 100644 third_party/grpc/src/core/tsi/test_creds/server1.key delete mode 100644 third_party/grpc/src/core/tsi/test_creds/server1.pem delete mode 100644 third_party/grpc/src/core/tsi/transport_security.c create mode 100644 third_party/grpc/src/core/tsi/transport_security.cc create mode 100644 third_party/grpc/src/core/tsi/transport_security_grpc.cc create mode 100644 third_party/grpc/src/core/tsi/transport_security_grpc.h delete mode 100644 third_party/grpc/src/cpp/client/channel.cc create mode 100644 third_party/grpc/src/cpp/client/channel_cc.cc create mode 100644 third_party/grpc/src/cpp/client/client_interceptor.cc create mode 100644 third_party/grpc/src/cpp/client/create_channel_posix.cc delete mode 100644 third_party/grpc/src/cpp/client/credentials.cc create mode 100644 third_party/grpc/src/cpp/client/credentials_cc.cc create mode 100644 third_party/grpc/src/cpp/client/cronet_credentials.cc create mode 100644 third_party/grpc/src/cpp/codegen/codegen_init.cc delete mode 100644 third_party/grpc/src/cpp/codegen/grpc_library.cc delete mode 100644 third_party/grpc/src/cpp/common/call.cc create mode 100644 third_party/grpc/src/cpp/common/channel_filter.cc create mode 100644 third_party/grpc/src/cpp/common/channel_filter.h delete mode 100644 third_party/grpc/src/cpp/common/completion_queue.cc create mode 100644 third_party/grpc/src/cpp/common/completion_queue_cc.cc create mode 100644 third_party/grpc/src/cpp/common/core_codegen.cc delete mode 100644 third_party/grpc/src/cpp/common/create_auth_context.h create mode 100644 third_party/grpc/src/cpp/common/resource_quota_cc.cc create mode 100644 third_party/grpc/src/cpp/common/version_cc.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/channel_filter.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/channel_filter.h create mode 100644 third_party/grpc/src/cpp/ext/filters/census/client_filter.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/client_filter.h create mode 100644 third_party/grpc/src/cpp/ext/filters/census/context.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/context.h create mode 100644 third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.h create mode 100644 third_party/grpc/src/cpp/ext/filters/census/measures.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/measures.h create mode 100644 third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.h create mode 100644 third_party/grpc/src/cpp/ext/filters/census/server_filter.cc create mode 100644 third_party/grpc/src/cpp/ext/filters/census/server_filter.h create mode 100644 third_party/grpc/src/cpp/ext/filters/census/views.cc create mode 100644 third_party/grpc/src/cpp/ext/proto_server_reflection.cc create mode 100644 third_party/grpc/src/cpp/ext/proto_server_reflection.h create mode 100644 third_party/grpc/src/cpp/ext/proto_server_reflection_plugin.cc delete mode 100644 third_party/grpc/src/cpp/proto/proto_utils.cc create mode 100644 third_party/grpc/src/cpp/server/channel_argument_option.cc create mode 100644 third_party/grpc/src/cpp/server/channelz/channelz_service.cc create mode 100644 third_party/grpc/src/cpp/server/channelz/channelz_service.h create mode 100644 third_party/grpc/src/cpp/server/channelz/channelz_service_plugin.cc delete mode 100644 third_party/grpc/src/cpp/server/fixed_size_thread_pool.cc delete mode 100644 third_party/grpc/src/cpp/server/fixed_size_thread_pool.h create mode 100644 third_party/grpc/src/cpp/server/health/default_health_check_service.cc create mode 100644 third_party/grpc/src/cpp/server/health/default_health_check_service.h create mode 100644 third_party/grpc/src/cpp/server/health/health_check_service.cc create mode 100644 third_party/grpc/src/cpp/server/health/health_check_service_server_builder_option.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/constants.h create mode 100644 third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats.h create mode 100644 third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_linux.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_macos.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_windows.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_data_store.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_data_store.h create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_reporter.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_reporter.h create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.h create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc create mode 100644 third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h create mode 100644 third_party/grpc/src/cpp/server/load_reporter/util.cc delete mode 100644 third_party/grpc/src/cpp/server/server.cc create mode 100644 third_party/grpc/src/cpp/server/server_cc.cc create mode 100644 third_party/grpc/src/cpp/server/server_posix.cc create mode 100644 third_party/grpc/src/cpp/thread_manager/thread_manager.cc create mode 100644 third_party/grpc/src/cpp/thread_manager/thread_manager.h delete mode 100644 third_party/grpc/src/cpp/util/byte_buffer.cc create mode 100644 third_party/grpc/src/cpp/util/byte_buffer_cc.cc create mode 100644 third_party/grpc/src/cpp/util/core_stats.cc create mode 100644 third_party/grpc/src/cpp/util/core_stats.h create mode 100644 third_party/grpc/src/cpp/util/error_details.cc delete mode 100644 third_party/grpc/src/cpp/util/slice.cc delete mode 100644 third_party/grpc/src/cpp/util/time.cc create mode 100644 third_party/grpc/src/cpp/util/time_cc.cc create mode 100644 third_party/nanopb/CONTRIBUTING.md diff --git a/third_party/grpc/BUILD b/third_party/grpc/BUILD index e1463a24f20009..3656b9d8425d43 100644 --- a/third_party/grpc/BUILD +++ b/third_party/grpc/BUILD @@ -1,6 +1,33 @@ -licenses(["unencumbered"]) +# gRPC Bazel BUILD file. +# +# Copyright 2016 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -package(default_visibility = ["//visibility:public"]) +licenses(["notice"]) # Apache v2 + +package( + default_visibility = ["//visibility:public"], + features = [ + "-layering_check", + "-parse_headers", + ], +) + +load( + ":build_defs.bzl", + "grpc_cc_library", +) filegroup( name = "srcs", @@ -9,32 +36,33 @@ filegroup( config_setting( name = "windows", - values = { "cpu": "x64_windows" }, + values = {"cpu": "x64_windows"}, ) filegroup( name = "bootstrap-grpc-jars", srcs = [ "grpc-auth-1.10.0.jar", + "grpc-context-1.10.0.jar", + "grpc-core-1.10.0.jar", "grpc-netty-1.10.0.jar", "grpc-protobuf-1.10.0.jar", "grpc-protobuf-lite-1.10.0.jar", "grpc-stub-1.10.0.jar", - "grpc-core-1.10.0.jar", - "grpc-context-1.10.0.jar", ], ) java_import( name = "grpc-jar", jars = [":bootstrap-grpc-jars"], + runtime_deps = [ + "//third_party:netty", + "//third_party:opencensus-api", + ], deps = [ - "//third_party:guava", - "//third_party:instrumentation", + "//third_party:guava", + "//third_party:instrumentation", ], - runtime_deps = ["//third_party:netty", - "//third_party:opencensus-api", - ], ) cc_binary( @@ -50,14 +78,7 @@ cc_binary( cc_binary( name = "cpp_plugin", - srcs = [ - "src/compiler/config.h", - "src/compiler/cpp_generator.cc", - "src/compiler/cpp_generator.h", - "src/compiler/cpp_generator_helpers.h", - "src/compiler/cpp_plugin.cc", - "src/compiler/generator_helpers.h", - ] + glob(["include/**/*.h"]), + srcs = ["src/compiler/cpp_plugin.cc"], copts = [ "-Ithird_party/grpc/include", "-Ithird_party/grpc", @@ -66,435 +87,1482 @@ cc_binary( "-lm", ], visibility = ["//visibility:public"], - deps = ["//third_party/protobuf:protoc_lib"], + deps = [":grpc_plugin_support"], ) -cc_library( +GPR_PUBLIC_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_windows.h", + "include/grpc/support/cpu.h", + "include/grpc/support/log.h", + "include/grpc/support/log_windows.h", + "include/grpc/support/port_platform.h", + "include/grpc/support/string_util.h", + "include/grpc/support/sync.h", + "include/grpc/support/sync_custom.h", + "include/grpc/support/sync_generic.h", + "include/grpc/support/sync_posix.h", + "include/grpc/support/sync_windows.h", + "include/grpc/support/thd_id.h", + "include/grpc/support/time.h", +] + +GRPC_PUBLIC_HDRS = [ + "include/grpc/byte_buffer.h", + "include/grpc/byte_buffer_reader.h", + "include/grpc/compression.h", + "include/grpc/fork.h", + "include/grpc/grpc.h", + "include/grpc/grpc_posix.h", + "include/grpc/grpc_security_constants.h", + "include/grpc/slice.h", + "include/grpc/slice_buffer.h", + "include/grpc/status.h", + "include/grpc/load_reporting.h", + "include/grpc/support/workaround_list.h", +] + +GRPCXX_SRCS = [ + "src/cpp/client/channel_cc.cc", + "src/cpp/client/client_context.cc", + "src/cpp/client/client_interceptor.cc", + "src/cpp/client/create_channel.cc", + "src/cpp/client/create_channel_internal.cc", + "src/cpp/client/create_channel_posix.cc", + "src/cpp/client/credentials_cc.cc", + "src/cpp/client/generic_stub.cc", + "src/cpp/common/alarm.cc", + "src/cpp/common/channel_arguments.cc", + "src/cpp/common/channel_filter.cc", + "src/cpp/common/completion_queue_cc.cc", + "src/cpp/common/core_codegen.cc", + "src/cpp/common/resource_quota_cc.cc", + "src/cpp/common/rpc_method.cc", + "src/cpp/common/version_cc.cc", + "src/cpp/server/async_generic_service.cc", + "src/cpp/server/channel_argument_option.cc", + "src/cpp/server/create_default_thread_pool.cc", + "src/cpp/server/dynamic_thread_pool.cc", + "src/cpp/server/health/default_health_check_service.cc", + "src/cpp/server/health/health_check_service.cc", + "src/cpp/server/health/health_check_service_server_builder_option.cc", + "src/cpp/server/server_builder.cc", + "src/cpp/server/server_cc.cc", + "src/cpp/server/server_context.cc", + "src/cpp/server/server_credentials.cc", + "src/cpp/server/server_posix.cc", + "src/cpp/thread_manager/thread_manager.cc", + "src/cpp/util/byte_buffer_cc.cc", + "src/cpp/util/status.cc", + "src/cpp/util/string_ref.cc", + "src/cpp/util/time_cc.cc", +] + +GRPCXX_HDRS = [ + "src/cpp/client/create_channel_internal.h", + "src/cpp/common/channel_filter.h", + "src/cpp/server/dynamic_thread_pool.h", + "src/cpp/server/health/default_health_check_service.h", + "src/cpp/server/thread_pool_interface.h", + "src/cpp/thread_manager/thread_manager.h", +] + +GRPCXX_PUBLIC_HDRS = [ + "include/grpc++/alarm.h", + "include/grpc++/channel.h", + "include/grpc++/client_context.h", + "include/grpc++/completion_queue.h", + "include/grpc++/create_channel.h", + "include/grpc++/create_channel_posix.h", + "include/grpc++/ext/health_check_service_server_builder_option.h", + "include/grpc++/generic/async_generic_service.h", + "include/grpc++/generic/generic_stub.h", + "include/grpc++/grpc++.h", + "include/grpc++/health_check_service_interface.h", + "include/grpc++/impl/call.h", + "include/grpc++/impl/channel_argument_option.h", + "include/grpc++/impl/client_unary_call.h", + "include/grpc++/impl/codegen/core_codegen.h", + "include/grpc++/impl/grpc_library.h", + "include/grpc++/impl/method_handler_impl.h", + "include/grpc++/impl/rpc_method.h", + "include/grpc++/impl/rpc_service_method.h", + "include/grpc++/impl/serialization_traits.h", + "include/grpc++/impl/server_builder_option.h", + "include/grpc++/impl/server_builder_plugin.h", + "include/grpc++/impl/server_initializer.h", + "include/grpc++/impl/service_type.h", + "include/grpc++/impl/sync_cxx11.h", + "include/grpc++/impl/sync_no_cxx11.h", + "include/grpc++/resource_quota.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++/server_posix.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/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", + "include/grpcpp/alarm.h", + "include/grpcpp/alarm_impl.h", + "include/grpcpp/channel.h", + "include/grpcpp/client_context.h", + "include/grpcpp/completion_queue.h", + "include/grpcpp/create_channel.h", + "include/grpcpp/create_channel_posix.h", + "include/grpcpp/ext/health_check_service_server_builder_option.h", + "include/grpcpp/generic/async_generic_service.h", + "include/grpcpp/generic/generic_stub.h", + "include/grpcpp/grpcpp.h", + "include/grpcpp/health_check_service_interface.h", + "include/grpcpp/impl/call.h", + "include/grpcpp/impl/channel_argument_option.h", + "include/grpcpp/impl/client_unary_call.h", + "include/grpcpp/impl/codegen/core_codegen.h", + "include/grpcpp/impl/grpc_library.h", + "include/grpcpp/impl/method_handler_impl.h", + "include/grpcpp/impl/rpc_method.h", + "include/grpcpp/impl/rpc_service_method.h", + "include/grpcpp/impl/serialization_traits.h", + "include/grpcpp/impl/server_builder_option.h", + "include/grpcpp/impl/server_builder_plugin.h", + "include/grpcpp/impl/server_initializer.h", + "include/grpcpp/impl/service_type.h", + "include/grpcpp/impl/sync_cxx11.h", + "include/grpcpp/impl/sync_no_cxx11.h", + "include/grpcpp/resource_quota.h", + "include/grpcpp/security/auth_context.h", + "include/grpcpp/security/auth_metadata_processor.h", + "include/grpcpp/security/credentials.h", + "include/grpcpp/security/server_credentials.h", + "include/grpcpp/server.h", + "include/grpcpp/server_builder.h", + "include/grpcpp/server_context.h", + "include/grpcpp/server_posix.h", + "include/grpcpp/support/async_stream.h", + "include/grpcpp/support/async_unary_call.h", + "include/grpcpp/support/byte_buffer.h", + "include/grpcpp/support/channel_arguments.h", + "include/grpcpp/support/client_callback.h", + "include/grpcpp/support/client_interceptor.h", + "include/grpcpp/support/config.h", + "include/grpcpp/support/interceptor.h", + "include/grpcpp/support/proto_buffer_reader.h", + "include/grpcpp/support/proto_buffer_writer.h", + "include/grpcpp/support/server_callback.h", + "include/grpcpp/support/server_interceptor.h", + "include/grpcpp/support/slice.h", + "include/grpcpp/support/status.h", + "include/grpcpp/support/status_code_enum.h", + "include/grpcpp/support/string_ref.h", + "include/grpcpp/support/stub_options.h", + "include/grpcpp/support/sync_stream.h", + "include/grpcpp/support/time.h", +] + +grpc_cc_library( + name = "gpr", + language = "c++", + public_hdrs = GPR_PUBLIC_HDRS, + standalone = True, + deps = [ + "gpr_base", + ], +) + +grpc_cc_library( name = "grpc_unsecure", srcs = [ - "src/core/census/aggregation.h", - "src/core/census/context.c", - "src/core/census/grpc_context.c", - "src/core/census/grpc_filter.c", - "src/core/census/grpc_filter.h", - "src/core/census/initialize.c", - "src/core/census/operation.c", - "src/core/census/placeholders.c", - "src/core/census/rpc_metric_id.h", - "src/core/census/tracing.c", - "src/core/channel/channel_args.c", - "src/core/channel/channel_args.h", - "src/core/channel/channel_stack.c", - "src/core/channel/channel_stack.h", - "src/core/channel/client_channel.c", - "src/core/channel/client_channel.h", - "src/core/channel/client_uchannel.c", - "src/core/channel/client_uchannel.h", - "src/core/channel/compress_filter.c", - "src/core/channel/compress_filter.h", - "src/core/channel/connected_channel.c", - "src/core/channel/connected_channel.h", - "src/core/channel/context.h", - "src/core/channel/http_client_filter.c", - "src/core/channel/http_client_filter.h", - "src/core/channel/http_server_filter.c", - "src/core/channel/http_server_filter.h", - "src/core/channel/subchannel_call_holder.c", - "src/core/channel/subchannel_call_holder.h", - "src/core/client_config/client_config.c", - "src/core/client_config/client_config.h", - "src/core/client_config/connector.c", - "src/core/client_config/connector.h", - "src/core/client_config/default_initial_connect_string.c", - "src/core/client_config/initial_connect_string.c", - "src/core/client_config/initial_connect_string.h", - "src/core/client_config/lb_policies/pick_first.c", - "src/core/client_config/lb_policies/pick_first.h", - "src/core/client_config/lb_policies/round_robin.c", - "src/core/client_config/lb_policies/round_robin.h", - "src/core/client_config/lb_policy.c", - "src/core/client_config/lb_policy.h", - "src/core/client_config/lb_policy_factory.c", - "src/core/client_config/lb_policy_factory.h", - "src/core/client_config/lb_policy_registry.c", - "src/core/client_config/lb_policy_registry.h", - "src/core/client_config/resolver.c", - "src/core/client_config/resolver.h", - "src/core/client_config/resolver_factory.c", - "src/core/client_config/resolver_factory.h", - "src/core/client_config/resolver_registry.c", - "src/core/client_config/resolver_registry.h", - "src/core/client_config/resolvers/dns_resolver.c", - "src/core/client_config/resolvers/dns_resolver.h", - "src/core/client_config/resolvers/sockaddr_resolver.c", - "src/core/client_config/resolvers/sockaddr_resolver.h", - "src/core/client_config/subchannel.c", - "src/core/client_config/subchannel.h", - "src/core/client_config/subchannel_factory.c", - "src/core/client_config/subchannel_factory.h", - "src/core/client_config/uri_parser.c", - "src/core/client_config/uri_parser.h", - "src/core/compression/algorithm.c", - "src/core/compression/algorithm_metadata.h", - "src/core/compression/message_compress.c", - "src/core/compression/message_compress.h", - "src/core/debug/trace.c", - "src/core/debug/trace.h", - "src/core/httpcli/format_request.c", - "src/core/httpcli/format_request.h", - "src/core/httpcli/httpcli.c", - "src/core/httpcli/httpcli.h", - "src/core/httpcli/parser.c", - "src/core/httpcli/parser.h", - "src/core/iomgr/closure.c", - "src/core/iomgr/closure.h", - "src/core/iomgr/endpoint.c", - "src/core/iomgr/endpoint.h", - "src/core/iomgr/endpoint_pair.h", - "src/core/iomgr/endpoint_pair_posix.c", - "src/core/iomgr/endpoint_pair_windows.c", - "src/core/iomgr/exec_ctx.c", - "src/core/iomgr/exec_ctx.h", - "src/core/iomgr/executor.c", - "src/core/iomgr/executor.h", - "src/core/iomgr/fd_posix.c", - "src/core/iomgr/fd_posix.h", - "src/core/iomgr/iocp_windows.c", - "src/core/iomgr/iocp_windows.h", - "src/core/iomgr/iomgr.c", - "src/core/iomgr/iomgr.h", - "src/core/iomgr/iomgr_internal.h", - "src/core/iomgr/iomgr_posix.c", - "src/core/iomgr/iomgr_posix.h", - "src/core/iomgr/iomgr_windows.c", - "src/core/iomgr/pollset.h", - "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_posix.h", - "src/core/iomgr/pollset_set.h", - "src/core/iomgr/pollset_set_posix.c", - "src/core/iomgr/pollset_set_posix.h", - "src/core/iomgr/pollset_set_windows.c", - "src/core/iomgr/pollset_set_windows.h", - "src/core/iomgr/pollset_windows.c", - "src/core/iomgr/pollset_windows.h", - "src/core/iomgr/resolve_address.h", - "src/core/iomgr/resolve_address_posix.c", - "src/core/iomgr/resolve_address_windows.c", - "src/core/iomgr/sockaddr.h", - "src/core/iomgr/sockaddr_posix.h", - "src/core/iomgr/sockaddr_utils.c", - "src/core/iomgr/sockaddr_utils.h", - "src/core/iomgr/sockaddr_win32.h", - "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_utils_posix.h", - "src/core/iomgr/socket_windows.c", - "src/core/iomgr/socket_windows.h", - "src/core/iomgr/tcp_client.h", - "src/core/iomgr/tcp_client_posix.c", - "src/core/iomgr/tcp_client_windows.c", - "src/core/iomgr/tcp_posix.c", - "src/core/iomgr/tcp_posix.h", - "src/core/iomgr/tcp_server.h", - "src/core/iomgr/tcp_server_posix.c", - "src/core/iomgr/tcp_server_windows.c", - "src/core/iomgr/tcp_windows.c", - "src/core/iomgr/tcp_windows.h", - "src/core/iomgr/time_averaged_stats.c", - "src/core/iomgr/time_averaged_stats.h", - "src/core/iomgr/timer.c", - "src/core/iomgr/timer.h", - "src/core/iomgr/timer_heap.c", - "src/core/iomgr/timer_heap.h", - "src/core/iomgr/timer_internal.h", - "src/core/iomgr/udp_server.c", - "src/core/iomgr/udp_server.h", - "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_pipe.h", - "src/core/iomgr/wakeup_fd_posix.c", - "src/core/iomgr/wakeup_fd_posix.h", - "src/core/iomgr/workqueue.h", - "src/core/iomgr/workqueue_posix.c", - "src/core/iomgr/workqueue_posix.h", - "src/core/iomgr/workqueue_windows.c", - "src/core/iomgr/workqueue_windows.h", - "src/core/json/json.c", - "src/core/json/json.h", - "src/core/json/json_common.h", - "src/core/json/json_reader.c", - "src/core/json/json_reader.h", - "src/core/json/json_string.c", - "src/core/json/json_writer.c", - "src/core/json/json_writer.h", - "src/core/profiling/basic_timers.c", - "src/core/profiling/stap_timers.c", - "src/core/profiling/timers.h", - "src/core/statistics/census_interface.h", - "src/core/statistics/census_rpc_stats.h", - "src/core/support/alloc.c", - "src/core/support/avl.c", - "src/core/support/block_annotate.h", - "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.h", - "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.h", - "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/murmur_hash.h", - "src/core/support/slice.c", - "src/core/support/slice_buffer.c", - "src/core/support/stack_lockfree.c", - "src/core/support/stack_lockfree.h", - "src/core/support/string.c", - "src/core/support/string.h", - "src/core/support/string_posix.c", - "src/core/support/string_win32.c", - "src/core/support/string_win32.h", - "src/core/support/subprocess_posix.c", - "src/core/support/subprocess_windows.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_internal.h", - "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_precise.c", - "src/core/support/time_precise.h", - "src/core/support/time_win32.c", - "src/core/support/tls_pthread.c", - "src/core/support/wrap_memcpy.c", - "src/core/surface/alarm.c", - "src/core/surface/api_trace.c", - "src/core/surface/api_trace.h", - "src/core/surface/byte_buffer.c", - "src/core/surface/byte_buffer_reader.c", - "src/core/surface/call.c", - "src/core/surface/call.h", - "src/core/surface/call_details.c", - "src/core/surface/call_log_batch.c", - "src/core/surface/call_test_only.h", - "src/core/surface/channel.c", - "src/core/surface/channel.h", - "src/core/surface/channel_connectivity.c", - "src/core/surface/channel_create.c", - "src/core/surface/channel_ping.c", - "src/core/surface/completion_queue.c", - "src/core/surface/completion_queue.h", - "src/core/surface/event_string.c", - "src/core/surface/event_string.h", - "src/core/surface/init.c", - "src/core/surface/init.h", - "src/core/surface/init_unsecure.c", - "src/core/surface/lame_client.c", - "src/core/surface/metadata_array.c", - "src/core/surface/server.c", - "src/core/surface/server.h", - "src/core/surface/server_chttp2.c", - "src/core/surface/server_create.c", - "src/core/surface/surface_trace.h", - "src/core/surface/validate_metadata.c", - "src/core/surface/version.c", - "src/core/transport/byte_stream.c", - "src/core/transport/byte_stream.h", - "src/core/transport/chttp2/alpn.c", - "src/core/transport/chttp2/alpn.h", - "src/core/transport/chttp2/bin_encoder.c", - "src/core/transport/chttp2/bin_encoder.h", - "src/core/transport/chttp2/frame.h", - "src/core/transport/chttp2/frame_data.c", - "src/core/transport/chttp2/frame_data.h", - "src/core/transport/chttp2/frame_goaway.c", - "src/core/transport/chttp2/frame_goaway.h", - "src/core/transport/chttp2/frame_ping.c", - "src/core/transport/chttp2/frame_ping.h", - "src/core/transport/chttp2/frame_rst_stream.c", - "src/core/transport/chttp2/frame_rst_stream.h", - "src/core/transport/chttp2/frame_settings.c", - "src/core/transport/chttp2/frame_settings.h", - "src/core/transport/chttp2/frame_window_update.c", - "src/core/transport/chttp2/frame_window_update.h", - "src/core/transport/chttp2/hpack_encoder.c", - "src/core/transport/chttp2/hpack_encoder.h", - "src/core/transport/chttp2/hpack_parser.c", - "src/core/transport/chttp2/hpack_parser.h", - "src/core/transport/chttp2/hpack_table.c", - "src/core/transport/chttp2/hpack_table.h", - "src/core/transport/chttp2/http2_errors.h", - "src/core/transport/chttp2/huffsyms.c", - "src/core/transport/chttp2/huffsyms.h", - "src/core/transport/chttp2/incoming_metadata.c", - "src/core/transport/chttp2/incoming_metadata.h", - "src/core/transport/chttp2/internal.h", - "src/core/transport/chttp2/parsing.c", - "src/core/transport/chttp2/status_conversion.c", - "src/core/transport/chttp2/status_conversion.h", - "src/core/transport/chttp2/stream_lists.c", - "src/core/transport/chttp2/stream_map.c", - "src/core/transport/chttp2/stream_map.h", - "src/core/transport/chttp2/timeout_encoding.c", - "src/core/transport/chttp2/timeout_encoding.h", - "src/core/transport/chttp2/varint.c", - "src/core/transport/chttp2/varint.h", - "src/core/transport/chttp2/writing.c", - "src/core/transport/chttp2_transport.c", - "src/core/transport/chttp2_transport.h", - "src/core/transport/connectivity_state.c", - "src/core/transport/connectivity_state.h", - "src/core/transport/metadata.c", - "src/core/transport/metadata.h", - "src/core/transport/metadata_batch.c", - "src/core/transport/metadata_batch.h", - "src/core/transport/static_metadata.c", - "src/core/transport/static_metadata.h", - "src/core/transport/transport.c", - "src/core/transport/transport.h", - "src/core/transport/transport_impl.h", - "src/core/transport/transport_op_string.c", + "src/core/lib/surface/init.cc", + "src/core/lib/surface/init_unsecure.cc", + "src/core/plugin_registry/grpc_unsecure_plugin_registry.cc", + ], + language = "c++", + public_hdrs = GRPC_PUBLIC_HDRS, + standalone = True, + deps = [ + "grpc_common", + "grpc_lb_policy_grpclb", + "grpc_lb_policy_xds", + ], +) + +cc_library( + name = "grpc++_public_hdrs", + hdrs = GRPCXX_PUBLIC_HDRS, +) + +grpc_cc_library( + name = "grpc++_unsecure", + srcs = [ + "src/cpp/client/insecure_credentials.cc", + "src/cpp/common/insecure_create_auth_context.cc", + "src/cpp/server/insecure_server_credentials.cc", + ], + language = "c++", + standalone = True, + deps = [ + "gpr", + "grpc++_base_unsecure", + "grpc++_codegen_base", + "grpc++_codegen_base_src", + "grpc++_codegen_proto", + "grpc_unsecure", + ], +) + +grpc_cc_library( + name = "grpc_plugin_support", + srcs = [ + "src/compiler/cpp_generator.cc", + "src/compiler/csharp_generator.cc", + "src/compiler/node_generator.cc", + "src/compiler/objective_c_generator.cc", + "src/compiler/php_generator.cc", + "src/compiler/python_generator.cc", + "src/compiler/ruby_generator.cc", ], hdrs = [ - "include/grpc/byte_buffer.h", - "include/grpc/byte_buffer_reader.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/node_generator.h", + "src/compiler/node_generator_helpers.h", + "src/compiler/objective_c_generator.h", + "src/compiler/objective_c_generator_helpers.h", + "src/compiler/php_generator.h", + "src/compiler/php_generator_helpers.h", + "src/compiler/protobuf_plugin.h", + "src/compiler/python_generator.h", + "src/compiler/python_generator_helpers.h", + "src/compiler/python_private_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/schema_interface.h", + ], + external_deps = [ + "protobuf:protoc_lib", + ], + language = "c++", + deps = [ + "grpc++_config_proto", + ], +) + +grpc_cc_library( + name = "census", + srcs = [ + "src/core/ext/filters/census/grpc_context.cc", + ], + language = "c++", + public_hdrs = [ "include/grpc/census.h", - "include/grpc/compression.h", - "include/grpc/grpc.h", - "include/grpc/impl/codegen/alloc.h", + ], + deps = [ + "grpc_base", + ], +) + +grpc_cc_library( + name = "gpr_base", + srcs = [ + "src/core/lib/gpr/alloc.cc", + "src/core/lib/gpr/arena.cc", + "src/core/lib/gpr/atm.cc", + "src/core/lib/gpr/cpu_iphone.cc", + "src/core/lib/gpr/cpu_linux.cc", + "src/core/lib/gpr/cpu_posix.cc", + "src/core/lib/gpr/cpu_windows.cc", + "src/core/lib/gpr/env_linux.cc", + "src/core/lib/gpr/env_posix.cc", + "src/core/lib/gpr/env_windows.cc", + "src/core/lib/gpr/host_port.cc", + "src/core/lib/gpr/log.cc", + "src/core/lib/gpr/log_android.cc", + "src/core/lib/gpr/log_linux.cc", + "src/core/lib/gpr/log_posix.cc", + "src/core/lib/gpr/log_windows.cc", + "src/core/lib/gpr/mpscq.cc", + "src/core/lib/gpr/murmur_hash.cc", + "src/core/lib/gpr/string.cc", + "src/core/lib/gpr/string_posix.cc", + "src/core/lib/gpr/string_util_windows.cc", + "src/core/lib/gpr/string_windows.cc", + "src/core/lib/gpr/sync.cc", + "src/core/lib/gpr/sync_posix.cc", + "src/core/lib/gpr/sync_windows.cc", + "src/core/lib/gpr/time.cc", + "src/core/lib/gpr/time_posix.cc", + "src/core/lib/gpr/time_precise.cc", + "src/core/lib/gpr/time_windows.cc", + "src/core/lib/gpr/tls_pthread.cc", + "src/core/lib/gpr/tmpfile_msys.cc", + "src/core/lib/gpr/tmpfile_posix.cc", + "src/core/lib/gpr/tmpfile_windows.cc", + "src/core/lib/gpr/wrap_memcpy.cc", + "src/core/lib/gprpp/fork.cc", + "src/core/lib/gprpp/thd_posix.cc", + "src/core/lib/gprpp/thd_windows.cc", + "src/core/lib/profiling/basic_timers.cc", + "src/core/lib/profiling/stap_timers.cc", + ], + hdrs = [ + "src/core/lib/gpr/alloc.h", + "src/core/lib/gpr/arena.h", + "src/core/lib/gpr/env.h", + "src/core/lib/gpr/host_port.h", + "src/core/lib/gpr/mpscq.h", + "src/core/lib/gpr/murmur_hash.h", + "src/core/lib/gpr/spinlock.h", + "src/core/lib/gpr/string.h", + "src/core/lib/gpr/string_windows.h", + "src/core/lib/gpr/time_precise.h", + "src/core/lib/gpr/tls.h", + "src/core/lib/gpr/tls_gcc.h", + "src/core/lib/gpr/tls_msvc.h", + "src/core/lib/gpr/tls_pthread.h", + "src/core/lib/gpr/tmpfile.h", + "src/core/lib/gpr/useful.h", + "src/core/lib/gprpp/abstract.h", + "src/core/lib/gprpp/fork.h", + "src/core/lib/gprpp/manual_constructor.h", + "src/core/lib/gprpp/memory.h", + "src/core/lib/gprpp/mutex_lock.h", + "src/core/lib/gprpp/thd.h", + "src/core/lib/profiling/timers.h", + ], + language = "c++", + public_hdrs = GPR_PUBLIC_HDRS, + deps = [ + "gpr_codegen", + ], +) + +grpc_cc_library( + name = "gpr_codegen", + language = "c++", + public_hdrs = [ "include/grpc/impl/codegen/atm.h", "include/grpc/impl/codegen/atm_gcc_atomic.h", "include/grpc/impl/codegen/atm_gcc_sync.h", - "include/grpc/impl/codegen/atm_win32.h", + "include/grpc/impl/codegen/atm_windows.h", + "include/grpc/impl/codegen/fork.h", + "include/grpc/impl/codegen/gpr_slice.h", + "include/grpc/impl/codegen/gpr_types.h", + "include/grpc/impl/codegen/log.h", + "include/grpc/impl/codegen/port_platform.h", + "include/grpc/impl/codegen/sync.h", + "include/grpc/impl/codegen/sync_custom.h", + "include/grpc/impl/codegen/sync_generic.h", + "include/grpc/impl/codegen/sync_posix.h", + "include/grpc/impl/codegen/sync_windows.h", + ], +) + +grpc_cc_library( + name = "grpc_trace", + srcs = ["src/core/lib/debug/trace.cc"], + hdrs = ["src/core/lib/debug/trace.h"], + language = "c++", + public_hdrs = GRPC_PUBLIC_HDRS, + deps = [ + "grpc_codegen", + ":gpr", + ], +) + +grpc_cc_library( + name = "atomic", + hdrs = [ + "src/core/lib/gprpp/atomic_with_atm.h", + "src/core/lib/gprpp/atomic_with_std.h", + ], + language = "c++", + public_hdrs = [ + "src/core/lib/gprpp/atomic.h", + ], + deps = [ + "gpr", + ], +) + +grpc_cc_library( + name = "inlined_vector", + language = "c++", + public_hdrs = [ + "src/core/lib/gprpp/inlined_vector.h", + ], + deps = [ + "gpr_base", + ], +) + +grpc_cc_library( + name = "debug_location", + language = "c++", + public_hdrs = ["src/core/lib/gprpp/debug_location.h"], +) + +grpc_cc_library( + name = "orphanable", + language = "c++", + public_hdrs = ["src/core/lib/gprpp/orphanable.h"], + deps = [ + "debug_location", + "gpr_base", + "grpc_trace", + "ref_counted", + "ref_counted_ptr", + ], +) + +grpc_cc_library( + name = "ref_counted", + language = "c++", + public_hdrs = ["src/core/lib/gprpp/ref_counted.h"], + deps = [ + "debug_location", + "gpr_base", + "grpc_trace", + "ref_counted_ptr", + ], +) + +grpc_cc_library( + name = "ref_counted_ptr", + language = "c++", + public_hdrs = ["src/core/lib/gprpp/ref_counted_ptr.h"], + deps = [ + "gpr_base", + ], +) + +grpc_cc_library( + name = "grpc_base_c", + srcs = [ + "src/core/lib/avl/avl.cc", + "src/core/lib/backoff/backoff.cc", + "src/core/lib/channel/channel_args.cc", + "src/core/lib/channel/channel_stack.cc", + "src/core/lib/channel/channel_stack_builder.cc", + "src/core/lib/channel/channel_trace.cc", + "src/core/lib/channel/channelz.cc", + "src/core/lib/channel/channelz_registry.cc", + "src/core/lib/channel/connected_channel.cc", + "src/core/lib/channel/handshaker.cc", + "src/core/lib/channel/handshaker_factory.cc", + "src/core/lib/channel/handshaker_registry.cc", + "src/core/lib/channel/status_util.cc", + "src/core/lib/compression/compression.cc", + "src/core/lib/compression/compression_internal.cc", + "src/core/lib/compression/message_compress.cc", + "src/core/lib/compression/stream_compression.cc", + "src/core/lib/compression/stream_compression_gzip.cc", + "src/core/lib/compression/stream_compression_identity.cc", + "src/core/lib/debug/stats.cc", + "src/core/lib/debug/stats_data.cc", + "src/core/lib/http/format_request.cc", + "src/core/lib/http/httpcli.cc", + "src/core/lib/http/parser.cc", + "src/core/lib/iomgr/buffer_list.cc", + "src/core/lib/iomgr/call_combiner.cc", + "src/core/lib/iomgr/combiner.cc", + "src/core/lib/iomgr/endpoint.cc", + "src/core/lib/iomgr/endpoint_pair_posix.cc", + "src/core/lib/iomgr/endpoint_pair_uv.cc", + "src/core/lib/iomgr/endpoint_pair_windows.cc", + "src/core/lib/iomgr/error.cc", + "src/core/lib/iomgr/ev_epoll1_linux.cc", + "src/core/lib/iomgr/ev_epollex_linux.cc", + "src/core/lib/iomgr/ev_poll_posix.cc", + "src/core/lib/iomgr/ev_posix.cc", + "src/core/lib/iomgr/ev_windows.cc", + "src/core/lib/iomgr/exec_ctx.cc", + "src/core/lib/iomgr/executor.cc", + "src/core/lib/iomgr/fork_posix.cc", + "src/core/lib/iomgr/fork_windows.cc", + "src/core/lib/iomgr/gethostname_fallback.cc", + "src/core/lib/iomgr/gethostname_host_name_max.cc", + "src/core/lib/iomgr/gethostname_sysconf.cc", + "src/core/lib/iomgr/internal_errqueue.cc", + "src/core/lib/iomgr/iocp_windows.cc", + "src/core/lib/iomgr/iomgr.cc", + "src/core/lib/iomgr/iomgr_custom.cc", + "src/core/lib/iomgr/iomgr_internal.cc", + "src/core/lib/iomgr/iomgr_posix.cc", + "src/core/lib/iomgr/iomgr_windows.cc", + "src/core/lib/iomgr/is_epollexclusive_available.cc", + "src/core/lib/iomgr/load_file.cc", + "src/core/lib/iomgr/lockfree_event.cc", + "src/core/lib/iomgr/network_status_tracker.cc", + "src/core/lib/iomgr/polling_entity.cc", + "src/core/lib/iomgr/pollset.cc", + "src/core/lib/iomgr/pollset_custom.cc", + "src/core/lib/iomgr/pollset_set.cc", + "src/core/lib/iomgr/pollset_set_custom.cc", + "src/core/lib/iomgr/pollset_set_windows.cc", + "src/core/lib/iomgr/pollset_uv.cc", + "src/core/lib/iomgr/pollset_windows.cc", + "src/core/lib/iomgr/resolve_address.cc", + "src/core/lib/iomgr/resolve_address_custom.cc", + "src/core/lib/iomgr/resolve_address_posix.cc", + "src/core/lib/iomgr/resolve_address_windows.cc", + "src/core/lib/iomgr/resource_quota.cc", + "src/core/lib/iomgr/sockaddr_utils.cc", + "src/core/lib/iomgr/socket_factory_posix.cc", + "src/core/lib/iomgr/socket_mutator.cc", + "src/core/lib/iomgr/socket_utils_common_posix.cc", + "src/core/lib/iomgr/socket_utils_linux.cc", + "src/core/lib/iomgr/socket_utils_posix.cc", + "src/core/lib/iomgr/socket_utils_windows.cc", + "src/core/lib/iomgr/socket_windows.cc", + "src/core/lib/iomgr/tcp_client.cc", + "src/core/lib/iomgr/tcp_client_custom.cc", + "src/core/lib/iomgr/tcp_client_posix.cc", + "src/core/lib/iomgr/tcp_client_windows.cc", + "src/core/lib/iomgr/tcp_custom.cc", + "src/core/lib/iomgr/tcp_posix.cc", + "src/core/lib/iomgr/tcp_server.cc", + "src/core/lib/iomgr/tcp_server_custom.cc", + "src/core/lib/iomgr/tcp_server_posix.cc", + "src/core/lib/iomgr/tcp_server_utils_posix_common.cc", + "src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc", + "src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc", + "src/core/lib/iomgr/tcp_server_windows.cc", + "src/core/lib/iomgr/tcp_uv.cc", + "src/core/lib/iomgr/tcp_windows.cc", + "src/core/lib/iomgr/time_averaged_stats.cc", + "src/core/lib/iomgr/timer.cc", + "src/core/lib/iomgr/timer_custom.cc", + "src/core/lib/iomgr/timer_generic.cc", + "src/core/lib/iomgr/timer_heap.cc", + "src/core/lib/iomgr/timer_manager.cc", + "src/core/lib/iomgr/timer_uv.cc", + "src/core/lib/iomgr/udp_server.cc", + "src/core/lib/iomgr/unix_sockets_posix.cc", + "src/core/lib/iomgr/unix_sockets_posix_noop.cc", + "src/core/lib/iomgr/wakeup_fd_cv.cc", + "src/core/lib/iomgr/wakeup_fd_eventfd.cc", + "src/core/lib/iomgr/wakeup_fd_nospecial.cc", + "src/core/lib/iomgr/wakeup_fd_pipe.cc", + "src/core/lib/iomgr/wakeup_fd_posix.cc", + "src/core/lib/json/json.cc", + "src/core/lib/json/json_reader.cc", + "src/core/lib/json/json_string.cc", + "src/core/lib/json/json_writer.cc", + "src/core/lib/slice/b64.cc", + "src/core/lib/slice/percent_encoding.cc", + "src/core/lib/slice/slice.cc", + "src/core/lib/slice/slice_buffer.cc", + "src/core/lib/slice/slice_intern.cc", + "src/core/lib/slice/slice_string_helpers.cc", + "src/core/lib/surface/api_trace.cc", + "src/core/lib/surface/byte_buffer.cc", + "src/core/lib/surface/byte_buffer_reader.cc", + "src/core/lib/surface/call.cc", + "src/core/lib/surface/call_details.cc", + "src/core/lib/surface/call_log_batch.cc", + "src/core/lib/surface/channel.cc", + "src/core/lib/surface/channel_init.cc", + "src/core/lib/surface/channel_ping.cc", + "src/core/lib/surface/channel_stack_type.cc", + "src/core/lib/surface/completion_queue.cc", + "src/core/lib/surface/completion_queue_factory.cc", + "src/core/lib/surface/event_string.cc", + "src/core/lib/surface/metadata_array.cc", + "src/core/lib/surface/server.cc", + "src/core/lib/surface/validate_metadata.cc", + "src/core/lib/surface/version.cc", + "src/core/lib/transport/bdp_estimator.cc", + "src/core/lib/transport/byte_stream.cc", + "src/core/lib/transport/connectivity_state.cc", + "src/core/lib/transport/error_utils.cc", + "src/core/lib/transport/metadata.cc", + "src/core/lib/transport/metadata_batch.cc", + "src/core/lib/transport/pid_controller.cc", + "src/core/lib/transport/service_config.cc", + "src/core/lib/transport/static_metadata.cc", + "src/core/lib/transport/status_conversion.cc", + "src/core/lib/transport/status_metadata.cc", + "src/core/lib/transport/timeout_encoding.cc", + "src/core/lib/transport/transport.cc", + "src/core/lib/transport/transport_op_string.cc", + "src/core/lib/uri/uri_parser.cc", + ], + hdrs = [ + "src/core/lib/avl/avl.h", + "src/core/lib/backoff/backoff.h", + "src/core/lib/channel/channel_args.h", + "src/core/lib/channel/channel_stack.h", + "src/core/lib/channel/channel_stack_builder.h", + "src/core/lib/channel/channel_trace.h", + "src/core/lib/channel/channelz.h", + "src/core/lib/channel/channelz_registry.h", + "src/core/lib/channel/connected_channel.h", + "src/core/lib/channel/context.h", + "src/core/lib/channel/handshaker.h", + "src/core/lib/channel/handshaker_factory.h", + "src/core/lib/channel/handshaker_registry.h", + "src/core/lib/channel/status_util.h", + "src/core/lib/compression/algorithm_metadata.h", + "src/core/lib/compression/compression_internal.h", + "src/core/lib/compression/message_compress.h", + "src/core/lib/compression/stream_compression.h", + "src/core/lib/compression/stream_compression_gzip.h", + "src/core/lib/compression/stream_compression_identity.h", + "src/core/lib/debug/stats.h", + "src/core/lib/debug/stats_data.h", + "src/core/lib/http/format_request.h", + "src/core/lib/http/httpcli.h", + "src/core/lib/http/parser.h", + "src/core/lib/iomgr/block_annotate.h", + "src/core/lib/iomgr/buffer_list.h", + "src/core/lib/iomgr/call_combiner.h", + "src/core/lib/iomgr/closure.h", + "src/core/lib/iomgr/combiner.h", + "src/core/lib/iomgr/dynamic_annotations.h", + "src/core/lib/iomgr/endpoint.h", + "src/core/lib/iomgr/endpoint_pair.h", + "src/core/lib/iomgr/error.h", + "src/core/lib/iomgr/error_internal.h", + "src/core/lib/iomgr/ev_epoll1_linux.h", + "src/core/lib/iomgr/ev_epollex_linux.h", + "src/core/lib/iomgr/ev_poll_posix.h", + "src/core/lib/iomgr/ev_posix.h", + "src/core/lib/iomgr/exec_ctx.h", + "src/core/lib/iomgr/executor.h", + "src/core/lib/iomgr/gethostname.h", + "src/core/lib/iomgr/gevent_util.h", + "src/core/lib/iomgr/internal_errqueue.h", + "src/core/lib/iomgr/iocp_windows.h", + "src/core/lib/iomgr/iomgr.h", + "src/core/lib/iomgr/iomgr_custom.h", + "src/core/lib/iomgr/iomgr_internal.h", + "src/core/lib/iomgr/iomgr_posix.h", + "src/core/lib/iomgr/is_epollexclusive_available.h", + "src/core/lib/iomgr/load_file.h", + "src/core/lib/iomgr/lockfree_event.h", + "src/core/lib/iomgr/nameser.h", + "src/core/lib/iomgr/network_status_tracker.h", + "src/core/lib/iomgr/polling_entity.h", + "src/core/lib/iomgr/pollset.h", + "src/core/lib/iomgr/pollset_custom.h", + "src/core/lib/iomgr/pollset_set.h", + "src/core/lib/iomgr/pollset_set_custom.h", + "src/core/lib/iomgr/pollset_set_windows.h", + "src/core/lib/iomgr/pollset_uv.h", + "src/core/lib/iomgr/pollset_windows.h", + "src/core/lib/iomgr/port.h", + "src/core/lib/iomgr/resolve_address.h", + "src/core/lib/iomgr/resolve_address_custom.h", + "src/core/lib/iomgr/resource_quota.h", + "src/core/lib/iomgr/sockaddr.h", + "src/core/lib/iomgr/sockaddr_custom.h", + "src/core/lib/iomgr/sockaddr_posix.h", + "src/core/lib/iomgr/sockaddr_utils.h", + "src/core/lib/iomgr/sockaddr_windows.h", + "src/core/lib/iomgr/socket_factory_posix.h", + "src/core/lib/iomgr/socket_mutator.h", + "src/core/lib/iomgr/socket_utils.h", + "src/core/lib/iomgr/socket_utils_posix.h", + "src/core/lib/iomgr/socket_windows.h", + "src/core/lib/iomgr/sys_epoll_wrapper.h", + "src/core/lib/iomgr/tcp_client.h", + "src/core/lib/iomgr/tcp_client_posix.h", + "src/core/lib/iomgr/tcp_custom.h", + "src/core/lib/iomgr/tcp_posix.h", + "src/core/lib/iomgr/tcp_server.h", + "src/core/lib/iomgr/tcp_server_utils_posix.h", + "src/core/lib/iomgr/tcp_windows.h", + "src/core/lib/iomgr/time_averaged_stats.h", + "src/core/lib/iomgr/timer.h", + "src/core/lib/iomgr/timer_custom.h", + "src/core/lib/iomgr/timer_generic.h", + "src/core/lib/iomgr/timer_heap.h", + "src/core/lib/iomgr/timer_manager.h", + "src/core/lib/iomgr/udp_server.h", + "src/core/lib/iomgr/unix_sockets_posix.h", + "src/core/lib/iomgr/wakeup_fd_cv.h", + "src/core/lib/iomgr/wakeup_fd_pipe.h", + "src/core/lib/iomgr/wakeup_fd_posix.h", + "src/core/lib/json/json.h", + "src/core/lib/json/json_common.h", + "src/core/lib/json/json_reader.h", + "src/core/lib/json/json_writer.h", + "src/core/lib/slice/b64.h", + "src/core/lib/slice/percent_encoding.h", + "src/core/lib/slice/slice_hash_table.h", + "src/core/lib/slice/slice_internal.h", + "src/core/lib/slice/slice_string_helpers.h", + "src/core/lib/slice/slice_weak_hash_table.h", + "src/core/lib/surface/api_trace.h", + "src/core/lib/surface/call.h", + "src/core/lib/surface/call_test_only.h", + "src/core/lib/surface/channel.h", + "src/core/lib/surface/channel_init.h", + "src/core/lib/surface/channel_stack_type.h", + "src/core/lib/surface/completion_queue.h", + "src/core/lib/surface/completion_queue_factory.h", + "src/core/lib/surface/event_string.h", + "src/core/lib/surface/init.h", + "src/core/lib/surface/lame_client.h", + "src/core/lib/surface/server.h", + "src/core/lib/surface/validate_metadata.h", + "src/core/lib/transport/bdp_estimator.h", + "src/core/lib/transport/byte_stream.h", + "src/core/lib/transport/connectivity_state.h", + "src/core/lib/transport/error_utils.h", + "src/core/lib/transport/http2_errors.h", + "src/core/lib/transport/metadata.h", + "src/core/lib/transport/metadata_batch.h", + "src/core/lib/transport/pid_controller.h", + "src/core/lib/transport/service_config.h", + "src/core/lib/transport/static_metadata.h", + "src/core/lib/transport/status_conversion.h", + "src/core/lib/transport/status_metadata.h", + "src/core/lib/transport/timeout_encoding.h", + "src/core/lib/transport/transport.h", + "src/core/lib/transport/transport_impl.h", + "src/core/lib/uri/uri_parser.h", + ], + external_deps = [ + "zlib", + ], + language = "c++", + public_hdrs = GRPC_PUBLIC_HDRS, + deps = [ + "gpr_base", + "grpc_codegen", + "grpc_trace", + "inlined_vector", + "orphanable", + "ref_counted", + "ref_counted_ptr", + ], +) + +grpc_cc_library( + name = "grpc_base", + srcs = [ + "src/core/lib/surface/lame_client.cc", + ], + language = "c++", + deps = [ + "atomic", + "grpc_base_c", + ], +) + +grpc_cc_library( + name = "grpc_common", + language = "c++", + deps = [ + "grpc_base", + # standard plugins + "census", + "grpc_deadline_filter", + "grpc_client_authority_filter", + "grpc_lb_policy_pick_first", + "grpc_lb_policy_round_robin", + "grpc_max_age_filter", + "grpc_message_size_filter", + "grpc_resolver_dns_ares", + "grpc_resolver_fake", + "grpc_resolver_dns_native", + "grpc_resolver_sockaddr", + "grpc_transport_chttp2_client_insecure", + "grpc_transport_chttp2_server_insecure", + "grpc_transport_inproc", + "grpc_workaround_cronet_compression_filter", + "grpc_server_backward_compatibility", + ], +) + +grpc_cc_library( + name = "grpc_cfstream", + srcs = [ + "src/core/lib/iomgr/cfstream_handle.cc", + "src/core/lib/iomgr/endpoint_cfstream.cc", + "src/core/lib/iomgr/error_cfstream.cc", + "src/core/lib/iomgr/iomgr_posix_cfstream.cc", + "src/core/lib/iomgr/tcp_client_cfstream.cc", + ], + hdrs = [ + "src/core/lib/iomgr/cfstream_handle.h", + "src/core/lib/iomgr/endpoint_cfstream.h", + "src/core/lib/iomgr/error_cfstream.h", + ], + deps = [ + ":gpr_base", + ":grpc_base", + ], +) + +grpc_cc_library( + name = "grpc_client_channel", + srcs = [ + "src/core/ext/filters/client_channel/backup_poller.cc", + "src/core/ext/filters/client_channel/channel_connectivity.cc", + "src/core/ext/filters/client_channel/client_channel.cc", + "src/core/ext/filters/client_channel/client_channel_channelz.cc", + "src/core/ext/filters/client_channel/client_channel_factory.cc", + "src/core/ext/filters/client_channel/client_channel_plugin.cc", + "src/core/ext/filters/client_channel/connector.cc", + "src/core/ext/filters/client_channel/health/health_check_client.cc", + "src/core/ext/filters/client_channel/http_connect_handshaker.cc", + "src/core/ext/filters/client_channel/http_proxy.cc", + "src/core/ext/filters/client_channel/lb_policy.cc", + "src/core/ext/filters/client_channel/lb_policy_registry.cc", + "src/core/ext/filters/client_channel/parse_address.cc", + "src/core/ext/filters/client_channel/proxy_mapper.cc", + "src/core/ext/filters/client_channel/proxy_mapper_registry.cc", + "src/core/ext/filters/client_channel/request_routing.cc", + "src/core/ext/filters/client_channel/resolver.cc", + "src/core/ext/filters/client_channel/resolver_registry.cc", + "src/core/ext/filters/client_channel/resolver_result_parsing.cc", + "src/core/ext/filters/client_channel/retry_throttle.cc", + "src/core/ext/filters/client_channel/server_address.cc", + "src/core/ext/filters/client_channel/subchannel.cc", + "src/core/ext/filters/client_channel/subchannel_index.cc", + ], + hdrs = [ + "src/core/ext/filters/client_channel/backup_poller.h", + "src/core/ext/filters/client_channel/client_channel.h", + "src/core/ext/filters/client_channel/client_channel_channelz.h", + "src/core/ext/filters/client_channel/client_channel_factory.h", + "src/core/ext/filters/client_channel/connector.h", + "src/core/ext/filters/client_channel/health/health_check_client.h", + "src/core/ext/filters/client_channel/http_connect_handshaker.h", + "src/core/ext/filters/client_channel/http_proxy.h", + "src/core/ext/filters/client_channel/lb_policy.h", + "src/core/ext/filters/client_channel/lb_policy_factory.h", + "src/core/ext/filters/client_channel/lb_policy_registry.h", + "src/core/ext/filters/client_channel/parse_address.h", + "src/core/ext/filters/client_channel/proxy_mapper.h", + "src/core/ext/filters/client_channel/proxy_mapper_registry.h", + "src/core/ext/filters/client_channel/request_routing.h", + "src/core/ext/filters/client_channel/resolver.h", + "src/core/ext/filters/client_channel/resolver_factory.h", + "src/core/ext/filters/client_channel/resolver_registry.h", + "src/core/ext/filters/client_channel/resolver_result_parsing.h", + "src/core/ext/filters/client_channel/retry_throttle.h", + "src/core/ext/filters/client_channel/server_address.h", + "src/core/ext/filters/client_channel/subchannel.h", + "src/core/ext/filters/client_channel/subchannel_index.h", + ], + language = "c++", + deps = [ + "gpr_base", + "grpc_base", + "grpc_client_authority_filter", + "grpc_deadline_filter", + "health_proto", + "inlined_vector", + "orphanable", + "ref_counted", + "ref_counted_ptr", + ], +) + +grpc_cc_library( + name = "grpc_max_age_filter", + srcs = [ + "src/core/ext/filters/max_age/max_age_filter.cc", + ], + hdrs = [ + "src/core/ext/filters/max_age/max_age_filter.h", + ], + language = "c++", + deps = [ + "grpc_base", + ], +) + +grpc_cc_library( + name = "grpc_deadline_filter", + srcs = [ + "src/core/ext/filters/deadline/deadline_filter.cc", + ], + hdrs = [ + "src/core/ext/filters/deadline/deadline_filter.h", + ], + language = "c++", + deps = [ + "grpc_base", + ], +) + +grpc_cc_library( + name = "grpc_client_authority_filter", + srcs = [ + "src/core/ext/filters/http/client_authority_filter.cc", + ], + hdrs = [ + "src/core/ext/filters/http/client_authority_filter.h", + ], + language = "c++", + deps = [ + "grpc_base", + ], +) + +grpc_cc_library( + name = "grpc_message_size_filter", + srcs = [ + "src/core/ext/filters/message_size/message_size_filter.cc", + ], + hdrs = [ + "src/core/ext/filters/message_size/message_size_filter.h", + ], + language = "c++", + deps = [ + "grpc_base", + ], +) + +grpc_cc_library( + name = "grpc_http_filters", + srcs = [ + "src/core/ext/filters/http/client/http_client_filter.cc", + "src/core/ext/filters/http/http_filters_plugin.cc", + "src/core/ext/filters/http/message_compress/message_compress_filter.cc", + "src/core/ext/filters/http/server/http_server_filter.cc", + ], + hdrs = [ + "src/core/ext/filters/http/client/http_client_filter.h", + "src/core/ext/filters/http/message_compress/message_compress_filter.h", + "src/core/ext/filters/http/server/http_server_filter.h", + ], + language = "c++", + deps = [ + "grpc_base", + ], +) + +grpc_cc_library( + name = "grpc_workaround_cronet_compression_filter", + srcs = [ + "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc", + ], + hdrs = [ + "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_server_backward_compatibility", + ], +) + +grpc_cc_library( + name = "grpc_codegen", + language = "c++", + public_hdrs = [ "include/grpc/impl/codegen/byte_buffer.h", + "include/grpc/impl/codegen/byte_buffer_reader.h", "include/grpc/impl/codegen/compression_types.h", "include/grpc/impl/codegen/connectivity_state.h", "include/grpc/impl/codegen/grpc_types.h", - "include/grpc/impl/codegen/log.h", - "include/grpc/impl/codegen/port_platform.h", "include/grpc/impl/codegen/propagation_bits.h", - "include/grpc/impl/codegen/slice.h", - "include/grpc/impl/codegen/slice_buffer.h", "include/grpc/impl/codegen/status.h", - "include/grpc/impl/codegen/sync.h", - "include/grpc/impl/codegen/sync_generic.h", - "include/grpc/impl/codegen/sync_posix.h", - "include/grpc/impl/codegen/sync_win32.h", - "include/grpc/impl/codegen/time.h", - "include/grpc/status.h", - "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/avl.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", - ], - copts = select({ - "//conditions:default": [ - "-std=gnu99", - "-Wimplicit-function-declaration", - ], - ":windows": ["/we4013"], - }), - includes = [ - ".", - "include", - ], - deps = [ - "//third_party/zlib", + "include/grpc/impl/codegen/slice.h", + ], + deps = [ + "gpr_codegen", ], ) -cc_library( - name = "grpc++_unsecure", +grpc_cc_library( + name = "health_proto", srcs = [ - "src/cpp/client/channel.cc", - "src/cpp/client/client_context.cc", - "src/cpp/client/create_channel.cc", - "src/cpp/client/create_channel_internal.cc", - "src/cpp/client/create_channel_internal.h", - "src/cpp/client/credentials.cc", - "src/cpp/client/generic_stub.cc", - "src/cpp/client/insecure_credentials.cc", - "src/cpp/codegen/grpc_library.cc", - "src/cpp/common/call.cc", - "src/cpp/common/channel_arguments.cc", - "src/cpp/common/completion_queue.cc", - "src/cpp/common/create_auth_context.h", - "src/cpp/common/insecure_create_auth_context.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/dynamic_thread_pool.h", - "src/cpp/server/fixed_size_thread_pool.cc", - "src/cpp/server/fixed_size_thread_pool.h", - "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/server/thread_pool_interface.h", - "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", + "src/core/ext/filters/client_channel/health/health.pb.c", + ], + hdrs = [ + "src/core/ext/filters/client_channel/health/health.pb.h", + ], + external_deps = [ + "nanopb", + ], + language = "c++", +) + +grpc_cc_library( + name = "grpclb_proto", + srcs = [ + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", + ], + hdrs = [ + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h", + ], + external_deps = [ + "nanopb", + ], + language = "c++", +) + +grpc_cc_library( + name = "grpc_lb_policy_grpclb", + srcs = [ + "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc", + "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc", + ], + hdrs = [ + "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", + "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", + ], + external_deps = [ + "nanopb", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + "grpc_resolver_fake", + "grpclb_proto", + ], +) + +grpc_cc_library( + name = "grpc_lb_policy_xds", + srcs = [ + "src/core/ext/filters/client_channel/lb_policy/xds/xds.cc", + "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc", + "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc", + "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc", + ], + hdrs = [ + "src/core/ext/filters/client_channel/lb_policy/xds/xds.h", + "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h", + "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h", + "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h", + ], + external_deps = [ + "nanopb", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + "grpc_resolver_fake", + "grpclb_proto", + ], +) + +grpc_cc_library( + name = "grpc_lb_subchannel_list", + hdrs = [ + "src/core/ext/filters/client_channel/lb_policy/subchannel_list.h", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + ], +) + +grpc_cc_library( + name = "grpc_lb_policy_pick_first", + srcs = [ + "src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + "grpc_lb_subchannel_list", + ], +) + +grpc_cc_library( + name = "grpc_lb_policy_round_robin", + srcs = [ + "src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + "grpc_lb_subchannel_list", + ], +) + +grpc_cc_library( + name = "lb_server_load_reporting_filter", + srcs = [ + "src/core/ext/filters/load_reporting/server_load_reporting_filter.cc", + ], + hdrs = [ + "src/core/ext/filters/load_reporting/registered_opencensus_objects.h", + "src/core/ext/filters/load_reporting/server_load_reporting_filter.h", + "src/cpp/server/load_reporter/constants.h", + ], + external_deps = [ + "opencensus-stats", + ], + language = "c++", + deps = [ + "grpc++_base", + "grpc_secure", + ], +) + +grpc_cc_library( + name = "lb_load_data_store", + srcs = [ + "src/cpp/server/load_reporter/load_data_store.cc", + ], + hdrs = [ + "src/cpp/server/load_reporter/constants.h", + "src/cpp/server/load_reporter/load_data_store.h", + ], + language = "c++", + deps = [ + "grpc++", + ], +) + +grpc_cc_library( + name = "lb_server_load_reporting_service_server_builder_plugin", + srcs = [ + "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc", + ], + hdrs = [ + "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h", + ], + language = "c++", + deps = [ + "lb_load_reporter_service", + ], +) + +grpc_cc_library( + name = "grpcpp_server_load_reporting", + srcs = [ + "src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc", + "src/cpp/server/load_reporter/util.cc", + ], + language = "c++", + public_hdrs = [ + "include/grpcpp/ext/server_load_reporting.h", + ], + deps = [ + "lb_server_load_reporting_filter", + "lb_server_load_reporting_service_server_builder_plugin", + ], + alwayslink = 1, +) + +grpc_cc_library( + name = "lb_load_reporter_service", + srcs = [ + "src/cpp/server/load_reporter/load_reporter_async_service_impl.cc", + ], + hdrs = [ + "src/cpp/server/load_reporter/load_reporter_async_service_impl.h", + ], + language = "c++", + deps = [ + "lb_load_reporter", + ], +) + +grpc_cc_library( + name = "lb_get_cpu_stats", + srcs = [ + "src/cpp/server/load_reporter/get_cpu_stats_linux.cc", + "src/cpp/server/load_reporter/get_cpu_stats_macos.cc", + "src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc", + "src/cpp/server/load_reporter/get_cpu_stats_windows.cc", + ], + hdrs = [ + "src/cpp/server/load_reporter/get_cpu_stats.h", + ], + language = "c++", + deps = [ + "grpc++", + ], +) + +grpc_cc_library( + name = "lb_load_reporter", + srcs = [ + "src/cpp/server/load_reporter/load_reporter.cc", + ], + hdrs = [ + "src/cpp/server/load_reporter/constants.h", + "src/cpp/server/load_reporter/load_reporter.h", + ], + external_deps = [ + "opencensus-stats", + ], + language = "c++", + deps = [ + "lb_get_cpu_stats", + "lb_load_data_store", + "//src/proto/grpc/lb/v1:load_reporter_proto", + ], +) + +grpc_cc_library( + name = "grpc_resolver_dns_native", + srcs = [ + "src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + ], +) + +# Only builds stubs because we define GRPC_ARES=0 +grpc_cc_library( + name = "grpc_resolver_dns_ares", + srcs = [ + "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc", + ], + hdrs = [ + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + ], +) + +grpc_cc_library( + name = "grpc_resolver_sockaddr", + srcs = [ + "src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + ], +) + +grpc_cc_library( + name = "grpc_resolver_fake", + srcs = ["src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc"], + hdrs = ["src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h"], + language = "c++", + visibility = ["//test:__subpackages__"], + deps = [ + "grpc_base", + "grpc_client_channel", + ], +) + +grpc_cc_library( + name = "grpc_transport_chttp2", + srcs = [ + "src/core/ext/transport/chttp2/transport/bin_decoder.cc", + "src/core/ext/transport/chttp2/transport/bin_encoder.cc", + "src/core/ext/transport/chttp2/transport/chttp2_plugin.cc", + "src/core/ext/transport/chttp2/transport/chttp2_transport.cc", + "src/core/ext/transport/chttp2/transport/context_list.cc", + "src/core/ext/transport/chttp2/transport/flow_control.cc", + "src/core/ext/transport/chttp2/transport/frame_data.cc", + "src/core/ext/transport/chttp2/transport/frame_goaway.cc", + "src/core/ext/transport/chttp2/transport/frame_ping.cc", + "src/core/ext/transport/chttp2/transport/frame_rst_stream.cc", + "src/core/ext/transport/chttp2/transport/frame_settings.cc", + "src/core/ext/transport/chttp2/transport/frame_window_update.cc", + "src/core/ext/transport/chttp2/transport/hpack_encoder.cc", + "src/core/ext/transport/chttp2/transport/hpack_parser.cc", + "src/core/ext/transport/chttp2/transport/hpack_table.cc", + "src/core/ext/transport/chttp2/transport/http2_settings.cc", + "src/core/ext/transport/chttp2/transport/huffsyms.cc", + "src/core/ext/transport/chttp2/transport/incoming_metadata.cc", + "src/core/ext/transport/chttp2/transport/parsing.cc", + "src/core/ext/transport/chttp2/transport/stream_lists.cc", + "src/core/ext/transport/chttp2/transport/stream_map.cc", + "src/core/ext/transport/chttp2/transport/varint.cc", + "src/core/ext/transport/chttp2/transport/writing.cc", + ], + hdrs = [ + "src/core/ext/transport/chttp2/transport/bin_decoder.h", + "src/core/ext/transport/chttp2/transport/bin_encoder.h", + "src/core/ext/transport/chttp2/transport/chttp2_transport.h", + "src/core/ext/transport/chttp2/transport/context_list.h", + "src/core/ext/transport/chttp2/transport/flow_control.h", + "src/core/ext/transport/chttp2/transport/frame.h", + "src/core/ext/transport/chttp2/transport/frame_data.h", + "src/core/ext/transport/chttp2/transport/frame_goaway.h", + "src/core/ext/transport/chttp2/transport/frame_ping.h", + "src/core/ext/transport/chttp2/transport/frame_rst_stream.h", + "src/core/ext/transport/chttp2/transport/frame_settings.h", + "src/core/ext/transport/chttp2/transport/frame_window_update.h", + "src/core/ext/transport/chttp2/transport/hpack_encoder.h", + "src/core/ext/transport/chttp2/transport/hpack_parser.h", + "src/core/ext/transport/chttp2/transport/hpack_table.h", + "src/core/ext/transport/chttp2/transport/http2_settings.h", + "src/core/ext/transport/chttp2/transport/huffsyms.h", + "src/core/ext/transport/chttp2/transport/incoming_metadata.h", + "src/core/ext/transport/chttp2/transport/internal.h", + "src/core/ext/transport/chttp2/transport/stream_map.h", + "src/core/ext/transport/chttp2/transport/varint.h", + ], + language = "c++", + deps = [ + "gpr_base", + "grpc_base", + "grpc_http_filters", + "grpc_transport_chttp2_alpn", + ], +) + +grpc_cc_library( + name = "grpc_transport_chttp2_alpn", + srcs = [ + "src/core/ext/transport/chttp2/alpn/alpn.cc", + ], + hdrs = [ + "src/core/ext/transport/chttp2/alpn/alpn.h", + ], + language = "c++", + deps = [ + "gpr", + ], +) + +grpc_cc_library( + name = "grpc_transport_chttp2_client_connector", + srcs = [ + "src/core/ext/transport/chttp2/client/authority.cc", + "src/core/ext/transport/chttp2/client/chttp2_connector.cc", + ], + hdrs = [ + "src/core/ext/transport/chttp2/client/authority.h", + "src/core/ext/transport/chttp2/client/chttp2_connector.h", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + "grpc_transport_chttp2", + ], +) + +grpc_cc_library( + name = "grpc_transport_chttp2_client_insecure", + srcs = [ + "src/core/ext/transport/chttp2/client/insecure/channel_create.cc", + "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_client_channel", + "grpc_transport_chttp2", + "grpc_transport_chttp2_client_connector", + ], +) + +grpc_cc_library( + name = "grpc_transport_chttp2_server", + srcs = [ + "src/core/ext/transport/chttp2/server/chttp2_server.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", + "src/core/ext/transport/chttp2/server/chttp2_server.h", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_transport_chttp2", + ], +) + +grpc_cc_library( + name = "grpc_transport_chttp2_server_insecure", + srcs = [ + "src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc", + "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc", + ], + language = "c++", + deps = [ + "grpc_base", + "grpc_transport_chttp2", + "grpc_transport_chttp2_server", + ], +) + +grpc_cc_library( + name = "grpc_transport_inproc", + srcs = [ + "src/core/ext/transport/inproc/inproc_plugin.cc", + "src/core/ext/transport/inproc/inproc_transport.cc", + ], + hdrs = [ + "src/core/ext/transport/inproc/inproc_transport.h", + ], + language = "c++", + deps = [ + "grpc_base", + ], +) + +grpc_cc_library( + name = "grpc++_base_unsecure", + srcs = GRPCXX_SRCS, + hdrs = GRPCXX_HDRS, + language = "c++", + public_hdrs = GRPCXX_PUBLIC_HDRS, + deps = [ + "grpc++_codegen_base", + "grpc_unsecure", + "health_proto", + ], +) + +grpc_cc_library( + name = "grpc++_codegen_base", + language = "c++", + public_hdrs = [ "include/grpc++/impl/codegen/async_stream.h", "include/grpc++/impl/codegen/async_unary_call.h", + "include/grpc++/impl/codegen/byte_buffer.h", "include/grpc++/impl/codegen/call.h", "include/grpc++/impl/codegen/call_hook.h", "include/grpc++/impl/codegen/channel_interface.h", @@ -503,10 +1571,11 @@ cc_library( "include/grpc++/impl/codegen/completion_queue.h", "include/grpc++/impl/codegen/completion_queue_tag.h", "include/grpc++/impl/codegen/config.h", - "include/grpc++/impl/codegen/config_protobuf.h", + "include/grpc++/impl/codegen/core_codegen_interface.h", + "include/grpc++/impl/codegen/create_auth_context.h", "include/grpc++/impl/codegen/grpc_library.h", + "include/grpc++/impl/codegen/metadata_map.h", "include/grpc++/impl/codegen/method_handler_impl.h", - "include/grpc++/impl/codegen/proto_utils.h", "include/grpc++/impl/codegen/rpc_method.h", "include/grpc++/impl/codegen/rpc_service_method.h", "include/grpc++/impl/codegen/security/auth_context.h", @@ -514,52 +1583,108 @@ cc_library( "include/grpc++/impl/codegen/server_context.h", "include/grpc++/impl/codegen/server_interface.h", "include/grpc++/impl/codegen/service_type.h", + "include/grpc++/impl/codegen/slice.h", "include/grpc++/impl/codegen/status.h", "include/grpc++/impl/codegen/status_code_enum.h", "include/grpc++/impl/codegen/string_ref.h", "include/grpc++/impl/codegen/stub_options.h", - "include/grpc++/impl/codegen/sync.h", - "include/grpc++/impl/codegen/sync_cxx11.h", - "include/grpc++/impl/codegen/sync_no_cxx11.h", "include/grpc++/impl/codegen/sync_stream.h", "include/grpc++/impl/codegen/time.h", - "include/grpc++/impl/grpc_library.h", - "include/grpc++/impl/method_handler_impl.h", - "include/grpc++/impl/proto_utils.h", - "include/grpc++/impl/rpc_method.h", - "include/grpc++/impl/rpc_service_method.h", - "include/grpc++/impl/serialization_traits.h", - "include/grpc++/impl/server_builder_option.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", - ], - deps = [ - ":grpc_unsecure", - "//third_party/protobuf:protobuf", + "include/grpcpp/impl/codegen/async_generic_service.h", + "include/grpcpp/impl/codegen/async_stream.h", + "include/grpcpp/impl/codegen/async_unary_call.h", + "include/grpcpp/impl/codegen/byte_buffer.h", + "include/grpcpp/impl/codegen/call.h", + "include/grpcpp/impl/codegen/call_hook.h", + "include/grpcpp/impl/codegen/call_op_set.h", + "include/grpcpp/impl/codegen/call_op_set_interface.h", + "include/grpcpp/impl/codegen/callback_common.h", + "include/grpcpp/impl/codegen/channel_interface.h", + "include/grpcpp/impl/codegen/client_callback.h", + "include/grpcpp/impl/codegen/client_context.h", + "include/grpcpp/impl/codegen/client_interceptor.h", + "include/grpcpp/impl/codegen/client_unary_call.h", + "include/grpcpp/impl/codegen/completion_queue.h", + "include/grpcpp/impl/codegen/completion_queue_tag.h", + "include/grpcpp/impl/codegen/config.h", + "include/grpcpp/impl/codegen/core_codegen_interface.h", + "include/grpcpp/impl/codegen/create_auth_context.h", + "include/grpcpp/impl/codegen/grpc_library.h", + "include/grpcpp/impl/codegen/intercepted_channel.h", + "include/grpcpp/impl/codegen/interceptor.h", + "include/grpcpp/impl/codegen/interceptor_common.h", + "include/grpcpp/impl/codegen/metadata_map.h", + "include/grpcpp/impl/codegen/method_handler_impl.h", + "include/grpcpp/impl/codegen/rpc_method.h", + "include/grpcpp/impl/codegen/rpc_service_method.h", + "include/grpcpp/impl/codegen/security/auth_context.h", + "include/grpcpp/impl/codegen/serialization_traits.h", + "include/grpcpp/impl/codegen/server_callback.h", + "include/grpcpp/impl/codegen/server_context.h", + "include/grpcpp/impl/codegen/server_interceptor.h", + "include/grpcpp/impl/codegen/server_interface.h", + "include/grpcpp/impl/codegen/service_type.h", + "include/grpcpp/impl/codegen/slice.h", + "include/grpcpp/impl/codegen/status.h", + "include/grpcpp/impl/codegen/status_code_enum.h", + "include/grpcpp/impl/codegen/string_ref.h", + "include/grpcpp/impl/codegen/stub_options.h", + "include/grpcpp/impl/codegen/sync_stream.h", + "include/grpcpp/impl/codegen/time.h", + ], + deps = [ + "grpc_codegen", + ], +) + +grpc_cc_library( + name = "grpc++_codegen_base_src", + srcs = [ + "src/cpp/codegen/codegen_init.cc", + ], + language = "c++", + deps = [ + "grpc++_codegen_base", + ], +) + +grpc_cc_library( + name = "grpc++_codegen_proto", + language = "c++", + public_hdrs = [ + "include/grpc++/impl/codegen/proto_utils.h", + "include/grpcpp/impl/codegen/proto_buffer_reader.h", + "include/grpcpp/impl/codegen/proto_buffer_writer.h", + "include/grpcpp/impl/codegen/proto_utils.h", + ], + deps = [ + "grpc++_codegen_base", + "grpc++_config_proto", + ], +) + +grpc_cc_library( + name = "grpc++_config_proto", + external_deps = [ + "protobuf:protobuf_headers", + ], + language = "c++", + public_hdrs = [ + "include/grpc++/impl/codegen/config_protobuf.h", + "include/grpcpp/impl/codegen/config_protobuf.h", + ], +) + +grpc_cc_library( + name = "grpc_server_backward_compatibility", + srcs = [ + "src/core/ext/filters/workarounds/workaround_utils.cc", + ], + hdrs = [ + "src/core/ext/filters/workarounds/workaround_utils.h", + ], + language = "c++", + deps = [ + "grpc_base", ], ) diff --git a/third_party/grpc/README.bazel.md b/third_party/grpc/README.bazel.md index 1b3521fd77eda7..c7a9354d7b095b 100644 --- a/third_party/grpc/README.bazel.md +++ b/third_party/grpc/README.bazel.md @@ -1,12 +1,14 @@ # How to update the C++ sources of gRPC: 1. `git clone http://github.com/grpc/grpc.git` in a convenient directory -2. `git checkout ` (current is `release-0_13`, commithash `78e04bbd`) +2. `git checkout ` (current is `v1.18.0`, commithash `007b721f`) 3. `mkdir -p third_party/grpc/src` -4. `cp -R /src/{compiler,core-cpp} third_party/grpc/src` +4. `cp -R /src/{compiler,core,cpp} third_party/grpc/src` 5. `cp -R /include third_party/grpc` -6. Update BUILD files by copying the rules from the BUILD file of gRPC -7. Patch in grpc.patch. It makes gRPC work under msys2. +6. `rm -rf third_party/grpc/src/core/tsi/test_creds` +7. Update BUILD files by copying the rules from the BUILD file of gRPC; + fix macros in third_party/grpc/build_defs.bzl if necessary +8. Update //third_party/nanopb if necessary # How to update the Java plugin: diff --git a/third_party/grpc/build_defs.bzl b/third_party/grpc/build_defs.bzl index aca5b66cec0b98..18042a4132cf43 100644 --- a/third_party/grpc/build_defs.bzl +++ b/third_party/grpc/build_defs.bzl @@ -4,38 +4,42 @@ load("//third_party/grpc:build_defs.bzl", "java_grpc_library") """ def _path_ignoring_repository(f): - if (len(f.owner.workspace_root) == 0): - return f.short_path - # If |f| is a generated file, it will have "bazel-out/*/genfiles" prefix - # before "external/workspace", so we need to add the starting index of "external/workspace" - return f.path[f.path.find(f.owner.workspace_root)+len(f.owner.workspace_root)+1:] + if (len(f.owner.workspace_root) == 0): + return f.short_path + + # If |f| is a generated file, it will have "bazel-out/*/genfiles" prefix + # before "external/workspace", so we need to add the starting index of "external/workspace" + return f.path[f.path.find(f.owner.workspace_root) + len(f.owner.workspace_root) + 1:] def _gensource_impl(ctx): - if len(ctx.attr.srcs) > 1: - fail("Only one src value supported", "srcs") - for s in ctx.attr.srcs: - if s.label.package != ctx.label.package: - print(("in srcs attribute of {0}: Proto source with label {1} should be in " - + "same package as consuming rule").format(ctx.label, s.label)) - srcdotjar = ctx.new_file(ctx.label.name + ".jar") - srcs = [f for dep in ctx.attr.srcs for f in dep.proto.direct_sources] - includes = [f for dep in ctx.attr.srcs for f in dep.proto.transitive_imports.to_list()] + if len(ctx.attr.srcs) > 1: + fail("Only one src value supported", "srcs") + for s in ctx.attr.srcs: + if s.label.package != ctx.label.package: + print(("in srcs attribute of {0}: Proto source with label {1} should be in " + + "same package as consuming rule").format(ctx.label, s.label)) + srcdotjar = ctx.new_file(ctx.label.name + ".jar") + srcs = [f for dep in ctx.attr.srcs for f in dep.proto.direct_sources] + includes = [f for dep in ctx.attr.srcs for f in dep.proto.transitive_imports.to_list()] - ctx.action( - command = " ".join([ - ctx.executable._protoc.path, - "--plugin=protoc-gen-grpc-java={0}".format(ctx.executable._java_plugin.path), - "--grpc-java_out=enable_deprecated={0}:{1}".format(str(ctx.attr.enable_deprecated).lower(), srcdotjar.path)] - + ["-I{0}={1}".format(_path_ignoring_repository(include), include.path) for include in includes] - + [src.path for src in srcs]), - inputs = [ctx.executable._java_plugin, ctx.executable._protoc] + srcs + includes, - outputs = [srcdotjar], - use_default_shell_env=True) + ctx.action( + command = " ".join([ + ctx.executable._protoc.path, + "--plugin=protoc-gen-grpc-java={0}".format(ctx.executable._java_plugin.path), + "--grpc-java_out=enable_deprecated={0}:{1}".format(str(ctx.attr.enable_deprecated).lower(), srcdotjar.path), + ] + + ["-I{0}={1}".format(_path_ignoring_repository(include), include.path) for include in includes] + + [src.path for src in srcs]), + inputs = [ctx.executable._java_plugin, ctx.executable._protoc] + srcs + includes, + outputs = [srcdotjar], + use_default_shell_env = True, + ) - ctx.action( - command = "cp '%s' '%s'" % (srcdotjar.path, ctx.outputs.srcjar.path), - inputs = [srcdotjar], - outputs = [ctx.outputs.srcjar]) + ctx.action( + command = "cp '%s' '%s'" % (srcdotjar.path, ctx.outputs.srcjar.path), + inputs = [srcdotjar], + outputs = [ctx.outputs.srcjar], + ) _java_grpc_gensource = rule( attrs = { @@ -65,44 +69,94 @@ _java_grpc_gensource = rule( implementation = _gensource_impl, ) -def java_grpc_library(name, srcs, deps, enable_deprecated=None, visibility=None, constraints=None, **kwargs): - """Generates and compiles gRPC Java sources for services defined in a proto - file. This rule is compatible with proto_library with java_api_version, - java_proto_library, and java_lite_proto_library. - Do note that this rule only scans through the proto file for RPC services. It - does not generate Java classes for proto messages. You will need a separate - proto_library with java_api_version, java_proto_library, or - java_lite_proto_library rule. - Args: - name: (str) A unique name for this rule. Required. - srcs: (list) a single proto_library target that contains the schema of the - service. Required. - deps: (list) a single java_proto_library target for the proto_library in - srcs. Required. - visibility: (list) the visibility list - **kwargs: Passed through to generated targets - """ - gensource_name = name + "_srcs" - _java_grpc_gensource( - name = gensource_name, - srcs = srcs, - enable_deprecated = enable_deprecated, - visibility = ["//visibility:private"], - tags = [ - "avoid_dep", - ], - **kwargs - ) - native.java_library( - name = name, - srcs = [gensource_name], - visibility = visibility, - deps = [ - "@io_bazel//third_party:javax_annotations", - "@io_bazel//third_party:jsr305", - "@io_bazel//third_party/grpc:grpc-jar", - "@io_bazel//third_party:guava", - "@com_google_protobuf//:protobuf_java", - ] + deps, - **kwargs - ) +def java_grpc_library(name, srcs, deps, enable_deprecated = None, visibility = None, constraints = None, **kwargs): + """Generates and compiles gRPC Java sources for services defined in a proto + file. This rule is compatible with proto_library with java_api_version, + java_proto_library, and java_lite_proto_library. + Do note that this rule only scans through the proto file for RPC services. It + does not generate Java classes for proto messages. You will need a separate + proto_library with java_api_version, java_proto_library, or + java_lite_proto_library rule. + Args: + name: (str) A unique name for this rule. Required. + srcs: (list) a single proto_library target that contains the schema of the + service. Required. + deps: (list) a single java_proto_library target for the proto_library in + srcs. Required. + visibility: (list) the visibility list + **kwargs: Passed through to generated targets + """ + gensource_name = name + "_srcs" + _java_grpc_gensource( + name = gensource_name, + srcs = srcs, + enable_deprecated = enable_deprecated, + visibility = ["//visibility:private"], + tags = [ + "avoid_dep", + ], + **kwargs + ) + native.java_library( + name = name, + srcs = [gensource_name], + visibility = visibility, + deps = [ + "@io_bazel//third_party:javax_annotations", + "@io_bazel//third_party:jsr305", + "@io_bazel//third_party/grpc:grpc-jar", + "@io_bazel//third_party:guava", + "@com_google_protobuf//:protobuf_java", + ] + deps, + **kwargs + ) + +def _get_external_deps(external_deps): + ret = [] + for dep in external_deps: + ret += ["//third_party/" + dep] + return ret + +# Simplified version of gRPC upstream's grpc_cc_library. +def grpc_cc_library( + name, + srcs = [], + public_hdrs = [], + hdrs = [], + external_deps = [], + deps = [], + standalone = False, + language = "C++", + testonly = False, + visibility = None, + alwayslink = 0, + data = []): + copts = [] + if language.upper() == "C": + copts = select({ + "//conditions:default": [ + "-std=c99", + "-Wimplicit-function-declaration", + ], + ":windows": ["/we4013"], + }) + native.cc_library( + name = name, + srcs = srcs, + defines = ["GRPC_ARES=0"], # Our use case doesn't need ares. + hdrs = hdrs + public_hdrs, + deps = deps + _get_external_deps(external_deps), + copts = copts, + visibility = visibility, + testonly = testonly, + linkopts = select({ + "//conditions:default": ["-pthread"], + ":windows": [], + }), + includes = [ + ".", + "include", + ], + alwayslink = alwayslink, + data = data, + ) diff --git a/third_party/grpc/grpc.patch b/third_party/grpc/grpc.patch index 5bb428f32c5f02..0ce4923af9edca 100644 --- a/third_party/grpc/grpc.patch +++ b/third_party/grpc/grpc.patch @@ -1,137 +1,3 @@ -diff --git a/third_party/grpc/BUILD b/third_party/grpc/BUILD -index 51e861f..a635ebc 100644 ---- a/third_party/grpc/BUILD -+++ b/third_party/grpc/BUILD -@@ -438,7 +438,7 @@ cc_library( - ], - copts = [ - "-std=gnu99", -- "-Wno-implicit-function-declaration", -+ "-w", - ], - includes = [ - ".", -@@ -570,6 +570,7 @@ cc_library( - "include/grpc++/support/sync_stream.h", - "include/grpc++/support/time.h", - ], -+ copts = ["-w"], - includes = [ - ".", - "include", -diff --git a/third_party/grpc/include/grpc/impl/codegen/port_platform.h b/third_party/grpc/include/grpc/impl/codegen/port_platform.h -index d265e9b..d22bffb 100644 ---- a/third_party/grpc/include/grpc/impl/codegen/port_platform.h -+++ b/third_party/grpc/include/grpc/impl/codegen/port_platform.h -@@ -74,7 +74,45 @@ - things. */ - - #if !defined(GPR_NO_AUTODETECT_PLATFORM) --#if defined(_WIN64) || defined(WIN64) -+ -+#if defined(__MSYS__) -+#define GPR_PLATFORM_STRING "msys2" -+#define GPR_POSIX_CRASH_HANDLER 1 -+#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_POSIX 1 -+#define GPR_GCC_ATOMIC 1 -+#define GPR_GCC_TLS 1 -+#define GPR_POSIX_MULTIPOLL_WITH_POLL -+#define GPR_POSIX_WAKEUP_FD 1 -+#define GPR_POSIX_SOCKET 1 -+#define GPR_POSIX_SOCKETADDR 1 -+#define GPR_POSIX_ENV 1 -+#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1 -+#define GPR_POSIX_SOCKETUTILS -+#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_POSIX_LOG 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(_WIN64) || defined(WIN64) - #define GPR_PLATFORM_STRING "windows" - #define GPR_WIN32 1 - #define GPR_ARCH_64 1 -diff --git a/third_party/grpc/src/core/iomgr/tcp_server_posix.c b/third_party/grpc/src/core/iomgr/tcp_server_posix.c -index 5e07f82..44b970d 100644 ---- a/third_party/grpc/src/core/iomgr/tcp_server_posix.c -+++ b/third_party/grpc/src/core/iomgr/tcp_server_posix.c -@@ -280,13 +280,38 @@ static int prepare_socket(int fd, const struct sockaddr *addr, - 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 (!grpc_set_socket_nonblocking(fd, 1)) { -+ gpr_log(GPR_ERROR, "Unable to set socket %d to nonblocking mode: %s", fd, -+ strerror(errno)); -+ goto error; -+ } -+ if (!grpc_set_socket_cloexec(fd, 1)) { -+ gpr_log(GPR_ERROR, "Unable to set socket %d to close-on-exec: %s", fd, -+ strerror(errno)); -+ goto error; -+ } -+ -+ if (addr->sa_family != AF_UNIX) { -+ if (!grpc_set_socket_low_latency(fd, 1)) { -+ gpr_log(GPR_ERROR, "Unable to set socket %d to low-latency mode: %s", fd, -+ strerror(errno)); -+ goto error; -+ } -+ -+#if !defined(__MSYS__) -+ if (!grpc_set_socket_reuse_addr(fd, 1)) { -+ gpr_log(GPR_ERROR, "Unable to set SO_REUSEADDR on socket %d: %s", fd, -+ strerror(errno)); -+ goto error; -+ } -+#endif -+ -+ if (!grpc_set_socket_no_sigpipe_if_possible(fd)) { -+ gpr_log(GPR_ERROR, "Unable to set SO_NOSIGPIPE on socket %d: %s", fd, -+ strerror(errno)); -+ goto error; -+ } - } - - GPR_ASSERT(addr_len < ~(socklen_t)0); -diff --git a/third_party/grpc/src/core/support/log_linux.c b/third_party/grpc/src/core/support/log_linux.c -index d66b7a3..93a0c1b 100644 ---- a/third_party/grpc/src/core/support/log_linux.c -+++ b/third_party/grpc/src/core/support/log_linux.c -@@ -51,7 +51,6 @@ - #include - #include - #include --#include - #include - #include - diff --git a/tools/build_rules/genproto.bzl b/tools/build_rules/genproto.bzl index 62284a3..bc76cd1 100644 --- a/tools/build_rules/genproto.bzl diff --git a/third_party/grpc/include/grpc++/alarm.h b/third_party/grpc/include/grpc++/alarm.h index e2618195f08fa0..dce742ee884513 100644 --- a/third_party/grpc/include/grpc++/alarm.h +++ b/third_party/grpc/include/grpc++/alarm.h @@ -1,69 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -/// An Alarm posts the user provided tag to its associated completion queue upon -/// expiry or cancellation. +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_ALARM_H #define GRPCXX_ALARM_H -#include -#include - -namespace grpc { - -class CompletionQueue; - -/// A thin wrapper around \a grpc_alarm (see / \a / src/core/surface/alarm.h). -class Alarm : private GrpcLibrary { - public: - /// Create a completion queue alarm instance associated to \a cq. - /// - /// Once the alarm expires (at \a deadline) or it's cancelled (see \a Cancel), - /// an event with tag \a tag will be added to \a cq. If the alarm expired, the - /// event's success bit will be true, false otherwise (ie, upon cancellation). - Alarm(CompletionQueue* cq, gpr_timespec deadline, void* tag); - - /// Destroy the given completion queue alarm, cancelling it in the process. - ~Alarm(); - - /// Cancel a completion queue alarm. Calling this function over an alarm that - /// has already fired has no effect. - void Cancel(); - - private: - grpc_alarm* const alarm_; // owned -}; - -} // namespace grpc +#include #endif // GRPCXX_ALARM_H diff --git a/third_party/grpc/include/grpc++/channel.h b/third_party/grpc/include/grpc++/channel.h index 80547f7ab8abba..b1154cefb34b8e 100644 --- a/third_party/grpc/include/grpc++/channel.h +++ b/third_party/grpc/include/grpc++/channel.h @@ -1,88 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_CHANNEL_H #define GRPCXX_CHANNEL_H -#include - -#include -#include -#include -#include -#include - -struct grpc_channel; - -namespace grpc { -/// Channels represent a connection to an endpoint. Created by \a CreateChannel. -class Channel GRPC_FINAL : public ChannelInterface, - public CallHook, - public std::enable_shared_from_this, - private GrpcLibrary { - 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) GRPC_OVERRIDE; - - private: - template - friend Status BlockingUnaryCall(ChannelInterface* channel, - const RpcMethod& method, - ClientContext* context, - const InputMessage& request, - OutputMessage* result); - 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) GRPC_OVERRIDE; - void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) GRPC_OVERRIDE; - void* RegisterMethod(const char* method) GRPC_OVERRIDE; - - void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, - gpr_timespec deadline, CompletionQueue* cq, - void* tag) GRPC_OVERRIDE; - bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, - gpr_timespec deadline) GRPC_OVERRIDE; - - const grpc::string host_; - grpc_channel* const c_channel_; // owned -}; - -} // namespace grpc +#include #endif // GRPCXX_CHANNEL_H diff --git a/third_party/grpc/include/grpc++/client_context.h b/third_party/grpc/include/grpc++/client_context.h index 3fbe6d1be80a61..4b236440596266 100644 --- a/third_party/grpc/include/grpc++/client_context.h +++ b/third_party/grpc/include/grpc++/client_context.h @@ -1,54 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -/// 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. +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. #ifndef GRPCXX_CLIENT_CONTEXT_H #define GRPCXX_CLIENT_CONTEXT_H -#include +#include #endif // GRPCXX_CLIENT_CONTEXT_H diff --git a/third_party/grpc/include/grpc++/completion_queue.h b/third_party/grpc/include/grpc++/completion_queue.h index f53f5ee56c0e16..98ef18f0f65a30 100644 --- a/third_party/grpc/include/grpc++/completion_queue.h +++ b/third_party/grpc/include/grpc++/completion_queue.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_COMPLETION_QUEUE_H #define GRPCXX_COMPLETION_QUEUE_H -#include +#include #endif // GRPCXX_COMPLETION_QUEUE_H diff --git a/third_party/grpc/include/grpc++/create_channel.h b/third_party/grpc/include/grpc++/create_channel.h index aff9c291c26d39..d95f3a97972b0a 100644 --- a/third_party/grpc/include/grpc++/create_channel.h +++ b/third_party/grpc/include/grpc++/create_channel.h @@ -1,71 +1,28 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #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 +#include #endif // GRPCXX_CREATE_CHANNEL_H diff --git a/third_party/grpc/include/grpc++/create_channel_posix.h b/third_party/grpc/include/grpc++/create_channel_posix.h new file mode 100644 index 00000000000000..8c8983ba60612f --- /dev/null +++ b/third_party/grpc/include/grpc++/create_channel_posix.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_CREATE_CHANNEL_POSIX_H +#define GRPCXX_CREATE_CHANNEL_POSIX_H + +#include + +#endif // GRPCXX_CREATE_CHANNEL_POSIX_H diff --git a/third_party/grpc/include/grpc++/ext/health_check_service_server_builder_option.h b/third_party/grpc/include/grpc++/ext/health_check_service_server_builder_option.h new file mode 100644 index 00000000000000..cb82fc03b6b734 --- /dev/null +++ b/third_party/grpc/include/grpc++/ext/health_check_service_server_builder_option.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H +#define GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H + +#include + +#endif // GRPCXX_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H diff --git a/third_party/grpc/include/grpc++/ext/proto_server_reflection_plugin.h b/third_party/grpc/include/grpc++/ext/proto_server_reflection_plugin.h new file mode 100644 index 00000000000000..02e21b92190978 --- /dev/null +++ b/third_party/grpc/include/grpc++/ext/proto_server_reflection_plugin.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H +#define GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H + +#include + +#endif // GRPCXX_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H diff --git a/third_party/grpc/include/grpc++/generic/async_generic_service.h b/third_party/grpc/include/grpc++/generic/async_generic_service.h index 9ae8391dc4cff0..d3283fac6f4fb4 100644 --- a/third_party/grpc/include/grpc++/generic/async_generic_service.h +++ b/third_party/grpc/include/grpc++/generic/async_generic_service.h @@ -1,76 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #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; - friend class ServerInterface; - - grpc::string method_; - grpc::string host_; -}; - -class AsyncGenericService GRPC_FINAL { - public: - AsyncGenericService() : 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 +#include #endif // GRPCXX_GENERIC_ASYNC_GENERIC_SERVICE_H diff --git a/third_party/grpc/include/grpc++/generic/generic_stub.h b/third_party/grpc/include/grpc++/generic/generic_stub.h index 5ac0371efb72d9..502953b5de997c 100644 --- a/third_party/grpc/include/grpc++/generic/generic_stub.h +++ b/third_party/grpc/include/grpc++/generic/generic_stub.h @@ -1,64 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #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 +#include #endif // GRPCXX_GENERIC_GENERIC_STUB_H diff --git a/third_party/grpc/include/grpc++/grpc++.h b/third_party/grpc/include/grpc++/grpc++.h index b7d5fb0bbcdc4d..9f1d7b1bc1ebaf 100644 --- a/third_party/grpc/include/grpc++/grpc++.h +++ b/third_party/grpc/include/grpc++/grpc++.h @@ -1,64 +1,28 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -/// \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. +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. #ifndef GRPCXX_GRPCXX_H #define GRPCXX_GRPCXX_H -#include - -#include -#include -#include -#include -#include -#include -#include +#include #endif // GRPCXX_GRPCXX_H diff --git a/third_party/grpc/include/grpc++/health_check_service_interface.h b/third_party/grpc/include/grpc++/health_check_service_interface.h new file mode 100644 index 00000000000000..0cb0668e48d8cb --- /dev/null +++ b/third_party/grpc/include/grpc++/health_check_service_interface.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H +#define GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H + +#include + +#endif // GRPCXX_HEALTH_CHECK_SERVICE_INTERFACE_H diff --git a/third_party/grpc/include/grpc++/impl/call.h b/third_party/grpc/include/grpc++/impl/call.h index 64b6563574d561..b1da2b6358d61e 100644 --- a/third_party/grpc/include/grpc++/impl/call.h +++ b/third_party/grpc/include/grpc++/impl/call.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CALL_H #define GRPCXX_IMPL_CALL_H -#include +#include #endif // GRPCXX_IMPL_CALL_H diff --git a/third_party/grpc/include/grpc++/impl/channel_argument_option.h b/third_party/grpc/include/grpc++/impl/channel_argument_option.h new file mode 100644 index 00000000000000..3468378de6efc9 --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/channel_argument_option.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H +#define GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H + +#include + +#endif // GRPCXX_IMPL_CHANNEL_ARGUMENT_OPTION_H diff --git a/third_party/grpc/include/grpc++/impl/client_unary_call.h b/third_party/grpc/include/grpc++/impl/client_unary_call.h index abe321eaccd718..75e656071f4489 100644 --- a/third_party/grpc/include/grpc++/impl/client_unary_call.h +++ b/third_party/grpc/include/grpc++/impl/client_unary_call.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CLIENT_UNARY_CALL_H #define GRPCXX_IMPL_CLIENT_UNARY_CALL_H -#include +#include #endif // GRPCXX_IMPL_CLIENT_UNARY_CALL_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/async_stream.h b/third_party/grpc/include/grpc++/impl/codegen/async_stream.h index b0410485f8582d..a034470a707ea3 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/async_stream.h +++ b/third_party/grpc/include/grpc++/impl/codegen/async_stream.h @@ -1,462 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H #define GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H -#include -#include -#include -#include -#include - -namespace grpc { - -class CompletionQueue; - -/// 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. - /// - /// Only one write may be outstanding at any given time. This means that - /// after calling Write, one must wait to receive \a tag from the completion - /// queue BEFORE calling Write again. - /// - /// \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(ChannelInterface* 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(ChannelInterface* 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(ChannelInterface* 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 +#include #endif // GRPCXX_IMPL_CODEGEN_ASYNC_STREAM_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/async_unary_call.h b/third_party/grpc/include/grpc++/impl/codegen/async_unary_call.h index f3c75dc3b1d771..2b08920a3042a1 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/async_unary_call.h +++ b/third_party/grpc/include/grpc++/impl/codegen/async_unary_call.h @@ -1,166 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H #define GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H -#include -#include -#include -#include -#include -#include -#include - -namespace grpc { - -class CompletionQueue; - -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(ChannelInterface* channel, CompletionQueue* cq, - const RpcMethod& method, ClientContext* context, - const W& request) - : context_(context), - call_(channel->CreateCall(method, context, cq)), - collection_(new CallOpSetCollection) { - collection_->init_buf_.SetCollection(collection_); - collection_->init_buf_.SendInitialMetadata(context->send_initial_metadata_); - // TODO(ctiller): don't assert - GPR_ASSERT(collection_->init_buf_.SendMessage(request).ok()); - collection_->init_buf_.ClientSendClose(); - call_.PerformOps(&collection_->init_buf_); - } - - void ReadInitialMetadata(void* tag) { - GPR_ASSERT(!context_->initial_metadata_received_); - - collection_->meta_buf_.SetCollection(collection_); - collection_->meta_buf_.set_output_tag(tag); - collection_->meta_buf_.RecvInitialMetadata(context_); - call_.PerformOps(&collection_->meta_buf_); - } - - void Finish(R* msg, Status* status, void* tag) { - collection_->finish_buf_.SetCollection(collection_); - collection_->finish_buf_.set_output_tag(tag); - if (!context_->initial_metadata_received_) { - collection_->finish_buf_.RecvInitialMetadata(context_); - } - collection_->finish_buf_.RecvMessage(msg); - collection_->finish_buf_.ClientRecvStatus(context_, status); - call_.PerformOps(&collection_->finish_buf_); - } - - private: - ClientContext* context_; - Call call_; - - class CallOpSetCollection : public CallOpSetCollectionInterface { - public: - SneakyCallOpSet init_buf_; - CallOpSet meta_buf_; - CallOpSet, - CallOpClientRecvStatus> finish_buf_; - }; - std::shared_ptr collection_; -}; - -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 +#include #endif // GRPCXX_IMPL_CODEGEN_ASYNC_UNARY_CALL_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/byte_buffer.h b/third_party/grpc/include/grpc++/impl/codegen/byte_buffer.h new file mode 100644 index 00000000000000..b754fa24e9caee --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/codegen/byte_buffer.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H +#define GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H + +#include + +#endif // GRPCXX_IMPL_CODEGEN_BYTE_BUFFER_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/call.h b/third_party/grpc/include/grpc++/impl/codegen/call.h index e65349ddd3a2d9..dadab5454ab71f 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/call.h +++ b/third_party/grpc/include/grpc++/impl/codegen/call.h @@ -1,596 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_CALL_H #define GRPCXX_IMPL_CODEGEN_CALL_H -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -struct grpc_byte_buffer; - -namespace grpc { - -class ByteBuffer; -class Call; -class CallHook; -class CompletionQueue; - -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 uint32_t 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 uint32_t mask) { flags_ |= mask; } - - void ClearBit(const uint32_t mask) { flags_ &= ~mask; } - - bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; } - - uint32_t 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 CallOpSet's, to be used whenever -/// CallOpSet objects must be thought of as a group. Each member -/// of the group should have a shared_ptr back to the collection, -/// as will the object that instantiates the collection, allowing -/// for automatic ref-counting. In practice, any actual use should -/// derive from this base class. This is specifically necessary if -/// some of the CallOpSet's in the collection are "Sneaky" and don't -/// report back to the C++ layer CQ operations -class CallOpSetCollectionInterface - : public std::enable_shared_from_this {}; - -/// 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; - } - - /// Mark this as belonging to a collection if needed - void SetCollection(std::shared_ptr collection) { - collection_ = collection; - } - - protected: - int max_message_size_; - std::shared_ptr collection_; -}; - -/// 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_; - collection_.reset(); // drop the ref at this point - 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; - } -}; - -// 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 +#include #endif // GRPCXX_IMPL_CODEGEN_CALL_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/call_hook.h b/third_party/grpc/include/grpc++/impl/codegen/call_hook.h index 0b6027293a37a3..cf5ed571b2df09 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/call_hook.h +++ b/third_party/grpc/include/grpc++/impl/codegen/call_hook.h @@ -1,51 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_CALL_HOOK_H #define GRPCXX_IMPL_CODEGEN_CALL_HOOK_H -namespace grpc { - -class CallOpSetInterface; -class Call; - -/// 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; -}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_CODEGEN_CALL_HOOK_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/channel_interface.h b/third_party/grpc/include/grpc++/impl/codegen/channel_interface.h index 6fcd5c315ca9c1..c6e782e9c41abc 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/channel_interface.h +++ b/third_party/grpc/include/grpc++/impl/codegen/channel_interface.h @@ -1,123 +1,28 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H #define GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H -#include -#include -#include - -namespace grpc { -class Call; -class ClientContext; -class RpcMethod; -class CallOpSetInterface; -class CompletionQueue; - -template -class ClientReader; -template -class ClientWriter; -template -class ClientReaderWriter; -template -class ClientAsyncReader; -template -class ClientAsyncWriter; -template -class ClientAsyncReaderWriter; -template -class ClientAsyncResponseReader; - -/// Codegen interface for \a grpc::Channel. -class ChannelInterface { - public: - virtual ~ChannelInterface() {} - /// Get the current channel state. If the channel is in IDLE and - /// \a try_to_connect is set to true, try to connect. - virtual grpc_connectivity_state GetState(bool try_to_connect) = 0; - - /// 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(ChannelInterface* channel, - const RpcMethod& method, - ClientContext* context, - const InputMessage& request, - OutputMessage* result); - friend class ::grpc::RpcMethod; - virtual Call CreateCall(const RpcMethod& method, ClientContext* context, - CompletionQueue* cq) = 0; - virtual void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) = 0; - virtual void* RegisterMethod(const char* method) = 0; - virtual void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, - gpr_timespec deadline, - CompletionQueue* cq, void* tag) = 0; - virtual bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, - gpr_timespec deadline) = 0; -}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_CODEGEN_CHANNEL_INTERFACE_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/client_context.h b/third_party/grpc/include/grpc++/impl/codegen/client_context.h index db2afe930c6a16..107532cb6b6782 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/client_context.h +++ b/third_party/grpc/include/grpc++/impl/codegen/client_context.h @@ -1,354 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -/// 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. +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. #ifndef GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H #define GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct census_context; -struct grpc_call; - -namespace grpc { - -class Channel; -class ChannelInterface; -class CompletionQueue; -class CallCredentials; -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; - } - - uint32_t c_bitmask() const { return propagate_; } - - private: - uint32_t 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 http://www.grpc.io/docs/guides/auth.html - 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(); - - /// Global Callbacks - /// - /// Can be set exactly once per application to install hooks whenever - /// a client context is constructed and destructed. - class GlobalCallbacks { - public: - virtual ~GlobalCallbacks() {} - virtual void DefaultConstructor(ClientContext* context) = 0; - virtual void Destructor(ClientContext* context) = 0; - }; - static void SetGlobalCallbacks(GlobalCallbacks* callbacks); - - 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(ChannelInterface* 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::mutex mu_; - grpc_call* call_; - bool call_canceled_; - 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 +#include #endif // GRPCXX_IMPL_CODEGEN_CLIENT_CONTEXT_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/client_unary_call.h b/third_party/grpc/include/grpc++/impl/codegen/client_unary_call.h index 0134dec800ccfa..f7dff1f1f05dc6 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/client_unary_call.h +++ b/third_party/grpc/include/grpc++/impl/codegen/client_unary_call.h @@ -1,75 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H #define GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H -#include -#include -#include -#include - -namespace grpc { - -class Channel; -class ClientContext; -class CompletionQueue; -class RpcMethod; - -// Wrapper that performs a blocking unary call -template -Status BlockingUnaryCall(ChannelInterface* 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 +#include #endif // GRPCXX_IMPL_CODEGEN_CLIENT_UNARY_CALL_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/completion_queue.h b/third_party/grpc/include/grpc++/impl/codegen/completion_queue.h index 102831e1c9b1ad..107549550bd116 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/completion_queue.h +++ b/third_party/grpc/include/grpc++/impl/codegen/completion_queue.h @@ -1,202 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -/// A completion queue implements a concurrent producer-consumer queue, with two -/// main methods, \a Next and \a AsyncNext. +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H #define GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H -#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 ChannelInterface; -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 : private 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(); - - /// 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(ChannelInterface* 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 -}; - -/// 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 +#include #endif // GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/completion_queue_tag.h b/third_party/grpc/include/grpc++/impl/codegen/completion_queue_tag.h index 8be2ac36d6e85e..994fa2a904a400 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/completion_queue_tag.h +++ b/third_party/grpc/include/grpc++/impl/codegen/completion_queue_tag.h @@ -1,52 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPCXX_COMPLETION_QUEUE_TAG_H -#define GRPCXX_COMPLETION_QUEUE_TAG_H +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. -namespace grpc { +#ifndef GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H +#define GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H -/// 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; -}; +#include -} // namespace grpc - -#endif // GRPCXX_COMPLETION_QUEUE_TAG_H +#endif // GRPCXX_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/config.h b/third_party/grpc/include/grpc++/impl/codegen/config.h index d782d5f5713e84..237bf38d3ed249 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/config.h +++ b/third_party/grpc/include/grpc++/impl/codegen/config.h @@ -1,116 +1,28 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_CONFIG_H #define GRPCXX_IMPL_CODEGEN_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 +#include #endif // GRPCXX_IMPL_CODEGEN_CONFIG_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/config_protobuf.h b/third_party/grpc/include/grpc++/impl/codegen/config_protobuf.h index 3b9f05cdff5b48..debd74aae79319 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/config_protobuf.h +++ b/third_party/grpc/include/grpc++/impl/codegen/config_protobuf.h @@ -1,72 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H #define GRPCXX_IMPL_CODEGEN_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 +#include #endif // GRPCXX_IMPL_CODEGEN_CONFIG_PROTOBUF_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/core_codegen.h b/third_party/grpc/include/grpc++/impl/codegen/core_codegen.h new file mode 100644 index 00000000000000..ee600a9d125841 --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/codegen/core_codegen.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H +#define GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H + +#include + +#endif // GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/core_codegen_interface.h b/third_party/grpc/include/grpc++/impl/codegen/core_codegen_interface.h new file mode 100644 index 00000000000000..03b3f675e19894 --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/codegen/core_codegen_interface.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H +#define GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H + +#include + +#endif // GRPCXX_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/create_auth_context.h b/third_party/grpc/include/grpc++/impl/codegen/create_auth_context.h new file mode 100644 index 00000000000000..ef89229f4d23c7 --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/codegen/create_auth_context.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H +#define GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H + +#include + +#endif // GRPCXX_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/grpc_library.h b/third_party/grpc/include/grpc++/impl/codegen/grpc_library.h index eb7152a2c60afc..33c3e2546c592d 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/grpc_library.h +++ b/third_party/grpc/include/grpc++/impl/codegen/grpc_library.h @@ -1,67 +1,28 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H #define GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H -#include - -namespace grpc { - -class GrpcLibraryInterface { - public: - virtual void init() = 0; - virtual void shutdown() = 0; -}; - -extern GrpcLibraryInterface* g_glip; - -class GrpcLibrary { - public: - GrpcLibrary() { - GPR_ASSERT(g_glip && - "gRPC library not initialized. See " - "grpc::internal::GrpcLibraryInitializer."); - g_glip->init(); - } - virtual ~GrpcLibrary() { - GPR_ASSERT(g_glip && - "gRPC library not initialized. See " - "grpc::internal::GrpcLibraryInitializer."); - g_glip->shutdown(); - } -}; - -} // namespace grpc +#include -#endif // GRPCXX_IMPL_GRPC_LIBRARY_H +#endif // GRPCXX_IMPL_CODEGEN_GRPC_LIBRARY_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/metadata_map.h b/third_party/grpc/include/grpc++/impl/codegen/metadata_map.h new file mode 100644 index 00000000000000..41c5ad375e159f --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/codegen/metadata_map.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_CODEGEN_METADATA_MAP_H +#define GRPCXX_IMPL_CODEGEN_METADATA_MAP_H + +#include + +#endif // GRPCXX_IMPL_CODEGEN_METADATA_MAP_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/method_handler_impl.h b/third_party/grpc/include/grpc++/impl/codegen/method_handler_impl.h index 1bf9bf05499c38..0bdb620616bd84 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/method_handler_impl.h +++ b/third_party/grpc/include/grpc++/impl/codegen/method_handler_impl.h @@ -1,203 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H #define GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H -#include -#include - -namespace grpc { - -// 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); - } -}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/proto_utils.h b/third_party/grpc/include/grpc++/impl/codegen/proto_utils.h index ce177104e00dbd..1f47884abc38da 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/proto_utils.h +++ b/third_party/grpc/include/grpc++/impl/codegen/proto_utils.h @@ -1,76 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H #define GRPCXX_IMPL_CODEGEN_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 +#include #endif // GRPCXX_IMPL_CODEGEN_PROTO_UTILS_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/rpc_method.h b/third_party/grpc/include/grpc++/impl/codegen/rpc_method.h index 85d5c1cfe2a325..2906c74dda70c0 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/rpc_method.h +++ b/third_party/grpc/include/grpc++/impl/codegen/rpc_method.h @@ -1,73 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_RPC_METHOD_H #define GRPCXX_IMPL_CODEGEN_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 +#include #endif // GRPCXX_IMPL_CODEGEN_RPC_METHOD_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/rpc_service_method.h b/third_party/grpc/include/grpc++/impl/codegen/rpc_service_method.h index 519d942fc43fed..999c0d5e935fcf 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/rpc_service_method.h +++ b/third_party/grpc/include/grpc++/impl/codegen/rpc_service_method.h @@ -1,92 +1,28 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H #define GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace grpc { -class ServerContext; -class StreamContextInterface; - -// 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; -}; - -// 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), server_tag_(nullptr), handler_(handler) {} - - void set_server_tag(void* tag) { server_tag_ = tag; } - void* server_tag() const { return server_tag_; } - // if MethodHandler is nullptr, then this is an async method - MethodHandler* handler() const { return handler_.get(); } - void ResetHandler() { handler_.reset(); } - - private: - void* server_tag_; - std::unique_ptr handler_; -}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_CODEGEN_RPC_SERVICE_METHOD_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/security/auth_context.h b/third_party/grpc/include/grpc++/impl/codegen/security/auth_context.h index 7544430811e936..b4663739a6a113 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/security/auth_context.h +++ b/third_party/grpc/include/grpc++/impl/codegen/security/auth_context.h @@ -1,110 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H #define GRPCXX_IMPL_CODEGEN_SECURITY_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 +#include #endif // GRPCXX_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/serialization_traits.h b/third_party/grpc/include/grpc++/impl/codegen/serialization_traits.h index 83c3b64eb3cce9..480575d10998be 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/serialization_traits.h +++ b/third_party/grpc/include/grpc++/impl/codegen/serialization_traits.h @@ -1,68 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H #define GRPCXX_IMPL_CODEGEN_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 +#include #endif // GRPCXX_IMPL_CODEGEN_SERIALIZATION_TRAITS_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/server_context.h b/third_party/grpc/include/grpc++/impl/codegen/server_context.h index ad08b8210d6707..1c3342d5d4b3b5 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/server_context.h +++ b/third_party/grpc/include/grpc++/impl/codegen/server_context.h @@ -1,214 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H #define GRPCXX_IMPL_CODEGEN_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; -class ServerInterface; - -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; - - // Cancel the Call from the server. This is a best-effort API and depending on - // when it is called, the RPC may still appear successful to the client. - // For example, if TryCancel() is called on a separate thread, it might race - // with the server handler which might return success to the client before - // TryCancel() was even started by the thread. - // - // It is the caller's responsibility to prevent such races and ensure that if - // TryCancel() is called, the serverhandler must return Status::CANCELLED. The - // only exception is that if the serverhandler is already returning an error - // status code, it is ok to not return Status::CANCELLED even if TryCancel() - // was called. - void TryCancel() 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::ServerInterface; - 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 +#include #endif // GRPCXX_IMPL_CODEGEN_SERVER_CONTEXT_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/server_interface.h b/third_party/grpc/include/grpc++/impl/codegen/server_interface.h index 96934edff35795..ceea44c50cb6e9 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/server_interface.h +++ b/third_party/grpc/include/grpc++/impl/codegen/server_interface.h @@ -1,254 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H #define GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H -#include -#include -#include - -namespace grpc { - -class AsyncGenericService; -class AsynchronousService; -class GenericServerContext; -class RpcService; -class ServerAsyncStreamingInterface; -class ServerCompletionQueue; -class ServerContext; -class ServerCredentials; -class Service; -class ThreadPoolInterface; - -/// Models a gRPC server. -/// -/// Servers are configured and started via \a grpc::ServerBuilder. -class ServerInterface : public CallHook { - public: - virtual ~ServerInterface() {} - - /// 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. - virtual void Wait() = 0; - - protected: - friend class AsynchronousService; - friend class Service; - - /// Register a service. This call does not take ownership of the service. - /// The service must exist for the lifetime of the Server instance. - virtual bool RegisterService(const grpc::string* host, Service* service) = 0; - - /// Register a generic service. This call does not take ownership of the - /// service. The service must exist for the lifetime of the Server instance. - virtual void RegisterAsyncGenericService(AsyncGenericService* service) = 0; - - /// 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. - virtual int AddListeningPort(const grpc::string& addr, - ServerCredentials* creds) = 0; - - /// 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. - virtual bool Start(ServerCompletionQueue** cqs, size_t num_cqs) = 0; - - /// Process one or more incoming calls. - virtual void RunRpc() = 0; - - /// Schedule \a RunRpc to run in the threadpool. - virtual void ScheduleCallback() = 0; - - virtual void ShutdownInternal(gpr_timespec deadline) = 0; - - virtual int max_message_size() const = 0; - - virtual grpc_server* server() = 0; - - virtual void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) = 0; - - class BaseAsyncRequest : public CompletionQueueTag { - public: - BaseAsyncRequest(ServerInterface* 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: - ServerInterface* 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(ServerInterface* 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, ServerInterface* 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, ServerInterface* 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(ServerInterface* 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_; - }; - - template - void RequestAsyncCall(RpcServiceMethod* method, ServerContext* context, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag, - Message* message) { - GPR_ASSERT(method); - new PayloadAsyncRequest(method->server_tag(), this, context, - stream, call_cq, notification_cq, tag, - message); - } - - void RequestAsyncCall(RpcServiceMethod* method, ServerContext* context, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag) { - GPR_ASSERT(method); - new NoPayloadAsyncRequest(method->server_tag(), 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); - } -}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_CODEGEN_SERVER_INTERFACE_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/service_type.h b/third_party/grpc/include/grpc++/impl/codegen/service_type.h index deb91a41d9633c..be02b75bae4f53 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/service_type.h +++ b/third_party/grpc/include/grpc++/impl/codegen/service_type.h @@ -1,161 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H #define GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H -#include -#include -#include -#include -#include - -namespace grpc { - -class Call; -class CompletionQueue; -class Server; -class ServerInterface; -class ServerCompletionQueue; -class ServerContext; - -class ServerAsyncStreamingInterface { - public: - virtual ~ServerAsyncStreamingInterface() {} - - virtual void SendInitialMetadata(void* tag) = 0; - - private: - friend class ServerInterface; - virtual void BindCall(Call* call) = 0; -}; - -class Service { - public: - Service() : server_(nullptr) {} - virtual ~Service() {} - - bool has_async_methods() const { - for (auto it = methods_.begin(); it != methods_.end(); ++it) { - if (*it && (*it)->handler() == nullptr) { - return true; - } - } - return false; - } - - bool has_synchronous_methods() const { - for (auto it = methods_.begin(); it != methods_.end(); ++it) { - if (*it && (*it)->handler() != nullptr) { - return true; - } - } - return false; - } - - bool has_generic_methods() const { - for (auto it = methods_.begin(); it != methods_.end(); ++it) { - if (it->get() == nullptr) { - return true; - } - } - return false; - } - - protected: - template - void RequestAsyncUnary(int index, ServerContext* context, Message* request, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, void* tag) { - server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, - notification_cq, tag, request); - } - void RequestAsyncClientStreaming(int index, ServerContext* context, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, - void* tag) { - server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, - notification_cq, tag); - } - template - void RequestAsyncServerStreaming(int index, ServerContext* context, - Message* request, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, - void* tag) { - server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, - notification_cq, tag, request); - } - void RequestAsyncBidiStreaming(int index, ServerContext* context, - ServerAsyncStreamingInterface* stream, - CompletionQueue* call_cq, - ServerCompletionQueue* notification_cq, - void* tag) { - server_->RequestAsyncCall(methods_[index].get(), context, stream, call_cq, - notification_cq, tag); - } - - void AddMethod(RpcServiceMethod* method) { methods_.emplace_back(method); } - - void MarkMethodAsync(int index) { - if (methods_[index].get() == nullptr) { - gpr_log(GPR_ERROR, - "Cannot mark the method as 'async' because it has already been " - "marked as 'generic'."); - return; - } - methods_[index]->ResetHandler(); - } - - void MarkMethodGeneric(int index) { - if (methods_[index]->handler() == nullptr) { - gpr_log(GPR_ERROR, - "Cannot mark the method as 'generic' because it has already been " - "marked as 'async'."); - } - methods_[index].reset(); - } - - private: - friend class Server; - friend class ServerInterface; - ServerInterface* server_; - std::vector> methods_; -}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_CODEGEN_SERVICE_TYPE_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/slice.h b/third_party/grpc/include/grpc++/impl/codegen/slice.h new file mode 100644 index 00000000000000..6714badc37d9b0 --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/codegen/slice.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_CODEGEN_SLICE_H +#define GRPCXX_IMPL_CODEGEN_SLICE_H + +#include + +#endif // GRPCXX_IMPL_CODEGEN_SLICE_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/status.h b/third_party/grpc/include/grpc++/impl/codegen/status.h index a509d311d4250e..6cf9459fff2925 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/status.h +++ b/third_party/grpc/include/grpc++/impl/codegen/status.h @@ -1,76 +1,28 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_STATUS_H #define GRPCXX_IMPL_CODEGEN_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 +#include #endif // GRPCXX_IMPL_CODEGEN_STATUS_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/status_code_enum.h b/third_party/grpc/include/grpc++/impl/codegen/status_code_enum.h index 9a90a18e2a5330..7503eaeeca6cff 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/status_code_enum.h +++ b/third_party/grpc/include/grpc++/impl/codegen/status_code_enum.h @@ -1,152 +1,28 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H #define GRPCXX_IMPL_CODEGEN_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 +#include #endif // GRPCXX_IMPL_CODEGEN_STATUS_CODE_ENUM_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/string_ref.h b/third_party/grpc/include/grpc++/impl/codegen/string_ref.h index e3af37e0c2aa1d..66e250efddbcea 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/string_ref.h +++ b/third_party/grpc/include/grpc++/impl/codegen/string_ref.h @@ -1,123 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_STRING_REF_H #define GRPCXX_IMPL_CODEGEN_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; - - // 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 +#include #endif // GRPCXX_IMPL_CODEGEN_STRING_REF_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/stub_options.h b/third_party/grpc/include/grpc++/impl/codegen/stub_options.h index 3912ae2bf9221a..07cb4417b7ab69 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/stub_options.h +++ b/third_party/grpc/include/grpc++/impl/codegen/stub_options.h @@ -1,43 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H #define GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H -namespace grpc { - -class StubOptions {}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_CODEGEN_STUB_OPTIONS_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/sync.h b/third_party/grpc/include/grpc++/impl/codegen/sync.h deleted file mode 100644 index 375af1543b8827..00000000000000 --- a/third_party/grpc/include/grpc++/impl/codegen/sync.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPCXX_IMPL_CODEGEN_SYNC_H -#define GRPCXX_IMPL_CODEGEN_SYNC_H - -#include - -#ifdef GRPC_CXX0X_NO_THREAD -#include -#else -#include -#endif - -#endif // GRPCXX_IMPL_CODEGEN_SYNC_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/sync_cxx11.h b/third_party/grpc/include/grpc++/impl/codegen/sync_cxx11.h deleted file mode 100644 index 49105476473e9d..00000000000000 --- a/third_party/grpc/include/grpc++/impl/codegen/sync_cxx11.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPCXX_IMPL_CODEGEN_SYNC_CXX11_H -#define GRPCXX_IMPL_CODEGEN_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_CODEGEN_SYNC_CXX11_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/sync_no_cxx11.h b/third_party/grpc/include/grpc++/impl/codegen/sync_no_cxx11.h deleted file mode 100644 index 8dea84736719be..00000000000000 --- a/third_party/grpc/include/grpc++/impl/codegen/sync_no_cxx11.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPCXX_IMPL_CODEGEN_SYNC_NO_CXX11_H -#define GRPCXX_IMPL_CODEGEN_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_CODEGEN_SYNC_NO_CXX11_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/sync_stream.h b/third_party/grpc/include/grpc++/impl/codegen/sync_stream.h index 33d25e837c677f..1e6ba27bf51da4 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/sync_stream.h +++ b/third_party/grpc/include/grpc++/impl/codegen/sync_stream.h @@ -1,415 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H #define GRPCXX_IMPL_CODEGEN_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(ChannelInterface* 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() GRPC_OVERRIDE { - 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(ChannelInterface* 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(ChannelInterface* 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() GRPC_OVERRIDE { - 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 +#include #endif // GRPCXX_IMPL_CODEGEN_SYNC_STREAM_H diff --git a/third_party/grpc/include/grpc++/impl/codegen/time.h b/third_party/grpc/include/grpc++/impl/codegen/time.h index bed7423341258b..f9b70f831356ee 100644 --- a/third_party/grpc/include/grpc++/impl/codegen/time.h +++ b/third_party/grpc/include/grpc++/impl/codegen/time.h @@ -1,111 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_CODEGEN_TIME_H #define GRPCXX_IMPL_CODEGEN_TIME_H -#include -#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 +#include #endif // GRPCXX_IMPL_CODEGEN_TIME_H diff --git a/third_party/grpc/include/grpc++/impl/grpc_library.h b/third_party/grpc/include/grpc++/impl/grpc_library.h index e8a075f5ebb1ce..f34a281ac3912f 100644 --- a/third_party/grpc/include/grpc++/impl/grpc_library.h +++ b/third_party/grpc/include/grpc++/impl/grpc_library.h @@ -1,67 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_GRPC_LIBRARY_H #define GRPCXX_IMPL_GRPC_LIBRARY_H -#include - -#include -#include -#include - -namespace grpc { - -namespace internal { -class GrpcLibrary GRPC_FINAL : public GrpcLibraryInterface { - public: - void init() GRPC_OVERRIDE { grpc_init(); } - - void shutdown() GRPC_OVERRIDE { grpc_shutdown(); } -}; - -static GrpcLibrary g_gli; - -class GrpcLibraryInitializer GRPC_FINAL { - public: - GrpcLibraryInitializer() { grpc::g_glip = &g_gli; } - - /// A no-op method to force the linker to reference this class, which will - /// take care of initializing and shutting down the gRPC runtime. - int summon() { return 0; } -}; - -} // namespace internal -} // namespace grpc +#include #endif // GRPCXX_IMPL_GRPC_LIBRARY_H diff --git a/third_party/grpc/include/grpc++/impl/method_handler_impl.h b/third_party/grpc/include/grpc++/impl/method_handler_impl.h index 305fd2865e4847..3840f48742f8c4 100644 --- a/third_party/grpc/include/grpc++/impl/method_handler_impl.h +++ b/third_party/grpc/include/grpc++/impl/method_handler_impl.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_METHOD_HANDLER_IMPL_H #define GRPCXX_IMPL_METHOD_HANDLER_IMPL_H -#include +#include #endif // GRPCXX_IMPL_METHOD_HANDLER_IMPL_H diff --git a/third_party/grpc/include/grpc++/impl/proto_utils.h b/third_party/grpc/include/grpc++/impl/proto_utils.h deleted file mode 100644 index 9124001e207f0f..00000000000000 --- a/third_party/grpc/include/grpc++/impl/proto_utils.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CPP_PROTO_PROTO_UTILS_H -#define GRPC_INTERNAL_CPP_PROTO_PROTO_UTILS_H - -#include - -#endif // GRPC_INTERNAL_CPP_PROTO_PROTO_UTILS_H diff --git a/third_party/grpc/include/grpc++/impl/rpc_method.h b/third_party/grpc/include/grpc++/impl/rpc_method.h index 8b5d1a32822903..7cba7c40c4598b 100644 --- a/third_party/grpc/include/grpc++/impl/rpc_method.h +++ b/third_party/grpc/include/grpc++/impl/rpc_method.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_RPC_METHOD_H #define GRPCXX_IMPL_RPC_METHOD_H -#include +#include #endif // GRPCXX_IMPL_RPC_METHOD_H diff --git a/third_party/grpc/include/grpc++/impl/rpc_service_method.h b/third_party/grpc/include/grpc++/impl/rpc_service_method.h index 101e6a636b1a7e..2c75087b7c8f1a 100644 --- a/third_party/grpc/include/grpc++/impl/rpc_service_method.h +++ b/third_party/grpc/include/grpc++/impl/rpc_service_method.h @@ -1,39 +1,28 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_RPC_SERVICE_METHOD_H #define GRPCXX_IMPL_RPC_SERVICE_METHOD_H -#include +#include #endif // GRPCXX_IMPL_RPC_SERVICE_METHOD_H diff --git a/third_party/grpc/include/grpc++/impl/serialization_traits.h b/third_party/grpc/include/grpc++/impl/serialization_traits.h index fad4398174bbec..33b3a0bb31e9fe 100644 --- a/third_party/grpc/include/grpc++/impl/serialization_traits.h +++ b/third_party/grpc/include/grpc++/impl/serialization_traits.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_SERIALIZATION_TRAITS_H #define GRPCXX_IMPL_SERIALIZATION_TRAITS_H -#include +#include #endif // GRPCXX_IMPL_SERIALIZATION_TRAITS_H diff --git a/third_party/grpc/include/grpc++/impl/server_builder_option.h b/third_party/grpc/include/grpc++/impl/server_builder_option.h index bcb19824fdf462..833f8db772f9b5 100644 --- a/third_party/grpc/include/grpc++/impl/server_builder_option.h +++ b/third_party/grpc/include/grpc++/impl/server_builder_option.h @@ -1,51 +1,28 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_SERVER_BUILDER_OPTION_H #define GRPCXX_IMPL_SERVER_BUILDER_OPTION_H -#include - -namespace grpc { - -/// Interface to pass an option to a \a ServerBuilder. -class ServerBuilderOption { - public: - virtual ~ServerBuilderOption() {} - /// Alter the \a ChannelArguments used to create the gRPC server. - virtual void UpdateArguments(ChannelArguments* args) = 0; -}; - -} // namespace grpc +#include #endif // GRPCXX_IMPL_SERVER_BUILDER_OPTION_H diff --git a/third_party/grpc/include/grpc++/impl/server_builder_plugin.h b/third_party/grpc/include/grpc++/impl/server_builder_plugin.h new file mode 100644 index 00000000000000..844d32c55f1bc5 --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/server_builder_plugin.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H +#define GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H + +#include + +#endif // GRPCXX_IMPL_SERVER_BUILDER_PLUGIN_H diff --git a/third_party/grpc/include/grpc++/impl/server_initializer.h b/third_party/grpc/include/grpc++/impl/server_initializer.h new file mode 100644 index 00000000000000..6a1669ccf574f1 --- /dev/null +++ b/third_party/grpc/include/grpc++/impl/server_initializer.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_IMPL_SERVER_INITIALIZER_H +#define GRPCXX_IMPL_SERVER_INITIALIZER_H + +#include + +#endif // GRPCXX_IMPL_SERVER_INITIALIZER_H diff --git a/third_party/grpc/include/grpc++/impl/service_type.h b/third_party/grpc/include/grpc++/impl/service_type.h index b33fbec739406d..86422160d4d5a2 100644 --- a/third_party/grpc/include/grpc++/impl/service_type.h +++ b/third_party/grpc/include/grpc++/impl/service_type.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_SERVICE_TYPE_H #define GRPCXX_IMPL_SERVICE_TYPE_H -#include +#include #endif // GRPCXX_IMPL_SERVICE_TYPE_H diff --git a/third_party/grpc/include/grpc++/impl/sync.h b/third_party/grpc/include/grpc++/impl/sync.h deleted file mode 100644 index 824139043bcb63..00000000000000 --- a/third_party/grpc/include/grpc++/impl/sync.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPCXX_IMPL_SYNC_H -#define GRPCXX_IMPL_SYNC_H - -#include - -#endif // GRPCXX_IMPL_SYNC_H diff --git a/third_party/grpc/include/grpc++/impl/sync_cxx11.h b/third_party/grpc/include/grpc++/impl/sync_cxx11.h index 22997a19c2ae89..8bcfb2ce81860a 100644 --- a/third_party/grpc/include/grpc++/impl/sync_cxx11.h +++ b/third_party/grpc/include/grpc++/impl/sync_cxx11.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_SYNC_CXX11_H #define GRPCXX_IMPL_SYNC_CXX11_H -#include +#include #endif // GRPCXX_IMPL_SYNC_CXX11_H diff --git a/third_party/grpc/include/grpc++/impl/sync_no_cxx11.h b/third_party/grpc/include/grpc++/impl/sync_no_cxx11.h index def800d6f46c6c..5264567387a39d 100644 --- a/third_party/grpc/include/grpc++/impl/sync_no_cxx11.h +++ b/third_party/grpc/include/grpc++/impl/sync_no_cxx11.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_IMPL_SYNC_NO_CXX11_H #define GRPCXX_IMPL_SYNC_NO_CXX11_H -#include +#include #endif // GRPCXX_IMPL_SYNC_NO_CXX11_H diff --git a/third_party/grpc/include/grpc++/impl/thd.h b/third_party/grpc/include/grpc++/impl/thd.h deleted file mode 100644 index f8d4258ac61b72..00000000000000 --- a/third_party/grpc/include/grpc++/impl/thd.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/include/grpc++/impl/thd_cxx11.h b/third_party/grpc/include/grpc++/impl/thd_cxx11.h deleted file mode 100644 index 2055b1d5389e28..00000000000000 --- a/third_party/grpc/include/grpc++/impl/thd_cxx11.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/include/grpc++/impl/thd_no_cxx11.h b/third_party/grpc/include/grpc++/impl/thd_no_cxx11.h deleted file mode 100644 index 3f981d3770fc4d..00000000000000 --- a/third_party/grpc/include/grpc++/impl/thd_no_cxx11.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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(); - } - template - thread(void (T::*fptr)(U arg), T *obj, U arg) { - func_ = new thread_function_arg(fptr, obj, arg); - joined_ = false; - start(); - } - ~thread() { - if (!joined_) std::terminate(); - delete func_; - } - thread(thread &&other) - : func_(other.func_), thd_(other.thd_), joined_(other.joined_) { - other.joined_ = true; - other.func_ = NULL; - } - 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_; - }; - template - class thread_function_arg : public thread_function_base { - public: - thread_function_arg(void (T::*fptr)(U arg), T *obj, U arg) - : fptr_(fptr), obj_(obj), arg_(arg) {} - virtual void call() { (obj_->*fptr_)(arg_); } - - private: - void (T::*fptr_)(U arg); - T *obj_; - U arg_; - }; - 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/third_party/grpc/include/grpc++/resource_quota.h b/third_party/grpc/include/grpc++/resource_quota.h new file mode 100644 index 00000000000000..aad1b5659fdc56 --- /dev/null +++ b/third_party/grpc/include/grpc++/resource_quota.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_RESOURCE_QUOTA_H +#define GRPCXX_RESOURCE_QUOTA_H + +#include + +#endif // GRPCXX_RESOURCE_QUOTA_H diff --git a/third_party/grpc/include/grpc++/security/auth_context.h b/third_party/grpc/include/grpc++/security/auth_context.h index bca8fa7c0ccb35..9fe59d448e3b96 100644 --- a/third_party/grpc/include/grpc++/security/auth_context.h +++ b/third_party/grpc/include/grpc++/security/auth_context.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPCXX_SUPPORT_AUTH_CONTEXT_H -#define GRPCXX_SUPPORT_AUTH_CONTEXT_H +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. -#include +#ifndef GRPCXX_SECURITY_AUTH_CONTEXT_H +#define GRPCXX_SECURITY_AUTH_CONTEXT_H -#endif // GRPCXX_SUPPORT_AUTH_CONTEXT_H +#include + +#endif // GRPCXX_SECURITY_AUTH_CONTEXT_H diff --git a/third_party/grpc/include/grpc++/security/auth_metadata_processor.h b/third_party/grpc/include/grpc++/security/auth_metadata_processor.h index 25011f33bafae0..d04531396552a5 100644 --- a/third_party/grpc/include/grpc++/security/auth_metadata_processor.h +++ b/third_party/grpc/include/grpc++/security/auth_metadata_processor.h @@ -1,73 +1,28 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPCXX_AUTH_METADATA_PROCESSOR_H_ -#define GRPCXX_AUTH_METADATA_PROCESSOR_H_ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. -#include +#ifndef GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H +#define GRPCXX_SECURITY_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_ +#endif // GRPCXX_SECURITY_AUTH_METADATA_PROCESSOR_H diff --git a/third_party/grpc/include/grpc++/security/credentials.h b/third_party/grpc/include/grpc++/security/credentials.h index e0806c0b7b416b..940441827dba43 100644 --- a/third_party/grpc/include/grpc++/security/credentials.h +++ b/third_party/grpc/include/grpc++/security/credentials.h @@ -1,232 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPCXX_CREDENTIALS_H -#define GRPCXX_CREDENTIALS_H +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. -#include -#include +#ifndef GRPCXX_SECURITY_CREDENTIALS_H +#define GRPCXX_SECURITY_CREDENTIALS_H -#include -#include -#include -#include +#include -struct grpc_call; - -namespace grpc { -class ChannelArguments; -class Channel; -class SecureChannelCredentials; -class CallCredentials; -class SecureCallCredentials; - -/// A channel credentials object encapsulates all the state needed by a client -/// to authenticate with a server for a given channel. -/// It can make various assertions, e.g., about the client’s identity, role -/// for all the calls on that channel. -/// -/// \see http://www.grpc.io/docs/guides/auth.html -class ChannelCredentials : private GrpcLibrary { - public: - ChannelCredentials(); - ~ChannelCredentials(); - - protected: - friend std::shared_ptr CompositeChannelCredentials( - const std::shared_ptr& channel_creds, - const std::shared_ptr& call_creds); - - virtual SecureChannelCredentials* 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; -}; - -/// A call credentials object encapsulates the state needed by a client to -/// authenticate with a server for a given call on a channel. -/// -/// \see http://www.grpc.io/docs/guides/auth.html -class CallCredentials : private GrpcLibrary { - public: - CallCredentials(); - ~CallCredentials(); - - /// Apply this instance's credentials to \a call. - virtual bool ApplyToCall(grpc_call* call) = 0; - - protected: - friend std::shared_ptr CompositeChannelCredentials( - const std::shared_ptr& channel_creds, - const std::shared_ptr& call_creds); - - friend std::shared_ptr CompositeCallCredentials( - const std::shared_ptr& creds1, - const std::shared_ptr& creds2); - - virtual SecureCallCredentials* AsSecureCredentials() = 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 a channel credentials and a call credentials into a composite -/// channel credentials. -std::shared_ptr CompositeChannelCredentials( - const std::shared_ptr& channel_creds, - const std::shared_ptr& call_creds); - -/// Combines two call credentials objects into a composite call credentials. -std::shared_ptr CompositeCallCredentials( - const std::shared_ptr& creds1, - const std::shared_ptr& creds2); - -/// Credentials for an unencrypted, unauthenticated channel -std::shared_ptr InsecureChannelCredentials(); - -// User defined metadata credentials. -class MetadataCredentialsPlugin { - public: - virtual ~MetadataCredentialsPlugin() {} - - // 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; } - - // Type of credentials this plugin is implementing. - virtual const char* GetType() const { return ""; } - - // Gets the auth metatada produced by this plugin. - // The fully qualified method name is: - // service_url + "/" + method_name. - // The channel_auth_context contains (among other things), the identity of - // the server. - virtual Status GetMetadata( - grpc::string_ref service_url, grpc::string_ref method_name, - const AuthContext& channel_auth_context, - std::multimap* metadata) = 0; -}; - -std::shared_ptr MetadataCredentialsFromPlugin( - std::unique_ptr plugin); - -} // namespace grpc - -#endif // GRPCXX_CREDENTIALS_H +#endif // GRPCXX_SECURITY_CREDENTIALS_H diff --git a/third_party/grpc/include/grpc++/security/server_credentials.h b/third_party/grpc/include/grpc++/security/server_credentials.h index e933825ec37977..c6d1c4f0e4c504 100644 --- a/third_party/grpc/include/grpc++/security/server_credentials.h +++ b/third_party/grpc/include/grpc++/security/server_credentials.h @@ -1,92 +1,28 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPCXX_SERVER_CREDENTIALS_H -#define GRPCXX_SERVER_CREDENTIALS_H +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. -#include -#include +#ifndef GRPCXX_SECURITY_SERVER_CREDENTIALS_H +#define GRPCXX_SECURITY_SERVER_CREDENTIALS_H -#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 +#endif // GRPCXX_SECURITY_SERVER_CREDENTIALS_H diff --git a/third_party/grpc/include/grpc++/server.h b/third_party/grpc/include/grpc++/server.h index 2a71073a7e2f89..086c24cc699fb2 100644 --- a/third_party/grpc/include/grpc++/server.h +++ b/third_party/grpc/include/grpc++/server.h @@ -1,189 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SERVER_H #define GRPCXX_SERVER_H -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct grpc_server; - -namespace grpc { - -class GenericServerContext; -class AsyncGenericService; -class ServerAsyncStreamingInterface; -class ServerContext; -class ThreadPoolInterface; - -/// Models a gRPC server. -/// -/// Servers are configured and started via \a grpc::ServerBuilder. -class Server GRPC_FINAL : public ServerInterface, private GrpcLibrary { - public: - ~Server(); - - /// 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() GRPC_OVERRIDE; - - /// Global Callbacks - /// - /// Can be set exactly once per application to install hooks whenever - /// a server event occurs - class GlobalCallbacks { - public: - virtual ~GlobalCallbacks() {} - /// Called before application callback for each synchronous server request - virtual void PreSynchronousRequest(ServerContext* context) = 0; - /// Called after application callback for each synchronous server request - virtual void PostSynchronousRequest(ServerContext* context) = 0; - }; - /// Set the global callback object. Can only be called once. Does not take - /// ownership of callbacks, and expects the pointed to object to be alive - /// until all server objects in the process have been destroyed. - static void SetGlobalCallbacks(GlobalCallbacks* callbacks); - - private: - friend class AsyncGenericService; - friend class ServerBuilder; - - class SyncRequest; - class AsyncRequest; - class ShutdownRequest; - - class UnimplementedAsyncRequestContext; - class UnimplementedAsyncRequest; - class UnimplementedAsyncResponse; - - /// 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, const ChannelArguments& args); - - /// 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, - Service* service) GRPC_OVERRIDE; - - /// 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) GRPC_OVERRIDE; - - /// 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) GRPC_OVERRIDE; - - /// 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) GRPC_OVERRIDE; - - /// Process one or more incoming calls. - void RunRpc() GRPC_OVERRIDE; - - /// Schedule \a RunRpc to run in the threadpool. - void ScheduleCallback() GRPC_OVERRIDE; - - void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) GRPC_OVERRIDE; - - void ShutdownInternal(gpr_timespec deadline) GRPC_OVERRIDE; - - int max_message_size() const GRPC_OVERRIDE { return max_message_size_; }; - - grpc_server* server() GRPC_OVERRIDE { return server_; }; - - 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::shared_ptr global_callbacks_; - - 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 +#include #endif // GRPCXX_SERVER_H diff --git a/third_party/grpc/include/grpc++/server_builder.h b/third_party/grpc/include/grpc++/server_builder.h index 73bcfb60387914..2c6dab44453b06 100644 --- a/third_party/grpc/include/grpc++/server_builder.h +++ b/third_party/grpc/include/grpc++/server_builder.h @@ -1,138 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SERVER_BUILDER_H #define GRPCXX_SERVER_BUILDER_H -#include -#include - -#include -#include -#include - -namespace grpc { - -class AsyncGenericService; -class CompletionQueue; -class RpcService; -class Server; -class ServerCompletionQueue; -class ServerCredentials; -class Service; - -/// 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(Service* 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, Service* service); - - /// Set max message size in bytes. - void SetMaxMessageSize(int max_message_size) { - max_message_size_ = max_message_size; - } - - /// Set the compression options to be used by the server. - void SetCompressionOptions(const grpc_compression_options& options) { - compression_options_ = options; - } - - void SetOption(std::unique_ptr option); - - /// 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; - struct NamedService { - explicit NamedService(Service* s) : service(s) {} - NamedService(const grpc::string& h, Service* s) - : host(new grpc::string(h)), service(s) {} - HostString host; - Service* service; - }; - - int max_message_size_; - grpc_compression_options compression_options_; - std::vector> options_; - std::vector> services_; - std::vector ports_; - std::vector cqs_; - std::shared_ptr creds_; - AsyncGenericService* generic_service_; -}; - -} // namespace grpc +#include #endif // GRPCXX_SERVER_BUILDER_H diff --git a/third_party/grpc/include/grpc++/server_context.h b/third_party/grpc/include/grpc++/server_context.h index 214188a03a9fb2..672ccdca6d9f8d 100644 --- a/third_party/grpc/include/grpc++/server_context.h +++ b/third_party/grpc/include/grpc++/server_context.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SERVER_CONTEXT_H #define GRPCXX_SERVER_CONTEXT_H -#include +#include #endif // GRPCXX_SERVER_CONTEXT_H diff --git a/third_party/grpc/include/grpc++/server_posix.h b/third_party/grpc/include/grpc++/server_posix.h new file mode 100644 index 00000000000000..d2866d96402bf1 --- /dev/null +++ b/third_party/grpc/include/grpc++/server_posix.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_SERVER_POSIX_H +#define GRPCXX_SERVER_POSIX_H + +#include + +#endif // GRPCXX_SERVER_POSIX_H diff --git a/third_party/grpc/include/grpc++/support/async_stream.h b/third_party/grpc/include/grpc++/support/async_stream.h index 88147ea9d655d8..9bb2b725c3cb7b 100644 --- a/third_party/grpc/include/grpc++/support/async_stream.h +++ b/third_party/grpc/include/grpc++/support/async_stream.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_ASYNC_STREAM_H #define GRPCXX_SUPPORT_ASYNC_STREAM_H -#include +#include #endif // GRPCXX_SUPPORT_ASYNC_STREAM_H diff --git a/third_party/grpc/include/grpc++/support/async_unary_call.h b/third_party/grpc/include/grpc++/support/async_unary_call.h index 6d74328be503bc..56fbf31417e7ac 100644 --- a/third_party/grpc/include/grpc++/support/async_unary_call.h +++ b/third_party/grpc/include/grpc++/support/async_unary_call.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H #define GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H -#include +#include #endif // GRPCXX_SUPPORT_ASYNC_UNARY_CALL_H diff --git a/third_party/grpc/include/grpc++/support/byte_buffer.h b/third_party/grpc/include/grpc++/support/byte_buffer.h index 82591a88ef2f4d..ec607ee01aac1f 100644 --- a/third_party/grpc/include/grpc++/support/byte_buffer.h +++ b/third_party/grpc/include/grpc++/support/byte_buffer.h @@ -1,110 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #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); - - /// Constuct a byte buffer by referencing elements of existing buffer - /// \a buf. Wrapper of core function grpc_byte_buffer_copy - ByteBuffer(const ByteBuffer& buf); - - ~ByteBuffer(); - - ByteBuffer& operator=(const 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; - - // takes ownership - void set_buffer(grpc_byte_buffer* buf) { - if (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 +#include #endif // GRPCXX_SUPPORT_BYTE_BUFFER_H diff --git a/third_party/grpc/include/grpc++/support/channel_arguments.h b/third_party/grpc/include/grpc++/support/channel_arguments.h index a2960a7ecce871..6d5300ccb795c9 100644 --- a/third_party/grpc/include/grpc++/support/channel_arguments.h +++ b/third_party/grpc/include/grpc++/support/channel_arguments.h @@ -1,104 +1,28 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #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. This option is for - /// testing only and should never be used in production. - 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); - - // Generic channel argument setter. Only for advanced use cases. - /// Set a pointer argument \a value under \a key. Owership is not transferred. - void SetPointer(const grpc::string& key, void* value); - - /// Set a textual argument \a value under \a key. - void SetString(const grpc::string& key, const grpc::string& value); - - private: - friend class SecureChannelCredentials; - friend class testing::ChannelArgumentsTest; - - // Returns empty string when it is not set. - grpc::string GetSslTargetNameOverride() const; - - std::vector args_; - std::list strings_; -}; - -} // namespace grpc +#include #endif // GRPCXX_SUPPORT_CHANNEL_ARGUMENTS_H diff --git a/third_party/grpc/include/grpc++/support/config.h b/third_party/grpc/include/grpc++/support/config.h index be8c89c4626503..f8eee5075a8ade 100644 --- a/third_party/grpc/include/grpc++/support/config.h +++ b/third_party/grpc/include/grpc++/support/config.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_CONFIG_H #define GRPCXX_SUPPORT_CONFIG_H -#include +#include #endif // GRPCXX_SUPPORT_CONFIG_H diff --git a/third_party/grpc/include/grpc++/support/config_protobuf.h b/third_party/grpc/include/grpc++/support/config_protobuf.h deleted file mode 100644 index 0b466e8e44baaf..00000000000000 --- a/third_party/grpc/include/grpc++/support/config_protobuf.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPCXX_SUPPORT_CONFIG_PROTOBUF_H -#define GRPCXX_SUPPORT_CONFIG_PROTOBUF_H - -#include - -#endif // GRPCXX_SUPPORT_CONFIG_PROTOBUF_H diff --git a/third_party/grpc/include/grpc++/support/error_details.h b/third_party/grpc/include/grpc++/support/error_details.h new file mode 100644 index 00000000000000..7ace308e52f1e1 --- /dev/null +++ b/third_party/grpc/include/grpc++/support/error_details.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_SUPPORT_ERROR_DETAILS_H +#define GRPCXX_SUPPORT_ERROR_DETAILS_H + +#include + +#endif // GRPCXX_SUPPORT_ERROR_DETAILS_H diff --git a/third_party/grpc/include/grpc++/support/slice.h b/third_party/grpc/include/grpc++/support/slice.h index 724691a0333994..b02b1a977eda5f 100644 --- a/third_party/grpc/include/grpc++/support/slice.h +++ b/third_party/grpc/include/grpc++/support/slice.h @@ -1,88 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_SLICE_H #define GRPCXX_SUPPORT_SLICE_H -#include -#include - -namespace grpc { - -/// A wrapper around \a gpr_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 uint8_t* begin() const { return GPR_SLICE_START_PTR(slice_); } - - /// Raw pointer to the end (one byte \em past the last element) of the slice. - const uint8_t* end() const { return GPR_SLICE_END_PTR(slice_); } - - private: - friend class ByteBuffer; - - gpr_slice slice_; -}; - -} // namespace grpc +#include #endif // GRPCXX_SUPPORT_SLICE_H diff --git a/third_party/grpc/include/grpc++/support/status.h b/third_party/grpc/include/grpc++/support/status.h index a33fbedf12f472..e58a18bdf99719 100644 --- a/third_party/grpc/include/grpc++/support/status.h +++ b/third_party/grpc/include/grpc++/support/status.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_STATUS_H #define GRPCXX_SUPPORT_STATUS_H -#include +#include #endif // GRPCXX_SUPPORT_STATUS_H diff --git a/third_party/grpc/include/grpc++/support/status_code_enum.h b/third_party/grpc/include/grpc++/support/status_code_enum.h index 21b9c40d7e8ac1..c278add0c65d23 100644 --- a/third_party/grpc/include/grpc++/support/status_code_enum.h +++ b/third_party/grpc/include/grpc++/support/status_code_enum.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_STATUS_CODE_ENUM_H #define GRPCXX_SUPPORT_STATUS_CODE_ENUM_H -#include +#include #endif // GRPCXX_SUPPORT_STATUS_CODE_ENUM_H diff --git a/third_party/grpc/include/grpc++/support/string_ref.h b/third_party/grpc/include/grpc++/support/string_ref.h index 195e5c80c5d6c8..49de6dafce92cd 100644 --- a/third_party/grpc/include/grpc++/support/string_ref.h +++ b/third_party/grpc/include/grpc++/support/string_ref.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_STRING_REF_H #define GRPCXX_SUPPORT_STRING_REF_H -#include +#include #endif // GRPCXX_SUPPORT_STRING_REF_H diff --git a/third_party/grpc/include/grpc++/support/stub_options.h b/third_party/grpc/include/grpc++/support/stub_options.h index f8f4c871812c26..a712ce8716c173 100644 --- a/third_party/grpc/include/grpc++/support/stub_options.h +++ b/third_party/grpc/include/grpc++/support/stub_options.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_STUB_OPTIONS_H #define GRPCXX_SUPPORT_STUB_OPTIONS_H -#include +#include #endif // GRPCXX_SUPPORT_STUB_OPTIONS_H diff --git a/third_party/grpc/include/grpc++/support/sync_stream.h b/third_party/grpc/include/grpc++/support/sync_stream.h index 2ea2ac544365e3..c118df9f7b46e8 100644 --- a/third_party/grpc/include/grpc++/support/sync_stream.h +++ b/third_party/grpc/include/grpc++/support/sync_stream.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_SYNC_STREAM_H #define GRPCXX_SUPPORT_SYNC_STREAM_H -#include +#include #endif // GRPCXX_SUPPORT_SYNC_STREAM_H diff --git a/third_party/grpc/include/grpc++/support/time.h b/third_party/grpc/include/grpc++/support/time.h index f0b758f254bdd5..d356b910013e7d 100644 --- a/third_party/grpc/include/grpc++/support/time.h +++ b/third_party/grpc/include/grpc++/support/time.h @@ -1,39 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + #ifndef GRPCXX_SUPPORT_TIME_H #define GRPCXX_SUPPORT_TIME_H -#include +#include #endif // GRPCXX_SUPPORT_TIME_H diff --git a/third_party/grpc/include/grpc++/test/mock_stream.h b/third_party/grpc/include/grpc++/test/mock_stream.h new file mode 100644 index 00000000000000..a29345b061f7be --- /dev/null +++ b/third_party/grpc/include/grpc++/test/mock_stream.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_TEST_MOCK_STREAM_H +#define GRPCXX_TEST_MOCK_STREAM_H + +#include + +#endif // GRPCXX_TEST_MOCK_STREAM_H diff --git a/third_party/grpc/include/grpc++/test/server_context_test_spouse.h b/third_party/grpc/include/grpc++/test/server_context_test_spouse.h new file mode 100644 index 00000000000000..48a4838c2d6984 --- /dev/null +++ b/third_party/grpc/include/grpc++/test/server_context_test_spouse.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// DEPRECATED: The headers in include/grpc++ are deprecated. Please include the +// headers in include/grpcpp instead. This header exists only for backwards +// compatibility. + +#ifndef GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H +#define GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H + +#include + +#endif // GRPCXX_TEST_SERVER_CONTEXT_TEST_SPOUSE_H diff --git a/third_party/grpc/include/grpc/byte_buffer.h b/third_party/grpc/include/grpc/byte_buffer.h index fa1fd24df9442a..ee740f47944308 100644 --- a/third_party/grpc/include/grpc/byte_buffer.h +++ b/third_party/grpc/include/grpc/byte_buffer.h @@ -1,39 +1,27 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_BYTE_BUFFER_H #define GRPC_BYTE_BUFFER_H +#include + #include +#include #endif /* GRPC_BYTE_BUFFER_H */ diff --git a/third_party/grpc/include/grpc/byte_buffer_reader.h b/third_party/grpc/include/grpc/byte_buffer_reader.h index b0e63a6da2b896..15e06cad7c1345 100644 --- a/third_party/grpc/include/grpc/byte_buffer_reader.h +++ b/third_party/grpc/include/grpc/byte_buffer_reader.h @@ -1,58 +1,26 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_BYTE_BUFFER_READER_H #define GRPC_BYTE_BUFFER_READER_H -#include -#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 +#include #endif /* GRPC_BYTE_BUFFER_READER_H */ diff --git a/third_party/grpc/include/grpc/census.h b/third_party/grpc/include/grpc/census.h index 6313b196f22423..4894f1c0965e25 100644 --- a/third_party/grpc/include/grpc/census.h +++ b/third_party/grpc/include/grpc/census.h @@ -1,42 +1,25 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015-2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -/* 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 GRPC_CENSUS_H +#define GRPC_CENSUS_H -#ifndef CENSUS_CENSUS_H -#define CENSUS_CENSUS_H +#include #include @@ -44,515 +27,14 @@ 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()). */ -CENSUS_API int census_initialize(int features); -CENSUS_API void census_shutdown(void); - -/** Return the features supported by the current census implementation (not all - * features will be available on all platforms). */ -CENSUS_API int census_supported(void); - -/** Return the census features currently enabled. */ -CENSUS_API int census_enabled(void); - /** A Census Context is a handle used by Census to represent the current tracing and stats collection information. Contexts should be propagated across RPC's - (this is the responsibility of the local RPC system). A context is typically - used as the first argument to most census functions. Conceptually, they - should be thought of as specific to a single RPC/thread. The user visible - context representation is that of a collection of key:value string pairs, - each of which is termed a 'tag'; these form the basis against which Census - metrics will be recorded. Keys are unique within a context. */ + (this is the responsibility of the local RPC system). */ typedef struct census_context census_context; -/* A tag is a key:value pair. The key is a non-empty, printable (UTF-8 - encoded), nil-terminated string. The value is a binary string, that may be - printable. There are limits on the sizes of both keys and values (see - CENSUS_MAX_TAG_KB_LEN definition below), and the number of tags that can be - propagated (CENSUS_MAX_PROPAGATED_TAGS). Users should also remember that - some systems may have limits on, e.g., the number of bytes that can be - transmitted as metadata, and that larger tags means more memory consumed - and time in processing. */ -typedef struct { - const char *key; - const char *value; - size_t value_len; - uint8_t flags; -} census_tag; - -/* Maximum length of a tag's key or value. */ -#define CENSUS_MAX_TAG_KV_LEN 255 -/* Maximum number of propagatable tags. */ -#define CENSUS_MAX_PROPAGATED_TAGS 255 - -/* Tag flags. */ -#define CENSUS_TAG_PROPAGATE 1 /* Tag should be propagated over RPC */ -#define CENSUS_TAG_STATS 2 /* Tag will be used for statistics aggregation */ -#define CENSUS_TAG_BINARY 4 /* Tag value is not printable */ -#define CENSUS_TAG_RESERVED 8 /* Reserved for internal use. */ -/* Flag values 8,16,32,64,128 are reserved for future/internal use. Clients - should not use or rely on their values. */ - -#define CENSUS_TAG_IS_PROPAGATED(flags) (flags & CENSUS_TAG_PROPAGATE) -#define CENSUS_TAG_IS_STATS(flags) (flags & CENSUS_TAG_STATS) -#define CENSUS_TAG_IS_BINARY(flags) (flags & CENSUS_TAG_BINARY) - -/* An instance of this structure is kept by every context, and records the - basic information associated with the creation of that context. */ -typedef struct { - int n_propagated_tags; /* number of propagated printable tags */ - int n_propagated_binary_tags; /* number of propagated binary tags */ - int n_local_tags; /* number of non-propagated (local) tags */ - int n_deleted_tags; /* number of tags that were deleted */ - int n_added_tags; /* number of tags that were added */ - int n_modified_tags; /* number of tags that were modified */ - int n_invalid_tags; /* number of tags with bad keys or values (e.g. - longer than CENSUS_MAX_TAG_KV_LEN) */ - int n_ignored_tags; /* number of tags ignored because of - CENSUS_MAX_PROPAGATED_TAGS limit. */ -} census_context_status; - -/* Create a new context, adding and removing tags from an existing context. - This will copy all tags from the 'tags' input, so it is recommended - to add as many tags in a single operation as is practical for the client. - @param base Base context to build upon. Can be NULL. - @param tags A set of tags to be added/changed/deleted. Tags with keys that - are in 'tags', but not 'base', are added to the tag set. Keys that are in - both 'tags' and 'base' will have their value/flags modified. Tags with keys - in both, but with NULL or zero-length values, will be deleted from the tag - set. Tags with invalid (too long or short) keys or values will be ignored. - If adding a tag will result in more than CENSUS_MAX_PROPAGATED_TAGS in either - binary or non-binary tags, they will be ignored, as will deletions of - tags that don't exist. - @param ntags number of tags in 'tags' - @param status If not NULL, will return a pointer to a census_context_status - structure containing information about the new context and status of the - tags used in its creation. - @return A new, valid census_context. -*/ -CENSUS_API census_context *census_context_create( - const census_context *base, const census_tag *tags, int ntags, - census_context_status const **status); - -/* Destroy a context. Once this function has been called, the context cannot - be reused. */ -CENSUS_API void census_context_destroy(census_context *context); - -/* Get a pointer to the original status from the context creation. */ -CENSUS_API const census_context_status *census_context_get_status( - const census_context *context); - -/* Structure used for iterating over the tegs in a context. API clients should - not use or reference internal fields - neither their contents or - presence/absence are guaranteed. */ -typedef struct { - const census_context *context; - int base; - int index; - char *kvm; -} census_context_iterator; - -/* Initialize a census_tag_iterator. Must be called before first use. */ -CENSUS_API void census_context_initialize_iterator( - const census_context *context, census_context_iterator *iterator); - -/* Get the contents of the "next" tag in the context. If there are no more - tags, returns 0 (and 'tag' contents will be unchanged), otherwise returns 1. - */ -CENSUS_API int census_context_next_tag(census_context_iterator *iterator, - census_tag *tag); - -/* Get a context tag by key. Returns 0 if the key is not present. */ -CENSUS_API int census_context_get_tag(const census_context *context, - const char *key, census_tag *tag); - -/* Tag set encode/decode functionality. These functionas are intended - for use by RPC systems only, for purposes of transmitting/receiving contexts. - */ - -/* Encode a context into a buffer. The propagated tags are encoded into the - buffer in two regions: one for printable tags, and one for binary tags. - @param context context to be encoded - @param buffer pointer to buffer. This address will be used to encode the - printable tags. - @param buf_size number of available bytes in buffer. - @param print_buf_size Will be set to the number of bytes consumed by - printable tags. - @param bin_buf_size Will be set to the number of bytes used to encode the - binary tags. - @return A pointer to the binary tag's encoded, or NULL if the buffer was - insufficiently large to hold the encoded tags. Thus, if successful, - printable tags are encoded into - [buffer, buffer + *print_buf_size) and binary tags into - [returned-ptr, returned-ptr + *bin_buf_size) (and the returned - pointer should be buffer + *print_buf_size) */ -CENSUS_API char *census_context_encode(const census_context *context, - char *buffer, size_t buf_size, - size_t *print_buf_size, - size_t *bin_buf_size); - -/* Decode context buffers encoded with census_context_encode(). Returns NULL - if there is an error in parsing either buffer. */ -CENSUS_API census_context *census_context_decode(const char *buffer, - size_t size, - const char *bin_buffer, - size_t bin_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. */ -CENSUS_API int census_trace_mask(const census_context *context); - -/** Set the trace mask associated with a context. */ -CENSUS_API 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_API 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)(int64_t id); - const char *(*get_rpc_method_name)(int64_t 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_API census_context *census_start_client_rpc_op( - const census_context *context, int64_t 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. -*/ -CENSUS_API 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_API census_context *census_start_server_rpc_op( - const char *buffer, int64_t 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_API 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. -*/ -CENSUS_API void census_end_op(census_context *context, int status); - -#define CENSUS_TRACE_RECORD_START_OP ((uint32_t)0) -#define CENSUS_TRACE_RECORD_END_OP ((uint32_t)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 -*/ -CENSUS_API void census_trace_print(census_context *context, uint32_t type, - const char *buffer, size_t n); - -/** Trace record. */ -typedef struct { - census_timestamp timestamp; /* Time of record creation */ - uint64_t trace_id; /* Trace ID associated with record */ - uint64_t op_id; /* Operation ID associated with record */ - uint32_t 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) -*/ -CENSUS_API 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. -*/ -CENSUS_API int census_get_trace_record(census_trace_record *trace_record); - -/** End a scan previously started by census_trace_scan_start() */ -CENSUS_API void census_trace_scan_end(); - -/* 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 { - uint32_t metric_id; - double value; -} census_value; - -/* Record new usage values against the given context. */ -CENSUS_API 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; /* Aaggregation initialization argument. */ -} 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 -*/ - -/* TODO(aveitch): consider if context is the right argument type to pass in - tags. */ -CENSUS_API census_view *census_view_create( - uint32_t metric_id, const census_context *tags, - const census_aggregation *aggregations, size_t naggregations); - -/** Destroy a previously created view. */ -CENSUS_API void census_view_delete(census_view *view); - -/** Metric ID associated with a view */ -CENSUS_API size_t census_view_metric(const census_view *view); - -/** Number of aggregations associated with view. */ -CENSUS_API size_t census_view_naggregations(const census_view *view); - -/** Get tags associated with view. */ -CENSUS_API const census_context *census_view_tags(const census_view *view); - -/** Get aggregation descriptors associated with a view. */ -CENSUS_API 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_context *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. -*/ -CENSUS_API const census_view_data *census_view_get_data( - const census_view *view); - -/** Reset all view data to zero for the specified view */ -CENSUS_API void census_view_reset(census_view *view); - #ifdef __cplusplus } #endif -#endif /* CENSUS_CENSUS_H */ +#endif /* GRPC_CENSUS_H */ diff --git a/third_party/grpc/include/grpc/compression.h b/third_party/grpc/include/grpc/compression.h index a2b65b0e8af1ef..a4f6a8faf2bacb 100644 --- a/third_party/grpc/include/grpc/compression.h +++ b/third_party/grpc/include/grpc/compression.h @@ -1,79 +1,72 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_COMPRESSION_H #define GRPC_COMPRESSION_H +#include + #include -#include #include +#include #ifdef __cplusplus extern "C" { #endif -/** 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. */ -GRPC_API int grpc_compression_algorithm_parse( - const char *name, size_t name_length, - grpc_compression_algorithm *algorithm); +/** Return if an algorithm is message compression algorithm. */ +GRPCAPI int grpc_compression_algorithm_is_message( + grpc_compression_algorithm algorithm); + +/** Return if an algorithm is stream compression algorithm. */ +GRPCAPI int grpc_compression_algorithm_is_stream( + grpc_compression_algorithm algorithm); + +/** Parses the \a slice as a grpc_compression_algorithm instance and updating \a + * algorithm. Returns 1 upon success, 0 otherwise. */ +GRPCAPI int grpc_compression_algorithm_parse( + grpc_slice value, grpc_compression_algorithm* algorithm); /** Updates \a name with the encoding name corresponding to a valid \a - * algorithm. Returns 1 upon success, 0 otherwise. */ -GRPC_API int grpc_compression_algorithm_name( - grpc_compression_algorithm algorithm, char **name); + * algorithm. Note that \a name is statically allocated and must *not* be freed. + * Returns 1 upon success, 0 otherwise. */ +GRPCAPI int grpc_compression_algorithm_name( + grpc_compression_algorithm algorithm, const char** name); -/** Returns the compression algorithm corresponding to \a level. - * - * It abort()s for unknown levels . */ -GRPC_API grpc_compression_algorithm -grpc_compression_algorithm_for_level(grpc_compression_level level); +/** Returns the compression algorithm corresponding to \a level for the + * compression algorithms encoded in the \a accepted_encodings bitset.*/ +GRPCAPI grpc_compression_algorithm grpc_compression_algorithm_for_level( + grpc_compression_level level, uint32_t accepted_encodings); -GRPC_API void grpc_compression_options_init(grpc_compression_options *opts); +GRPCAPI void grpc_compression_options_init(grpc_compression_options* opts); /** Mark \a algorithm as enabled in \a opts. */ -GRPC_API void grpc_compression_options_enable_algorithm( - grpc_compression_options *opts, grpc_compression_algorithm algorithm); +GRPCAPI void grpc_compression_options_enable_algorithm( + grpc_compression_options* opts, grpc_compression_algorithm algorithm); /** Mark \a algorithm as disabled in \a opts. */ -GRPC_API void grpc_compression_options_disable_algorithm( - grpc_compression_options *opts, grpc_compression_algorithm algorithm); +GRPCAPI 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. */ -GRPC_API int grpc_compression_options_is_algorithm_enabled( - const grpc_compression_options *opts, grpc_compression_algorithm algorithm); +GRPCAPI int grpc_compression_options_is_algorithm_enabled( + const grpc_compression_options* opts, grpc_compression_algorithm algorithm); #ifdef __cplusplus } diff --git a/third_party/grpc/include/grpc/fork.h b/third_party/grpc/include/grpc/fork.h new file mode 100644 index 00000000000000..26f9df9871bee9 --- /dev/null +++ b/third_party/grpc/include/grpc/fork.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_FORK_H +#define GRPC_FORK_H + +#include + +#include + +#endif /* GRPC_FORK_H */ diff --git a/third_party/grpc/include/grpc/grpc.h b/third_party/grpc/include/grpc/grpc.h index ac4c536be315d9..fec7f5269e15bd 100644 --- a/third_party/grpc/include/grpc/grpc.h +++ b/third_party/grpc/include/grpc/grpc.h @@ -1,48 +1,35 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015-2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_GRPC_H #define GRPC_GRPC_H +#include + #include -#include #include -#include -#include #include -#include #include +#include +#include +#include +#include #ifdef __cplusplus extern "C" { @@ -55,11 +42,11 @@ extern "C" { * functionality lives in grpc_security.h. */ -GRPC_API void grpc_metadata_array_init(grpc_metadata_array *array); -GRPC_API void grpc_metadata_array_destroy(grpc_metadata_array *array); +GRPCAPI void grpc_metadata_array_init(grpc_metadata_array* array); +GRPCAPI void grpc_metadata_array_destroy(grpc_metadata_array* array); -GRPC_API void grpc_call_details_init(grpc_call_details *details); -GRPC_API void grpc_call_details_destroy(grpc_call_details *details); +GRPCAPI void grpc_call_details_init(grpc_call_details* details); +GRPCAPI void grpc_call_details_destroy(grpc_call_details* details); /** Registers a plugin to be initialized and destroyed with the library. @@ -69,29 +56,68 @@ GRPC_API void grpc_call_details_destroy(grpc_call_details *details); (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. */ -GRPC_API void grpc_register_plugin(void (*init)(void), void (*destroy)(void)); +GRPCAPI void grpc_register_plugin(void (*init)(void), void (*destroy)(void)); /** Initialize the grpc library. + After it's called, a matching invocation to grpc_shutdown() is expected. + 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). */ -GRPC_API void grpc_init(void); +GRPCAPI void grpc_init(void); /** Shut down the grpc library. + Before it's called, there should haven been a matching invocation to + grpc_init(). + 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. */ -GRPC_API void grpc_shutdown(void); +GRPCAPI void grpc_shutdown(void); + +/** EXPERIMENTAL. Returns 1 if the grpc library has been initialized. + TODO(ericgribkoff) Decide if this should be promoted to non-experimental as + part of stabilizing the fork support API, as tracked in + https://github.com/grpc/grpc/issues/15334 */ +GRPCAPI int grpc_is_initialized(void); /** Return a string representing the current version of grpc */ -GRPC_API const char *grpc_version_string(void); +GRPCAPI const char* grpc_version_string(void); + +/** Return a string specifying what the 'g' in gRPC stands for */ +GRPCAPI const char* grpc_g_stands_for(void); + +/** Returns the completion queue factory based on the attributes. MAY return a + NULL if no factory can be found */ +GRPCAPI const grpc_completion_queue_factory* +grpc_completion_queue_factory_lookup( + const grpc_completion_queue_attributes* attributes); + +/** Helper function to create a completion queue with grpc_cq_completion_type + of GRPC_CQ_NEXT and grpc_cq_polling_type of GRPC_CQ_DEFAULT_POLLING */ +GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_next( + void* reserved); + +/** Helper function to create a completion queue with grpc_cq_completion_type + of GRPC_CQ_PLUCK and grpc_cq_polling_type of GRPC_CQ_DEFAULT_POLLING */ +GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_pluck( + void* reserved); + +/** Helper function to create a completion queue with grpc_cq_completion_type + of GRPC_CQ_CALLBACK and grpc_cq_polling_type of GRPC_CQ_DEFAULT_POLLING. + This function is experimental. */ +GRPCAPI grpc_completion_queue* grpc_completion_queue_create_for_callback( + grpc_experimental_completion_queue_functor* shutdown_callback, + void* reserved); /** Create a completion queue */ -GRPC_API grpc_completion_queue *grpc_completion_queue_create(void *reserved); +GRPCAPI grpc_completion_queue* grpc_completion_queue_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attributes, void* reserved); /** Blocks until an event is available, the completion queue is being shut down, or deadline is reached. @@ -101,9 +127,9 @@ GRPC_API grpc_completion_queue *grpc_completion_queue_create(void *reserved); Callers must not call grpc_completion_queue_next and grpc_completion_queue_pluck simultaneously on the same completion queue. */ -GRPC_API grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, - gpr_timespec deadline, - void *reserved); +GRPCAPI 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. @@ -116,9 +142,9 @@ GRPC_API grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, Completion queues support a maximum of GRPC_MAX_COMPLETION_QUEUE_PLUCKERS concurrently executing plucks at any time. */ -GRPC_API grpc_event -grpc_completion_queue_pluck(grpc_completion_queue *cq, void *tag, - gpr_timespec deadline, void *reserved); +GRPCAPI 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 */ @@ -131,84 +157,107 @@ grpc_completion_queue_pluck(grpc_completion_queue *cq, void *tag, After calling this function applications should ensure that no NEW work is added to be published on this completion queue. */ -GRPC_API void grpc_completion_queue_shutdown(grpc_completion_queue *cq); +GRPCAPI 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 */ -GRPC_API void grpc_completion_queue_destroy(grpc_completion_queue *cq); +GRPCAPI void grpc_completion_queue_destroy(grpc_completion_queue* cq); -/** Create a completion queue alarm instance associated to \a cq. - * - * Once the alarm expires (at \a deadline) or it's cancelled (see \a - * grpc_alarm_cancel), an event with tag \a tag will be added to \a cq. If the - * alarm expired, the event's success bit will be true, false otherwise (ie, - * upon cancellation). */ -GRPC_API grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, - gpr_timespec deadline, void *tag); - -/** Cancel a completion queue alarm. Calling this function over an alarm that - * has already fired has no effect. */ -GRPC_API void grpc_alarm_cancel(grpc_alarm *alarm); - -/** Destroy the given completion queue alarm, cancelling it in the process. */ -GRPC_API void grpc_alarm_destroy(grpc_alarm *alarm); +/*********** EXPERIMENTAL API ************/ +/** Initializes a thread local cache for \a cq. + * grpc_flush_cq_tls_cache() MUST be called on the same thread, + * with the same cq. + */ +GRPCAPI void grpc_completion_queue_thread_local_cache_init( + grpc_completion_queue* cq); + +/*********** EXPERIMENTAL API ************/ +/** Flushes the thread local cache for \a cq. + * Returns 1 if there was contents in the cache. If there was an event + * in \a cq tls cache, its tag is placed in tag, and ok is set to the + * event success. + */ +GRPCAPI int grpc_completion_queue_thread_local_cache_flush( + grpc_completion_queue* cq, void** tag, int* ok); /** Check the connectivity state of a channel. */ -GRPC_API grpc_connectivity_state -grpc_channel_check_connectivity_state(grpc_channel *channel, - int try_to_connect); +GRPCAPI grpc_connectivity_state grpc_channel_check_connectivity_state( + grpc_channel* channel, int try_to_connect); + +/** Number of active "external connectivity state watchers" attached to a + * channel. + * Useful for testing. **/ +GRPCAPI int grpc_channel_num_external_connectivity_watchers( + grpc_channel* channel); /** 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. */ -GRPC_API void grpc_channel_watch_connectivity_state( - grpc_channel *channel, grpc_connectivity_state last_observed_state, - gpr_timespec deadline, grpc_completion_queue *cq, void *tag); +GRPCAPI void grpc_channel_watch_connectivity_state( + grpc_channel* channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue* cq, void* tag); + +/** Check whether a grpc channel supports connectivity watcher */ +GRPCAPI int grpc_channel_support_connectivity_watcher(grpc_channel* channel); /** 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_API grpc_call *grpc_channel_create_call( - grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, - grpc_completion_queue *completion_queue, const char *method, - const char *host, gpr_timespec deadline, void *reserved); + to propagate properties from the server call to this new client call, + depending on the value of \a propagation_mask (see propagation_bits.h for + possible values). */ +GRPCAPI grpc_call* grpc_channel_create_call( + grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask, + grpc_completion_queue* completion_queue, grpc_slice method, + const grpc_slice* host, gpr_timespec deadline, void* reserved); /** Ping the channels peer (load balanced channels will select one sub-channel to ping); if the channel is not connected, posts a failed. */ -GRPC_API void grpc_channel_ping(grpc_channel *channel, - grpc_completion_queue *cq, void *tag, - void *reserved); +GRPCAPI void grpc_channel_ping(grpc_channel* channel, grpc_completion_queue* cq, + void* tag, void* reserved); /** Pre-register a method/host pair on a channel. */ -GRPC_API void *grpc_channel_register_call(grpc_channel *channel, - const char *method, const char *host, - void *reserved); +GRPCAPI 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. + \sa grpc_channel_create_call. */ +GRPCAPI grpc_call* grpc_channel_create_registered_call( + grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask, + grpc_completion_queue* completion_queue, void* registered_call_handle, + gpr_timespec deadline, void* reserved); -/** Create a call given a handle returned from grpc_channel_register_call */ -GRPC_API grpc_call *grpc_channel_create_registered_call( - grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, - grpc_completion_queue *completion_queue, void *registered_call_handle, - gpr_timespec deadline, void *reserved); +/** Allocate memory in the grpc_call arena: this memory is automatically + discarded at call completion */ +GRPCAPI void* grpc_call_arena_alloc(grpc_call* call, size_t size); /** 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. + batch. + If a call to grpc_call_start_batch returns GRPC_CALL_OK you must call + grpc_completion_queue_next or grpc_completion_queue_pluck on the completion + queue associated with 'call' for work to be performed. If a call to + grpc_call_start_batch returns any value other than GRPC_CALL_OK it is + guaranteed that no state associated with 'call' is changed and it is not + appropriate to call grpc_completion_queue_next or + grpc_completion_queue_pluck consequent to the failed grpc_call_start_batch + call. + If a call to grpc_call_start_batch with an empty batch returns + GRPC_CALL_OK, the tag is put in the completion queue immediately. 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_API grpc_call_error grpc_call_start_batch(grpc_call *call, - const grpc_op *ops, size_t nops, - void *tag, void *reserved); + receive operations. Access to grpc_call_start_batch with an empty batch is + thread-compatible. */ +GRPCAPI 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 @@ -218,38 +267,52 @@ GRPC_API grpc_call_error grpc_call_start_batch(grpc_call *call, 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. */ -GRPC_API char *grpc_call_get_peer(grpc_call *call); +GRPCAPI char* grpc_call_get_peer(grpc_call* call); struct census_context; -/* Set census context for a call; Must be called before first call to +/** Set census context for a call; Must be called before first call to grpc_call_start_batch(). */ -GRPC_API void grpc_census_call_set_context(grpc_call *call, - struct census_context *context); +GRPCAPI void grpc_census_call_set_context(grpc_call* call, + struct census_context* context); -/* Retrieve the calls current census context. */ -GRPC_API struct census_context *grpc_census_call_get_context(grpc_call *call); +/** Retrieve the calls current census context. */ +GRPCAPI struct census_context* grpc_census_call_get_context(grpc_call* call); /** Return a newly allocated string representing the target a channel was created for. */ -GRPC_API char *grpc_channel_get_target(grpc_channel *channel); +GRPCAPI char* grpc_channel_get_target(grpc_channel* channel); + +/** Request info about the channel. + \a channel_info indicates what information is being requested and + how that information will be returned. + \a channel_info is owned by the caller. */ +GRPCAPI void grpc_channel_get_info(grpc_channel* channel, + const grpc_channel_info* channel_info); + +/** EXPERIMENTAL. Resets the channel's connect backoff. + TODO(roth): When we see whether this proves useful, either promote + to non-experimental or remove it. */ +GRPCAPI void grpc_channel_reset_connect_backoff(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_API grpc_channel *grpc_insecure_channel_create( - const char *target, const grpc_channel_args *args, void *reserved); + clients will want to simply pass NULL. The user data in 'args' need only + live through the invocation of this function. However, if any args of the + 'pointer' type are passed, then the referenced vtable must be maintained + by the caller until grpc_channel_destroy terminates. See grpc_channel_args + definition for more on this. */ +GRPCAPI 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_API grpc_channel *grpc_lame_client_channel_create( - const char *target, grpc_status_code error_code, const char *error_message); +GRPCAPI 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 */ -GRPC_API void grpc_channel_destroy(grpc_channel *channel); +GRPCAPI void grpc_channel_destroy(grpc_channel* channel); -/* Error handling for grpc_call +/** 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 @@ -258,23 +321,32 @@ GRPC_API void grpc_channel_destroy(grpc_channel *channel); /** 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 + are thread-safe, and can be called at any point before grpc_call_unref is called.*/ -GRPC_API grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved); +GRPCAPI 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_API grpc_call_error -grpc_call_cancel_with_status(grpc_call *call, grpc_status_code status, - const char *description, void *reserved); + remote endpoint. + Note that \a description doesn't need be a static string. + It doesn't need to be alive after the call to + grpc_call_cancel_with_status completes. + */ +GRPCAPI grpc_call_error grpc_call_cancel_with_status(grpc_call* call, + grpc_status_code status, + const char* description, + void* reserved); + +/** Ref a call. + THREAD SAFETY: grpc_call_ref is thread-compatible */ +GRPCAPI void grpc_call_ref(grpc_call* call); -/** Destroy a call. - THREAD SAFETY: grpc_call_destroy is thread-compatible */ -GRPC_API void grpc_call_destroy(grpc_call *call); +/** Unref a call. + THREAD SAFETY: grpc_call_unref is thread-compatible */ +GRPCAPI void grpc_call_unref(grpc_call* call); /** Request notification of a new call. Once a call is received, a notification tagged with \a tag_new is added to @@ -284,13 +356,19 @@ GRPC_API void grpc_call_destroy(grpc_call *call); 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_API 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); +GRPCAPI 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); + +/** How to handle payloads for a registered method */ +typedef enum { + /** Don't try to read the payload */ + GRPC_SRM_PAYLOAD_NONE, + /** Read the initial payload as a byte buffer */ + GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER +} grpc_server_register_method_payload_handling; /** Registers a method in the server. Methods to this (host, method) pair will not be reported by @@ -299,43 +377,47 @@ grpc_server_request_call(grpc_server *server, grpc_call **call, registered_method (as returned by this function). Must be called before grpc_server_start. Returns NULL on failure. */ -GRPC_API void *grpc_server_register_method(grpc_server *server, - const char *method, - const char *host); +GRPCAPI void* grpc_server_register_method( + grpc_server* server, const char* method, const char* host, + grpc_server_register_method_payload_handling payload_handling, + uint32_t flags); /** 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_API 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); +GRPCAPI 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_API grpc_server *grpc_server_create(const grpc_channel_args *args, - void *reserved); + be NULL. The user data in 'args' need only live through the invocation of + this function. However, if any args of the 'pointer' type are passed, then + the referenced vtable must be maintained by the caller until + grpc_server_destroy terminates. See grpc_channel_args definition for more + on this. */ +GRPCAPI 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. */ -GRPC_API void grpc_server_register_completion_queue(grpc_server *server, - grpc_completion_queue *cq, - void *reserved); +GRPCAPI 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 */ -GRPC_API int grpc_server_add_insecure_http2_port(grpc_server *server, - const char *addr); +GRPCAPI int grpc_server_add_insecure_http2_port(grpc_server* server, + const char* addr); /** Start a server - tells all listeners to start listening */ -GRPC_API void grpc_server_start(grpc_server *server); +GRPCAPI void grpc_server_start(grpc_server* server); /** Begin shutting down a server. After completion, no new calls or connections will be admitted. @@ -344,19 +426,19 @@ GRPC_API void grpc_server_start(grpc_server *server); 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. */ -GRPC_API void grpc_server_shutdown_and_notify(grpc_server *server, - grpc_completion_queue *cq, - void *tag); +GRPCAPI void grpc_server_shutdown_and_notify(grpc_server* server, + grpc_completion_queue* cq, + void* tag); /** Cancel all in-progress calls. Only usable after shutdown. */ -GRPC_API void grpc_server_cancel_all_calls(grpc_server *server); +GRPCAPI 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). */ -GRPC_API void grpc_server_destroy(grpc_server *server); +GRPCAPI void grpc_server_destroy(grpc_server* server); /** Enable or disable a tracer. @@ -366,18 +448,83 @@ GRPC_API void grpc_server_destroy(grpc_server *server); Use of this function is not strictly thread-safe, but the thread-safety issues raised by it should not be of concern. */ -GRPC_API int grpc_tracer_set_enabled(const char *name, int enabled); +GRPCAPI int grpc_tracer_set_enabled(const char* name, int enabled); /** Check whether a metadata key is legal (will be accepted by core) */ -GRPC_API int grpc_header_key_is_legal(const char *key, size_t length); +GRPCAPI int grpc_header_key_is_legal(grpc_slice slice); /** Check whether a non-binary metadata value is legal (will be accepted by core) */ -GRPC_API int grpc_header_nonbin_value_is_legal(const char *value, - size_t length); +GRPCAPI int grpc_header_nonbin_value_is_legal(grpc_slice slice); /** Check whether a metadata key corresponds to a binary value */ -GRPC_API int grpc_is_binary_header(const char *key, size_t length); +GRPCAPI int grpc_is_binary_header(grpc_slice slice); + +/** Convert grpc_call_error values to a string */ +GRPCAPI const char* grpc_call_error_to_string(grpc_call_error error); + +/** Create a buffer pool */ +GRPCAPI grpc_resource_quota* grpc_resource_quota_create(const char* trace_name); + +/** Add a reference to a buffer pool */ +GRPCAPI void grpc_resource_quota_ref(grpc_resource_quota* resource_quota); + +/** Drop a reference to a buffer pool */ +GRPCAPI void grpc_resource_quota_unref(grpc_resource_quota* resource_quota); + +/** Update the size of a buffer pool */ +GRPCAPI void grpc_resource_quota_resize(grpc_resource_quota* resource_quota, + size_t new_size); + +/** Update the size of the maximum number of threads allowed */ +GRPCAPI void grpc_resource_quota_set_max_threads( + grpc_resource_quota* resource_quota, int new_max_threads); + +/** Fetch a vtable for a grpc_channel_arg that points to a grpc_resource_quota + */ +GRPCAPI const grpc_arg_pointer_vtable* grpc_resource_quota_arg_vtable(void); + +/************* CHANNELZ API *************/ +/** Channelz is under active development. The following APIs will see some + churn as the feature is implemented. This comment will be removed once + channelz is officially supported, and these APIs become stable. For now + you may track the progress by following this github issue: + https://github.com/grpc/grpc/issues/15340 + + the following APIs return allocated JSON strings that match the response + objects from the channelz proto, found here: + https://github.com/grpc/grpc/blob/master/src/proto/grpc/channelz/channelz.proto. + + For easy conversion to protobuf, The JSON is formatted according to: + https://developers.google.com/protocol-buffers/docs/proto3#json. */ + +/* Gets all root channels (i.e. channels the application has directly + created). This does not include subchannels nor non-top level channels. + The returned string is allocated and must be freed by the application. */ +GRPCAPI char* grpc_channelz_get_top_channels(intptr_t start_channel_id); + +/* Gets all servers that exist in the process. */ +GRPCAPI char* grpc_channelz_get_servers(intptr_t start_server_id); + +/* Returns a single Server, or else a NOT_FOUND code. */ +GRPCAPI char* grpc_channelz_get_server(intptr_t server_id); + +/* Gets all server sockets that exist in the server. */ +GRPCAPI char* grpc_channelz_get_server_sockets(intptr_t server_id, + intptr_t start_socket_id, + intptr_t max_results); + +/* Returns a single Channel, or else a NOT_FOUND code. The returned string + is allocated and must be freed by the application. */ +GRPCAPI char* grpc_channelz_get_channel(intptr_t channel_id); + +/* Returns a single Subchannel, or else a NOT_FOUND code. The returned string + is allocated and must be freed by the application. */ +GRPCAPI char* grpc_channelz_get_subchannel(intptr_t subchannel_id); + +/* Returns a single Socket, or else a NOT_FOUND code. The returned string + is allocated and must be freed by the application. */ +GRPCAPI char* grpc_channelz_get_socket(intptr_t socket_id); #ifdef __cplusplus } diff --git a/third_party/grpc/include/grpc/grpc_cronet.h b/third_party/grpc/include/grpc/grpc_cronet.h new file mode 100644 index 00000000000000..289cfcda675a96 --- /dev/null +++ b/third_party/grpc/include/grpc/grpc_cronet.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_GRPC_CRONET_H +#define GRPC_GRPC_CRONET_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +GRPCAPI grpc_channel* grpc_cronet_secure_channel_create( + void* engine, const char* target, const grpc_channel_args* args, + void* reserved); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_GRPC_CRONET_H */ diff --git a/third_party/grpc/include/grpc/grpc_posix.h b/third_party/grpc/include/grpc/grpc_posix.h new file mode 100644 index 00000000000000..fbce5e13c11214 --- /dev/null +++ b/third_party/grpc/include/grpc/grpc_posix.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_GRPC_POSIX_H +#define GRPC_GRPC_POSIX_H + +#include + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \mainpage GRPC Core POSIX + * + * The GRPC Core POSIX library provides some POSIX-specific low-level + * functionality on top of GRPC Core. + */ + +/** Create a client channel to 'target' using file descriptor 'fd'. The 'target' + argument will be used to indicate the name for this channel. See the comment + for grpc_insecure_channel_create for description of 'args' argument. */ +GRPCAPI grpc_channel* grpc_insecure_channel_create_from_fd( + const char* target, int fd, const grpc_channel_args* args); + +/** Add the connected communication channel based on file descriptor 'fd' to the + 'server'. The 'fd' must be an open file descriptor corresponding to a + connected socket. Events from the file descriptor may come on any of the + server completion queues (i.e completion queues registered via the + grpc_server_register_completion_queue API). + + The 'reserved' pointer MUST be NULL. + */ +GRPCAPI void grpc_server_add_insecure_channel_from_fd(grpc_server* server, + void* reserved, int fd); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_GRPC_POSIX_H */ diff --git a/third_party/grpc/include/grpc/grpc_security.h b/third_party/grpc/include/grpc/grpc_security.h index 28881c3a11ab97..de90971cc552a2 100644 --- a/third_party/grpc/include/grpc/grpc_security.h +++ b/third_party/grpc/include/grpc/grpc_security.h @@ -1,188 +1,190 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_GRPC_SECURITY_H #define GRPC_GRPC_SECURITY_H +#include + #include +#include #include #ifdef __cplusplus extern "C" { #endif -/* --- 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" +/** --- Authentication Context. --- */ typedef struct grpc_auth_context grpc_auth_context; typedef struct grpc_auth_property_iterator { - const grpc_auth_context *ctx; + const grpc_auth_context* ctx; size_t index; - const char *name; + const char* name; } grpc_auth_property_iterator; -/* value, if not NULL, is guaranteed to be NULL terminated. */ +/** value, if not NULL, is guaranteed to be NULL terminated. */ typedef struct grpc_auth_property { - char *name; - char *value; + char* name; + char* value; size_t value_length; } grpc_auth_property; -/* Returns NULL when the iterator is at the end. */ -GRPC_API const grpc_auth_property *grpc_auth_property_iterator_next( - grpc_auth_property_iterator *it); +/** Returns NULL when the iterator is at the end. */ +GRPCAPI const grpc_auth_property* grpc_auth_property_iterator_next( + grpc_auth_property_iterator* it); -/* Iterates over the auth context. */ -GRPC_API grpc_auth_property_iterator -grpc_auth_context_property_iterator(const grpc_auth_context *ctx); +/** Iterates over the auth context. */ +GRPCAPI 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 +/** Gets the peer identity. Returns an empty iterator (first _next will return NULL) if the peer is not authenticated. */ -GRPC_API grpc_auth_property_iterator -grpc_auth_context_peer_identity(const grpc_auth_context *ctx); +GRPCAPI 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 +/** 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_API grpc_auth_property_iterator -grpc_auth_context_find_properties_by_name(const grpc_auth_context *ctx, - const char *name); +GRPCAPI 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 +/** Gets the name of the property that indicates the peer identity. Will return NULL if the peer is not authenticated. */ -GRPC_API const char *grpc_auth_context_peer_identity_property_name( - const grpc_auth_context *ctx); +GRPCAPI const char* grpc_auth_context_peer_identity_property_name( + const grpc_auth_context* ctx); -/* Returns 1 if the peer is authenticated, 0 otherwise. */ -GRPC_API int grpc_auth_context_peer_is_authenticated( - const grpc_auth_context *ctx); +/** Returns 1 if the peer is authenticated, 0 otherwise. */ +GRPCAPI int grpc_auth_context_peer_is_authenticated( + const grpc_auth_context* ctx); -/* Gets the auth context from the call. Caller needs to call +/** Gets the auth context from the call. Caller needs to call grpc_auth_context_release on the returned context. */ -GRPC_API grpc_auth_context *grpc_call_auth_context(grpc_call *call); +GRPCAPI grpc_auth_context* grpc_call_auth_context(grpc_call* call); -/* Releases the auth context returned from grpc_call_auth_context. */ -GRPC_API void grpc_auth_context_release(grpc_auth_context *context); +/** Releases the auth context returned from grpc_call_auth_context. */ +GRPCAPI 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. */ -GRPC_API void grpc_auth_context_add_property(grpc_auth_context *ctx, - const char *name, - const char *value, - size_t value_length); +/** Add a property. */ +GRPCAPI 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. */ -GRPC_API void grpc_auth_context_add_cstring_property(grpc_auth_context *ctx, - const char *name, - const char *value); +/** Add a C string property. */ +GRPCAPI 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 +/** Sets the property name. Returns 1 if successful or 0 in case of failure (which means that no property with this name exists). */ -GRPC_API int grpc_auth_context_set_peer_identity_property_name( - grpc_auth_context *ctx, const char *name); +GRPCAPI int grpc_auth_context_set_peer_identity_property_name( + grpc_auth_context* ctx, const char* name); + +/** --- SSL Session Cache. --- + + A SSL session cache object represents a way to cache client sessions + between connections. Only ticket-based resumption is supported. */ + +typedef struct grpc_ssl_session_cache grpc_ssl_session_cache; + +/** Create LRU cache for client-side SSL sessions with the given capacity. + If capacity is < 1, a default capacity is used instead. */ +GRPCAPI grpc_ssl_session_cache* grpc_ssl_session_cache_create_lru( + size_t capacity); -/* --- grpc_channel_credentials object. --- +/** Destroy SSL session cache. */ +GRPCAPI void grpc_ssl_session_cache_destroy(grpc_ssl_session_cache* cache); + +/** Create a channel arg with the given cache object. */ +GRPCAPI grpc_arg +grpc_ssl_session_cache_create_channel_arg(grpc_ssl_session_cache* cache); + +/** --- grpc_channel_credentials object. --- A channel credentials object represents a way to authenticate a client on a channel. */ typedef struct grpc_channel_credentials grpc_channel_credentials; -/* Releases a channel credentials object. +/** Releases a channel credentials object. The creator of the credentials object is responsible for its release. */ -GRPC_API void grpc_channel_credentials_release(grpc_channel_credentials *creds); +GRPCAPI void grpc_channel_credentials_release(grpc_channel_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. +/** 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_API grpc_channel_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" - -/* Results for the SSL roots override callback. */ -typedef enum { - GRPC_SSL_ROOTS_OVERRIDE_OK, - GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY, /* Do not try fallback options. */ - GRPC_SSL_ROOTS_OVERRIDE_FAIL -} grpc_ssl_roots_override_result; - -/* Callback for getting the SSL roots override from the application. +GRPCAPI grpc_channel_credentials* grpc_google_default_credentials_create(void); + +/** Callback for getting the SSL roots override from the application. In case of success, *pem_roots_certs must be set to a NULL terminated string containing the list of PEM encoded root certificates. The ownership is passed to the core and freed (laster by the core) with gpr_free. If this function fails and GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment is set to a valid path, it will override the roots specified this func */ typedef grpc_ssl_roots_override_result (*grpc_ssl_roots_override_callback)( - char **pem_root_certs); + char** pem_root_certs); -/* Setup a callback to override the default TLS/SSL roots. +/** Setup a callback to override the default TLS/SSL roots. This function is not thread-safe and must be called at initialization time before any ssl credentials are created to have the desired side effect. If GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment is set to a valid path, the callback will not be called. */ -void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb); +GRPCAPI void grpc_set_ssl_roots_override_callback( + grpc_ssl_roots_override_callback cb); -/* Object that holds a private key / certificate chain pair in PEM format. */ +/** 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 + /** private_key is the NULL-terminated string containing the PEM encoding of the client's private key. */ - const char *private_key; + const char* private_key; - /* cert_chain is the NULL-terminated string containing the PEM encoding of + /** cert_chain is the NULL-terminated string containing the PEM encoding of the client's certificate chain. */ - const char *cert_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 +/** Object that holds additional peer-verification options on a secure + channel. */ +typedef struct { + /** If non-NULL this callback will be invoked with the expected + target_name, the peer's certificate (in PEM format), and whatever + userdata pointer is set below. If a non-zero value is returned by this + callback then it is treated as a verification failure. Invocation of + the callback is blocking, so any implementation should be light-weight. + */ + int (*verify_peer_callback)(const char* target_name, const char* peer_pem, + void* userdata); + /** Arbitrary userdata that will be passed as the last argument to + verify_peer_callback. */ + void* verify_peer_callback_userdata; + /** A destruct callback that will be invoked when the channel is being + cleaned up. The userdata argument will be passed to it. The intent is + to perform any cleanup associated with that userdata. */ + void (*verify_peer_destruct)(void* userdata); +} verify_peer_options; + +/** Creates an SSL credentials object. + - pem_root_certs 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, @@ -191,12 +193,19 @@ typedef struct { 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_API grpc_channel_credentials *grpc_ssl_credentials_create( - const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, - void *reserved); - -/* --- grpc_call_credentials object. + not have such a key/cert pair. + - verify_options is an optional verify_peer_options object which holds + additional options controlling how peer certificates are verified. For + example, you can supply a callback which receives the peer's certificate + with which you can do additional verification. Can be NULL, in which + case verification will retain default behavior. Any settings in + verify_options are copied during this call, so the verify_options + object can be released afterwards. */ +GRPCAPI grpc_channel_credentials* grpc_ssl_credentials_create( + const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, + const verify_peer_options* verify_options, void* reserved); + +/** --- grpc_call_credentials object. A call credentials object represents a way to authenticate on a particular call. These credentials can be composed with a channel credentials object @@ -204,58 +213,58 @@ GRPC_API grpc_channel_credentials *grpc_ssl_credentials_create( typedef struct grpc_call_credentials grpc_call_credentials; -/* Releases a call credentials object. +/** Releases a call credentials object. The creator of the credentials object is responsible for its release. */ -GRPC_API void grpc_call_credentials_release(grpc_call_credentials *creds); +GRPCAPI void grpc_call_credentials_release(grpc_call_credentials* creds); -/* Creates a composite channel credentials object. */ -GRPC_API grpc_channel_credentials *grpc_composite_channel_credentials_create( - grpc_channel_credentials *channel_creds, grpc_call_credentials *call_creds, - void *reserved); +/** Creates a composite channel credentials object. */ +GRPCAPI grpc_channel_credentials* grpc_composite_channel_credentials_create( + grpc_channel_credentials* channel_creds, grpc_call_credentials* call_creds, + void* reserved); -/* Creates a composite call credentials object. */ -GRPC_API grpc_call_credentials *grpc_composite_call_credentials_create( - grpc_call_credentials *creds1, grpc_call_credentials *creds2, - void *reserved); +/** Creates a composite call credentials object. */ +GRPCAPI grpc_call_credentials* grpc_composite_call_credentials_create( + grpc_call_credentials* creds1, grpc_call_credentials* creds2, + void* reserved); -/* Creates a compute engine credentials object for connecting to Google. +/** 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_API grpc_call_credentials *grpc_google_compute_engine_credentials_create( - void *reserved); +GRPCAPI grpc_call_credentials* grpc_google_compute_engine_credentials_create( + void* reserved); -GRPC_API gpr_timespec grpc_max_auth_token_lifetime(); +GRPCAPI gpr_timespec grpc_max_auth_token_lifetime(void); -/* Creates a JWT credentials object. May return NULL if the input is invalid. +/** 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_API grpc_call_credentials * -grpc_service_account_jwt_access_credentials_create(const char *json_key, +GRPCAPI grpc_call_credentials* +grpc_service_account_jwt_access_credentials_create(const char* json_key, gpr_timespec token_lifetime, - void *reserved); + void* reserved); -/* Creates an Oauth2 Refresh Token credentials object for connecting to Google. +/** 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_API grpc_call_credentials *grpc_google_refresh_token_credentials_create( - const char *json_refresh_token, void *reserved); +GRPCAPI grpc_call_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 +/** Creates an Oauth2 Access Token credentials with an access token that was aquired by an out of band mechanism. */ -GRPC_API grpc_call_credentials *grpc_access_token_credentials_create( - const char *access_token, void *reserved); +GRPCAPI grpc_call_credentials* grpc_access_token_credentials_create( + const char* access_token, void* reserved); -/* Creates an IAM credentials object for connecting to Google. */ -GRPC_API grpc_call_credentials *grpc_google_iam_credentials_create( - const char *authorization_token, const char *authority_selector, - void *reserved); +/** Creates an IAM credentials object for connecting to Google. */ +GRPCAPI grpc_call_credentials* grpc_google_iam_credentials_create( + const char* authorization_token, const char* authority_selector, + void* reserved); -/* Callback function to be called by the metadata credentials plugin +/** Callback function to be called by the metadata credentials plugin implementation when the metadata is ready. - user_data is the opaque pointer that was passed in the get_metadata method of the grpc_metadata_credentials_plugin (see below). @@ -267,74 +276,139 @@ GRPC_API grpc_call_credentials *grpc_google_iam_credentials_create( - error_details contains details about the error if any. In case of success it should be NULL and will be otherwise ignored. */ typedef void (*grpc_credentials_plugin_metadata_cb)( - void *user_data, const grpc_metadata *creds_md, size_t num_creds_md, - grpc_status_code status, const char *error_details); + void* user_data, const grpc_metadata* creds_md, size_t num_creds_md, + grpc_status_code status, const char* error_details); -/* Context that can be used by metadata credentials plugin in order to create +/** Context that can be used by metadata credentials plugin in order to create auth related metadata. */ typedef struct { - /* The fully qualifed service url. */ - const char *service_url; + /** The fully qualifed service url. */ + const char* service_url; - /* The method name of the RPC being called (not fully qualified). + /** The method name of the RPC being called (not fully qualified). The fully qualified method name can be built from the service_url: full_qualified_method_name = ctx->service_url + '/' + ctx->method_name. */ - const char *method_name; + const char* method_name; - /* The auth_context of the channel which gives the server's identity. */ - const grpc_auth_context *channel_auth_context; + /** The auth_context of the channel which gives the server's identity. */ + const grpc_auth_context* channel_auth_context; - /* Reserved for future use. */ - void *reserved; + /** Reserved for future use. */ + void* reserved; } grpc_auth_metadata_context; -/* grpc_metadata_credentials plugin is an API user provided structure used to +/** Maximum number of metadata entries returnable by a credentials plugin via + a synchronous return. */ +#define GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX 4 + +/** grpc_metadata_credentials plugin is an API user provided structure used to create grpc_credentials objects that can be set on a channel (composed) or a call. See grpc_credentials_metadata_create_from_plugin below. The grpc client stack will call the get_metadata method of the plugin for every call in scope for the credentials created from it. */ typedef struct { - /* The implementation of this method has to be non-blocking. - - context is the information that can be used by the plugin to create auth - metadata. - - cb is the callback that needs to be called when the metadata is ready. - - user_data needs to be passed as the first parameter of the callback. */ - void (*get_metadata)(void *state, grpc_auth_metadata_context context, - grpc_credentials_plugin_metadata_cb cb, void *user_data); - - /* Destroys the plugin state. */ - void (*destroy)(void *state); - - /* State that will be set as the first parameter of the methods above. */ - void *state; - - /* Type of credentials that this plugin is implementing. */ - const char *type; + /** The implementation of this method has to be non-blocking, but can + be performed synchronously or asynchronously. + + If processing occurs synchronously, returns non-zero and populates + creds_md, num_creds_md, status, and error_details. In this case, + the caller takes ownership of the entries in creds_md and of + error_details. Note that if the plugin needs to return more than + GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX entries in creds_md, it must + return asynchronously. + + If processing occurs asynchronously, returns zero and invokes \a cb + when processing is completed. \a user_data will be passed as the + first parameter of the callback. NOTE: \a cb MUST be invoked in a + different thread, not from the thread in which \a get_metadata() is + invoked. + + \a context is the information that can be used by the plugin to create + auth metadata. */ + int (*get_metadata)( + void* state, grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, void* user_data, + grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], + size_t* num_creds_md, grpc_status_code* status, + const char** error_details); + + /** Destroys the plugin state. */ + void (*destroy)(void* state); + + /** State that will be set as the first parameter of the methods above. */ + void* state; + + /** Type of credentials that this plugin is implementing. */ + const char* type; } grpc_metadata_credentials_plugin; -/* Creates a credentials object from a plugin. */ -GRPC_API grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( - grpc_metadata_credentials_plugin plugin, void *reserved); +/** Creates a credentials object from a plugin. */ +GRPCAPI grpc_call_credentials* grpc_metadata_credentials_create_from_plugin( + grpc_metadata_credentials_plugin plugin, void* reserved); -/* --- Secure channel creation. --- */ +/** --- Secure channel creation. --- */ -/* Creates a secure channel using the passed-in credentials. */ -GRPC_API grpc_channel *grpc_secure_channel_create( - grpc_channel_credentials *creds, const char *target, - const grpc_channel_args *args, void *reserved); +/** Creates a secure channel using the passed-in credentials. Additional + channel level configuration MAY be provided by grpc_channel_args, though + the expectation is that most clients will want to simply pass NULL. The + user data in 'args' need only live through the invocation of this function. + However, if any args of the 'pointer' type are passed, then the referenced + vtable must be maintained by the caller until grpc_channel_destroy + terminates. See grpc_channel_args definition for more on this. */ +GRPCAPI grpc_channel* grpc_secure_channel_create( + grpc_channel_credentials* creds, const char* target, + const grpc_channel_args* args, void* reserved); -/* --- grpc_server_credentials object. --- +/** --- 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. +/** Releases a server_credentials object. The creator of the server_credentials object is responsible for its release. */ -GRPC_API void grpc_server_credentials_release(grpc_server_credentials *creds); +GRPCAPI void grpc_server_credentials_release(grpc_server_credentials* creds); -/* Creates an SSL server_credentials object. +/** Server certificate config object holds the server's public certificates and + associated private keys, as well as any CA certificates needed for client + certificate validation (if applicable). Create using + grpc_ssl_server_certificate_config_create(). */ +typedef struct grpc_ssl_server_certificate_config + grpc_ssl_server_certificate_config; + +/** Creates a grpc_ssl_server_certificate_config 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 must be at least 1. + - It is the caller's responsibility to free this object via + grpc_ssl_server_certificate_config_destroy(). */ +GRPCAPI grpc_ssl_server_certificate_config* +grpc_ssl_server_certificate_config_create( + const char* pem_root_certs, + const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs); + +/** Destroys a grpc_ssl_server_certificate_config object. */ +GRPCAPI void grpc_ssl_server_certificate_config_destroy( + grpc_ssl_server_certificate_config* config); + +/** Callback to retrieve updated SSL server certificates, private keys, and + trusted CAs (for client authentication). + - user_data parameter, if not NULL, contains opaque data to be used by the + callback. + - Use grpc_ssl_server_certificate_config_create to create the config. + - The caller assumes ownership of the config. */ +typedef grpc_ssl_certificate_config_reload_status ( + *grpc_ssl_server_certificate_config_callback)( + void* user_data, grpc_ssl_server_certificate_config** config); + +/** Deprecated in favor of grpc_ssl_server_credentials_create_ex. + 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. @@ -345,29 +419,73 @@ GRPC_API void grpc_server_credentials_release(grpc_server_credentials *creds); - 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_API 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. +GRPCAPI 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); + +/** Deprecated in favor of grpc_ssl_server_credentials_create_with_options. + Same as grpc_ssl_server_credentials_create method except uses + grpc_ssl_client_certificate_request_type enum to support more ways to + authenticate client cerificates.*/ +GRPCAPI grpc_server_credentials* grpc_ssl_server_credentials_create_ex( + const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs, + grpc_ssl_client_certificate_request_type client_certificate_request, + void* reserved); + +typedef struct grpc_ssl_server_credentials_options + grpc_ssl_server_credentials_options; + +/** Creates an options object using a certificate config. Use this method when + the certificates and keys of the SSL server will not change during the + server's lifetime. + - Takes ownership of the certificate_config parameter. */ +GRPCAPI grpc_ssl_server_credentials_options* +grpc_ssl_server_credentials_create_options_using_config( + grpc_ssl_client_certificate_request_type client_certificate_request, + grpc_ssl_server_certificate_config* certificate_config); + +/** Creates an options object using a certificate config fetcher. Use this + method to reload the certificates and keys of the SSL server without + interrupting the operation of the server. Initial certificate config will be + fetched during server initialization. + - user_data parameter, if not NULL, contains opaque data which will be passed + to the fetcher (see definition of + grpc_ssl_server_certificate_config_callback). */ +GRPCAPI grpc_ssl_server_credentials_options* +grpc_ssl_server_credentials_create_options_using_config_fetcher( + grpc_ssl_client_certificate_request_type client_certificate_request, + grpc_ssl_server_certificate_config_callback cb, void* user_data); + +/** Destroys a grpc_ssl_server_credentials_options object. */ +GRPCAPI void grpc_ssl_server_credentials_options_destroy( + grpc_ssl_server_credentials_options* options); + +/** Creates an SSL server_credentials object using the provided options struct. + - Takes ownership of the options parameter. */ +GRPCAPI grpc_server_credentials* +grpc_ssl_server_credentials_create_with_options( + grpc_ssl_server_credentials_options* options); + +/** --- 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 */ -GRPC_API int grpc_server_add_secure_http2_port(grpc_server *server, - const char *addr, - grpc_server_credentials *creds); +GRPCAPI int grpc_server_add_secure_http2_port(grpc_server* server, + const char* addr, + grpc_server_credentials* creds); -/* --- Call specific credentials. --- */ +/** --- Call specific credentials. --- */ -/* Sets a credentials to a call. Can only be called on the client side before +/** Sets a credentials to a call. Can only be called on the client side before grpc_call_start_batch. */ -GRPC_API grpc_call_error -grpc_call_set_credentials(grpc_call *call, grpc_call_credentials *creds); +GRPCAPI grpc_call_error grpc_call_set_credentials(grpc_call* call, + grpc_call_credentials* creds); -/* --- Auth Metadata Processing --- */ +/** --- Auth Metadata Processing --- */ -/* Callback function that is called when the metadata processing is done. +/** 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. @@ -377,25 +495,119 @@ grpc_call_set_credentials(grpc_call *call, grpc_call_credentials *creds); 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); + 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. */ +/** Pluggable server-side metadata processor object. */ typedef struct { - /* The context object is read/write: it contains the properties of the + /** 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; + 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; -GRPC_API void grpc_server_credentials_set_auth_metadata_processor( - grpc_server_credentials *creds, grpc_auth_metadata_processor processor); +GRPCAPI void grpc_server_credentials_set_auth_metadata_processor( + grpc_server_credentials* creds, grpc_auth_metadata_processor processor); + +/** --- ALTS channel/server credentials --- **/ + +/** + * Main interface for ALTS credentials options. The options will contain + * information that will be passed from grpc to TSI layer such as RPC protocol + * versions. ALTS client (channel) and server credentials will have their own + * implementation of this interface. The APIs listed in this header are + * thread-compatible. It is used for experimental purpose for now and subject + * to change. + */ +typedef struct grpc_alts_credentials_options grpc_alts_credentials_options; + +/** + * This method creates a grpc ALTS credentials client options instance. + * It is used for experimental purpose for now and subject to change. + */ +GRPCAPI grpc_alts_credentials_options* +grpc_alts_credentials_client_options_create(void); + +/** + * This method creates a grpc ALTS credentials server options instance. + * It is used for experimental purpose for now and subject to change. + */ +GRPCAPI grpc_alts_credentials_options* +grpc_alts_credentials_server_options_create(void); + +/** + * This method adds a target service account to grpc client's ALTS credentials + * options instance. It is used for experimental purpose for now and subject + * to change. + * + * - options: grpc ALTS credentials options instance. + * - service_account: service account of target endpoint. + */ +GRPCAPI void grpc_alts_credentials_client_options_add_target_service_account( + grpc_alts_credentials_options* options, const char* service_account); + +/** + * This method destroys a grpc_alts_credentials_options instance by + * de-allocating all of its occupied memory. It is used for experimental purpose + * for now and subject to change. + * + * - options: a grpc_alts_credentials_options instance that needs to be + * destroyed. + */ +GRPCAPI void grpc_alts_credentials_options_destroy( + grpc_alts_credentials_options* options); + +/** + * This method creates an ALTS channel credential object. It is used for + * experimental purpose for now and subject to change. + * + * - options: grpc ALTS credentials options instance for client. + * + * It returns the created ALTS channel credential object. + */ +GRPCAPI grpc_channel_credentials* grpc_alts_credentials_create( + const grpc_alts_credentials_options* options); + +/** + * This method creates an ALTS server credential object. It is used for + * experimental purpose for now and subject to change. + * + * - options: grpc ALTS credentials options instance for server. + * + * It returns the created ALTS server credential object. + */ +GRPCAPI grpc_server_credentials* grpc_alts_server_credentials_create( + const grpc_alts_credentials_options* options); + +/** --- Local channel/server credentials --- **/ + +/** + * This method creates a local channel credential object. It is used for + * experimental purpose for now and subject to change. + * + * - type: local connection type + * + * It returns the created local channel credential object. + */ +GRPCAPI grpc_channel_credentials* grpc_local_credentials_create( + grpc_local_connect_type type); + +/** + * This method creates a local server credential object. It is used for + * experimental purpose for now and subject to change. + * + * - type: local connection type + * + * It returns the created local server credential object. + */ +GRPCAPI grpc_server_credentials* grpc_local_server_credentials_create( + grpc_local_connect_type type); #ifdef __cplusplus } diff --git a/third_party/grpc/include/grpc/grpc_security_constants.h b/third_party/grpc/include/grpc/grpc_security_constants.h new file mode 100644 index 00000000000000..a082f670107736 --- /dev/null +++ b/third_party/grpc/include/grpc/grpc_security_constants.h @@ -0,0 +1,118 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_GRPC_SECURITY_CONSTANTS_H +#define GRPC_GRPC_SECURITY_CONSTANTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#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" +#define GRPC_X509_PEM_CERT_PROPERTY_NAME "x509_pem_cert" +#define GRPC_SSL_SESSION_REUSED_PROPERTY "ssl_session_reused" + +/** 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" + +/** 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" + +/** Results for the SSL roots override callback. */ +typedef enum { + GRPC_SSL_ROOTS_OVERRIDE_OK, + GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY, /** Do not try fallback options. */ + GRPC_SSL_ROOTS_OVERRIDE_FAIL +} grpc_ssl_roots_override_result; + +/** Callback results for dynamically loading a SSL certificate config. */ +typedef enum { + GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED, + GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW, + GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_FAIL +} grpc_ssl_certificate_config_reload_status; + +typedef enum { + /** Server does not request client certificate. + The certificate presented by the client is not checked by the server at + all. (A client may present a self signed or signed certificate or not + present a certificate at all and any of those option would be accepted) */ + GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, + /** Server requests client certificate but does not enforce that the client + presents a certificate. + + If the client presents a certificate, the client authentication is left to + the application (the necessary metadata will be available to the + application via authentication context properties, see grpc_auth_context). + + The client's key certificate pair must be valid for the SSL connection to + be established. */ + GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, + /** Server requests client certificate but does not enforce that the client + presents a certificate. + + If the client presents a certificate, the client authentication is done by + the gRPC framework. (For a successful connection the client needs to either + present a certificate that can be verified against the root certificate + configured by the server or not present a certificate at all) + + The client's key certificate pair must be valid for the SSL connection to + be established. */ + GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, + /** Server requests client certificate and enforces that the client presents a + certificate. + + If the client presents a certificate, the client authentication is left to + the application (the necessary metadata will be available to the + application via authentication context properties, see grpc_auth_context). + + The client's key certificate pair must be valid for the SSL connection to + be established. */ + GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, + /** Server requests client certificate and enforces that the client presents a + certificate. + + The cerificate presented by the client is verified by the gRPC framework. + (For a successful connection the client needs to present a certificate that + can be verified against the root certificate configured by the server) + + The client's key certificate pair must be valid for the SSL connection to + be established. */ + GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY +} grpc_ssl_client_certificate_request_type; + +/** + * Type of local connections for which local channel/server credentials will be + * applied. It supports UDS and local TCP connections. + */ +typedef enum { UDS = 0, LOCAL_TCP } grpc_local_connect_type; + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_GRPC_SECURITY_CONSTANTS_H */ diff --git a/third_party/grpc/include/grpc/grpc_zookeeper.h b/third_party/grpc/include/grpc/grpc_zookeeper.h deleted file mode 100644 index b23bd88b433907..00000000000000 --- a/third_party/grpc/include/grpc/grpc_zookeeper.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/** 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/third_party/grpc/include/grpc/impl/codegen/alloc.h b/third_party/grpc/include/grpc/impl/codegen/alloc.h deleted file mode 100644 index dc7ba78846ce24..00000000000000 --- a/third_party/grpc/include/grpc/impl/codegen/alloc.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_IMPL_CODEGEN_ALLOC_H -#define GRPC_IMPL_CODEGEN_ALLOC_H - -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct gpr_allocation_functions { - void *(*malloc_fn)(size_t size); - void *(*realloc_fn)(void *ptr, size_t size); - void (*free_fn)(void *ptr); -} gpr_allocation_functions; - -/* malloc, never returns NULL */ -GPR_API void *gpr_malloc(size_t size); -/* free */ -GPR_API void gpr_free(void *ptr); -/* realloc, never returns NULL */ -GPR_API void *gpr_realloc(void *p, size_t size); -/* aligned malloc, never returns NULL, will align to 1 << alignment_log */ -GPR_API void *gpr_malloc_aligned(size_t size, size_t alignment_log); -/* free memory allocated by gpr_malloc_aligned */ -GPR_API void gpr_free_aligned(void *ptr); - -/** Request the family of allocation functions in \a functions be used. NOTE - * that this request will be honored in a *best effort* basis and that no - * guarantees are made about the default functions (eg, malloc) being called. */ -GPR_API void gpr_set_allocation_functions(gpr_allocation_functions functions); - -/** Return the family of allocation functions currently in effect. */ -GPR_API gpr_allocation_functions gpr_get_allocation_functions(); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_IMPL_CODEGEN_ALLOC_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/atm.h b/third_party/grpc/include/grpc/impl/codegen/atm.h index 35b97506ac96e8..00d83f0604fa70 100644 --- a/third_party/grpc/include/grpc/impl/codegen/atm.h +++ b/third_party/grpc/include/grpc/impl/codegen/atm.h @@ -1,40 +1,25 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_ATM_H #define GRPC_IMPL_CODEGEN_ATM_H -/* This interface provides atomic operations and barriers. +/** 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 @@ -61,6 +46,7 @@ // Atomically return *p, with acquire semantics. gpr_atm gpr_atm_acq_load(gpr_atm *p); + gpr_atm gpr_atm_no_barrier_load(gpr_atm *p); // Atomically set *p = value, with release semantics. void gpr_atm_rel_store(gpr_atm *p, gpr_atm value); @@ -75,6 +61,10 @@ 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); + int gpr_atm_full_cas(gpr_atm *p, gpr_atm o, gpr_atm n); + + // Atomically, set *p=n and return the old value of *p + gpr_atm gpr_atm_full_xchg(gpr_atm *p, gpr_atm n); */ #include @@ -83,10 +73,23 @@ #include #elif defined(GPR_GCC_SYNC) #include -#elif defined(GPR_WIN32_ATOMIC) -#include +#elif defined(GPR_WINDOWS_ATOMIC) +#include #else #error could not determine platform for atm #endif +#ifdef __cplusplus +extern "C" { +#endif + +/** Adds \a delta to \a *value, clamping the result to the range specified + by \a min and \a max. Returns the new value. */ +gpr_atm gpr_atm_no_barrier_clamped_add(gpr_atm* value, gpr_atm delta, + gpr_atm min, gpr_atm max); + +#ifdef __cplusplus +} +#endif + #endif /* GRPC_IMPL_CODEGEN_ATM_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/atm_gcc_atomic.h b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_atomic.h index 196d2aeeb34ab3..5879708548251d 100644 --- a/third_party/grpc/include/grpc/impl/codegen/atm_gcc_atomic.h +++ b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_atomic.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -38,7 +23,27 @@ __atomic_* interface. */ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef intptr_t gpr_atm; +#define GPR_ATM_MAX INTPTR_MAX +#define GPR_ATM_MIN INTPTR_MIN + +#ifdef GPR_LOW_LEVEL_COUNTERS +extern gpr_atm gpr_counter_atm_cas; +extern gpr_atm gpr_counter_atm_add; +#define GPR_ATM_INC_COUNTER(counter) \ + __atomic_fetch_add(&counter, 1, __ATOMIC_RELAXED) +#define GPR_ATM_INC_CAS_THEN(blah) \ + (GPR_ATM_INC_COUNTER(gpr_counter_atm_cas), blah) +#define GPR_ATM_INC_ADD_THEN(blah) \ + (GPR_ATM_INC_COUNTER(gpr_counter_atm_add), blah) +#else +#define GPR_ATM_INC_CAS_THEN(blah) blah +#define GPR_ATM_INC_ADD_THEN(blah) blah +#endif #define gpr_atm_full_barrier() (__atomic_thread_fence(__ATOMIC_SEQ_CST)) @@ -50,23 +55,37 @@ typedef intptr_t gpr_atm; (__atomic_store_n((p), (intptr_t)(value), __ATOMIC_RELAXED)) #define gpr_atm_no_barrier_fetch_add(p, delta) \ - (__atomic_fetch_add((p), (intptr_t)(delta), __ATOMIC_RELAXED)) + GPR_ATM_INC_ADD_THEN( \ + __atomic_fetch_add((p), (intptr_t)(delta), __ATOMIC_RELAXED)) #define gpr_atm_full_fetch_add(p, delta) \ - (__atomic_fetch_add((p), (intptr_t)(delta), __ATOMIC_ACQ_REL)) + GPR_ATM_INC_ADD_THEN( \ + __atomic_fetch_add((p), (intptr_t)(delta), __ATOMIC_ACQ_REL)) + +static __inline int gpr_atm_no_barrier_cas(gpr_atm* p, gpr_atm o, gpr_atm n) { + return GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n( + p, &o, n, 0, __ATOMIC_RELAXED, __ATOMIC_RELAXED)); +} -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 GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n( + p, &o, n, 0, __ATOMIC_ACQUIRE, __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 GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n( + p, &o, n, 0, __ATOMIC_RELEASE, __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); +static __inline int gpr_atm_full_cas(gpr_atm* p, gpr_atm o, gpr_atm n) { + return GPR_ATM_INC_CAS_THEN(__atomic_compare_exchange_n( + p, &o, n, 0, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)); +} + +#define gpr_atm_full_xchg(p, n) \ + GPR_ATM_INC_CAS_THEN(__atomic_exchange_n((p), (n), __ATOMIC_ACQ_REL)) + +#ifdef __cplusplus } +#endif #endif /* GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/atm_gcc_sync.h b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_sync.h index 56f755949d8e18..728c3d5412fe74 100644 --- a/third_party/grpc/include/grpc/impl/codegen/atm_gcc_sync.h +++ b/third_party/grpc/include/grpc/impl/codegen/atm_gcc_sync.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -39,6 +24,10 @@ #include typedef intptr_t gpr_atm; +#define GPR_ATM_MAX INTPTR_MAX +#define GPR_ATM_MIN INTPTR_MIN +#define GPR_ATM_INC_CAS_THEN(blah) blah +#define GPR_ATM_INC_ADD_THEN(blah) blah #define GPR_ATM_COMPILE_BARRIER_() __asm__ __volatile__("" : : : "memory") @@ -51,24 +40,24 @@ typedef intptr_t gpr_atm; #define gpr_atm_full_barrier() (__sync_synchronize()) -static __inline gpr_atm gpr_atm_acq_load(const gpr_atm *p) { +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) { +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) { +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) { +static __inline void gpr_atm_no_barrier_store(gpr_atm* p, gpr_atm value) { GPR_ATM_COMPILE_BARRIER_(); *p = value; } @@ -83,5 +72,14 @@ static __inline void gpr_atm_no_barrier_store(gpr_atm *p, gpr_atm value) { #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)) +#define gpr_atm_full_cas(p, o, n) gpr_atm_acq_cas((p), (o), (n)) + +static __inline gpr_atm gpr_atm_full_xchg(gpr_atm* p, gpr_atm n) { + gpr_atm cur; + do { + cur = gpr_atm_acq_load(p); + } while (!gpr_atm_rel_cas(p, cur, n)); + return cur; +} #endif /* GRPC_IMPL_CODEGEN_ATM_GCC_SYNC_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/atm_win32.h b/third_party/grpc/include/grpc/impl/codegen/atm_win32.h deleted file mode 100644 index 9d26462d84f86e..00000000000000 --- a/third_party/grpc/include/grpc/impl/codegen/atm_win32.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_IMPL_CODEGEN_ATM_WIN32_H -#define GRPC_IMPL_CODEGEN_ATM_WIN32_H - -/* Win32 variant of atm_platform.h */ -#include - -typedef intptr_t 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_IMPL_CODEGEN_ATM_WIN32_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/atm_windows.h b/third_party/grpc/include/grpc/impl/codegen/atm_windows.h new file mode 100644 index 00000000000000..c016b90095f3ba --- /dev/null +++ b/third_party/grpc/include/grpc/impl/codegen/atm_windows.h @@ -0,0 +1,128 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_IMPL_CODEGEN_ATM_WINDOWS_H +#define GRPC_IMPL_CODEGEN_ATM_WINDOWS_H + +/** Win32 variant of atm_platform.h */ +#include + +typedef intptr_t gpr_atm; +#define GPR_ATM_MAX INTPTR_MAX +#define GPR_ATM_MIN INTPTR_MIN +#define GPR_ATM_INC_CAS_THEN(blah) blah +#define GPR_ATM_INC_ADD_THEN(blah) blah + +#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 int gpr_atm_full_cas(gpr_atm* p, gpr_atm o, gpr_atm n) { +#ifdef GPR_ARCH_64 + return o == (gpr_atm)InterlockedCompareExchange64((volatile LONGLONG*)p, + (LONGLONG)n, (LONGLONG)o); +#else + return o == (gpr_atm)InterlockedCompareExchange((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; +} + +static __inline gpr_atm gpr_atm_full_xchg(gpr_atm* p, gpr_atm n) { + return (gpr_atm)InterlockedExchangePointer((PVOID*)p, (PVOID)n); +} + +#endif /* GRPC_IMPL_CODEGEN_ATM_WINDOWS_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/byte_buffer.h b/third_party/grpc/include/grpc/impl/codegen/byte_buffer.h index 5049461f73fd69..774655ed66fe68 100644 --- a/third_party/grpc/include/grpc/impl/codegen/byte_buffer.h +++ b/third_party/grpc/include/grpc/impl/codegen/byte_buffer.h @@ -1,72 +1,38 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_BYTE_BUFFER_H #define GRPC_IMPL_CODEGEN_BYTE_BUFFER_H -#include -#include +#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_API grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *slices, - size_t nslices); +GRPCAPI grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_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 @@ -74,45 +40,46 @@ GRPC_API grpc_byte_buffer *grpc_raw_byte_buffer_create(gpr_slice *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_API grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create( - gpr_slice *slices, size_t nslices, grpc_compression_algorithm compression); +GRPCAPI grpc_byte_buffer* grpc_raw_compressed_byte_buffer_create( + grpc_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_API grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb); +GRPCAPI grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb); /** Returns the size of the given byte buffer, in bytes. */ -GRPC_API size_t grpc_byte_buffer_length(grpc_byte_buffer *bb); +GRPCAPI size_t grpc_byte_buffer_length(grpc_byte_buffer* bb); /** Destroys \a byte_buffer deallocating all its memory. */ -GRPC_API void grpc_byte_buffer_destroy(grpc_byte_buffer *byte_buffer); +GRPCAPI 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 */ -GRPC_API void grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, - grpc_byte_buffer *buffer); +/** Initialize \a reader to read over \a buffer. + * Returns 1 upon success, 0 otherwise. */ +GRPCAPI int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader, + grpc_byte_buffer* buffer); /** Cleanup and destroy \a reader */ -GRPC_API void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader); +GRPCAPI 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. */ -GRPC_API int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, - gpr_slice *slice); + * grpc_slice_unref on the result. */ +GRPCAPI int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader, + grpc_slice* slice); /** Merge all data from \a reader into single slice */ -GRPC_API gpr_slice -grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader); +GRPCAPI grpc_slice +grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader* reader); /** Returns a RAW byte buffer instance from the output of \a reader. */ -GRPC_API grpc_byte_buffer *grpc_raw_byte_buffer_from_reader( - grpc_byte_buffer_reader *reader); +GRPCAPI grpc_byte_buffer* grpc_raw_byte_buffer_from_reader( + grpc_byte_buffer_reader* reader); #ifdef __cplusplus } diff --git a/third_party/grpc/include/grpc/impl/codegen/byte_buffer_reader.h b/third_party/grpc/include/grpc/impl/codegen/byte_buffer_reader.h new file mode 100644 index 00000000000000..e06e19558a1ca2 --- /dev/null +++ b/third_party/grpc/include/grpc/impl/codegen/byte_buffer_reader.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H +#define GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct grpc_byte_buffer; + +struct grpc_byte_buffer_reader { + struct grpc_byte_buffer* buffer_in; + struct grpc_byte_buffer* buffer_out; + /** Different current objects correspond to different types of byte buffers */ + union grpc_byte_buffer_reader_current { + /** Index into a slice buffer's array of slices */ + unsigned index; + } current; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_IMPL_CODEGEN_BYTE_BUFFER_READER_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/compression_types.h b/third_party/grpc/include/grpc/impl/codegen/compression_types.h index 0daccd92f237e4..f778b005b9b239 100644 --- a/third_party/grpc/include/grpc/impl/codegen/compression_types.h +++ b/third_party/grpc/include/grpc/impl/codegen/compression_types.h @@ -1,33 +1,18 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -40,19 +25,49 @@ 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" +/** To be used as initial metadata key for the request of a concrete compression + * algorithm */ +#define GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY \ + "grpc-internal-encoding-request" -/* The various compression algorithms supported by GRPC */ +/** To be used in channel arguments. + * + * \addtogroup grpc_arg_keys + * \{ */ +/** Default compression algorithm for the channel. + * Its value is an int from the \a grpc_compression_algorithm enum. */ +#define GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM \ + "grpc.default_compression_algorithm" +/** Default compression level for the channel. + * Its value is an int from the \a grpc_compression_level enum. */ +#define GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL "grpc.default_compression_level" +/** Compression algorithms supported by the channel. + * Its value is a bitset (an int). Bits correspond to algorithms in \a + * grpc_compression_algorithm. For example, its LSB corresponds to + * GRPC_COMPRESS_NONE, the next bit to GRPC_COMPRESS_DEFLATE, etc. + * Unset bits disable support for the algorithm. By default all algorithms are + * supported. It's not possible to disable GRPC_COMPRESS_NONE (the attempt will + * be ignored). */ +#define GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET \ + "grpc.compression_enabled_algorithms_bitset" +/** \} */ + +/** The various compression algorithms supported by gRPC (not sorted by + * compression level) */ typedef enum { GRPC_COMPRESS_NONE = 0, GRPC_COMPRESS_DEFLATE, GRPC_COMPRESS_GZIP, + /* EXPERIMENTAL: Stream compression is currently experimental. */ + GRPC_COMPRESS_STREAM_GZIP, /* TODO(ctiller): snappy */ GRPC_COMPRESS_ALGORITHMS_COUNT } grpc_compression_algorithm; +/** Compression levels allow a party with knowledge of its peer's accepted + * encodings to request compression in an abstract way. The level-algorithm + * mapping is performed internally and depends on the peer's supported + * compression algorithms. */ typedef enum { GRPC_COMPRESS_LEVEL_NONE = 0, GRPC_COMPRESS_LEVEL_LOW, @@ -62,8 +77,28 @@ typedef enum { } grpc_compression_level; typedef struct grpc_compression_options { - uint32_t enabled_algorithms_bitset; /**< All algs are enabled by default */ - grpc_compression_algorithm default_compression_algorithm; /**< for channel */ + /** All algs are enabled by default. This option corresponds to the channel + * argument key behind \a GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET + */ + uint32_t enabled_algorithms_bitset; + + /** The default compression level. It'll be used in the absence of call + * specific settings. This option corresponds to the channel + * argument key behind \a GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL. If present, + * takes precedence over \a default_algorithm. + * TODO(dgq): currently only available for server channels. */ + struct grpc_compression_options_default_level { + int is_set; + grpc_compression_level level; + } default_level; + + /** The default message compression algorithm. It'll be used in the absence of + * call specific settings. This option corresponds to the channel argument key + * behind \a GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM. */ + struct grpc_compression_options_default_algorithm { + int is_set; + grpc_compression_algorithm algorithm; + } default_algorithm; } grpc_compression_options; #ifdef __cplusplus diff --git a/third_party/grpc/include/grpc/impl/codegen/connectivity_state.h b/third_party/grpc/include/grpc/impl/codegen/connectivity_state.h index 5bb9eb8f4eeeed..b70dbef356441d 100644 --- a/third_party/grpc/include/grpc/impl/codegen/connectivity_state.h +++ b/third_party/grpc/include/grpc/impl/codegen/connectivity_state.h @@ -1,33 +1,18 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -49,7 +34,7 @@ typedef enum { /** 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_CHANNEL_SHUTDOWN } grpc_connectivity_state; #ifdef __cplusplus diff --git a/third_party/grpc/include/grpc/impl/codegen/fork.h b/third_party/grpc/include/grpc/impl/codegen/fork.h new file mode 100644 index 00000000000000..555df3490f08e2 --- /dev/null +++ b/third_party/grpc/include/grpc/impl/codegen/fork.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_IMPL_CODEGEN_FORK_H +#define GRPC_IMPL_CODEGEN_FORK_H + +/** + * gRPC applications should call this before calling fork(). There should be no + * active gRPC function calls between calling grpc_prefork() and + * grpc_postfork_parent()/grpc_postfork_child(). + * + * + * Typical use: + * grpc_prefork(); + * int pid = fork(); + * if (pid) { + * grpc_postfork_parent(); + * // Parent process.. + * } else { + * grpc_postfork_child(); + * // Child process... + * } + */ + +void grpc_prefork(void); + +void grpc_postfork_parent(void); + +void grpc_postfork_child(void); + +void grpc_fork_handlers_auto_register(void); + +#endif /* GRPC_IMPL_CODEGEN_FORK_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/gpr_slice.h b/third_party/grpc/include/grpc/impl/codegen/gpr_slice.h new file mode 100644 index 00000000000000..89fa72d5fc9e42 --- /dev/null +++ b/third_party/grpc/include/grpc/impl/codegen/gpr_slice.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GRPC_IMPL_CODEGEN_GPR_SLICE_H +#define GRPC_IMPL_CODEGEN_GPR_SLICE_H + +/** WARNING: Please do not use this header. This was added as a temporary + * measure to not break some of the external projects that depend on + * gpr_slice_* functions. We are actively working on moving all the + * gpr_slice_* references to grpc_slice_* and this file will be removed + */ + +/* TODO (sreek) - Allowed by default but will be very soon turned off */ +#define GRPC_ALLOW_GPR_SLICE_FUNCTIONS 1 + +#ifdef GRPC_ALLOW_GPR_SLICE_FUNCTIONS + +#define gpr_slice_refcount grpc_slice_refcount +#define gpr_slice grpc_slice +#define gpr_slice_buffer grpc_slice_buffer + +#define gpr_slice_ref grpc_slice_ref +#define gpr_slice_unref grpc_slice_unref +#define gpr_slice_new grpc_slice_new +#define gpr_slice_new_with_user_data grpc_slice_new_with_user_data +#define gpr_slice_new_with_len grpc_slice_new_with_len +#define gpr_slice_malloc grpc_slice_malloc +#define gpr_slice_from_copied_string grpc_slice_from_copied_string +#define gpr_slice_from_copied_buffer grpc_slice_from_copied_buffer +#define gpr_slice_from_static_string grpc_slice_from_static_string +#define gpr_slice_sub grpc_slice_sub +#define gpr_slice_sub_no_ref grpc_slice_sub_no_ref +#define gpr_slice_split_tail grpc_slice_split_tail +#define gpr_slice_split_head grpc_slice_split_head +#define gpr_slice_cmp grpc_slice_cmp +#define gpr_slice_str_cmp grpc_slice_str_cmp + +#define gpr_slice_buffer grpc_slice_buffer +#define gpr_slice_buffer_init grpc_slice_buffer_init +#define gpr_slice_buffer_destroy grpc_slice_buffer_destroy +#define gpr_slice_buffer_add grpc_slice_buffer_add +#define gpr_slice_buffer_add_indexed grpc_slice_buffer_add_indexed +#define gpr_slice_buffer_addn grpc_slice_buffer_addn +#define gpr_slice_buffer_tiny_add grpc_slice_buffer_tiny_add +#define gpr_slice_buffer_pop grpc_slice_buffer_pop +#define gpr_slice_buffer_reset_and_unref grpc_slice_buffer_reset_and_unref +#define gpr_slice_buffer_swap grpc_slice_buffer_swap +#define gpr_slice_buffer_move_into grpc_slice_buffer_move_into +#define gpr_slice_buffer_trim_end grpc_slice_buffer_trim_end +#define gpr_slice_buffer_move_first grpc_slice_buffer_move_first +#define gpr_slice_buffer_take_first grpc_slice_buffer_take_first + +#endif /* GRPC_ALLOW_GPR_SLICE_FUNCTIONS */ + +#endif /* GRPC_IMPL_CODEGEN_GPR_SLICE_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/gpr_types.h b/third_party/grpc/include/grpc/impl/codegen/gpr_types.h new file mode 100644 index 00000000000000..d7bb54527ece42 --- /dev/null +++ b/third_party/grpc/include/grpc/impl/codegen/gpr_types.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_IMPL_CODEGEN_GPR_TYPES_H +#define GRPC_IMPL_CODEGEN_GPR_TYPES_H + +#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; + +/** Analogous to struct timespec. On some machines, absolute times may be in + * local time. */ +typedef struct gpr_timespec { + int64_t tv_sec; + int32_t 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; + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_IMPL_CODEGEN_GPR_TYPES_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/grpc_types.h b/third_party/grpc/include/grpc/impl/codegen/grpc_types.h index ea1e96cf1d5b1e..a9fb27946e037e 100644 --- a/third_party/grpc/include/grpc/impl/codegen/grpc_types.h +++ b/third_party/grpc/include/grpc/impl/codegen/grpc_types.h @@ -1,40 +1,29 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_GRPC_TYPES_H #define GRPC_IMPL_CODEGEN_GRPC_TYPES_H -#include +#include + +#include +#include +#include #include #include @@ -43,8 +32,27 @@ extern "C" { #endif -/** Completion Queues enable notification of the completion of asynchronous - actions. */ +typedef enum { + GRPC_BB_RAW + /** Future types may include GRPC_BB_PROTOBUF, etc. */ +} grpc_byte_buffer_type; + +typedef struct grpc_byte_buffer { + void* reserved; + grpc_byte_buffer_type type; + union grpc_byte_buffer_data { + struct /* internal */ { + void* reserved[8]; + } reserved; + struct grpc_compressed_buffer { + grpc_compression_algorithm compression; + grpc_slice_buffer slice_buffer; + } raw; + } data; +} grpc_byte_buffer; + +/** Completion Queues enable notification of the completion of + * asynchronous actions. */ typedef struct grpc_completion_queue grpc_completion_queue; /** An alarm associated with a completion queue. */ @@ -61,6 +69,12 @@ typedef struct grpc_server grpc_server; can have messages written to it and read from it. */ typedef struct grpc_call grpc_call; +/** The Socket Mutator interface allows changes on socket options */ +typedef struct grpc_socket_mutator grpc_socket_mutator; + +/** The Socket Factory interface creates and binds sockets */ +typedef struct grpc_socket_factory grpc_socket_factory; + /** Type specifier for grpc_arg */ typedef enum { GRPC_ARG_STRING, @@ -68,6 +82,12 @@ typedef enum { GRPC_ARG_POINTER } grpc_arg_type; +typedef struct grpc_arg_pointer_vtable { + void* (*copy)(void* p); + void (*destroy)(void* p); + int (*cmp)(void* p, void* q); +} grpc_arg_pointer_vtable; + /** A single argument... each argument has a key and a value A note on naming keys: @@ -82,14 +102,13 @@ typedef enum { their keys so that it's possible to change them in the future. */ typedef struct { grpc_arg_type type; - char *key; - union { - char *string; + char* key; + union grpc_arg_value { + char* string; int integer; - struct { - void *p; - void *(*copy)(void *p); - void (*destroy)(void *p); + struct grpc_arg_pointer { + void* p; + const grpc_arg_pointer_vtable* vtable; } pointer; } value; } grpc_arg; @@ -99,52 +118,244 @@ typedef struct { 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). */ + by arbitrary parties. All evaluation is performed at channel creation + time (i.e. the keys and values in this structure need only live through the + creation invocation). + + However, if one of the args has grpc_arg_type==GRPC_ARG_POINTER, then the + grpc_arg_pointer_vtable must live until the channel args are done being + used by core (i.e. when the object for use with which they were passed + is destroyed). + + See the description of the \ref grpc_arg_keys "available args" for more + details. */ typedef struct { size_t num_args; - grpc_arg *args; + grpc_arg* args; } grpc_channel_args; -/* Channel argument keys: */ -/** Enable census for tracing and stats collection */ +/** \defgroup grpc_arg_keys + * Channel argument keys. + * \{ + */ +/** If non-zero, enable census for tracing and stats collection. */ #define GRPC_ARG_ENABLE_CENSUS "grpc.census" +/** If non-zero, enable load reporting. */ +#define GRPC_ARG_ENABLE_LOAD_REPORTING "grpc.loadreporting" +/** Request that optional features default to off (regardless of what they + usually default to) - to enable tight control over what gets enabled */ +#define GRPC_ARG_MINIMAL_STACK "grpc.minimal_stack" /** Maximum number of concurrent incoming streams to allow on a http2 - connection */ + connection. Int valued. */ #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 */ +/** Maximum message length that the channel can receive. Int valued, bytes. + -1 means unlimited. */ +#define GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH "grpc.max_receive_message_length" +/** \deprecated For backward compatibility. + * Use GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH instead. */ +#define GRPC_ARG_MAX_MESSAGE_LENGTH GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH +/** Maximum message length that the channel can send. Int valued, bytes. + -1 means unlimited. */ +#define GRPC_ARG_MAX_SEND_MESSAGE_LENGTH "grpc.max_send_message_length" +/** Maximum time that a channel may have no outstanding rpcs. Int valued, + milliseconds. INT_MAX means unlimited. */ +#define GRPC_ARG_MAX_CONNECTION_IDLE_MS "grpc.max_connection_idle_ms" +/** Maximum time that a channel may exist. Int valued, milliseconds. + * INT_MAX means unlimited. */ +#define GRPC_ARG_MAX_CONNECTION_AGE_MS "grpc.max_connection_age_ms" +/** Grace period after the chennel reaches its max age. Int valued, + milliseconds. INT_MAX means unlimited. */ +#define GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS "grpc.max_connection_age_grace_ms" +/** Enable/disable support for per-message compression. Defaults to 1, unless + GRPC_ARG_MINIMAL_STACK is enabled, in which case it defaults to 0. */ +#define GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION "grpc.per_message_compression" +/** Enable/disable support for deadline checking. Defaults to 1, unless + GRPC_ARG_MINIMAL_STACK is enabled, in which case it defaults to 0 */ +#define GRPC_ARG_ENABLE_DEADLINE_CHECKS "grpc.enable_deadline_checking" +/** Initial stream ID for http2 transports. Int valued. */ #define GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER \ "grpc.http2.initial_sequence_number" /** Amount to read ahead on individual streams. Defaults to 64kb, larger values can help throughput on high-latency connections. NOTE: at some point we'd like to auto-tune this, and this parameter - will become a no-op. */ + will become a no-op. Int valued, bytes. */ #define GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES "grpc.http2.lookahead_bytes" -/** How much memory to use for hpack decoding */ +/** How much memory to use for hpack decoding. Int valued, bytes. */ #define GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER \ "grpc.http2.hpack_table_size.decoder" -/** How much memory to use for hpack encoding */ +/** How much memory to use for hpack encoding. Int valued, bytes. */ #define GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER \ "grpc.http2.hpack_table_size.encoder" -/** Default authority to pass if none specified on call construction */ +/** How big a frame are we willing to receive via HTTP2. + Min 16384, max 16777215. Larger values give lower CPU usage for large + messages, but more head of line blocking for small messages. */ +#define GRPC_ARG_HTTP2_MAX_FRAME_SIZE "grpc.http2.max_frame_size" +/** Should BDP probing be performed? */ +#define GRPC_ARG_HTTP2_BDP_PROBE "grpc.http2.bdp_probe" +/** Minimum time between sending successive ping frames without receiving any + data frame, Int valued, milliseconds. */ +#define GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS \ + "grpc.http2.min_time_between_pings_ms" +/** Minimum allowed time between a server receiving successive ping frames + without sending any data frame. Int valued, milliseconds */ +#define GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS \ + "grpc.http2.min_ping_interval_without_data_ms" +/** Channel arg to override the http2 :scheme header */ +#define GRPC_ARG_HTTP2_SCHEME "grpc.http2_scheme" +/** How many pings can we send before needing to send a data frame or header + frame? (0 indicates that an infinite number of pings can be sent without + sending a data frame or header frame) */ +#define GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA \ + "grpc.http2.max_pings_without_data" +/** How many misbehaving pings the server can bear before sending goaway and + closing the transport? (0 indicates that the server can bear an infinite + number of misbehaving pings) */ +#define GRPC_ARG_HTTP2_MAX_PING_STRIKES "grpc.http2.max_ping_strikes" +/** How much data are we willing to queue up per stream if + GRPC_WRITE_BUFFER_HINT is set? This is an upper bound */ +#define GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE "grpc.http2.write_buffer_size" +/** Should we allow receipt of true-binary data on http2 connections? + Defaults to on (1) */ +#define GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY "grpc.http2.true_binary" +/** After a duration of this time the client/server pings its peer to see if the + transport is still alive. Int valued, milliseconds. */ +#define GRPC_ARG_KEEPALIVE_TIME_MS "grpc.keepalive_time_ms" +/** After waiting for a duration of this time, if the keepalive ping sender does + not receive the ping ack, it will close the transport. Int valued, + milliseconds. */ +#define GRPC_ARG_KEEPALIVE_TIMEOUT_MS "grpc.keepalive_timeout_ms" +/** Is it permissible to send keepalive pings without any outstanding streams. + Int valued, 0(false)/1(true). */ +#define GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS \ + "grpc.keepalive_permit_without_calls" +/** Default authority to pass if none specified on call construction. A string. + * */ #define GRPC_ARG_DEFAULT_AUTHORITY "grpc.default_authority" /** Primary user agent: goes at the start of the user-agent metadata - sent on each request */ + sent on each request. A string. */ #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 */ + sent on each request. A string. */ #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. */ +/** The minimum time between subsequent connection attempts, in ms */ +#define GRPC_ARG_MIN_RECONNECT_BACKOFF_MS "grpc.min_reconnect_backoff_ms" +/** The maximum time between subsequent connection attempts, in ms */ +#define GRPC_ARG_MAX_RECONNECT_BACKOFF_MS "grpc.max_reconnect_backoff_ms" +/** The time between the first and second connection attempts, in ms */ +#define GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS \ + "grpc.initial_reconnect_backoff_ms" +/** Minimum amount of time between DNS resolutions, in ms */ +#define GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS \ + "grpc.dns_min_time_between_resolutions_ms" +/** The timeout used on servers for finishing handshaking on an incoming + connection. Defaults to 120 seconds. */ +#define GRPC_ARG_SERVER_HANDSHAKE_TIMEOUT_MS "grpc.server_handshake_timeout_ms" +/** This *should* be used for testing only. + 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 \a GRPC_ARG_STRING. 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" +/** If non-zero, a pointer to a session cache (a pointer of type + grpc_ssl_session_cache*). (use grpc_ssl_session_cache_arg_vtable() to fetch + an appropriate pointer arg vtable) */ +#define GRPC_SSL_SESSION_CACHE_ARG "grpc.ssl_session_cache" +/** Maximum metadata size, in bytes. Note this limit applies to the max sum of + all metadata key-value entries in a batch of headers. */ +#define GRPC_ARG_MAX_METADATA_SIZE "grpc.max_metadata_size" +/** If non-zero, allow the use of SO_REUSEPORT if it's available (default 1) */ +#define GRPC_ARG_ALLOW_REUSEPORT "grpc.so_reuseport" +/** If non-zero, a pointer to a buffer pool (a pointer of type + * grpc_resource_quota*). (use grpc_resource_quota_arg_vtable() to fetch an + * appropriate pointer arg vtable) */ +#define GRPC_ARG_RESOURCE_QUOTA "grpc.resource_quota" +/** If non-zero, expand wildcard addresses to a list of local addresses. */ +#define GRPC_ARG_EXPAND_WILDCARD_ADDRS "grpc.expand_wildcard_addrs" +/** Service config data in JSON form. + This value will be ignored if the name resolver returns a service config. */ +#define GRPC_ARG_SERVICE_CONFIG "grpc.service_config" +/** Disable looking up the service config via the name resolver. */ +#define GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION \ + "grpc.service_config_disable_resolution" +/** LB policy name. */ +#define GRPC_ARG_LB_POLICY_NAME "grpc.lb_policy_name" +/** The grpc_socket_mutator instance that set the socket options. A pointer. */ +#define GRPC_ARG_SOCKET_MUTATOR "grpc.socket_mutator" +/** The grpc_socket_factory instance to create and bind sockets. A pointer. */ +#define GRPC_ARG_SOCKET_FACTORY "grpc.socket_factory" +/** The maximum amount of memory used by trace events per channel trace node. + * Once the maximum is reached, subsequent events will evict the oldest events + * from the buffer. The unit for this knob is bytes. Setting it to zero causes + * channel tracing to be disabled. */ +#define GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE \ + "grpc.max_channel_trace_event_memory_per_node" +/** If non-zero, gRPC library will track stats and information at at per channel + * level. Disabling channelz naturally disables channel tracing. The default + * is for channelz to be enabled. */ +#define GRPC_ARG_ENABLE_CHANNELZ "grpc.enable_channelz" +/** If non-zero, Cronet transport will coalesce packets to fewer frames + * when possible. */ +#define GRPC_ARG_USE_CRONET_PACKET_COALESCING \ + "grpc.use_cronet_packet_coalescing" +/** Channel arg (integer) setting how large a slice to try and read from the + wire each time recvmsg (or equivalent) is called **/ +#define GRPC_ARG_TCP_READ_CHUNK_SIZE "grpc.experimental.tcp_read_chunk_size" +/** Note this is not a "channel arg" key. This is the default slice size to use + * when trying to read from the wire if the GRPC_ARG_TCP_READ_CHUNK_SIZE + * channel arg is unspecified. */ +#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 +#define GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE \ + "grpc.experimental.tcp_min_read_chunk_size" +#define GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE \ + "grpc.experimental.tcp_max_read_chunk_size" +/* Timeout in milliseconds to use for calls to the grpclb load balancer. + If 0 or unset, the balancer calls will have no deadline. */ +#define GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS "grpc.grpclb_call_timeout_ms" +/* Timeout in milliseconds to wait for the serverlist from the grpclb load + balancer before using fallback backend addresses from the resolver. + If 0, fallback will never be used. Default value is 10000. */ +#define GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS "grpc.grpclb_fallback_timeout_ms" +/** If non-zero, grpc server's cronet compression workaround will be enabled */ +#define GRPC_ARG_WORKAROUND_CRONET_COMPRESSION \ + "grpc.workaround.cronet_compression" +/** String defining the optimization target for a channel. + Can be: "latency" - attempt to minimize latency at the cost of throughput + "blend" - try to balance latency and throughput + "throughput" - attempt to maximize throughput at the expense of + latency + Defaults to "blend". In the current implementation "blend" is equivalent to + "latency". */ +#define GRPC_ARG_OPTIMIZATION_TARGET "grpc.optimization_target" +/** If set to zero, disables retry behavior. Otherwise, transparent retries + are enabled for all RPCs, and configurable retries are enabled when they + are configured via the service config. For details, see: + https://github.com/grpc/proposal/blob/master/A6-client-retries.md + */ +#define GRPC_ARG_ENABLE_RETRIES "grpc.enable_retries" +/** Per-RPC retry buffer size, in bytes. Default is 256 KiB. */ +#define GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE "grpc.per_rpc_retry_buffer_size" +/** Channel arg that carries the bridged objective c object for custom metrics + * logging filter. */ +#define GRPC_ARG_MOBILE_LOG_CONTEXT "grpc.mobile_log_context" +/** If non-zero, client authority filter is disabled for the channel */ +#define GRPC_ARG_DISABLE_CLIENT_AUTHORITY_FILTER \ + "grpc.disable_client_authority_filter" +/** If set to zero, disables use of http proxies. Enabled by default. */ +#define GRPC_ARG_ENABLE_HTTP_PROXY "grpc.enable_http_proxy" +/** If set to non zero, surfaces the user agent string to the server. User + agent is surfaced by default. */ +#define GRPC_ARG_SURFACE_USER_AGENT "grpc.surface_user_agent" +/** If set, inhibits health checking (which may be enabled via the + * service config.) */ +#define GRPC_ARG_INHIBIT_HEALTH_CHECKING "grpc.inhibit_health_checking" +/** If set, determines the number of milliseconds that the c-ares based + * DNS resolver will wait on queries before cancelling them. The default value + * is 10000. Setting this to "0" will disable c-ares query timeouts + * entirely. */ +#define GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS "grpc.dns_ares_query_timeout" +/** \} */ /** 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. @@ -176,14 +387,23 @@ typedef enum grpc_call_error { 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 */ + /** 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_BATCH_TOO_BIG, + /** payload type requested is not the type registered */ + GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH, + /** completion queue has been shutdown */ + GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN } grpc_call_error; -/* Write Flags: */ +/** Default send/receive message size limits in bytes. -1 for unlimited. */ +/** TODO(roth) Make this match the default receive limit after next release */ +#define GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH -1 +#define GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH (4 * 1024 * 1024) + +/** 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. */ @@ -191,21 +411,47 @@ typedef enum grpc_call_error { /** Force compression to be disabled for a particular write (start_write/add_metadata). Illegal on invoke/accept. */ #define GRPC_WRITE_NO_COMPRESS (0x00000002u) +/** Force this message to be written to the socket before completing it */ +#define GRPC_WRITE_THROUGH (0x00000004u) /** Mask of all valid flags. */ -#define GRPC_WRITE_USED_MASK (GRPC_WRITE_BUFFER_HINT | GRPC_WRITE_NO_COMPRESS) +#define GRPC_WRITE_USED_MASK \ + (GRPC_WRITE_BUFFER_HINT | GRPC_WRITE_NO_COMPRESS | GRPC_WRITE_THROUGH) + +/** Initial metadata flags */ +/** Signal that the call is idempotent */ +#define GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST (0x00000010u) +/** Signal that the call should not return UNAVAILABLE before it has started */ +#define GRPC_INITIAL_METADATA_WAIT_FOR_READY (0x00000020u) +/** Signal that the call is cacheable. GRPC is free to use GET verb */ +#define GRPC_INITIAL_METADATA_CACHEABLE_REQUEST (0x00000040u) +/** Signal that GRPC_INITIAL_METADATA_WAIT_FOR_READY was explicitly set + by the calling application. */ +#define GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET (0x00000080u) +/** Signal that the initial metadata should be corked */ +#define GRPC_INITIAL_METADATA_CORKED (0x00000100u) + +/** Mask of all valid flags */ +#define GRPC_INITIAL_METADATA_USED_MASK \ + (GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST | \ + GRPC_INITIAL_METADATA_WAIT_FOR_READY | \ + GRPC_INITIAL_METADATA_CACHEABLE_REQUEST | \ + GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET | \ + GRPC_INITIAL_METADATA_CORKED | GRPC_WRITE_THROUGH) /** A single metadata element */ typedef struct grpc_metadata { - const char *key; - const char *value; - size_t value_length; + /** the key, value values are expected to line up with grpc_mdelem: if + changing them, update metadata.h at the same time. */ + grpc_slice key; + grpc_slice value; + uint32_t 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]; + struct /* internal */ { + void* obfuscated[4]; } internal_data; } grpc_metadata; @@ -225,27 +471,29 @@ typedef enum grpc_completion_type { 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. */ + /** If the grpc_completion_type is GRPC_OP_COMPLETE, this field indicates + whether the operation was successful or not; 0 in case of failure and + non-zero in case of success. + If grpc_completion_type is GRPC_QUEUE_SHUTDOWN or GRPC_QUEUE_TIMEOUT, this + field is guaranteed to be 0 */ int success; /** The tag passed to grpc_call_start_batch etc to start this operation. Only GRPC_OP_COMPLETE has a tag. */ - void *tag; + void* tag; } grpc_event; typedef struct { size_t count; size_t capacity; - grpc_metadata *metadata; + grpc_metadata* metadata; } grpc_metadata_array; typedef struct { - char *method; - size_t method_capacity; - char *host; - size_t host_capacity; + grpc_slice method; + grpc_slice host; gpr_timespec deadline; - void *reserved; + uint32_t flags; + void* reserved; } grpc_call_details; typedef enum { @@ -260,15 +508,13 @@ typedef enum { 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. */ + 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. */ + 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. @@ -286,11 +532,15 @@ typedef enum { 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. */ + server. This op completes after the close has been received by the + server. This operation always succeeds, meaning ops paired with + this operation will also appear to succeed, even though they may not + have. */ GRPC_OP_RECV_CLOSE_ON_SERVER } grpc_op_type; +struct grpc_byte_buffer; + /** Operation data: one field for each op type (except SEND_CLOSE_FROM_CLIENT which has no arguments) */ typedef struct grpc_op { @@ -299,73 +549,166 @@ typedef struct grpc_op { /** Write flags bitset for grpc_begin_messages */ uint32_t flags; /** Reserved for future usage */ - void *reserved; - union { + void* reserved; + union grpc_op_data { /** Reserved for future usage */ - struct { - void *reserved[8]; + struct /* internal */ { + void* reserved[8]; } reserved; - struct { + struct grpc_op_send_initial_metadata { size_t count; - grpc_metadata *metadata; + grpc_metadata* metadata; + /** If \a is_set, \a compression_level will be used for the call. + * Otherwise, \a compression_level won't be considered */ + struct grpc_op_send_initial_metadata_maybe_compression_level { + uint8_t is_set; + grpc_compression_level level; + } maybe_compression_level; } send_initial_metadata; - grpc_byte_buffer *send_message; - struct { + struct grpc_op_send_message { + /** This op takes ownership of the slices in send_message. After + * a call completes, the contents of send_message are not guaranteed + * and likely empty. The original owner should still call + * grpc_byte_buffer_destroy() on this object however. + */ + struct grpc_byte_buffer* send_message; + } send_message; + struct grpc_op_send_status_from_server { size_t trailing_metadata_count; - grpc_metadata *trailing_metadata; + grpc_metadata* trailing_metadata; grpc_status_code status; - const char *status_details; + /** optional: set to NULL if no details need sending, non-NULL if they do + * pointer will not be retained past the start_batch call + */ + grpc_slice* 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; + struct grpc_op_recv_initial_metadata { + grpc_metadata_array* recv_initial_metadata; + } 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. + The returned byte buffer will be NULL if trailing metadata was + received instead of a message. */ - grpc_byte_buffer **recv_message; - struct { + struct grpc_op_recv_message { + struct grpc_byte_buffer** recv_message; + } recv_message; + struct grpc_op_recv_status_on_client { /** 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; + this value, or reuse it in a future op. */ + grpc_metadata_array* trailing_metadata; + grpc_status_code* status; + grpc_slice* status_details; + /** If this is not nullptr, it will be populated with the full fidelity + * error string for debugging purposes. The application is responsible + * for freeing the data by using gpr_free(). */ + const char** error_string; } recv_status_on_client; - struct { + struct grpc_op_recv_close_on_server { /** 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; + int* cancelled; } recv_close_on_server; } data; } grpc_op; +/** Information requested from the channel. */ +typedef struct { + /** If non-NULL, will be set to point to a string indicating the LB + * policy name. Caller takes ownership. */ + char** lb_policy_name; + /** If non-NULL, will be set to point to a string containing the + * service config used by the channel in JSON form. */ + char** service_config_json; +} grpc_channel_info; + +typedef struct grpc_resource_quota grpc_resource_quota; + +/** Completion queues internally MAY maintain a set of file descriptors in a + structure called 'pollset'. This enum specifies if a completion queue has an + associated pollset and any restrictions on the type of file descriptors that + can be present in the pollset. + + I/O progress can only be made when grpc_completion_queue_next() or + grpc_completion_queue_pluck() are called on the completion queue (unless the + grpc_cq_polling_type is GRPC_CQ_NON_POLLING) and hence it is very important + to actively call these APIs */ +typedef enum { + /** The completion queue will have an associated pollset and there is no + restriction on the type of file descriptors the pollset may contain */ + GRPC_CQ_DEFAULT_POLLING, + + /** Similar to GRPC_CQ_DEFAULT_POLLING except that the completion queues will + not contain any 'listening file descriptors' (i.e file descriptors used to + listen to incoming channels) */ + GRPC_CQ_NON_LISTENING, + + /** The completion queue will not have an associated pollset. Note that + grpc_completion_queue_next() or grpc_completion_queue_pluck() MUST still + be called to pop events from the completion queue; it is not required to + call them actively to make I/O progress */ + GRPC_CQ_NON_POLLING +} grpc_cq_polling_type; + +/** Specifies the type of APIs to use to pop events from the completion queue */ +typedef enum { + /** Events are popped out by calling grpc_completion_queue_next() API ONLY */ + GRPC_CQ_NEXT, + + /** Events are popped out by calling grpc_completion_queue_pluck() API ONLY*/ + GRPC_CQ_PLUCK, + + /** EXPERIMENTAL: Events trigger a callback specified as the tag */ + GRPC_CQ_CALLBACK +} grpc_cq_completion_type; + +/** EXPERIMENTAL: Specifies an interface class to be used as a tag + for callback-based completion queues. This can be used directly, + as the first element of a struct in C, or as a base class in C++. + Its "run" value should be assigned to some non-member function, such as + a static method. */ +typedef struct grpc_experimental_completion_queue_functor { + /** The run member specifies a function that will be called when this + tag is extracted from the completion queue. Its arguments will be a + pointer to this functor and a boolean that indicates whether the + operation succeeded (non-zero) or failed (zero) */ + void (*functor_run)(struct grpc_experimental_completion_queue_functor*, int); +} grpc_experimental_completion_queue_functor; + +/* The upgrade to version 2 is currently experimental. */ + +#define GRPC_CQ_CURRENT_VERSION 2 +#define GRPC_CQ_VERSION_MINIMUM_FOR_CALLBACKABLE 2 +typedef struct grpc_completion_queue_attributes { + /** The version number of this structure. More fields might be added to this + structure in future. */ + int version; /** Set to GRPC_CQ_CURRENT_VERSION */ + + grpc_cq_completion_type cq_completion_type; + + grpc_cq_polling_type cq_polling_type; + + /* END OF VERSION 1 CQ ATTRIBUTES */ + + /* EXPERIMENTAL: START OF VERSION 2 CQ ATTRIBUTES */ + /** When creating a callbackable CQ, pass in a functor to get invoked when + * shutdown is complete */ + grpc_experimental_completion_queue_functor* cq_shutdown_cb; + + /* END OF VERSION 2 CQ ATTRIBUTES */ +} grpc_completion_queue_attributes; + +/** The completion queue factory structure is opaque to the callers of grpc */ +typedef struct grpc_completion_queue_factory grpc_completion_queue_factory; + #ifdef __cplusplus } #endif diff --git a/third_party/grpc/include/grpc/impl/codegen/log.h b/third_party/grpc/include/grpc/impl/codegen/log.h index 5df91c65098574..ad7f0241582bc8 100644 --- a/third_party/grpc/include/grpc/impl/codegen/log.h +++ b/third_party/grpc/include/grpc/impl/codegen/log.h @@ -1,49 +1,34 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_LOG_H #define GRPC_IMPL_CODEGEN_LOG_H -#include /* for abort() */ -#include - #include +#include +#include /* for abort() */ + #ifdef __cplusplus extern "C" { #endif -/* GPR log API. +/** GPR log API. Usage (within grpc): @@ -53,7 +38,7 @@ extern "C" { 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 +/** 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, @@ -61,48 +46,65 @@ typedef enum gpr_log_severity { 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); +#define GPR_LOG_VERBOSITY_UNSET -1 + +/** Returns a string representation of the log severity */ +GPRAPI const char* gpr_log_severity_string(gpr_log_severity severity); -/* Macros to build log contexts at various severity levels */ +/** 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 +/** Log a message. It's advised to use GPR_xxx above to generate the context * for each message */ -GPR_API void gpr_log(const char *file, int line, gpr_log_severity severity, - const char *format, ...); +GPRAPI void gpr_log(const char* file, int line, gpr_log_severity severity, + const char* format, ...) GPR_PRINT_FORMAT_CHECK(4, 5); -GPR_API void gpr_log_message(const char *file, int line, - gpr_log_severity severity, const char *message); +GPRAPI int gpr_should_log(gpr_log_severity severity); -/* Log overrides: applications can use this API to intercept logging calls +GPRAPI void gpr_log_message(const char* file, int line, + gpr_log_severity severity, const char* message); + +/** Set global log verbosity */ +GPRAPI void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print); + +GPRAPI void gpr_log_verbosity_init(void); + +/** Log overrides: applications can use this API to intercept logging calls and use their own implementations */ -typedef struct { - const char *file; +struct gpr_log_func_args { + const char* file; int line; gpr_log_severity severity; - const char *message; -} gpr_log_func_args; + const char* message; +}; -typedef void (*gpr_log_func)(gpr_log_func_args *args); -GPR_API void gpr_set_log_function(gpr_log_func func); +typedef struct gpr_log_func_args gpr_log_func_args; -/* abort() the process if x is zero, having written a line to the log. +typedef void (*gpr_log_func)(gpr_log_func_args* args); +GPRAPI 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)) { \ + if (GPR_UNLIKELY(!(x))) { \ gpr_log(GPR_ERROR, "assertion failed: %s", #x); \ abort(); \ } \ } while (0) +#ifndef NDEBUG +#define GPR_DEBUG_ASSERT(x) GPR_ASSERT(x) +#else +#define GPR_DEBUG_ASSERT(x) +#endif + #ifdef __cplusplus } #endif diff --git a/third_party/grpc/include/grpc/impl/codegen/port_platform.h b/third_party/grpc/include/grpc/impl/codegen/port_platform.h index d22bffb0b8792f..031c0c36aeff15 100644 --- a/third_party/grpc/include/grpc/impl/codegen/port_platform.h +++ b/third_party/grpc/include/grpc/impl/codegen/port_platform.h @@ -1,39 +1,32 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_PORT_PLATFORM_H #define GRPC_IMPL_CODEGEN_PORT_PLATFORM_H +/* + * Define GPR_BACKWARDS_COMPATIBILITY_MODE to try harder to be ABI + * compatible with older platforms (currently only on Linux) + * Causes: + * - some libc calls to be gotten via dlsym + * - some syscalls to be made directly + */ + /* Get windows.h included everywhere (we need it) */ #if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32) #ifndef WIN32_LEAN_AND_MEAN @@ -74,97 +67,81 @@ things. */ #if !defined(GPR_NO_AUTODETECT_PLATFORM) - -#if defined(__MSYS__) -#define GPR_PLATFORM_STRING "msys2" -#define GPR_POSIX_CRASH_HANDLER 1 -#ifndef _BSD_SOURCE -#define _BSD_SOURCE +#if defined(_WIN64) || defined(WIN64) || defined(_WIN32) || defined(WIN32) +#if defined(_WIN64) || defined(WIN64) +#define GPR_ARCH_64 1 +#else +#define GPR_ARCH_32 1 #endif -#ifndef _DEFAULT_SOURCE -#define _DEFAULT_SOURCE +#define GPR_PLATFORM_STRING "windows" +#define GPR_WINDOWS 1 +#define GPR_WINDOWS_SUBPROCESS 1 +#define GPR_WINDOWS_ENV +#ifdef __MSYS__ +#define GPR_GETPID_IN_UNISTD_H 1 +#define GPR_MSYS_TMPFILE +#define GPR_POSIX_LOG +#define GPR_POSIX_STRING +#define GPR_POSIX_TIME +#else +#define GPR_GETPID_IN_PROCESS_H 1 +#define GPR_WINDOWS_TMPFILE +#define GPR_WINDOWS_LOG +#define GPR_WINDOWS_CRASH_HANDLER 1 +#define GPR_WINDOWS_STRING +#define GPR_WINDOWS_TIME #endif -#ifndef _GNU_SOURCE -#define _GNU_SOURCE +#ifdef __GNUC__ +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#else +#define GPR_WINDOWS_ATOMIC 1 +#define GPR_MSVC_TLS 1 #endif -#include +#elif defined(GPR_MANYLINUX1) +// TODO(atash): manylinux1 is just another __linux__ but with ancient +// libraries; it should be integrated with the `__linux__` definitions below. +#define GPR_PLATFORM_STRING "manylinux" +#define GPR_POSIX_CRASH_HANDLER 1 #define GPR_CPU_POSIX 1 #define GPR_GCC_ATOMIC 1 #define GPR_GCC_TLS 1 -#define GPR_POSIX_MULTIPOLL_WITH_POLL -#define GPR_POSIX_WAKEUP_FD 1 -#define GPR_POSIX_SOCKET 1 -#define GPR_POSIX_SOCKETADDR 1 -#define GPR_POSIX_ENV 1 -#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1 -#define GPR_POSIX_SOCKETUTILS -#define GPR_POSIX_FILE 1 +#define GPR_LINUX 1 +#define GPR_LINUX_LOG 1 +#define GPR_SUPPORT_CHANNELS_FROM_FD 1 +#define GPR_LINUX_ENV 1 +#define GPR_POSIX_TMPFILE 1 #define GPR_POSIX_STRING 1 #define GPR_POSIX_SUBPROCESS 1 #define GPR_POSIX_SYNC 1 #define GPR_POSIX_TIME 1 -#define GPR_POSIX_LOG 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(_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 -#define GPR_WINDOWS_SUBPROCESS 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 -#define GPR_WINDOWS_SUBPROCESS 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 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ #define GPR_ARCH_32 1 -#define GPR_CPU_LINUX 1 +#endif /* _LP64 */ +#define GPR_CPU_POSIX 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_TMPFILE 1 +#define GPR_ANDROID_LOG 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 +#define GPR_SUPPORT_CHANNELS_FROM_FD 1 #elif defined(__linux__) -#define GPR_POSIX_CRASH_HANDLER 1 #define GPR_PLATFORM_STRING "linux" #ifndef _BSD_SOURCE #define _BSD_SOURCE @@ -180,70 +157,81 @@ #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 -#endif +#define GPR_LINUX_LOG +#define GPR_SUPPORT_CHANNELS_FROM_FD 1 #define GPR_LINUX_ENV 1 -#ifndef GPR_LINUX_EVENTFD -#define GPR_POSIX_NO_SPECIAL_WAKEUP_FD 1 -#endif -#ifndef GPR_LINUX_SOCKETUTILS -#define GPR_POSIX_SOCKETUTILS -#endif -#define GPR_POSIX_FILE 1 +#define GPR_POSIX_TMPFILE 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 */ +#ifdef __GLIBC__ +#define GPR_POSIX_CRASH_HANDLER 1 +#define GPR_LINUX_PTHREAD_NAME 1 +#include +#else /* musl libc */ +#define GPR_MUSL_LIBC_COMPAT 1 +#endif #elif defined(__APPLE__) +#include #include #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif -#define GPR_MSG_IOVLEN_TYPE int #if TARGET_OS_IPHONE -#define GPR_FORBID_UNREACHABLE_CODE 1 #define GPR_PLATFORM_STRING "ios" #define GPR_CPU_IPHONE 1 #define GPR_PTHREAD_TLS 1 #else /* TARGET_OS_IPHONE */ #define GPR_PLATFORM_STRING "osx" +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED +#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7 +#define GPR_CPU_IPHONE 1 +#define GPR_PTHREAD_TLS 1 +#else /* __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_7 */ +#define GPR_CPU_POSIX 1 +/* TODO(vjpai): there is a reported issue in bazel build for Mac where __thread + in a header is currently not working (bazelbuild/bazel#4341). Remove + the following conditional and use GPR_GCC_TLS when that is fixed */ +#ifndef GRPC_BAZEL_BUILD +#define GPR_GCC_TLS 1 +#else /* GRPC_BAZEL_BUILD */ +#define GPR_PTHREAD_TLS 1 +#endif /* GRPC_BAZEL_BUILD */ +#define GPR_APPLE_PTHREAD_NAME 1 +#endif +#else /* __MAC_OS_X_VERSION_MIN_REQUIRED */ #define GPR_CPU_POSIX 1 +/* TODO(vjpai): Remove the following conditional and use only GPR_GCC_TLS + when bazelbuild/bazel#4341 is fixed */ +#ifndef GRPC_BAZEL_BUILD #define GPR_GCC_TLS 1 +#else /* GRPC_BAZEL_BUILD */ +#define GPR_PTHREAD_TLS 1 +#endif /* GRPC_BAZEL_BUILD */ +#endif #define GPR_POSIX_CRASH_HANDLER 1 #endif +#define GPR_APPLE 1 #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_TMPFILE 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 +/* TODO(mxyan): Remove when CFStream becomes default */ +#ifndef GRPC_CFSTREAM +#define GPR_SUPPORT_CHANNELS_FROM_FD 1 +#endif #ifdef _LP64 #define GPR_ARCH_64 1 #else /* _LP64 */ @@ -254,34 +242,151 @@ #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif +#define GPR_FREEBSD 1 #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_TMPFILE 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_SUPPORT_CHANNELS_FROM_FD 1 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#elif defined(__OpenBSD__) +#define GPR_PLATFORM_STRING "openbsd" +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#define GPR_OPENBSD 1 +#define GPR_CPU_POSIX 1 +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#define GPR_POSIX_LOG 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_TMPFILE 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_SUPPORT_CHANNELS_FROM_FD 1 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#elif defined(__sun) && defined(__SVR4) +#define GPR_PLATFORM_STRING "solaris" +#define GPR_SOLARIS 1 +#define GPR_CPU_POSIX 1 +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#define GPR_POSIX_LOG 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_TMPFILE 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 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#elif defined(_AIX) +#define GPR_PLATFORM_STRING "aix" +#ifndef _ALL_SOURCE +#define _ALL_SOURCE +#endif +#define GPR_AIX 1 +#define GPR_CPU_POSIX 1 +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#define GPR_POSIX_LOG 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_TMPFILE 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 +#ifdef _LP64 +#define GPR_ARCH_64 1 +#else /* _LP64 */ +#define GPR_ARCH_32 1 +#endif /* _LP64 */ +#elif defined(__native_client__) +#define GPR_PLATFORM_STRING "nacl" +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#define GPR_NACL 1 +#define GPR_CPU_POSIX 1 +#define GPR_GCC_ATOMIC 1 +#define GPR_GCC_TLS 1 +#define GPR_POSIX_LOG 1 +#define GPR_POSIX_ENV 1 +#define GPR_POSIX_TMPFILE 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 +#error "Could not auto-detect platform" #endif #endif /* GPR_NO_AUTODETECT_PLATFORM */ +/* + * There are platforms for which TLS should not be used even though the + * compiler makes it seem like it's supported (Android NDK < r12b for example). + * This is primarily because of linker problems and toolchain misconfiguration: + * TLS isn't supported until NDK r12b per + * https://developer.android.com/ndk/downloads/revision_history.html + * TLS also does not work with Android NDK if GCC is being used as the compiler + * instead of Clang. + * Since NDK r16, `__NDK_MAJOR__` and `__NDK_MINOR__` are defined in + * . For NDK < r16, users should define these macros, + * e.g. `-D__NDK_MAJOR__=11 -D__NKD_MINOR__=0` for NDK r11. */ +#if defined(__ANDROID__) && defined(GPR_GCC_TLS) +#if __has_include() +#include +#endif /* __has_include() */ +#if (defined(__clang__) && defined(__NDK_MAJOR__) && defined(__NDK_MINOR__) && \ + ((__NDK_MAJOR__ < 12) || \ + ((__NDK_MAJOR__ == 12) && (__NDK_MINOR__ < 1)))) || \ + (defined(__GNUC__) && !defined(__clang__)) +#undef GPR_GCC_TLS +#define GPR_PTHREAD_TLS 1 +#endif +#endif /*defined(__ANDROID__) && defined(GPR_GCC_TLS) */ + +#if defined(__has_include) +#if __has_include() +#define GRPC_HAS_CXX11_ATOMIC +#endif /* __has_include() */ +#endif /* defined(__has_include) */ + #ifndef GPR_PLATFORM_STRING #warning "GPR_PLATFORM_STRING not auto-detected" #define GPR_PLATFORM_STRING "unknown" @@ -331,29 +436,19 @@ typedef unsigned __int64 uint64_t; /* Validate platform combinations */ #if defined(GPR_GCC_ATOMIC) + defined(GPR_GCC_SYNC) + \ - defined(GPR_WIN32_ATOMIC) != \ + defined(GPR_WINDOWS_ATOMIC) != \ 1 -#error Must define exactly one of GPR_GCC_ATOMIC, GPR_GCC_SYNC, GPR_WIN32_ATOMIC +#error Must define exactly one of GPR_GCC_ATOMIC, GPR_GCC_SYNC, GPR_WINDOWS_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) != \ +#if defined(GPR_CPU_LINUX) + defined(GPR_CPU_POSIX) + defined(GPR_WINDOWS) + \ + 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 +#error Must define exactly one of GPR_CPU_LINUX, GPR_CPU_POSIX, GPR_WINDOWS, GPR_CPU_IPHONE, GPR_CPU_CUSTOM #endif #if defined(GPR_MSVC_TLS) + defined(GPR_GCC_TLS) + defined(GPR_PTHREAD_TLS) + \ @@ -366,13 +461,36 @@ typedef unsigned __int64 uint64_t; power of two */ #define GPR_MAX_ALIGNMENT 16 +#ifndef GRPC_ARES +#define GRPC_ARES 1 +#endif + #ifndef GRPC_MUST_USE_RESULT -#ifdef __GNUC__ +#if defined(__GNUC__) && !defined(__MINGW32__) #define GRPC_MUST_USE_RESULT __attribute__((warn_unused_result)) +#define GPR_ALIGN_STRUCT(n) __attribute__((aligned(n))) #else #define GRPC_MUST_USE_RESULT +#define GPR_ALIGN_STRUCT(n) +#endif #endif + +#ifndef GRPC_UNUSED +#if defined(__GNUC__) && !defined(__MINGW32__) +#define GRPC_UNUSED __attribute__((unused)) +#else +#define GRPC_UNUSED +#endif +#endif + +#ifndef GPR_PRINT_FORMAT_CHECK +#ifdef __GNUC__ +#define GPR_PRINT_FORMAT_CHECK(FORMAT_STR, ARGS) \ + __attribute__((format(printf, FORMAT_STR, ARGS))) +#else +#define GPR_PRINT_FORMAT_CHECK(FORMAT_STR, ARGS) #endif +#endif /* GPR_PRINT_FORMAT_CHECK */ #if GPR_FORBID_UNREACHABLE_CODE #define GPR_UNREACHABLE_CODE(STATEMENT) @@ -385,16 +503,66 @@ typedef unsigned __int64 uint64_t; } while (0) #endif /* GPR_FORBID_UNREACHABLE_CODE */ -#ifndef GPR_API -#define GPR_API +#ifndef GPRAPI +#define GPRAPI +#endif + +#ifndef GRPCAPI +#define GRPCAPI GPRAPI #endif -#ifndef GRPC_API -#define GRPC_API GPR_API +#ifndef CENSUSAPI +#define CENSUSAPI GRPCAPI +#endif + +#ifndef GPR_ATTRIBUTE_NO_TSAN /* (1) */ +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#define GPR_ATTRIBUTE_NO_TSAN __attribute__((no_sanitize("thread"))) +#endif /* __has_feature(thread_sanitizer) */ +#endif /* defined(__has_feature) */ +#ifndef GPR_ATTRIBUTE_NO_TSAN /* (2) */ +#define GPR_ATTRIBUTE_NO_TSAN +#endif /* GPR_ATTRIBUTE_NO_TSAN (2) */ +#endif /* GPR_ATTRIBUTE_NO_TSAN (1) */ + +/* GRPC_TSAN_ENABLED will be defined, when compiled with thread sanitizer. */ +#if defined(__SANITIZE_THREAD__) +#define GRPC_TSAN_ENABLED +#elif defined(__has_feature) +#if __has_feature(thread_sanitizer) +#define GRPC_TSAN_ENABLED #endif +#endif + +/* GRPC_ALLOW_EXCEPTIONS should be 0 or 1 if exceptions are allowed or not */ +#ifndef GRPC_ALLOW_EXCEPTIONS +/* If not already set, set to 1 on Windows (style guide standard) but to + * 0 on non-Windows platforms unless the compiler defines __EXCEPTIONS */ +#ifdef GPR_WINDOWS +#define GRPC_ALLOW_EXCEPTIONS 1 +#else /* GPR_WINDOWS */ +#ifdef __EXCEPTIONS +#define GRPC_ALLOW_EXCEPTIONS 1 +#else /* __EXCEPTIONS */ +#define GRPC_ALLOW_EXCEPTIONS 0 +#endif /* __EXCEPTIONS */ +#endif /* __GPR_WINDOWS */ +#endif /* GRPC_ALLOW_EXCEPTIONS */ + +/* Use GPR_LIKELY only in cases where you are sure that a certain outcome is the + * most likely. Ideally, also collect performance numbers to justify the claim. + */ +#ifdef __GNUC__ +#define GPR_LIKELY(x) __builtin_expect((x), 1) +#define GPR_UNLIKELY(x) __builtin_expect((x), 0) +#else /* __GNUC__ */ +#define GPR_LIKELY(x) (x) +#define GPR_UNLIKELY(x) (x) +#endif /* __GNUC__ */ -#ifndef CENSUS_API -#define CENSUS_API GRPC_API +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS #endif #endif /* GRPC_IMPL_CODEGEN_PORT_PLATFORM_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/propagation_bits.h b/third_party/grpc/include/grpc/impl/codegen/propagation_bits.h index cdd699710c2378..824bdbd8c9bef9 100644 --- a/third_party/grpc/include/grpc/impl/codegen/propagation_bits.h +++ b/third_party/grpc/include/grpc/impl/codegen/propagation_bits.h @@ -1,38 +1,23 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_IMPL_CODEGEN_H -#define GRPC_IMPL_CODEGEN_H +#ifndef GRPC_IMPL_CODEGEN_PROPAGATION_BITS_H +#define GRPC_IMPL_CODEGEN_PROPAGATION_BITS_H #include @@ -40,7 +25,7 @@ extern "C" { #endif -/* Propagation bits: this can be bitwise or-ed to form propagation_mask for +/** Propagation bits: this can be bitwise or-ed to form propagation_mask for * grpc_call */ /** Propagate deadline */ #define GRPC_PROPAGATE_DEADLINE ((uint32_t)1) @@ -50,7 +35,7 @@ extern "C" { /** Propagate cancellation */ #define GRPC_PROPAGATE_CANCELLATION ((uint32_t)8) -/* Default propagation mask: clients of the core API are encouraged to encode +/** 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 @@ -64,4 +49,4 @@ extern "C" { } #endif -#endif /* GRPC_IMPL_CODEGEN_H */ +#endif /* GRPC_IMPL_CODEGEN_PROPAGATION_BITS_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/slice.h b/third_party/grpc/include/grpc/impl/codegen/slice.h index d60a40ca352bfe..90dbfd3b1f8583 100644 --- a/third_party/grpc/include/grpc/impl/codegen/slice.h +++ b/third_party/grpc/include/grpc/impl/codegen/slice.h @@ -1,48 +1,33 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_SLICE_H #define GRPC_IMPL_CODEGEN_SLICE_H -#include +#include #include -#ifdef __cplusplus -extern "C" { -#endif +#include -/* Slice API +typedef struct grpc_slice grpc_slice; + +/** 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 @@ -55,41 +40,95 @@ extern "C" { 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 +typedef struct grpc_slice_refcount_vtable { + void (*ref)(void*); + void (*unref)(void*); + int (*eq)(grpc_slice a, grpc_slice b); + uint32_t (*hash)(grpc_slice slice); +} grpc_slice_refcount_vtable; + +/** Reference count container for grpc_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(uint8_t *) - 1) - -/* A gpr_slice s, if initialized, represents the byte range + Typically client code should not touch this, and use grpc_slice_malloc, + grpc_slice_new, or grpc_slice_new_with_len instead. */ +typedef struct grpc_slice_refcount { + const grpc_slice_refcount_vtable* vtable; + /** If a subset of this slice is taken, use this pointer for the refcount. + Typically points back to the refcount itself, however iterning + implementations can use this to avoid a verification step on each hash + or equality check */ + struct grpc_slice_refcount* sub_refcount; +} grpc_slice_refcount; + +/* Inlined half of grpc_slice is allowed to expand the size of the overall type + by this many bytes */ +#define GRPC_SLICE_INLINE_EXTRA_SIZE sizeof(void*) + +#define GRPC_SLICE_INLINED_SIZE \ + (sizeof(size_t) + sizeof(uint8_t*) - 1 + GRPC_SLICE_INLINE_EXTRA_SIZE) + +/** A grpc_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. + when the ref count reaches zero (see grpc_slice_new() and grp_slice_unref()). + Multiple grpc_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 { - uint8_t *bytes; +struct grpc_slice { + struct grpc_slice_refcount* refcount; + union grpc_slice_data { + struct grpc_slice_refcounted { + uint8_t* bytes; size_t length; } refcounted; - struct { + struct grpc_slice_inlined { uint8_t length; - uint8_t bytes[GPR_SLICE_INLINED_SIZE]; + uint8_t bytes[GRPC_SLICE_INLINED_SIZE]; } inlined; } data; -} gpr_slice; +}; + +#define GRPC_SLICE_BUFFER_INLINE_ELEMENTS 8 + +/** Represents an expandable array of slices, to be interpreted as a + single item. */ +typedef struct grpc_slice_buffer { + /** This is for internal use only. External users (i.e any code outside grpc + * core) MUST NOT use this field */ + grpc_slice* base_slices; + + /** slices in the array (Points to the first valid grpc_slice in the array) */ + grpc_slice* slices; + /** the number of slices in the array */ + size_t count; + /** the number of slices allocated in the array. External users (i.e any code + * outside grpc core) MUST NOT use this field */ + size_t capacity; + /** the combined length of all slices in the array */ + size_t length; + /** inlined elements to avoid allocations */ + grpc_slice inlined[GRPC_SLICE_BUFFER_INLINE_ELEMENTS]; +} grpc_slice_buffer; + +#define GRPC_SLICE_START_PTR(slice) \ + ((slice).refcount ? (slice).data.refcounted.bytes \ + : (slice).data.inlined.bytes) +#define GRPC_SLICE_LENGTH(slice) \ + ((slice).refcount ? (slice).data.refcounted.length \ + : (slice).data.inlined.length) +#define GRPC_SLICE_SET_LENGTH(slice, newlen) \ + ((slice).refcount ? ((slice).data.refcounted.length = (size_t)(newlen)) \ + : ((slice).data.inlined.length = (uint8_t)(newlen))) +#define GRPC_SLICE_END_PTR(slice) \ + GRPC_SLICE_START_PTR(slice) + GRPC_SLICE_LENGTH(slice) +#define GRPC_SLICE_IS_EMPTY(slice) (GRPC_SLICE_LENGTH(slice) == 0) + +#ifdef GRPC_ALLOW_GPR_SLICE_FUNCTIONS +/* Duplicate GPR_* definitions */ #define GPR_SLICE_START_PTR(slice) \ ((slice).refcount ? (slice).data.refcounted.bytes \ : (slice).data.inlined.bytes) @@ -100,83 +139,9 @@ typedef struct gpr_slice { ((slice).refcount ? ((slice).data.refcounted.length = (size_t)(newlen)) \ : ((slice).data.inlined.length = (uint8_t)(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_API 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. */ -GPR_API 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_API 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_API 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_API 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_API 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_API gpr_slice gpr_slice_from_copied_buffer(const char *source, size_t len); - -/* Create a slice pointing to constant memory */ -GPR_API gpr_slice gpr_slice_from_static_string(const char *source); - -/* 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_API 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_API 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_API 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_API gpr_slice gpr_slice_split_head(gpr_slice *s, size_t split); - -GPR_API 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. */ -GPR_API int gpr_slice_cmp(gpr_slice a, gpr_slice b); -GPR_API int gpr_slice_str_cmp(gpr_slice a, const char *b); - -#ifdef __cplusplus -} -#endif + GRPC_SLICE_START_PTR(slice) + GRPC_SLICE_LENGTH(slice) +#define GPR_SLICE_IS_EMPTY(slice) (GRPC_SLICE_LENGTH(slice) == 0) + +#endif /* GRPC_ALLOW_GPR_SLICE_FUNCTIONS */ #endif /* GRPC_IMPL_CODEGEN_SLICE_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/slice_buffer.h b/third_party/grpc/include/grpc/impl/codegen/slice_buffer.h deleted file mode 100644 index 8428c2f539b240..00000000000000 --- a/third_party/grpc/include/grpc/impl/codegen/slice_buffer.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_IMPL_CODEGEN_SLICE_BUFFER_H -#define GRPC_IMPL_CODEGEN_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 */ -GPR_API void gpr_slice_buffer_init(gpr_slice_buffer *sb); -/* destroy a slice buffer - unrefs any held elements */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API size_t -gpr_slice_buffer_add_indexed(gpr_slice_buffer *sb, gpr_slice slice); -GPR_API 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_API uint8_t *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, size_t len); -/* pop the last buffer, but don't unref it */ -GPR_API void gpr_slice_buffer_pop(gpr_slice_buffer *sb); -/* clear a slice buffer, unref all elements */ -GPR_API void gpr_slice_buffer_reset_and_unref(gpr_slice_buffer *sb); -/* swap the contents of two slice buffers */ -GPR_API void gpr_slice_buffer_swap(gpr_slice_buffer *a, gpr_slice_buffer *b); -/* move all of the elements of src into dst */ -GPR_API void gpr_slice_buffer_move_into(gpr_slice_buffer *src, - gpr_slice_buffer *dst); -/* remove n bytes from the end of a slice buffer */ -GPR_API void gpr_slice_buffer_trim_end(gpr_slice_buffer *src, size_t n, - gpr_slice_buffer *garbage); -/* move the first n bytes of src into dst */ -GPR_API void gpr_slice_buffer_move_first(gpr_slice_buffer *src, size_t n, - gpr_slice_buffer *dst); -/* take the first slice in the slice buffer */ -GPR_API gpr_slice gpr_slice_buffer_take_first(gpr_slice_buffer *src); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_IMPL_CODEGEN_SLICE_BUFFER_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/status.h b/third_party/grpc/include/grpc/impl/codegen/status.h index 814bb4c92ccadc..9bc3dc95609ce8 100644 --- a/third_party/grpc/include/grpc/impl/codegen/status.h +++ b/third_party/grpc/include/grpc/impl/codegen/status.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -39,40 +24,40 @@ extern "C" { #endif typedef enum { - /* Not an error; returned on success */ + /** Not an error; returned on success */ GRPC_STATUS_OK = 0, - /* The operation was cancelled (typically by the caller). */ + /** The operation was cancelled (typically by the caller). */ GRPC_STATUS_CANCELLED = 1, - /* Unknown error. An example of where this error may be returned is + /** 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 + /** 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 + /** 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. */ + /** 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) + /** 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 + /** 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 @@ -80,15 +65,15 @@ typedef enum { instead for those errors). */ GRPC_STATUS_PERMISSION_DENIED = 7, - /* The request does not have valid authentication credentials for the + /** 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 + /** 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 + /** 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. @@ -109,14 +94,14 @@ typedef enum { read-modify-write on the same resource. */ GRPC_STATUS_FAILED_PRECONDITION = 9, - /* The operation was aborted, typically due to a concurrency issue + /** 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 + /** 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 @@ -133,26 +118,31 @@ typedef enum { they are done. */ GRPC_STATUS_OUT_OF_RANGE = 11, - /* Operation is not implemented or not supported/enabled in this service. */ + /** Operation is not implemented or not supported/enabled in this service. */ GRPC_STATUS_UNIMPLEMENTED = 12, - /* Internal errors. Means some invariants expected by underlying + /** 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 + /** The service is currently unavailable. This is a most likely a transient condition and may be corrected by retrying with a backoff. + WARNING: Although data MIGHT not have been transmitted when this + status occurs, there is NOT A GUARANTEE that the server has not seen + anything. So in general it is unsafe to retry on this status code + if the call is non-idempotent. + See litmus test above for deciding between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE. */ GRPC_STATUS_UNAVAILABLE = 14, - /* Unrecoverable data loss or corruption. */ + /** Unrecoverable data loss or corruption. */ GRPC_STATUS_DATA_LOSS = 15, - /* Force users to include a default branch: */ + /** Force users to include a default branch: */ GRPC_STATUS__DO_NOT_USE = -1 } grpc_status_code; diff --git a/third_party/grpc/include/grpc/impl/codegen/sync.h b/third_party/grpc/include/grpc/impl/codegen/sync.h index 04ff0dc5bf9d73..3df68c644f75a7 100644 --- a/third_party/grpc/include/grpc/impl/codegen/sync.h +++ b/third_party/grpc/include/grpc/impl/codegen/sync.h @@ -1,39 +1,24 @@ /* * - * Copyright 2016, Google Inc. - * All rights reserved. + * Copyright 2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_SYNC_H #define GRPC_IMPL_CODEGEN_SYNC_H -/* Synchronization primitives for GPR. +/** Synchronization primitives for GPR. The type gpr_mu provides a non-reentrant mutex (lock). @@ -52,263 +37,25 @@ provides no memory barriers. */ +#ifdef __cplusplus +extern "C" { +#endif + /* 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) +#elif defined(GPR_WINDOWS) +#include +#elif defined(GPR_CUSTOM_SYNC) +#include +#else #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. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API 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.*/ -GPR_API 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. abs_deadline can be either - an absolute deadline, or a GPR_TIMESPAN. May return even when not - woken explicitly. Requires: *mu and *cv initialized; the calling thread - holds an exclusive lock on *mu. */ -GPR_API 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. */ -GPR_API void gpr_cv_signal(gpr_cv *cv); - -/* Wake all threads waiting on *cv. Requires: *cv initialized. */ -GPR_API 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.*/ -GPR_API 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. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API void gpr_ref_init(gpr_refcount *r, int n); - -/* Increment the reference count *r. Requires *r initialized. */ -GPR_API void gpr_ref(gpr_refcount *r); - -/* Increment the reference count *r by n. Requires *r initialized, n > 0. */ -GPR_API 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. */ -GPR_API 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. */ -GPR_API void gpr_stats_init(gpr_stats_counter *c, intptr_t n); - -/* *c += inc. Requires: *c initialized. */ -GPR_API void gpr_stats_inc(gpr_stats_counter *c, intptr_t inc); - -/* Return *c. Requires: *c initialized. */ -GPR_API intptr_t 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 diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_custom.h b/third_party/grpc/include/grpc/impl/codegen/sync_custom.h new file mode 100644 index 00000000000000..69b1bf6cd19cc6 --- /dev/null +++ b/third_party/grpc/include/grpc/impl/codegen/sync_custom.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H +#define GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H + +#include + +#include + +/* Users defining GPR_CUSTOM_SYNC need to define the following macros. */ + +#ifdef GPR_CUSTOM_SYNC + +typedef GPR_CUSTOM_MU_TYPE gpr_mu; +typedef GPR_CUSTOM_CV_TYPE gpr_cv; +typedef GPR_CUSTOM_ONCE_TYPE gpr_once; + +#define GPR_ONCE_INIT GPR_CUSTOM_ONCE_INIT + +#endif + +#endif /* GRPC_IMPL_CODEGEN_SYNC_CUSTOM_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_generic.h b/third_party/grpc/include/grpc/impl/codegen/sync_generic.h index 71443b5794f08e..d64db58a842f68 100644 --- a/third_party/grpc/include/grpc/impl/codegen/sync_generic.h +++ b/third_party/grpc/include/grpc/impl/codegen/sync_generic.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -35,19 +20,27 @@ #define GRPC_IMPL_CODEGEN_SYNC_GENERIC_H /* Generic type defintions for gpr_sync. */ +#include + #include /* gpr_event */ -typedef struct { gpr_atm state; } gpr_event; +typedef struct { + gpr_atm state; +} gpr_event; #define GPR_EVENT_INIT \ { 0 } /* gpr_refcount */ -typedef struct { gpr_atm count; } gpr_refcount; +typedef struct { + gpr_atm count; +} gpr_refcount; /* gpr_stats_counter */ -typedef struct { gpr_atm value; } gpr_stats_counter; +typedef struct { + gpr_atm value; +} gpr_stats_counter; #define GPR_STATS_INIT \ { 0 } diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_posix.h b/third_party/grpc/include/grpc/impl/codegen/sync_posix.h index f6e0a91816dcf7..d927046c53ebb9 100644 --- a/third_party/grpc/include/grpc/impl/codegen/sync_posix.h +++ b/third_party/grpc/include/grpc/impl/codegen/sync_posix.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_IMPL_CODEGEN_SYNC_POSIX_H #define GRPC_IMPL_CODEGEN_SYNC_POSIX_H +#include + #include #include diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_win32.h b/third_party/grpc/include/grpc/impl/codegen/sync_win32.h deleted file mode 100644 index 1a6596c571ab47..00000000000000 --- a/third_party/grpc/include/grpc/impl/codegen/sync_win32.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_IMPL_CODEGEN_SYNC_WIN32_H -#define GRPC_IMPL_CODEGEN_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_IMPL_CODEGEN_SYNC_WIN32_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/sync_windows.h b/third_party/grpc/include/grpc/impl/codegen/sync_windows.h new file mode 100644 index 00000000000000..ba5d5aede0fc89 --- /dev/null +++ b/third_party/grpc/include/grpc/impl/codegen/sync_windows.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_IMPL_CODEGEN_SYNC_WINDOWS_H +#define GRPC_IMPL_CODEGEN_SYNC_WINDOWS_H + +#include + +#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_IMPL_CODEGEN_SYNC_WINDOWS_H */ diff --git a/third_party/grpc/include/grpc/impl/codegen/time.h b/third_party/grpc/include/grpc/impl/codegen/time.h deleted file mode 100644 index 4ed1c3cbd86b5c..00000000000000 --- a/third_party/grpc/include/grpc/impl/codegen/time.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_IMPL_CODEGEN_TIME_H -#define GRPC_IMPL_CODEGEN_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 { - int64_t tv_sec; - int32_t 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_API gpr_timespec -gpr_time_0(gpr_clock_type type); /* The zero time interval. */ -GPR_API gpr_timespec gpr_inf_future(gpr_clock_type type); /* The far future */ -GPR_API 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 */ -GPR_API void gpr_time_init(void); - -/* Return the current time measured from the given clocks epoch. */ -GPR_API gpr_timespec gpr_now(gpr_clock_type clock); - -/* Convert a timespec from one clock to another */ -GPR_API 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. */ -GPR_API int gpr_time_cmp(gpr_timespec a, gpr_timespec b); - -GPR_API gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b); -GPR_API gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b); - -/* Add and subtract times. Calculations saturate at infinities. */ -GPR_API gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b); -GPR_API gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b); - -/* Return a timespec representing a given number of time units. INT64_MIN is - interpreted as gpr_inf_past, and INT64_MAX as gpr_inf_future. */ -GPR_API gpr_timespec gpr_time_from_micros(int64_t x, gpr_clock_type clock_type); -GPR_API gpr_timespec gpr_time_from_nanos(int64_t x, gpr_clock_type clock_type); -GPR_API gpr_timespec gpr_time_from_millis(int64_t x, gpr_clock_type clock_type); -GPR_API gpr_timespec -gpr_time_from_seconds(int64_t x, gpr_clock_type clock_type); -GPR_API gpr_timespec -gpr_time_from_minutes(int64_t x, gpr_clock_type clock_type); -GPR_API gpr_timespec gpr_time_from_hours(int64_t x, gpr_clock_type clock_type); - -GPR_API int32_t gpr_time_to_millis(gpr_timespec timespec); - -/* Return 1 if two times are equal or within threshold of each other, - 0 otherwise */ -GPR_API int gpr_time_similar(gpr_timespec a, gpr_timespec b, - gpr_timespec threshold); - -/* Sleep until at least 'until' - an absolute timeout */ -GPR_API void gpr_sleep_until(gpr_timespec until); - -GPR_API double gpr_timespec_to_micros(gpr_timespec t); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_IMPL_CODEGEN_TIME_H */ diff --git a/third_party/grpc/include/grpc/load_reporting.h b/third_party/grpc/include/grpc/load_reporting.h new file mode 100644 index 00000000000000..55f50ea85e90a4 --- /dev/null +++ b/third_party/grpc/include/grpc/load_reporting.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_LOAD_REPORTING_H +#define GRPC_LOAD_REPORTING_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Metadata key for the gRPC LB load balancer token. + * + * The value corresponding to this key is an opaque token that is given to the + * frontend as part of each pick; the frontend sends this token to the backend + * in each request it sends when using that pick. The token is used by the + * backend to verify the request and to allow the backend to report load to the + * gRPC LB system. */ +#define GRPC_LB_TOKEN_MD_KEY "lb-token" + +/** Metadata key for gRPC LB cost reporting. + * + * The value corresponding to this key is an opaque binary blob reported by the + * backend as part of its trailing metadata containing cost information for the + * call. */ +#define GRPC_LB_COST_MD_KEY "lb-cost-bin" + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_LOAD_REPORTING_H */ diff --git a/third_party/grpc/include/grpc/module.modulemap b/third_party/grpc/include/grpc/module.modulemap new file mode 100644 index 00000000000000..9612a05bf5bf26 --- /dev/null +++ b/third_party/grpc/include/grpc/module.modulemap @@ -0,0 +1,76 @@ + +framework module grpc { + umbrella header "grpc.h" + + header "support/alloc.h" + header "support/atm.h" + header "support/cpu.h" + header "support/log.h" + header "support/log_windows.h" + header "support/port_platform.h" + header "support/string_util.h" + header "support/sync.h" + header "support/sync_generic.h" + header "support/thd_id.h" + header "support/time.h" + header "impl/codegen/atm.h" + header "impl/codegen/fork.h" + header "impl/codegen/gpr_slice.h" + header "impl/codegen/gpr_types.h" + header "impl/codegen/log.h" + header "impl/codegen/port_platform.h" + header "impl/codegen/sync.h" + header "impl/codegen/sync_generic.h" + header "impl/codegen/byte_buffer.h" + header "impl/codegen/byte_buffer_reader.h" + header "impl/codegen/compression_types.h" + header "impl/codegen/connectivity_state.h" + header "impl/codegen/grpc_types.h" + header "impl/codegen/propagation_bits.h" + header "impl/codegen/slice.h" + header "impl/codegen/status.h" + header "impl/codegen/atm.h" + header "impl/codegen/fork.h" + header "impl/codegen/gpr_slice.h" + header "impl/codegen/gpr_types.h" + header "impl/codegen/log.h" + header "impl/codegen/port_platform.h" + header "impl/codegen/sync.h" + header "impl/codegen/sync_generic.h" + header "grpc_security.h" + header "byte_buffer.h" + header "byte_buffer_reader.h" + header "compression.h" + header "fork.h" + header "grpc.h" + header "grpc_posix.h" + header "grpc_security_constants.h" + header "load_reporting.h" + header "slice.h" + header "slice_buffer.h" + header "status.h" + header "support/workaround_list.h" + header "census.h" + + textual header "support/atm_gcc_atomic.h" + textual header "support/atm_gcc_sync.h" + textual header "support/atm_windows.h" + textual header "support/sync_custom.h" + textual header "support/sync_posix.h" + textual header "support/sync_windows.h" + textual header "impl/codegen/atm_gcc_atomic.h" + textual header "impl/codegen/atm_gcc_sync.h" + textual header "impl/codegen/atm_windows.h" + textual header "impl/codegen/sync_custom.h" + textual header "impl/codegen/sync_posix.h" + textual header "impl/codegen/sync_windows.h" + textual header "impl/codegen/atm_gcc_atomic.h" + textual header "impl/codegen/atm_gcc_sync.h" + textual header "impl/codegen/atm_windows.h" + textual header "impl/codegen/sync_custom.h" + textual header "impl/codegen/sync_posix.h" + textual header "impl/codegen/sync_windows.h" + + export * + module * { export * } +} diff --git a/third_party/grpc/include/grpc/slice.h b/third_party/grpc/include/grpc/slice.h new file mode 100644 index 00000000000000..ce482922afaf24 --- /dev/null +++ b/third_party/grpc/include/grpc/slice.h @@ -0,0 +1,172 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SLICE_H +#define GRPC_SLICE_H + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Increment the refcount of s. Requires slice is initialized. + Returns s. */ +GPRAPI grpc_slice grpc_slice_ref(grpc_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 grpc_slice_new(start, + len, dest) where dest!=NULL , then (*dest)(start) is called, else if s is + ultimately derived from a call to grpc_slice_new_with_len(start, len, dest) + where dest!=NULL , then (*dest)(start, len). Requires s initialized. */ +GPRAPI void grpc_slice_unref(grpc_slice s); + +/** Copy slice - create a new slice that contains the same data as s */ +GPRAPI grpc_slice grpc_slice_copy(grpc_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. */ +GPRAPI grpc_slice grpc_slice_new(void* p, size_t len, void (*destroy)(void*)); + +/** Equivalent to grpc_slice_new, but with a separate pointer that is + passed to the destroy function. This function can be useful when + the data is part of a larger structure that must be destroyed when + the data is no longer needed. */ +GPRAPI grpc_slice grpc_slice_new_with_user_data(void* p, size_t len, + void (*destroy)(void*), + void* user_data); + +/** Equivalent to grpc_slice_new, but with a two argument destroy function that + also takes the slice length. */ +GPRAPI grpc_slice grpc_slice_new_with_len(void* p, size_t len, + void (*destroy)(void*, size_t)); + +/** Equivalent to grpc_slice_new(malloc(len), len, free), but saves one malloc() + call. + Aborts if malloc() fails. */ +GPRAPI grpc_slice grpc_slice_malloc(size_t length); +GPRAPI grpc_slice grpc_slice_malloc_large(size_t length); + +#define GRPC_SLICE_MALLOC(len) grpc_slice_malloc(len) + +/** Intern a slice: + + The return value for two invocations of this function with the same sequence + of bytes is a slice which points to the same memory. */ +GPRAPI grpc_slice grpc_slice_intern(grpc_slice slice); + +/** Create a slice by copying a string. + Does not preserve null terminators. + Equivalent to: + size_t len = strlen(source); + grpc_slice slice = grpc_slice_malloc(len); + memcpy(slice->data, source, len); */ +GPRAPI grpc_slice grpc_slice_from_copied_string(const char* source); + +/** Create a slice by copying a buffer. + Equivalent to: + grpc_slice slice = grpc_slice_malloc(len); + memcpy(slice->data, source, len); */ +GPRAPI grpc_slice grpc_slice_from_copied_buffer(const char* source, size_t len); + +/** Create a slice pointing to constant memory */ +GPRAPI grpc_slice grpc_slice_from_static_string(const char* source); + +/** Create a slice pointing to constant memory */ +GPRAPI grpc_slice grpc_slice_from_static_buffer(const void* source, size_t len); + +/** Return a result slice derived from s, which shares a ref count with \a s, + where result.data==s.data+begin, and result.length==end-begin. The ref count + of \a s is increased by one. Do not assign result back to \a s. + Requires s initialized, begin <= end, begin <= s.length, and + end <= source->length. */ +GPRAPI grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end); + +/** The same as grpc_slice_sub, but without altering the ref count */ +GPRAPI grpc_slice grpc_slice_sub_no_ref(grpc_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 */ +GPRAPI grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split); + +typedef enum { + GRPC_SLICE_REF_TAIL = 1, + GRPC_SLICE_REF_HEAD = 2, + GRPC_SLICE_REF_BOTH = 1 + 2 +} grpc_slice_ref_whom; + +/** The same as grpc_slice_split_tail, but with an option to skip altering + * refcounts (grpc_slice_split_tail_maybe_ref(..., true) is equivalent to + * grpc_slice_split_tail(...)) */ +GPRAPI grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice* s, size_t split, + grpc_slice_ref_whom ref_whom); + +/** 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 */ +GPRAPI grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split); + +GPRAPI grpc_slice grpc_empty_slice(void); + +GPRAPI uint32_t grpc_slice_default_hash_impl(grpc_slice s); +GPRAPI int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b); + +GPRAPI int grpc_slice_eq(grpc_slice a, grpc_slice b); + +/** 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. */ +GPRAPI int grpc_slice_cmp(grpc_slice a, grpc_slice b); +GPRAPI int grpc_slice_str_cmp(grpc_slice a, const char* b); + +/** return non-zero if the first blen bytes of a are equal to b */ +GPRAPI int grpc_slice_buf_start_eq(grpc_slice a, const void* b, size_t blen); + +/** return the index of the last instance of \a c in \a s, or -1 if not found */ +GPRAPI int grpc_slice_rchr(grpc_slice s, char c); +GPRAPI int grpc_slice_chr(grpc_slice s, char c); + +/** return the index of the first occurance of \a needle in \a haystack, or -1 + if it's not found */ +GPRAPI int grpc_slice_slice(grpc_slice haystack, grpc_slice needle); + +GPRAPI uint32_t grpc_slice_hash(grpc_slice s); + +/** Do two slices point at the same memory, with the same length + If a or b is inlined, actually compares data */ +GPRAPI int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b); + +/** Return a slice pointing to newly allocated memory that has the same contents + * as \a s */ +GPRAPI grpc_slice grpc_slice_dup(grpc_slice a); + +/** Return a copy of slice as a C string. Offers no protection against embedded + NULL's. Returned string must be freed with gpr_free. */ +GPRAPI char* grpc_slice_to_c_string(grpc_slice s); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SLICE_H */ diff --git a/third_party/grpc/include/grpc/slice_buffer.h b/third_party/grpc/include/grpc/slice_buffer.h new file mode 100644 index 00000000000000..3260019ca79ae8 --- /dev/null +++ b/third_party/grpc/include/grpc/slice_buffer.h @@ -0,0 +1,84 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SLICE_BUFFER_H +#define GRPC_SLICE_BUFFER_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** initialize a slice buffer */ +GPRAPI void grpc_slice_buffer_init(grpc_slice_buffer* sb); +/** destroy a slice buffer - unrefs any held elements */ +GPRAPI void grpc_slice_buffer_destroy(grpc_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. */ +GPRAPI void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_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. */ +GPRAPI size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer* sb, + grpc_slice slice); +GPRAPI void grpc_slice_buffer_addn(grpc_slice_buffer* sb, grpc_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 */ +GPRAPI uint8_t* grpc_slice_buffer_tiny_add(grpc_slice_buffer* sb, size_t len); +/** pop the last buffer, but don't unref it */ +GPRAPI void grpc_slice_buffer_pop(grpc_slice_buffer* sb); +/** clear a slice buffer, unref all elements */ +GPRAPI void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb); +/** swap the contents of two slice buffers */ +GPRAPI void grpc_slice_buffer_swap(grpc_slice_buffer* a, grpc_slice_buffer* b); +/** move all of the elements of src into dst */ +GPRAPI void grpc_slice_buffer_move_into(grpc_slice_buffer* src, + grpc_slice_buffer* dst); +/** remove n bytes from the end of a slice buffer */ +GPRAPI void grpc_slice_buffer_trim_end(grpc_slice_buffer* src, size_t n, + grpc_slice_buffer* garbage); +/** move the first n bytes of src into dst */ +GPRAPI void grpc_slice_buffer_move_first(grpc_slice_buffer* src, size_t n, + grpc_slice_buffer* dst); +/** move the first n bytes of src into dst without adding references */ +GPRAPI void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer* src, + size_t n, + grpc_slice_buffer* dst); +/** move the first n bytes of src into dst (copying them) */ +GPRAPI void grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer* src, + size_t n, void* dst); +/** take the first slice in the slice buffer */ +GPRAPI grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer* src); +/** undo the above with (a possibly different) \a slice */ +GPRAPI void grpc_slice_buffer_undo_take_first(grpc_slice_buffer* src, + grpc_slice slice); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SLICE_BUFFER_H */ diff --git a/third_party/grpc/include/grpc/status.h b/third_party/grpc/include/grpc/status.h index 3889a1648609be..ecb9668bbb8e54 100644 --- a/third_party/grpc/include/grpc/status.h +++ b/third_party/grpc/include/grpc/status.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_STATUS_H #define GRPC_STATUS_H +#include + #include #endif /* GRPC_STATUS_H */ diff --git a/third_party/grpc/include/grpc/support/alloc.h b/third_party/grpc/include/grpc/support/alloc.h index 649cb2ba7a04d7..8bd940bec47e3f 100644 --- a/third_party/grpc/include/grpc/support/alloc.h +++ b/third_party/grpc/include/grpc/support/alloc.h @@ -1,39 +1,68 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_ALLOC_H #define GRPC_SUPPORT_ALLOC_H -#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct gpr_allocation_functions { + void* (*malloc_fn)(size_t size); + void* (*zalloc_fn)(size_t size); /** if NULL, uses malloc_fn then memset */ + void* (*realloc_fn)(void* ptr, size_t size); + void (*free_fn)(void* ptr); +} gpr_allocation_functions; + +/** malloc. + * If size==0, always returns NULL. Otherwise this function never returns NULL. + * The pointer returned is suitably aligned for any kind of variable it could + * contain. + */ +GPRAPI void* gpr_malloc(size_t size); +/** like malloc, but zero all bytes before returning them */ +GPRAPI void* gpr_zalloc(size_t size); +/** free */ +GPRAPI void gpr_free(void* ptr); +/** realloc, never returns NULL */ +GPRAPI void* gpr_realloc(void* p, size_t size); +/** aligned malloc, never returns NULL, will align to alignment, which + * must be a power of 2. */ +GPRAPI void* gpr_malloc_aligned(size_t size, size_t alignment); +/** free memory allocated by gpr_malloc_aligned */ +GPRAPI void gpr_free_aligned(void* ptr); + +/** Request the family of allocation functions in \a functions be used. NOTE + * that this request will be honored in a *best effort* basis and that no + * guarantees are made about the default functions (eg, malloc) being called. + * The functions.free_fn implementation must be a no-op for NULL input. */ +GPRAPI void gpr_set_allocation_functions(gpr_allocation_functions functions); + +/** Return the family of allocation functions currently in effect. */ +GPRAPI gpr_allocation_functions gpr_get_allocation_functions(void); + +#ifdef __cplusplus +} +#endif #endif /* GRPC_SUPPORT_ALLOC_H */ diff --git a/third_party/grpc/include/grpc/support/atm.h b/third_party/grpc/include/grpc/support/atm.h index 37975db8f4e766..073b0a6fcf1b08 100644 --- a/third_party/grpc/include/grpc/support/atm.h +++ b/third_party/grpc/include/grpc/support/atm.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_ATM_H #define GRPC_SUPPORT_ATM_H +#include + #include #endif /* GRPC_SUPPORT_ATM_H */ diff --git a/third_party/grpc/include/grpc/support/atm_gcc_atomic.h b/third_party/grpc/include/grpc/support/atm_gcc_atomic.h index 7e1f7fd55a575b..ae603db497a4f6 100644 --- a/third_party/grpc/include/grpc/support/atm_gcc_atomic.h +++ b/third_party/grpc/include/grpc/support/atm_gcc_atomic.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H -#define GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H +#ifndef GRPC_SUPPORT_ATM_GCC_ATOMIC_H +#define GRPC_SUPPORT_ATM_GCC_ATOMIC_H + +#include #include -#endif /* GRPC_IMPL_CODEGEN_ATM_GCC_ATOMIC_H */ +#endif /* GRPC_SUPPORT_ATM_GCC_ATOMIC_H */ diff --git a/third_party/grpc/include/grpc/support/atm_gcc_sync.h b/third_party/grpc/include/grpc/support/atm_gcc_sync.h index 4a61dbd4a5edf7..6f51fdb1aa1302 100644 --- a/third_party/grpc/include/grpc/support/atm_gcc_sync.h +++ b/third_party/grpc/include/grpc/support/atm_gcc_sync.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_ATM_GCC_SYNC_H #define GRPC_SUPPORT_ATM_GCC_SYNC_H +#include + #include #endif /* GRPC_SUPPORT_ATM_GCC_SYNC_H */ diff --git a/third_party/grpc/include/grpc/support/atm_win32.h b/third_party/grpc/include/grpc/support/atm_win32.h deleted file mode 100644 index ebb6a674a2e706..00000000000000 --- a/third_party/grpc/include/grpc/support/atm_win32.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_ATM_WIN32_H -#define GRPC_SUPPORT_ATM_WIN32_H - -#include - -#endif /* GRPC_SUPPORT_ATM_WIN32_H */ diff --git a/third_party/grpc/include/grpc/support/atm_windows.h b/third_party/grpc/include/grpc/support/atm_windows.h new file mode 100644 index 00000000000000..36955e4dae0170 --- /dev/null +++ b/third_party/grpc/include/grpc/support/atm_windows.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SUPPORT_ATM_WINDOWS_H +#define GRPC_SUPPORT_ATM_WINDOWS_H + +#include + +#include + +#endif /* GRPC_SUPPORT_ATM_WINDOWS_H */ diff --git a/third_party/grpc/include/grpc/support/avl.h b/third_party/grpc/include/grpc/support/avl.h deleted file mode 100644 index 3433124c6fa5d0..00000000000000 --- a/third_party/grpc/include/grpc/support/avl.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_AVL_H -#define GRPC_SUPPORT_AVL_H - -#include - -/** internal node of an AVL tree */ -typedef struct gpr_avl_node { - gpr_refcount refs; - void *key; - void *value; - struct gpr_avl_node *left; - struct gpr_avl_node *right; - long height; -} gpr_avl_node; - -typedef struct gpr_avl_vtable { - /** destroy a key */ - void (*destroy_key)(void *key); - /** copy a key, returning new value */ - void *(*copy_key)(void *key); - /** compare key1, key2; return <0 if key1 < key2, - >0 if key1 > key2, 0 if key1 == key2 */ - long (*compare_keys)(void *key1, void *key2); - /** destroy a value */ - void (*destroy_value)(void *value); - /** copy a value */ - void *(*copy_value)(void *value); -} gpr_avl_vtable; - -/** "pointer" to an AVL tree - this is a reference - counted object - use gpr_avl_ref to add a reference, - gpr_avl_unref when done with a reference */ -typedef struct gpr_avl { - const gpr_avl_vtable *vtable; - gpr_avl_node *root; -} gpr_avl; - -/** create an immutable AVL tree */ -GPR_API gpr_avl gpr_avl_create(const gpr_avl_vtable *vtable); -/** add a reference to an existing tree - returns - the tree as a convenience */ -GPR_API gpr_avl gpr_avl_ref(gpr_avl avl); -/** remove a reference to a tree - destroying it if there - are no references left */ -GPR_API void gpr_avl_unref(gpr_avl avl); -/** return a new tree with (key, value) added to avl. - implicitly unrefs avl to allow easy chaining. - if key exists in avl, the new tree's key entry updated - (i.e. a duplicate is not created) */ -GPR_API gpr_avl gpr_avl_add(gpr_avl avl, void *key, void *value); -/** return a new tree with key deleted */ -GPR_API gpr_avl gpr_avl_remove(gpr_avl avl, void *key); -/** lookup key, and return the associated value. - does not mutate avl. - returns NULL if key is not found. */ -GPR_API void *gpr_avl_get(gpr_avl avl, void *key); - -#endif diff --git a/third_party/grpc/include/grpc/support/cmdline.h b/third_party/grpc/include/grpc/support/cmdline.h deleted file mode 100644 index 6f442e3cf23b2b..00000000000000 --- a/third_party/grpc/include/grpc/support/cmdline.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_CMDLINE_H -#define GRPC_SUPPORT_CMDLINE_H - -#include - -#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_API 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) */ -GPR_API void gpr_cmdline_add_int(gpr_cmdline *cl, const char *name, - const char *help, int *value); -/* The same, for a boolean flag */ -GPR_API void gpr_cmdline_add_flag(gpr_cmdline *cl, const char *name, - const char *help, int *value); -/* And for a string */ -GPR_API void gpr_cmdline_add_string(gpr_cmdline *cl, const char *name, - const char *help, char **value); -/* Set a callback for non-named arguments */ -GPR_API 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); -/* Enable surviving failure: default behavior is to exit the process */ -GPR_API void gpr_cmdline_set_survive_failure(gpr_cmdline *cl); -/* Parse the command line; returns 1 on success, on failure either dies - (by default) or returns 0 if gpr_cmdline_set_survive_failure() has been - called */ -GPR_API int gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv); -/* Destroy the parser */ -GPR_API void gpr_cmdline_destroy(gpr_cmdline *cl); -/* Get a string describing usage */ -GPR_API char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_SUPPORT_CMDLINE_H */ diff --git a/third_party/grpc/include/grpc/support/cpu.h b/third_party/grpc/include/grpc/support/cpu.h index db4bdd42aabbac..f0e898e8596286 100644 --- a/third_party/grpc/include/grpc/support/cpu.h +++ b/third_party/grpc/include/grpc/support/cpu.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -40,17 +25,17 @@ extern "C" { #endif -/* Interface providing CPU information for currently running system */ +/** Interface providing CPU information for currently running system */ -/* Return the number of CPU cores on the current system. Will return 0 if +/** Return the number of CPU cores on the current system. Will return 0 if the information is not available. */ -GPR_API unsigned gpr_cpu_num_cores(void); +GPRAPI unsigned gpr_cpu_num_cores(void); -/* Return the CPU on which the current thread is executing; N.B. This should +/** 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] */ -GPR_API unsigned gpr_cpu_current_cpu(void); +GPRAPI unsigned gpr_cpu_current_cpu(void); #ifdef __cplusplus } // extern "C" diff --git a/third_party/grpc/include/grpc/support/histogram.h b/third_party/grpc/include/grpc/support/histogram.h deleted file mode 100644 index 9c807ea27db11e..00000000000000 --- a/third_party/grpc/include/grpc/support/histogram.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_HISTOGRAM_H -#define GRPC_SUPPORT_HISTOGRAM_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct gpr_histogram gpr_histogram; - -GPR_API gpr_histogram *gpr_histogram_create(double resolution, - double max_bucket_start); -GPR_API void gpr_histogram_destroy(gpr_histogram *h); -GPR_API 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 */ -GPR_API int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src); - -GPR_API double gpr_histogram_percentile(gpr_histogram *histogram, - double percentile); -GPR_API double gpr_histogram_mean(gpr_histogram *histogram); -GPR_API double gpr_histogram_stddev(gpr_histogram *histogram); -GPR_API double gpr_histogram_variance(gpr_histogram *histogram); -GPR_API double gpr_histogram_maximum(gpr_histogram *histogram); -GPR_API double gpr_histogram_minimum(gpr_histogram *histogram); -GPR_API double gpr_histogram_count(gpr_histogram *histogram); -GPR_API double gpr_histogram_sum(gpr_histogram *histogram); -GPR_API double gpr_histogram_sum_of_squares(gpr_histogram *histogram); - -GPR_API const uint32_t *gpr_histogram_get_contents(gpr_histogram *histogram, - size_t *count); -GPR_API void gpr_histogram_merge_contents(gpr_histogram *histogram, - const uint32_t *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/third_party/grpc/include/grpc/support/host_port.h b/third_party/grpc/include/grpc/support/host_port.h deleted file mode 100644 index ee9c294ebcd204..00000000000000 --- a/third_party/grpc/include/grpc/support/host_port.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_HOST_PORT_H -#define GRPC_SUPPORT_HOST_PORT_H - -#include - -#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. */ -GPR_API 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. */ -GPR_API int gpr_split_host_port(const char *name, char **host, char **port); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_SUPPORT_HOST_PORT_H */ diff --git a/third_party/grpc/include/grpc/support/log.h b/third_party/grpc/include/grpc/support/log.h index e6f6dd931cd992..8d8742b95d6097 100644 --- a/third_party/grpc/include/grpc/support/log.h +++ b/third_party/grpc/include/grpc/support/log.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_LOG_H #define GRPC_SUPPORT_LOG_H +#include + #include #endif /* GRPC_SUPPORT_LOG_H */ diff --git a/third_party/grpc/include/grpc/support/log_win32.h b/third_party/grpc/include/grpc/support/log_win32.h deleted file mode 100644 index 1470e5b48409f4..00000000000000 --- a/third_party/grpc/include/grpc/support/log_win32.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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. - */ -GPR_API char *gpr_format_message(int messageid); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_SUPPORT_LOG_WIN32_H */ diff --git a/third_party/grpc/include/grpc/support/log_windows.h b/third_party/grpc/include/grpc/support/log_windows.h new file mode 100644 index 00000000000000..e833f9d9dfddc0 --- /dev/null +++ b/third_party/grpc/include/grpc/support/log_windows.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SUPPORT_LOG_WINDOWS_H +#define GRPC_SUPPORT_LOG_WINDOWS_H + +#include + +#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. + */ +GPRAPI char* gpr_format_message(int messageid); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_LOG_WINDOWS_H */ diff --git a/third_party/grpc/include/grpc/support/port_platform.h b/third_party/grpc/include/grpc/support/port_platform.h index 6b14f8b0b0eaed..26025dcd9dd1b9 100644 --- a/third_party/grpc/include/grpc/support/port_platform.h +++ b/third_party/grpc/include/grpc/support/port_platform.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ diff --git a/third_party/grpc/include/grpc/support/slice.h b/third_party/grpc/include/grpc/support/slice.h deleted file mode 100644 index b3a73b2c99ffcb..00000000000000 --- a/third_party/grpc/include/grpc/support/slice.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_SLICE_H -#define GRPC_SUPPORT_SLICE_H - -#include - -#endif /* GRPC_SUPPORT_SLICE_H */ diff --git a/third_party/grpc/include/grpc/support/slice_buffer.h b/third_party/grpc/include/grpc/support/slice_buffer.h deleted file mode 100644 index 7bee4dde346cce..00000000000000 --- a/third_party/grpc/include/grpc/support/slice_buffer.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_SLICE_BUFFER_H -#define GRPC_SUPPORT_SLICE_BUFFER_H - -#include - -#endif /* GRPC_SUPPORT_SLICE_BUFFER_H */ diff --git a/third_party/grpc/include/grpc/support/string_util.h b/third_party/grpc/include/grpc/support/string_util.h index 74f2cb371a278b..2679160c1bb48e 100644 --- a/third_party/grpc/include/grpc/support/string_util.h +++ b/third_party/grpc/include/grpc/support/string_util.h @@ -1,50 +1,39 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_STRING_UTIL_H #define GRPC_SUPPORT_STRING_UTIL_H +#include + +#include + #ifdef __cplusplus extern "C" { #endif -/* String utility functions */ +/** String utility functions */ -/* Returns a copy of src that can be passed to gpr_free(). +/** Returns a copy of src that can be passed to gpr_free(). If allocation fails or if src is NULL, returns NULL. */ -GPR_API char *gpr_strdup(const char *src); +GPRAPI char* gpr_strdup(const char* src); -/* printf to a newly-allocated string. The set of supported formats may vary +/** 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'), @@ -52,7 +41,8 @@ GPR_API char *gpr_strdup(const char *src); On error, returns -1 and sets *strp to NULL. If the format string is bad, the result is undefined. */ -GPR_API int gpr_asprintf(char **strp, const char *format, ...); +GPRAPI int gpr_asprintf(char** strp, const char* format, ...) + GPR_PRINT_FORMAT_CHECK(2, 3); #ifdef __cplusplus } diff --git a/third_party/grpc/include/grpc/support/subprocess.h b/third_party/grpc/include/grpc/support/subprocess.h deleted file mode 100644 index ffbdf67357712f..00000000000000 --- a/third_party/grpc/include/grpc/support/subprocess.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_SUBPROCESS_H -#define GRPC_SUPPORT_SUBPROCESS_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct gpr_subprocess gpr_subprocess; - -/* .exe on windows, empty on unices */ -GPR_API const char *gpr_subprocess_binary_extension(); - -GPR_API gpr_subprocess *gpr_subprocess_create(int argc, const char **argv); -/* if subprocess has not been joined, kill it */ -GPR_API void gpr_subprocess_destroy(gpr_subprocess *p); -/* returns exit status; can be called at most once */ -GPR_API int gpr_subprocess_join(gpr_subprocess *p); -GPR_API void gpr_subprocess_interrupt(gpr_subprocess *p); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif diff --git a/third_party/grpc/include/grpc/support/sync.h b/third_party/grpc/include/grpc/support/sync.h index 89cc76f7848f34..da820dece5ef53 100644 --- a/third_party/grpc/include/grpc/support/sync.h +++ b/third_party/grpc/include/grpc/support/sync.h @@ -1,39 +1,282 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_SYNC_H #define GRPC_SUPPORT_SYNC_H +#include + +#include /* for gpr_timespec */ #include +#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. */ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI 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.*/ +GPRAPI 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. abs_deadline can be either + an absolute deadline, or a GPR_TIMESPAN. May return even when not + woken explicitly. Requires: *mu and *cv initialized; the calling thread + holds an exclusive lock on *mu. */ +GPRAPI 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. */ +GPRAPI void gpr_cv_signal(gpr_cv* cv); + +/** Wake all threads waiting on *cv. Requires: *cv initialized. */ +GPRAPI 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.*/ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI 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. */ +GPRAPI void gpr_ref_init(gpr_refcount* r, int n); + +/** Increment the reference count *r. Requires *r initialized. */ +GPRAPI void gpr_ref(gpr_refcount* r); + +/** Increment the reference count *r. Requires *r initialized. + Crashes if refcount is zero */ +GPRAPI void gpr_ref_non_zero(gpr_refcount* r); + +/** Increment the reference count *r by n. Requires *r initialized, n > 0. */ +GPRAPI 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. */ +GPRAPI int gpr_unref(gpr_refcount* r); + +/** Return non-zero iff the reference count of *r is one, and thus is owned + by exactly one object. */ +GPRAPI int gpr_ref_is_unique(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. */ +GPRAPI void gpr_stats_init(gpr_stats_counter* c, intptr_t n); + +/** *c += inc. Requires: *c initialized. */ +GPRAPI void gpr_stats_inc(gpr_stats_counter* c, intptr_t inc); + +/** Return *c. Requires: *c initialized. */ +GPRAPI intptr_t 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 +} // extern "C" +#endif + #endif /* GRPC_SUPPORT_SYNC_H */ diff --git a/third_party/grpc/include/grpc/support/sync_custom.h b/third_party/grpc/include/grpc/support/sync_custom.h new file mode 100644 index 00000000000000..27cf0e0578d1d4 --- /dev/null +++ b/third_party/grpc/include/grpc/support/sync_custom.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SUPPORT_SYNC_CUSTOM_H +#define GRPC_SUPPORT_SYNC_CUSTOM_H + +#include + +#include + +#endif /* GRPC_SUPPORT_SYNC_CUSTOM_H */ diff --git a/third_party/grpc/include/grpc/support/sync_generic.h b/third_party/grpc/include/grpc/support/sync_generic.h index 28e3ab647be0b0..93028c4af05b8f 100644 --- a/third_party/grpc/include/grpc/support/sync_generic.h +++ b/third_party/grpc/include/grpc/support/sync_generic.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_SYNC_GENERIC_H #define GRPC_SUPPORT_SYNC_GENERIC_H +#include + #include #endif /* GRPC_SUPPORT_SYNC_GENERIC_H */ diff --git a/third_party/grpc/include/grpc/support/sync_posix.h b/third_party/grpc/include/grpc/support/sync_posix.h index fa0324fbfa2380..3dce7ee48cc092 100644 --- a/third_party/grpc/include/grpc/support/sync_posix.h +++ b/third_party/grpc/include/grpc/support/sync_posix.h @@ -1,39 +1,26 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_SYNC_POSIX_H #define GRPC_SUPPORT_SYNC_POSIX_H +#include + #include #endif /* GRPC_SUPPORT_SYNC_POSIX_H */ diff --git a/third_party/grpc/include/grpc/support/sync_win32.h b/third_party/grpc/include/grpc/support/sync_win32.h deleted file mode 100644 index bfbc99e9712495..00000000000000 --- a/third_party/grpc/include/grpc/support/sync_win32.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_SUPPORT_SYNC_WIN32_H -#define GRPC_SUPPORT_SYNC_WIN32_H - -#include - -#endif /* GRPC_SUPPORT_SYNC_WIN32_H */ diff --git a/third_party/grpc/include/grpc/support/sync_windows.h b/third_party/grpc/include/grpc/support/sync_windows.h new file mode 100644 index 00000000000000..a493c86422011c --- /dev/null +++ b/third_party/grpc/include/grpc/support/sync_windows.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SUPPORT_SYNC_WINDOWS_H +#define GRPC_SUPPORT_SYNC_WINDOWS_H + +#include + +#include + +#endif /* GRPC_SUPPORT_SYNC_WINDOWS_H */ diff --git a/third_party/grpc/include/grpc/support/thd.h b/third_party/grpc/include/grpc/support/thd.h deleted file mode 100644 index 84e964fc07bee1..00000000000000 --- a/third_party/grpc/include/grpc/support/thd.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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 uintptr_t 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. */ -GPR_API 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_API gpr_thd_options gpr_thd_options_default(void); - -/* Set the thread to become detached on startup - this is the default. */ -GPR_API void gpr_thd_options_set_detached(gpr_thd_options *options); - -/* Set the thread to become joinable - mutually exclusive with detached. */ -GPR_API void gpr_thd_options_set_joinable(gpr_thd_options *options); - -/* Returns non-zero if the option detached is set. */ -GPR_API int gpr_thd_options_is_detached(const gpr_thd_options *options); - -/* Returns non-zero if the option joinable is set. */ -GPR_API int gpr_thd_options_is_joinable(const gpr_thd_options *options); - -/* Returns the identifier of the current thread. */ -GPR_API gpr_thd_id gpr_thd_currentid(void); - -/* Blocks until the specified thread properly terminates. - Calling this on a detached thread has unpredictable results. */ -GPR_API void gpr_thd_join(gpr_thd_id t); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_SUPPORT_THD_H */ diff --git a/third_party/grpc/include/grpc/support/thd_id.h b/third_party/grpc/include/grpc/support/thd_id.h new file mode 100644 index 00000000000000..e9b2b7ec2ae3a8 --- /dev/null +++ b/third_party/grpc/include/grpc/support/thd_id.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SUPPORT_THD_ID_H +#define GRPC_SUPPORT_THD_ID_H +/** Thread ID interface for GPR. + + Used by some wrapped languages for logging purposes. + + Types + gpr_thd_id a unique opaque identifier for a thread. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef uintptr_t gpr_thd_id; + +/** Returns the identifier of the current thread. */ +GPRAPI gpr_thd_id gpr_thd_currentid(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_SUPPORT_THD_ID_H */ diff --git a/third_party/grpc/include/grpc/support/time.h b/third_party/grpc/include/grpc/support/time.h index 2c7f8533a2b1a4..550ffc2c209838 100644 --- a/third_party/grpc/include/grpc/support/time.h +++ b/third_party/grpc/include/grpc/support/time.h @@ -1,39 +1,92 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_SUPPORT_TIME_H #define GRPC_SUPPORT_TIME_H -#include +#include + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Time constants. */ +GPRAPI gpr_timespec +gpr_time_0(gpr_clock_type type); /** The zero time interval. */ +GPRAPI gpr_timespec gpr_inf_future(gpr_clock_type type); /** The far future */ +GPRAPI 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 */ +GPRAPI void gpr_time_init(void); + +/** Return the current time measured from the given clocks epoch. */ +GPRAPI gpr_timespec gpr_now(gpr_clock_type clock); + +/** Convert a timespec from one clock to another */ +GPRAPI 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. */ +GPRAPI int gpr_time_cmp(gpr_timespec a, gpr_timespec b); + +GPRAPI gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b); +GPRAPI gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b); + +/** Add and subtract times. Calculations saturate at infinities. */ +GPRAPI gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b); +GPRAPI gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b); + +/** Return a timespec representing a given number of time units. INT64_MIN is + interpreted as gpr_inf_past, and INT64_MAX as gpr_inf_future. */ +GPRAPI gpr_timespec gpr_time_from_micros(int64_t x, gpr_clock_type clock_type); +GPRAPI gpr_timespec gpr_time_from_nanos(int64_t x, gpr_clock_type clock_type); +GPRAPI gpr_timespec gpr_time_from_millis(int64_t x, gpr_clock_type clock_type); +GPRAPI gpr_timespec gpr_time_from_seconds(int64_t x, gpr_clock_type clock_type); +GPRAPI gpr_timespec gpr_time_from_minutes(int64_t x, gpr_clock_type clock_type); +GPRAPI gpr_timespec gpr_time_from_hours(int64_t x, gpr_clock_type clock_type); + +GPRAPI int32_t gpr_time_to_millis(gpr_timespec timespec); + +/** Return 1 if two times are equal or within threshold of each other, + 0 otherwise */ +GPRAPI int gpr_time_similar(gpr_timespec a, gpr_timespec b, + gpr_timespec threshold); + +/** Sleep until at least 'until' - an absolute timeout */ +GPRAPI void gpr_sleep_until(gpr_timespec until); + +GPRAPI double gpr_timespec_to_micros(gpr_timespec t); + +#ifdef __cplusplus +} +#endif #endif /* GRPC_SUPPORT_TIME_H */ diff --git a/third_party/grpc/include/grpc/support/tls.h b/third_party/grpc/include/grpc/support/tls.h deleted file mode 100644 index 43addc7f14556c..00000000000000 --- a/third_party/grpc/include/grpc/support/tls.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 intptr_t. - - 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/third_party/grpc/include/grpc/support/tls_gcc.h b/third_party/grpc/include/grpc/support/tls_gcc.h deleted file mode 100644 index a697ad05b0b929..00000000000000 --- a/third_party/grpc/include/grpc/support/tls_gcc.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 { - intptr_t 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/third_party/grpc/include/grpc/support/tls_msvc.h b/third_party/grpc/include/grpc/support/tls_msvc.h deleted file mode 100644 index 987a514f0371c6..00000000000000 --- a/third_party/grpc/include/grpc/support/tls_msvc.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 { - intptr_t 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/third_party/grpc/include/grpc/support/tls_pthread.h b/third_party/grpc/include/grpc/support/tls_pthread.h deleted file mode 100644 index 699ee6b1d382c3..00000000000000 --- a/third_party/grpc/include/grpc/support/tls_pthread.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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) ((intptr_t)pthread_getspecific((tls)->key)) -#ifdef __cplusplus -extern "C" { -#endif -intptr_t gpr_tls_set(struct gpr_pthread_thread_local *tls, intptr_t value); -#ifdef __cplusplus -} -#endif - -#endif diff --git a/third_party/grpc/include/grpc/support/useful.h b/third_party/grpc/include/grpc/support/useful.h deleted file mode 100644 index 9f08d788c0d5de..00000000000000 --- a/third_party/grpc/include/grpc/support/useful.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/include/grpc/support/workaround_list.h b/third_party/grpc/include/grpc/support/workaround_list.h new file mode 100644 index 00000000000000..e28dfa14398786 --- /dev/null +++ b/third_party/grpc/include/grpc/support/workaround_list.h @@ -0,0 +1,31 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SUPPORT_WORKAROUND_LIST_H +#define GRPC_SUPPORT_WORKAROUND_LIST_H + +/* The list of IDs of server workarounds currently maintained by gRPC. For + * explanation and detailed descriptions of workarounds, see + * /doc/workarounds.md + */ +typedef enum { + GRPC_WORKAROUND_ID_CRONET_COMPRESSION = 0, + GRPC_MAX_WORKAROUND_ID +} grpc_workaround_list; + +#endif /* GRPC_SUPPORT_WORKAROUND_LIST_H */ diff --git a/third_party/grpc/include/grpcpp/alarm.h b/third_party/grpc/include/grpcpp/alarm.h new file mode 100644 index 00000000000000..2343c1149c5abf --- /dev/null +++ b/third_party/grpc/include/grpcpp/alarm.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_ALARM_H +#define GRPCPP_ALARM_H + +#include + +namespace grpc { + +typedef ::grpc_impl::Alarm Alarm; +} + +#endif // GRPCPP_ALARM_H diff --git a/third_party/grpc/include/grpcpp/alarm_impl.h b/third_party/grpc/include/grpcpp/alarm_impl.h new file mode 100644 index 00000000000000..7844e7c8866efe --- /dev/null +++ b/third_party/grpc/include/grpcpp/alarm_impl.h @@ -0,0 +1,116 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// An Alarm posts the user provided tag to its associated completion queue upon +/// expiry or cancellation. +#ifndef GRPCPP_ALARM_IMPL_H +#define GRPCPP_ALARM_IMPL_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace grpc_impl { + +/// A thin wrapper around \a grpc_alarm (see / \a / src/core/surface/alarm.h). +class Alarm : private ::grpc::GrpcLibraryCodegen { + public: + /// Create an unset completion queue alarm + Alarm(); + + /// Destroy the given completion queue alarm, cancelling it in the process. + ~Alarm(); + + /// DEPRECATED: Create and set a completion queue alarm instance associated to + /// \a cq. + /// This form is deprecated because it is inherently racy. + /// \internal We rely on the presence of \a cq for grpc initialization. If \a + /// cq were ever to be removed, a reference to a static + /// internal::GrpcLibraryInitializer instance would need to be introduced + /// here. \endinternal. + template + Alarm(::grpc::CompletionQueue* cq, const T& deadline, void* tag) : Alarm() { + SetInternal(cq, ::grpc::TimePoint(deadline).raw_time(), tag); + } + + /// Trigger an alarm instance on completion queue \a cq at the specified time. + /// Once the alarm expires (at \a deadline) or it's cancelled (see \a Cancel), + /// an event with tag \a tag will be added to \a cq. If the alarm expired, the + /// event's success bit will be true, false otherwise (ie, upon cancellation). + template + void Set(::grpc::CompletionQueue* cq, const T& deadline, void* tag) { + SetInternal(cq, ::grpc::TimePoint(deadline).raw_time(), tag); + } + + /// Alarms aren't copyable. + Alarm(const Alarm&) = delete; + Alarm& operator=(const Alarm&) = delete; + + /// Alarms are movable. + Alarm(Alarm&& rhs) : alarm_(rhs.alarm_) { rhs.alarm_ = nullptr; } + Alarm& operator=(Alarm&& rhs) { + alarm_ = rhs.alarm_; + rhs.alarm_ = nullptr; + return *this; + } + + /// Cancel a completion queue alarm. Calling this function over an alarm that + /// has already fired has no effect. + void Cancel(); + + /// NOTE: class experimental_type is not part of the public API of this class + /// TODO(vjpai): Move these contents to the public API of Alarm when + /// they are no longer experimental + class experimental_type { + public: + explicit experimental_type(Alarm* alarm) : alarm_(alarm) {} + + /// Set an alarm to invoke callback \a f. The argument to the callback + /// states whether the alarm expired at \a deadline (true) or was cancelled + /// (false) + template + void Set(const T& deadline, std::function f) { + alarm_->SetInternal(::grpc::TimePoint(deadline).raw_time(), + std::move(f)); + } + + private: + Alarm* alarm_; + }; + + /// NOTE: The function experimental() is not stable public API. It is a view + /// to the experimental components of this class. It may be changed or removed + /// at any time. + experimental_type experimental() { return experimental_type(this); } + + private: + void SetInternal(::grpc::CompletionQueue* cq, gpr_timespec deadline, + void* tag); + void SetInternal(gpr_timespec deadline, std::function f); + + ::grpc::internal::CompletionQueueTag* alarm_; +}; + +} // namespace grpc_impl + +#endif // GRPCPP_ALARM_IMPL_H diff --git a/third_party/grpc/include/grpcpp/channel.h b/third_party/grpc/include/grpcpp/channel.h new file mode 100644 index 00000000000000..ee833960698a55 --- /dev/null +++ b/third_party/grpc/include/grpcpp/channel.h @@ -0,0 +1,114 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_CHANNEL_H +#define GRPCPP_CHANNEL_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +struct grpc_channel; + +namespace grpc { + +namespace experimental { +/// Resets the channel's connection backoff. +/// TODO(roth): Once we see whether this proves useful, either create a gRFC +/// and change this to be a method of the Channel class, or remove it. +void ChannelResetConnectionBackoff(Channel* channel); +} // namespace experimental + +/// Channels represent a connection to an endpoint. Created by \a CreateChannel. +class Channel final : public ChannelInterface, + public internal::CallHook, + public std::enable_shared_from_this, + private GrpcLibraryCodegen { + 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) override; + + /// Returns the LB policy name, or the empty string if not yet available. + grpc::string GetLoadBalancingPolicyName() const; + + /// Returns the service config in JSON form, or the empty string if + /// not available. + grpc::string GetServiceConfigJSON() const; + + private: + template + friend class internal::BlockingUnaryCallImpl; + friend void experimental::ChannelResetConnectionBackoff(Channel* channel); + friend std::shared_ptr CreateChannelInternal( + const grpc::string& host, grpc_channel* c_channel, + std::vector< + std::unique_ptr> + interceptor_creators); + friend class internal::InterceptedChannel; + Channel(const grpc::string& host, grpc_channel* c_channel, + std::vector< + std::unique_ptr> + interceptor_creators); + + internal::Call CreateCall(const internal::RpcMethod& method, + ClientContext* context, + CompletionQueue* cq) override; + void PerformOpsOnCall(internal::CallOpSetInterface* ops, + internal::Call* call) override; + void* RegisterMethod(const char* method) override; + + void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline, CompletionQueue* cq, + void* tag) override; + bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline) override; + + CompletionQueue* CallbackCQ() override; + + internal::Call CreateCallInternal(const internal::RpcMethod& method, + ClientContext* context, CompletionQueue* cq, + size_t interceptor_pos) override; + + const grpc::string host_; + grpc_channel* const c_channel_; // owned + + // mu_ protects callback_cq_ (the per-channel callbackable completion queue) + std::mutex mu_; + + // callback_cq_ references the callbackable completion queue associated + // with this channel (if any). It is set on the first call to CallbackCQ(). + // It is _not owned_ by the channel; ownership belongs with its internal + // shutdown callback tag (invoked when the CQ is fully shutdown). + CompletionQueue* callback_cq_ = nullptr; + + std::vector> + interceptor_creators_; +}; + +} // namespace grpc + +#endif // GRPCPP_CHANNEL_H diff --git a/third_party/grpc/include/grpcpp/client_context.h b/third_party/grpc/include/grpcpp/client_context.h new file mode 100644 index 00000000000000..1994fcc6bc8f2a --- /dev/null +++ b/third_party/grpc/include/grpcpp/client_context.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// 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 persistent at channel construction time +/// (see \a grpc::CreateCustomChannel). +/// +/// \warning ClientContext instances should \em not be reused across rpcs. + +#ifndef GRPCPP_CLIENT_CONTEXT_H +#define GRPCPP_CLIENT_CONTEXT_H + +#include + +#endif // GRPCPP_CLIENT_CONTEXT_H diff --git a/third_party/grpc/include/grpcpp/completion_queue.h b/third_party/grpc/include/grpcpp/completion_queue.h new file mode 100644 index 00000000000000..123b277f5f0cb8 --- /dev/null +++ b/third_party/grpc/include/grpcpp/completion_queue.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_COMPLETION_QUEUE_H +#define GRPCPP_COMPLETION_QUEUE_H + +#include + +#endif // GRPCPP_COMPLETION_QUEUE_H diff --git a/third_party/grpc/include/grpcpp/create_channel.h b/third_party/grpc/include/grpcpp/create_channel.h new file mode 100644 index 00000000000000..e8a2a70581d87f --- /dev/null +++ b/third_party/grpc/include/grpcpp/create_channel.h @@ -0,0 +1,79 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_CREATE_CHANNEL_H +#define GRPCPP_CREATE_CHANNEL_H + +#include + +#include +#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 (one on which all operations +/// fail) is returned. +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 (one on which all operations +/// fail) 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 experimental { +/// Create a new \em custom \a Channel pointing to \a target with \a +/// interceptors being invoked per call. +/// +/// \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 (one on which all operations +/// fail) is returned. +/// \param args Options for channel creation. +std::shared_ptr CreateCustomChannelWithInterceptors( + const grpc::string& target, + const std::shared_ptr& creds, + const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators); +} // namespace experimental +} // namespace grpc + +#endif // GRPCPP_CREATE_CHANNEL_H diff --git a/third_party/grpc/include/grpcpp/create_channel_posix.h b/third_party/grpc/include/grpcpp/create_channel_posix.h new file mode 100644 index 00000000000000..808514041b8cd4 --- /dev/null +++ b/third_party/grpc/include/grpcpp/create_channel_posix.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_CREATE_CHANNEL_POSIX_H +#define GRPCPP_CREATE_CHANNEL_POSIX_H + +#include + +#include +#include +#include + +namespace grpc { + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +/// Create a new \a Channel communicating over the given file descriptor. +/// +/// \param target The name of the target. +/// \param fd The file descriptor representing a socket. +std::shared_ptr CreateInsecureChannelFromFd(const grpc::string& target, + int fd); + +/// Create a new \a Channel communicating over given file descriptor with custom +/// channel arguments. +/// +/// \param target The name of the target. +/// \param fd The file descriptor representing a socket. +/// \param args Options for channel creation. +std::shared_ptr CreateCustomInsecureChannelFromFd( + const grpc::string& target, int fd, const ChannelArguments& args); + +namespace experimental { + +/// Create a new \a Channel communicating over given file descriptor with custom +/// channel arguments. +/// +/// \param target The name of the target. +/// \param fd The file descriptor representing a socket. +/// \param args Options for channel creation. +/// \param interceptor_creators Vector of interceptor factory objects. +std::shared_ptr CreateCustomInsecureChannelWithInterceptorsFromFd( + const grpc::string& target, int fd, const ChannelArguments& args, + std::unique_ptr>> + interceptor_creators); + +} // namespace experimental + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD + +} // namespace grpc + +#endif // GRPCPP_CREATE_CHANNEL_POSIX_H diff --git a/third_party/grpc/include/grpcpp/ext/channelz_service_plugin.h b/third_party/grpc/include/grpcpp/ext/channelz_service_plugin.h new file mode 100644 index 00000000000000..af3192d4513e07 --- /dev/null +++ b/third_party/grpc/include/grpcpp/ext/channelz_service_plugin.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_EXT_CHANNELZ_SERVICE_PLUGIN_H +#define GRPCPP_EXT_CHANNELZ_SERVICE_PLUGIN_H + +#include + +#include +#include +#include + +namespace grpc { +namespace channelz { +namespace experimental { + +/// Add channelz server plugin to \a ServerBuilder. This function should +/// be called at static initialization time. This service is experimental +/// for now. Track progress in https://github.com/grpc/grpc/issues/15988. +void InitChannelzService(); + +} // namespace experimental +} // namespace channelz +} // namespace grpc + +#endif // GRPCPP_EXT_CHANNELZ_SERVICE_PLUGIN_H diff --git a/third_party/grpc/include/grpcpp/ext/health_check_service_server_builder_option.h b/third_party/grpc/include/grpcpp/ext/health_check_service_server_builder_option.h new file mode 100644 index 00000000000000..dd43b05ef0769f --- /dev/null +++ b/third_party/grpc/include/grpcpp/ext/health_check_service_server_builder_option.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H +#define GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H + +#include + +#include +#include +#include + +namespace grpc { + +class HealthCheckServiceServerBuilderOption : public ServerBuilderOption { + public: + /// The ownership of \a hc will be taken and transferred to the grpc server. + /// To explicitly disable default service, pass in a nullptr. + explicit HealthCheckServiceServerBuilderOption( + std::unique_ptr hc); + ~HealthCheckServiceServerBuilderOption() override {} + void UpdateArguments(ChannelArguments* args) override; + void UpdatePlugins( + std::vector>* plugins) override; + + private: + std::unique_ptr hc_; +}; + +} // namespace grpc + +#endif // GRPCPP_EXT_HEALTH_CHECK_SERVICE_SERVER_BUILDER_OPTION_H diff --git a/third_party/grpc/include/grpcpp/ext/proto_server_reflection_plugin.h b/third_party/grpc/include/grpcpp/ext/proto_server_reflection_plugin.h new file mode 100644 index 00000000000000..1cfdc1b6ccfe4a --- /dev/null +++ b/third_party/grpc/include/grpcpp/ext/proto_server_reflection_plugin.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H +#define GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H + +#include +#include + +namespace grpc { +class ServerInitializer; +class ProtoServerReflection; +} // namespace grpc + +namespace grpc { +namespace reflection { + +class ProtoServerReflectionPlugin : public ::grpc::ServerBuilderPlugin { + public: + ProtoServerReflectionPlugin(); + ::grpc::string name() override; + void InitServer(::grpc::ServerInitializer* si) override; + void Finish(::grpc::ServerInitializer* si) override; + void ChangeArguments(const ::grpc::string& name, void* value) override; + bool has_async_methods() const override; + bool has_sync_methods() const override; + + private: + std::shared_ptr reflection_service_; +}; + +/// Add proto reflection plugin to \a ServerBuilder. +/// This function should be called at the static initialization time. +void InitProtoReflectionServerBuilderPlugin(); + +} // namespace reflection +} // namespace grpc + +#endif // GRPCPP_EXT_PROTO_SERVER_REFLECTION_PLUGIN_H diff --git a/third_party/grpc/include/grpcpp/ext/server_load_reporting.h b/third_party/grpc/include/grpcpp/ext/server_load_reporting.h new file mode 100644 index 00000000000000..939569c19aaf16 --- /dev/null +++ b/third_party/grpc/include/grpcpp/ext/server_load_reporting.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_EXT_SERVER_LOAD_REPORTING_H +#define GRPCPP_EXT_SERVER_LOAD_REPORTING_H + +#include + +#include +#include +#include +#include + +namespace grpc { +namespace load_reporter { +namespace experimental { + +// The ServerBuilderOption to enable server-side load reporting feature. To +// enable the feature, please make sure the binary builds with the +// grpcpp_server_load_reporting library and set this option in the +// ServerBuilder. +class LoadReportingServiceServerBuilderOption : public ServerBuilderOption { + public: + void UpdateArguments(::grpc::ChannelArguments* args) override; + void UpdatePlugins(std::vector>* + plugins) override; +}; + +// Adds the load reporting cost with \a cost_name and \a cost_value in the +// trailing metadata of the server context. +void AddLoadReportingCost(grpc::ServerContext* ctx, + const grpc::string& cost_name, double cost_value); + +} // namespace experimental +} // namespace load_reporter +} // namespace grpc + +#endif // GRPCPP_EXT_SERVER_LOAD_REPORTING_H diff --git a/third_party/grpc/include/grpcpp/generic/async_generic_service.h b/third_party/grpc/include/grpcpp/generic/async_generic_service.h new file mode 100644 index 00000000000000..2c67edc5170225 --- /dev/null +++ b/third_party/grpc/include/grpcpp/generic/async_generic_service.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H +#define GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H + +#include + +#endif // GRPCPP_GENERIC_ASYNC_GENERIC_SERVICE_H diff --git a/third_party/grpc/include/grpcpp/generic/generic_stub.h b/third_party/grpc/include/grpcpp/generic/generic_stub.h new file mode 100644 index 00000000000000..eb014184e4a6a3 --- /dev/null +++ b/third_party/grpc/include/grpcpp/generic/generic_stub.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_GENERIC_GENERIC_STUB_H +#define GRPCPP_GENERIC_GENERIC_STUB_H + +#include + +#include +#include +#include +#include +#include + +namespace grpc { + +class CompletionQueue; +typedef ClientAsyncReaderWriter + GenericClientAsyncReaderWriter; +typedef ClientAsyncResponseReader GenericClientAsyncResponseReader; + +/// Generic stubs provide a type-unsafe interface to call gRPC methods +/// by name. +class GenericStub final { + public: + explicit GenericStub(std::shared_ptr channel) + : channel_(channel) {} + + /// Setup a call to a named method \a method using \a context, but don't + /// start it. Let it be started explicitly with StartCall and a tag. + /// The return value only indicates whether or not registration of the call + /// succeeded (i.e. the call won't proceed if the return value is nullptr). + std::unique_ptr PrepareCall( + ClientContext* context, const grpc::string& method, CompletionQueue* cq); + + /// Setup a unary call to a named method \a method using \a context, and don't + /// start it. Let it be started explicitly with StartCall. + /// The return value only indicates whether or not registration of the call + /// succeeded (i.e. the call won't proceed if the return value is nullptr). + std::unique_ptr PrepareUnaryCall( + ClientContext* context, const grpc::string& method, + const ByteBuffer& request, CompletionQueue* cq); + + /// DEPRECATED for multi-threaded use + /// Begin a call to a named method \a method using \a context. + /// A tag \a tag will be delivered to \a cq when the call has been started + /// (i.e, initial metadata has been sent). + /// The return value only indicates whether or not registration of the call + /// succeeded (i.e. the call won't proceed if the return value is nullptr). + std::unique_ptr Call( + ClientContext* context, const grpc::string& method, CompletionQueue* cq, + void* tag); + + /// NOTE: class experimental_type is not part of the public API of this class + /// TODO(vjpai): Move these contents to the public API of GenericStub when + /// they are no longer experimental + class experimental_type { + public: + explicit experimental_type(GenericStub* stub) : stub_(stub) {} + + void UnaryCall(ClientContext* context, const grpc::string& method, + const ByteBuffer* request, ByteBuffer* response, + std::function on_completion); + + void PrepareBidiStreamingCall( + ClientContext* context, const grpc::string& method, + experimental::ClientBidiReactor* reactor); + + private: + GenericStub* stub_; + }; + + /// NOTE: The function experimental() is not stable public API. It is a view + /// to the experimental components of this class. It may be changed or removed + /// at any time. + experimental_type experimental() { return experimental_type(this); } + + private: + std::shared_ptr channel_; +}; + +} // namespace grpc + +#endif // GRPCPP_GENERIC_GENERIC_STUB_H diff --git a/third_party/grpc/include/grpcpp/grpcpp.h b/third_party/grpc/include/grpcpp/grpcpp.h new file mode 100644 index 00000000000000..aa8a24203bce68 --- /dev/null +++ b/third_party/grpc/include/grpcpp/grpcpp.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// \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](https://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. +/// +/// Streaming calls are handled with the streaming classes in +/// \ref sync_stream.h and +/// \ref async_stream.h. +/// +/// Refer to the +/// [examples](https://github.com/grpc/grpc/blob/master/examples/cpp) +/// for code putting these pieces into play. + +#ifndef GRPCPP_GRPCPP_H +#define GRPCPP_GRPCPP_H + +// Pragma for http://include-what-you-use.org/ tool, tells that following +// headers are not private for grpcpp.h and are part of its interface. +// IWYU pragma: begin_exports +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// IWYU pragma: end_exports + +namespace grpc { +/// Return gRPC library version. +grpc::string Version(); +} // namespace grpc + +#endif // GRPCPP_GRPCPP_H diff --git a/third_party/grpc/include/grpcpp/health_check_service_interface.h b/third_party/grpc/include/grpcpp/health_check_service_interface.h new file mode 100644 index 00000000000000..dfd4c3983af29a --- /dev/null +++ b/third_party/grpc/include/grpcpp/health_check_service_interface.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H +#define GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H + +#include + +namespace grpc { + +const char kHealthCheckServiceInterfaceArg[] = + "grpc.health_check_service_interface"; + +/// The gRPC server uses this interface to expose the health checking service +/// without depending on protobuf. +class HealthCheckServiceInterface { + public: + virtual ~HealthCheckServiceInterface() {} + + /// Set or change the serving status of the given \a service_name. + virtual void SetServingStatus(const grpc::string& service_name, + bool serving) = 0; + /// Apply to all registered service names. + virtual void SetServingStatus(bool serving) = 0; + + /// Set all registered service names to not serving and prevent future + /// state changes. + virtual void Shutdown() {} +}; + +/// Enable/disable the default health checking service. This applies to all C++ +/// servers created afterwards. For each server, user can override the default +/// with a HealthCheckServiceServerBuilderOption. +/// NOT thread safe. +void EnableDefaultHealthCheckService(bool enable); + +/// Returns whether the default health checking service is enabled. +/// NOT thread safe. +bool DefaultHealthCheckServiceEnabled(); + +} // namespace grpc + +#endif // GRPCPP_HEALTH_CHECK_SERVICE_INTERFACE_H diff --git a/third_party/grpc/include/grpc++/impl/README.md b/third_party/grpc/include/grpcpp/impl/README.md similarity index 100% rename from third_party/grpc/include/grpc++/impl/README.md rename to third_party/grpc/include/grpcpp/impl/README.md diff --git a/third_party/grpc/include/grpcpp/impl/call.h b/third_party/grpc/include/grpcpp/impl/call.h new file mode 100644 index 00000000000000..a6b13126934b8c --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/call.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CALL_H +#define GRPCPP_IMPL_CALL_H + +#include + +#endif // GRPCPP_IMPL_CALL_H diff --git a/third_party/grpc/include/grpcpp/impl/channel_argument_option.h b/third_party/grpc/include/grpcpp/impl/channel_argument_option.h new file mode 100644 index 00000000000000..0c4882478e2d8b --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/channel_argument_option.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H +#define GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H + +#include +#include + +#include +#include + +namespace grpc { + +std::unique_ptr MakeChannelArgumentOption( + const grpc::string& name, const grpc::string& value); +std::unique_ptr MakeChannelArgumentOption( + const grpc::string& name, int value); + +} // namespace grpc + +#endif // GRPCPP_IMPL_CHANNEL_ARGUMENT_OPTION_H diff --git a/third_party/grpc/include/grpcpp/impl/client_unary_call.h b/third_party/grpc/include/grpcpp/impl/client_unary_call.h new file mode 100644 index 00000000000000..378482c54038f0 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/client_unary_call.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CLIENT_UNARY_CALL_H +#define GRPCPP_IMPL_CLIENT_UNARY_CALL_H + +#include + +#endif // GRPCPP_IMPL_CLIENT_UNARY_CALL_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/async_generic_service.h b/third_party/grpc/include/grpcpp/impl/codegen/async_generic_service.h new file mode 100644 index 00000000000000..2a0e1b408814f3 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/async_generic_service.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H +#define GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H + +#include +#include + +struct grpc_server; + +namespace grpc { + +typedef ServerAsyncReaderWriter + GenericServerAsyncReaderWriter; +typedef ServerAsyncResponseWriter GenericServerAsyncResponseWriter; +typedef ServerAsyncReader GenericServerAsyncReader; +typedef ServerAsyncWriter GenericServerAsyncWriter; + +class GenericServerContext final : public ServerContext { + public: + const grpc::string& method() const { return method_; } + const grpc::string& host() const { return host_; } + + private: + friend class Server; + friend class ServerInterface; + + grpc::string method_; + grpc::string host_; +}; + +// A generic service at the server side accepts all RPC methods and hosts. It is +// typically used in proxies. The generic service can be registered to a server +// which also has other services. +// Sample usage: +// ServerBuilder builder; +// auto cq = builder.AddCompletionQueue(); +// AsyncGenericService generic_service; +// builder.RegisterAsyncGenericService(&generic_service); +// auto server = builder.BuildAndStart(); +// +// // request a new call +// GenericServerContext context; +// GenericServerAsyncReaderWriter stream; +// generic_service.RequestCall(&context, &stream, cq.get(), cq.get(), tag); +// +// When tag is retrieved from cq->Next(), context.method() can be used to look +// at the method and the RPC can be handled accordingly. +class AsyncGenericService final { + public: + AsyncGenericService() : 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 // GRPCPP_IMPL_CODEGEN_ASYNC_GENERIC_SERVICE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/async_stream.h b/third_party/grpc/include/grpcpp/impl/codegen/async_stream.h new file mode 100644 index 00000000000000..bfb2df4f232bcd --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/async_stream.h @@ -0,0 +1,1134 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H +#define GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H + +#include +#include +#include +#include +#include +#include + +namespace grpc { + +class CompletionQueue; + +namespace internal { +/// Common interface for all client side asynchronous streaming. +class ClientAsyncStreamingInterface { + public: + virtual ~ClientAsyncStreamingInterface() {} + + /// Start the call that was set up by the constructor, but only if the + /// constructor was invoked through the "Prepare" API which doesn't actually + /// start the call + virtual void StartCall(void* tag) = 0; + + /// Request notification of the reading of the initial metadata. Completion + /// will be notified by \a tag on the associated completion queue. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a AsyncReaderInterface::Read method. + /// + /// \param[in] tag Tag identifying this request. + virtual void ReadInitialMetadata(void* tag) = 0; + + /// Indicate that the stream is to be finished and request notification for + /// when the call has been ended. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when both: + /// * the client side has no more message to send + /// (this can be declared implicitly by calling this method, or + /// explicitly through an earlier call to the WritesDone method + /// of the class in use, e.g. \a ClientAsyncWriterInterface::WritesDone or + /// \a ClientAsyncReaderWriterInterface::WritesDone). + /// * there are no more messages to be received from the server (this can + /// be known implicitly by the calling code, or explicitly from an + /// earlier call to \a AsyncReaderInterface::Read that yielded a failed + /// result, e.g. cq->Next(&read_tag, &ok) filled in 'ok' with 'false'). + /// + /// The tag will be returned when either: + /// - all incoming messages have been read and the server has returned + /// a status. + /// - the server has returned a non-OK status. + /// - the call failed for some reason and the library generated a + /// status. + /// + /// Note that implementations of this method attempt to receive initial + /// metadata from the server if initial metadata hasn't yet been received. + /// + /// \param[in] tag Tag identifying this request. + /// \param[out] status To be updated with the operation status. + 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. + /// This is thread-safe with respect to \a Write or \a WritesDone methods. It + /// should not be called concurrently with other streaming APIs + /// on the same stream. It is not meaningful to call it concurrently + /// with another \a AsyncReaderInterface::Read on the same stream since reads + /// on the same stream are delivered in order. + /// + /// \param[out] msg Where to eventually store the read message. + /// \param[in] tag The tag identifying the operation. + /// + /// Side effect: note that this method attempt to receive initial metadata for + /// a stream if it hasn't yet been received. + 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. + /// + /// Only one write may be outstanding at any given time. This means that + /// after calling Write, one must wait to receive \a tag from the completion + /// queue BEFORE calling Write again. + /// This is thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// + /// \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; + + /// Request the writing of \a msg using WriteOptions \a options with + /// identifying tag \a tag. + /// + /// Only one write may be outstanding at any given time. This means that + /// after calling Write, one must wait to receive \a tag from the completion + /// queue BEFORE calling Write again. + /// WriteOptions \a options is used to set the write options of this message. + /// This is thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] tag The tag identifying the operation. + virtual void Write(const W& msg, WriteOptions options, void* tag) = 0; + + /// Request the writing of \a msg and coalesce it with the writing + /// of trailing metadata, using WriteOptions \a options with + /// identifying tag \a tag. + /// + /// For client, WriteLast is equivalent of performing Write and + /// WritesDone in a single step. + /// For server, WriteLast buffers the \a msg. The writing of \a msg is held + /// until Finish is called, where \a msg and trailing metadata are coalesced + /// and write is initiated. Note that WriteLast can only buffer \a msg up to + /// the flow control window size. If \a msg size is larger than the window + /// size, it will be sent on wire without buffering. + /// + /// gRPC doesn't take ownership or a reference to \a msg, so it is safe to + /// to deallocate once Write returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] tag The tag identifying the operation. + void WriteLast(const W& msg, WriteOptions options, void* tag) { + Write(msg, options.set_last_message(), tag); + } +}; + +} // namespace internal + +template +class ClientAsyncReaderInterface + : public internal::ClientAsyncStreamingInterface, + public internal::AsyncReaderInterface {}; + +namespace internal { +template +class ClientAsyncReaderFactory { + public: + /// Create a stream object. + /// Write the first request out if \a start is set. + /// \a tag will be notified on \a cq when the call has been started and + /// \a request has been written out. If \a start is not set, \a tag must be + /// nullptr and the actual call must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + template + static ClientAsyncReader* Create(ChannelInterface* channel, + CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, + ClientContext* context, const W& request, + bool start, void* tag) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + return new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncReader))) + ClientAsyncReader(call, context, request, start, tag); + } +}; +} // namespace internal + +/// Async client-side API for doing server-streaming RPCs, +/// where the incoming message stream coming from the server has +/// messages of type \a R. +template +class ClientAsyncReader final : public ClientAsyncReaderInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncReader)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall(void* tag) override { + assert(!started_); + started_ = true; + StartCallInternal(tag); + } + + /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata + /// method for semantics. + /// + /// Side effect: + /// - upon receiving initial metadata from the server, + /// the \a ClientContext associated with this call is updated, and the + /// calling code can access the received metadata through the + /// \a ClientContext. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_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) override { + assert(started_); + read_ops_.set_output_tag(tag); + if (!context_->initial_metadata_received_) { + read_ops_.RecvInitialMetadata(context_); + } + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata received from the server. + void Finish(Status* status, void* tag) override { + assert(started_); + 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: + friend class internal::ClientAsyncReaderFactory; + template + ClientAsyncReader(::grpc::internal::Call call, ClientContext* context, + const W& request, bool start, void* tag) + : context_(context), call_(call), started_(start) { + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(init_ops_.SendMessage(request).ok()); + init_ops_.ClientSendClose(); + if (start) { + StartCallInternal(tag); + } else { + assert(tag == nullptr); + } + } + + void StartCallInternal(void* tag) { + init_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + init_ops_.set_output_tag(tag); + call_.PerformOps(&init_ops_); + } + + ClientContext* context_; + ::grpc::internal::Call call_; + bool started_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + init_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; +}; + +/// Common interface for client side asynchronous writing. +template +class ClientAsyncWriterInterface + : public internal::ClientAsyncStreamingInterface, + public internal::AsyncWriterInterface { + public: + /// Signal the client is done with the writes (half-close the client stream). + /// Thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// \param[in] tag The tag identifying the operation. + virtual void WritesDone(void* tag) = 0; +}; + +namespace internal { +template +class ClientAsyncWriterFactory { + public: + /// Create a stream object. + /// Start the RPC if \a start is set + /// \a tag will be notified on \a cq when the call has been started (i.e. + /// intitial metadata sent) and \a request has been written out. + /// If \a start is not set, \a tag must be nullptr and the actual call + /// must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + /// \a response will be filled in with the single expected response + /// message from the server upon a successful call to the \a Finish + /// method of this instance. + template + static ClientAsyncWriter* Create(ChannelInterface* channel, + CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, + ClientContext* context, R* response, + bool start, void* tag) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + return new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncWriter))) + ClientAsyncWriter(call, context, response, start, tag); + } +}; +} // namespace internal + +/// Async API on the client side for doing client-streaming RPCs, +/// where the outgoing message stream going to the server contains +/// messages of type \a W. +template +class ClientAsyncWriter final : public ClientAsyncWriterInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncWriter)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall(void* tag) override { + assert(!started_); + started_ = true; + StartCallInternal(tag); + } + + /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method for + /// semantics. + /// + /// Side effect: + /// - upon receiving initial metadata from the server, the \a ClientContext + /// associated with this call is updated, and the calling code can access + /// the received metadata through the \a ClientContext. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_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) override { + assert(started_); + write_ops_.set_output_tag(tag); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, WriteOptions options, void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WritesDone(void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + write_ops_.ClientSendClose(); + call_.PerformOps(&write_ops_); + } + + /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata received from the server. + /// - attempts to fill in the \a response parameter passed to this class's + /// constructor with the server's response message. + void Finish(Status* status, void* tag) override { + assert(started_); + 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: + friend class internal::ClientAsyncWriterFactory; + template + ClientAsyncWriter(::grpc::internal::Call call, ClientContext* context, + R* response, bool start, void* tag) + : context_(context), call_(call), started_(start) { + finish_ops_.RecvMessage(response); + finish_ops_.AllowNoMessage(); + if (start) { + StartCallInternal(tag); + } else { + assert(tag == nullptr); + } + } + + void StartCallInternal(void* tag) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + // if corked bit is set in context, we just keep the initial metadata + // buffered up to coalesce with later message send. No op is performed. + if (!context_->initial_metadata_corked_) { + write_ops_.set_output_tag(tag); + call_.PerformOps(&write_ops_); + } + } + + ClientContext* context_; + ::grpc::internal::Call call_; + bool started_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpGenericRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; +}; + +/// Async client-side interface for bi-directional streaming, +/// where the client-to-server message stream has messages of type \a W, +/// and the server-to-client message stream has messages of type \a R. +template +class ClientAsyncReaderWriterInterface + : public internal::ClientAsyncStreamingInterface, + public internal::AsyncWriterInterface, + public internal::AsyncReaderInterface { + public: + /// Signal the client is done with the writes (half-close the client stream). + /// Thread-safe with respect to \a AsyncReaderInterface::Read + /// + /// \param[in] tag The tag identifying the operation. + virtual void WritesDone(void* tag) = 0; +}; + +namespace internal { +template +class ClientAsyncReaderWriterFactory { + public: + /// Create a stream object. + /// Start the RPC request if \a start is set. + /// \a tag will be notified on \a cq when the call has been started (i.e. + /// intitial metadata sent). If \a start is not set, \a tag must be + /// nullptr and the actual call must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + static ClientAsyncReaderWriter* Create( + ChannelInterface* channel, CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, ClientContext* context, + bool start, void* tag) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + + return new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncReaderWriter))) + ClientAsyncReaderWriter(call, context, start, tag); + } +}; +} // namespace internal + +/// Async client-side interface for bi-directional streaming, +/// where the outgoing message stream going to the server +/// has messages of type \a W, and the incoming message stream coming +/// from the server has messages of type \a R. +template +class ClientAsyncReaderWriter final + : public ClientAsyncReaderWriterInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncReaderWriter)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall(void* tag) override { + assert(!started_); + started_ = true; + StartCallInternal(tag); + } + + /// See the \a ClientAsyncStreamingInterface.ReadInitialMetadata method + /// for semantics of this method. + /// + /// Side effect: + /// - upon receiving initial metadata from the server, the \a ClientContext + /// is updated with it, and then the receiving initial metadata can + /// be accessed through this \a ClientContext. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_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) override { + assert(started_); + 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) override { + assert(started_); + write_ops_.set_output_tag(tag); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, WriteOptions options, void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WritesDone(void* tag) override { + assert(started_); + write_ops_.set_output_tag(tag); + write_ops_.ClientSendClose(); + call_.PerformOps(&write_ops_); + } + + /// See the \a ClientAsyncStreamingInterface.Finish method for semantics. + /// Side effect + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata sent from the server. + void Finish(Status* status, void* tag) override { + assert(started_); + 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: + friend class internal::ClientAsyncReaderWriterFactory; + ClientAsyncReaderWriter(::grpc::internal::Call call, ClientContext* context, + bool start, void* tag) + : context_(context), call_(call), started_(start) { + if (start) { + StartCallInternal(tag); + } else { + assert(tag == nullptr); + } + } + + void StartCallInternal(void* tag) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + // if corked bit is set in context, we just keep the initial metadata + // buffered up to coalesce with later message send. No op is performed. + if (!context_->initial_metadata_corked_) { + write_ops_.set_output_tag(tag); + call_.PerformOps(&write_ops_); + } + } + + ClientContext* context_; + ::grpc::internal::Call call_; + bool started_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; +}; + +template +class ServerAsyncReaderInterface + : public internal::ServerAsyncStreamingInterface, + public internal::AsyncReaderInterface { + public: + /// Indicate that the stream is to be finished with a certain status code + /// and also send out \a msg response to the client. + /// Request notification for when the server has sent the response and the + /// appropriate signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when: + /// * all messages from the client have been received (either known + /// implictly, or explicitly because a previous + /// \a AsyncReaderInterface::Read operation with a non-ok result, + /// e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'). + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), response message, and status, or if + /// some failure occurred when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a msg or \a status, so it + /// is safe to to deallocate once Finish returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + /// \param[in] msg To be sent to the client as the response for this call. + virtual void Finish(const W& msg, const Status& status, void* tag) = 0; + + /// Indicate that the stream is to be finished with a certain + /// non-OK status code. + /// Request notification for when the server has sent the appropriate + /// signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// This call is meant to end the call with some error, and can be called at + /// any point that the server would like to "fail" the call (though note + /// this shouldn't be called concurrently with any other "sending" call, like + /// \a AsyncWriterInterface::Write). + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), and status, or if some failure occurred + /// when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once FinishWithError returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + /// - Note: \a status must have a non-OK code. + virtual void FinishWithError(const Status& status, void* tag) = 0; +}; + +/// Async server-side API for doing client-streaming RPCs, +/// where the incoming message stream from the client has messages of type \a R, +/// and the single response message sent from the server is type \a W. +template +class ServerAsyncReader final : public ServerAsyncReaderInterface { + public: + explicit ServerAsyncReader(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Implicit input parameter: + /// - The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) override { + read_ops_.set_output_tag(tag); + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + /// See the \a ServerAsyncReaderInterface.Read method for semantics + /// + /// Side effect: + /// - also sends initial metadata if not alreay sent. + /// - uses the \a ServerContext associated with this call to send possible + /// initial and trailing metadata. + /// + /// Note: \a msg is not sent if \a status has a non-OK code. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once Finish returns. + void Finish(const W& msg, const Status& status, void* tag) override { + finish_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + 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_); + } + + /// See the \a ServerAsyncReaderInterface.Read method for semantics + /// + /// Side effect: + /// - also sends initial metadata if not alreay sent. + /// - uses the \a ServerContext associated with this call to send possible + /// initial and trailing metadata. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once FinishWithError returns. + void FinishWithError(const Status& status, void* tag) override { + GPR_CODEGEN_ASSERT(!status.ok()); + finish_ops_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + ::grpc::internal::Call call_; + ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; +}; + +template +class ServerAsyncWriterInterface + : public internal::ServerAsyncStreamingInterface, + public internal::AsyncWriterInterface { + public: + /// Indicate that the stream is to be finished with a certain status code. + /// Request notification for when the server has sent the appropriate + /// signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when either: + /// * all messages from the client have been received (either known + /// implictly, or explicitly because a previous \a + /// AsyncReaderInterface::Read operation with a non-ok + /// result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' with 'false'. + /// * it is desired to end the call early with some non-OK status code. + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), response message, and status, or if + /// some failure occurred when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + virtual void Finish(const Status& status, void* tag) = 0; + + /// Request the writing of \a msg and coalesce it with trailing metadata which + /// contains \a status, using WriteOptions options with + /// identifying tag \a tag. + /// + /// WriteAndFinish is equivalent of performing WriteLast and Finish + /// in a single step. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] status The Status that server returns to client. + /// \param[in] tag The tag identifying the operation. + virtual void WriteAndFinish(const W& msg, WriteOptions options, + const Status& status, void* tag) = 0; +}; + +/// Async server-side API for doing server streaming RPCs, +/// where the outgoing message stream from the server has messages of type \a W. +template +class ServerAsyncWriter final : public ServerAsyncWriterInterface { + public: + explicit ServerAsyncWriter(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Implicit input parameter: + /// - The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + /// + /// \param[in] tag Tag identifying this request. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_ops_); + } + + void Write(const W& msg, void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, WriteOptions options, void* tag) override { + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + + EnsureInitialMetadataSent(&write_ops_); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncWriterInterface.WriteAndFinish method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used + /// for sending trailing (and initial) metadata to the client. + /// + /// Note: \a status must have an OK code. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. + void WriteAndFinish(const W& msg, WriteOptions options, const Status& status, + void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + options.set_buffer_hint(); + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncWriterInterface.Finish method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used for sending + /// trailing (and initial if not already sent) metadata to the client. + /// + /// Note: there are no restrictions are the code of + /// \a status,it may be non-OK + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + void Finish(const Status& status, void* tag) override { + finish_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&finish_ops_); + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + template + void EnsureInitialMetadataSent(T* ops) { + if (!ctx_->sent_initial_metadata_) { + ops->SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops->set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + } + + ::grpc::internal::Call call_; + ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; +}; + +/// Server-side interface for asynchronous bi-directional streaming. +template +class ServerAsyncReaderWriterInterface + : public internal::ServerAsyncStreamingInterface, + public internal::AsyncWriterInterface, + public internal::AsyncReaderInterface { + public: + /// Indicate that the stream is to be finished with a certain status code. + /// Request notification for when the server has sent the appropriate + /// signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// It is appropriate to call this method when either: + /// * all messages from the client have been received (either known + /// implictly, or explicitly because a previous \a + /// AsyncReaderInterface::Read operation + /// with a non-ok result (e.g., cq->Next(&read_tag, &ok) filled in 'ok' + /// with 'false'. + /// * it is desired to end the call early with some non-OK status code. + /// + /// This operation will end when the server has finished sending out initial + /// metadata (if not sent already), response message, and status, or if some + /// failure occurred when trying to do so. + /// + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of this call. + virtual void Finish(const Status& status, void* tag) = 0; + + /// Request the writing of \a msg and coalesce it with trailing metadata which + /// contains \a status, using WriteOptions options with + /// identifying tag \a tag. + /// + /// WriteAndFinish is equivalent of performing WriteLast and Finish in a + /// single step. + /// + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. + /// + /// \param[in] msg The message to be written. + /// \param[in] options The WriteOptions to be used to write this message. + /// \param[in] status The Status that server returns to client. + /// \param[in] tag The tag identifying the operation. + virtual void WriteAndFinish(const W& msg, WriteOptions options, + const Status& status, void* tag) = 0; +}; + +/// Async server-side API for doing bidirectional streaming RPCs, +/// where the incoming message stream coming from the client has messages of +/// type \a R, and the outgoing message stream coming from the server has +/// messages of type \a W. +template +class ServerAsyncReaderWriter final + : public ServerAsyncReaderWriterInterface { + public: + explicit ServerAsyncReaderWriter(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Implicit input parameter: + /// - The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + /// + /// \param[in] tag Tag identifying this request. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_ops_.set_output_tag(tag); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_ops_); + } + + void Read(R* msg, void* tag) override { + read_ops_.set_output_tag(tag); + read_ops_.RecvMessage(msg); + call_.PerformOps(&read_ops_); + } + + void Write(const W& msg, void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg).ok()); + call_.PerformOps(&write_ops_); + } + + void Write(const W& msg, WriteOptions options, void* tag) override { + write_ops_.set_output_tag(tag); + if (options.is_last_message()) { + options.set_buffer_hint(); + } + EnsureInitialMetadataSent(&write_ops_); + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncReaderWriterInterface.WriteAndFinish + /// method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used + /// for sending trailing (and initial) metadata to the client. + /// + /// Note: \a status must have an OK code. + // + /// gRPC doesn't take ownership or a reference to \a msg and \a status, so it + /// is safe to to deallocate once WriteAndFinish returns. + void WriteAndFinish(const W& msg, WriteOptions options, const Status& status, + void* tag) override { + write_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&write_ops_); + options.set_buffer_hint(); + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(msg, options).ok()); + write_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&write_ops_); + } + + /// See the \a ServerAsyncReaderWriterInterface.Finish method for semantics. + /// + /// Implicit input parameter: + /// - the \a ServerContext associated with this call is used for sending + /// trailing (and initial if not already sent) metadata to the client. + /// + /// Note: there are no restrictions are the code of \a status, + /// it may be non-OK + // + /// gRPC doesn't take ownership or a reference to \a status, so it is safe to + /// to deallocate once Finish returns. + void Finish(const Status& status, void* tag) override { + finish_ops_.set_output_tag(tag); + EnsureInitialMetadataSent(&finish_ops_); + + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_ops_); + } + + private: + friend class ::grpc::Server; + + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + template + void EnsureInitialMetadataSent(T* ops) { + if (!ctx_->sent_initial_metadata_) { + ops->SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops->set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + } + + ::grpc::internal::Call call_; + ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage> read_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + write_ops_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpServerSendStatus> + finish_ops_; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_ASYNC_STREAM_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/async_unary_call.h b/third_party/grpc/include/grpcpp/impl/codegen/async_unary_call.h new file mode 100644 index 00000000000000..89dcb124189b6b --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/async_unary_call.h @@ -0,0 +1,317 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H +#define GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H + +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +class CompletionQueue; +extern CoreCodegenInterface* g_core_codegen_interface; + +/// An interface relevant for async client side unary RPCs (which send +/// one request message to a server and receive one response message). +template +class ClientAsyncResponseReaderInterface { + public: + virtual ~ClientAsyncResponseReaderInterface() {} + + /// Start the call that was set up by the constructor, but only if the + /// constructor was invoked through the "Prepare" API which doesn't actually + /// start the call + virtual void StartCall() = 0; + + /// Request notification of the reading of initial metadata. Completion + /// will be notified by \a tag on the associated completion queue. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a Finish method. + /// + /// \param[in] tag Tag identifying this request. + virtual void ReadInitialMetadata(void* tag) = 0; + + /// Request to receive the server's response \a msg and final \a status for + /// the call, and to notify \a tag on this call's completion queue when + /// finished. + /// + /// This function will return when either: + /// - when the server's response message and status have been received. + /// - when the server has returned a non-OK status (no message expected in + /// this case). + /// - when the call failed for some reason and the library generated a + /// non-OK status. + /// + /// \param[in] tag Tag identifying this request. + /// \param[out] status To be updated with the operation status. + /// \param[out] msg To be filled in with the server's response message. + virtual void Finish(R* msg, Status* status, void* tag) = 0; +}; + +namespace internal { +template +class ClientAsyncResponseReaderFactory { + public: + /// Start a call and write the request out if \a start is set. + /// \a tag will be notified on \a cq when the call has been started (i.e. + /// intitial metadata sent) and \a request has been written out. + /// If \a start is not set, the actual call must be initiated by StartCall + /// Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + template + static ClientAsyncResponseReader* Create( + ChannelInterface* channel, CompletionQueue* cq, + const ::grpc::internal::RpcMethod& method, ClientContext* context, + const W& request, bool start) { + ::grpc::internal::Call call = channel->CreateCall(method, context, cq); + return new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientAsyncResponseReader))) + ClientAsyncResponseReader(call, context, request, start); + } +}; +} // namespace internal + +/// Async API for client-side unary RPCs, where the message response +/// received from the server is of type \a R. +template +class ClientAsyncResponseReader final + : public ClientAsyncResponseReaderInterface { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientAsyncResponseReader)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void StartCall() override { + assert(!started_); + started_ = true; + StartCallInternal(); + } + + /// See \a ClientAsyncResponseReaderInterface::ReadInitialMetadata for + /// semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata sent from the server. + void ReadInitialMetadata(void* tag) override { + assert(started_); + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + single_buf.set_output_tag(tag); + single_buf.RecvInitialMetadata(context_); + call_.PerformOps(&single_buf); + initial_metadata_read_ = true; + } + + /// See \a ClientAysncResponseReaderInterface::Finish for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible initial and trailing metadata sent from the server. + void Finish(R* msg, Status* status, void* tag) override { + assert(started_); + if (initial_metadata_read_) { + finish_buf.set_output_tag(tag); + finish_buf.RecvMessage(msg); + finish_buf.AllowNoMessage(); + finish_buf.ClientRecvStatus(context_, status); + call_.PerformOps(&finish_buf); + } else { + single_buf.set_output_tag(tag); + single_buf.RecvInitialMetadata(context_); + single_buf.RecvMessage(msg); + single_buf.AllowNoMessage(); + single_buf.ClientRecvStatus(context_, status); + call_.PerformOps(&single_buf); + } + } + + private: + friend class internal::ClientAsyncResponseReaderFactory; + ClientContext* const context_; + ::grpc::internal::Call call_; + bool started_; + bool initial_metadata_read_ = false; + + template + ClientAsyncResponseReader(::grpc::internal::Call call, ClientContext* context, + const W& request, bool start) + : context_(context), call_(call), started_(start) { + // Bind the metadata at time of StartCallInternal but set up the rest here + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(single_buf.SendMessage(request).ok()); + single_buf.ClientSendClose(); + if (start) StartCallInternal(); + } + + void StartCallInternal() { + single_buf.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + } + + // disable operator new + static void* operator new(std::size_t size); + static void* operator new(std::size_t size, void* p) { return p; } + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose, + ::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + single_buf; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + finish_buf; +}; + +/// Async server-side API for handling unary calls, where the single +/// response message sent to the client is of type \a W. +template +class ServerAsyncResponseWriter final + : public internal::ServerAsyncStreamingInterface { + public: + explicit ServerAsyncResponseWriter(ServerContext* ctx) + : call_(nullptr, nullptr, nullptr), ctx_(ctx) {} + + /// See \a ServerAsyncStreamingInterface::SendInitialMetadata for semantics. + /// + /// Side effect: + /// The initial metadata that will be sent to the client from this op will + /// be taken from the \a ServerContext associated with the call. + /// + /// \param[in] tag Tag identifying this request. + void SendInitialMetadata(void* tag) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + meta_buf_.set_output_tag(tag); + meta_buf_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_buf_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_.PerformOps(&meta_buf_); + } + + /// Indicate that the stream is to be finished and request notification + /// when the server has sent the appropriate signals to the client to + /// end the call. Should not be used concurrently with other operations. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of the call. + /// \param[in] msg Message to be sent to the client. + /// + /// Side effect: + /// - also sends initial metadata if not already sent (using the + /// \a ServerContext associated with this call). + /// + /// Note: if \a status has a non-OK code, then \a msg will not be sent, + /// and the client will receive only the status with possible trailing + /// metadata. + void Finish(const W& msg, const Status& status, void* tag) { + finish_buf_.set_output_tag(tag); + finish_buf_.set_core_cq_tag(&finish_buf_); + if (!ctx_->sent_initial_metadata_) { + finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_buf_.set_compression_level(ctx_->compression_level()); + } + 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_); + } + + /// Indicate that the stream is to be finished with a non-OK status, + /// and request notification for when the server has finished sending the + /// appropriate signals to the client to end the call. + /// Should not be used concurrently with other operations. + /// + /// \param[in] tag Tag identifying this request. + /// \param[in] status To be sent to the client as the result of the call. + /// - Note: \a status must have a non-OK code. + /// + /// Side effect: + /// - also sends initial metadata if not already sent (using the + /// \a ServerContext associated with this call). + void FinishWithError(const Status& status, void* tag) { + GPR_CODEGEN_ASSERT(!status.ok()); + finish_buf_.set_output_tag(tag); + if (!ctx_->sent_initial_metadata_) { + finish_buf_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_buf_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_buf_.ServerSendStatus(&ctx_->trailing_metadata_, status); + call_.PerformOps(&finish_buf_); + } + + private: + void BindCall(::grpc::internal::Call* call) override { call_ = *call; } + + ::grpc::internal::Call call_; + ServerContext* ctx_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + meta_buf_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpServerSendStatus> + finish_buf_; +}; + +} // namespace grpc + +namespace std { +template +class default_delete> { + public: + void operator()(void* p) {} +}; +template +class default_delete> { + public: + void operator()(void* p) {} +}; +} // namespace std + +#endif // GRPCPP_IMPL_CODEGEN_ASYNC_UNARY_CALL_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/byte_buffer.h b/third_party/grpc/include/grpcpp/impl/codegen/byte_buffer.h new file mode 100644 index 00000000000000..a77e36dfc5022b --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/byte_buffer.h @@ -0,0 +1,224 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H +#define GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H + +#include + +#include +#include +#include +#include +#include + +#include + +namespace grpc { + +class ServerInterface; +class ByteBuffer; +class ServerInterface; + +namespace internal { +class CallOpSendMessage; +template +class CallOpRecvMessage; +class CallOpGenericRecvMessage; +class MethodHandler; +template +class RpcMethodHandler; +template +class ServerStreamingHandler; +template +class CallbackUnaryHandler; +template +class CallbackServerStreamingHandler; +template +class ErrorMethodHandler; +template +class DeserializeFuncType; +class GrpcByteBufferPeer; +template +class RpcMethodHandler; +template +class ServerStreamingHandler; + +} // namespace internal +/// A sequence of bytes. +class ByteBuffer 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) { + // The following assertions check that the representation of a grpc::Slice + // is identical to that of a grpc_slice: it has a grpc_slice field, and + // nothing else. + static_assert(std::is_same::value, + "Slice must have same representation as grpc_slice"); + static_assert(sizeof(Slice) == sizeof(grpc_slice), + "Slice must have same representation as grpc_slice"); + // The following assertions check that the representation of a ByteBuffer is + // identical to grpc_byte_buffer*: it has a grpc_byte_buffer* field, + // and nothing else. + static_assert(std::is_same::value, + "ByteBuffer must have same representation as " + "grpc_byte_buffer*"); + static_assert(sizeof(ByteBuffer) == sizeof(grpc_byte_buffer*), + "ByteBuffer must have same representation as " + "grpc_byte_buffer*"); + // The const_cast is legal if grpc_raw_byte_buffer_create() does no more + // than its advertised side effect of increasing the reference count of the + // slices it processes, and such an increase does not affect the semantics + // seen by the caller of this constructor. + buffer_ = g_core_codegen_interface->grpc_raw_byte_buffer_create( + reinterpret_cast(const_cast(slices)), nslices); + } + + /// Constuct a byte buffer by referencing elements of existing buffer + /// \a buf. Wrapper of core function grpc_byte_buffer_copy . This is not + /// a deep copy; it is just a referencing. As a result, its performance is + /// size-independent. + ByteBuffer(const ByteBuffer& buf); + + ~ByteBuffer() { + if (buffer_) { + g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_); + } + } + + /// Wrapper of core function grpc_byte_buffer_copy . This is not + /// a deep copy; it is just a referencing. As a result, its performance is + /// size-independent. + ByteBuffer& operator=(const ByteBuffer&); + + /// Dump (read) the buffer contents into \a slices. + Status Dump(std::vector* slices) const; + + /// Remove all data. + void Clear() { + if (buffer_) { + g_core_codegen_interface->grpc_byte_buffer_destroy(buffer_); + buffer_ = nullptr; + } + } + + /// Make a duplicate copy of the internals of this byte + /// buffer so that we have our own owned version of it. + /// bbuf.Duplicate(); is equivalent to bbuf=bbuf; but is actually readable. + /// This is not a deep copy; it is a referencing and its performance + /// is size-independent. + void Duplicate() { + buffer_ = g_core_codegen_interface->grpc_byte_buffer_copy(buffer_); + } + + /// Forget underlying byte buffer without destroying + /// Use this only for un-owned byte buffers + void Release() { buffer_ = nullptr; } + + /// Buffer size in bytes. + size_t Length() const { + return buffer_ == nullptr + ? 0 + : g_core_codegen_interface->grpc_byte_buffer_length(buffer_); + } + + /// Swap the state of *this and *other. + void Swap(ByteBuffer* other) { + grpc_byte_buffer* tmp = other->buffer_; + other->buffer_ = buffer_; + buffer_ = tmp; + } + + /// Is this ByteBuffer valid? + bool Valid() const { return (buffer_ != nullptr); } + + private: + friend class SerializationTraits; + friend class ServerInterface; + friend class internal::CallOpSendMessage; + template + friend class internal::CallOpRecvMessage; + friend class internal::CallOpGenericRecvMessage; + template + friend class RpcMethodHandler; + template + friend class ServerStreamingHandler; + template + friend class internal::RpcMethodHandler; + template + friend class internal::ServerStreamingHandler; + template + friend class internal::CallbackUnaryHandler; + template + friend class ::grpc::internal::CallbackServerStreamingHandler; + template + friend class internal::ErrorMethodHandler; + template + friend class internal::DeserializeFuncType; + friend class ProtoBufferReader; + friend class ProtoBufferWriter; + friend class internal::GrpcByteBufferPeer; + + grpc_byte_buffer* buffer_; + + // takes ownership + void set_buffer(grpc_byte_buffer* buf) { + if (buffer_) { + Clear(); + } + buffer_ = buf; + } + + grpc_byte_buffer* c_buffer() { return buffer_; } + grpc_byte_buffer** c_buffer_ptr() { return &buffer_; } + + class ByteBufferPointer { + public: + ByteBufferPointer(const ByteBuffer* b) + : bbuf_(const_cast(b)) {} + operator ByteBuffer*() { return bbuf_; } + operator grpc_byte_buffer*() { return bbuf_->buffer_; } + operator grpc_byte_buffer**() { return &bbuf_->buffer_; } + + private: + ByteBuffer* bbuf_; + }; + ByteBufferPointer bbuf_ptr() const { return ByteBufferPointer(this); } +}; + +template <> +class SerializationTraits { + public: + static Status Deserialize(ByteBuffer* byte_buffer, ByteBuffer* dest) { + dest->set_buffer(byte_buffer->buffer_); + return Status::OK; + } + static Status Serialize(const ByteBuffer& source, ByteBuffer* buffer, + bool* own_buffer) { + *buffer = source; + *own_buffer = true; + return Status::OK; + } +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_BYTE_BUFFER_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call.h b/third_party/grpc/include/grpcpp/impl/codegen/call.h new file mode 100644 index 00000000000000..c040c30dd9d026 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/call.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GRPCPP_IMPL_CODEGEN_CALL_H +#define GRPCPP_IMPL_CODEGEN_CALL_H + +#include +#include + +namespace grpc { +class CompletionQueue; + +namespace experimental { +class ClientRpcInfo; +class ServerRpcInfo; +} // namespace experimental +namespace internal { +class CallHook; +class CallOpSetInterface; + +/// Straightforward wrapping of the C call object +class Call final { + public: + Call() + : call_hook_(nullptr), + cq_(nullptr), + call_(nullptr), + max_receive_message_size_(-1) {} + /** call is owned by the caller */ + Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq) + : call_hook_(call_hook), + cq_(cq), + call_(call), + max_receive_message_size_(-1) {} + + Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq, + experimental::ClientRpcInfo* rpc_info) + : call_hook_(call_hook), + cq_(cq), + call_(call), + max_receive_message_size_(-1), + client_rpc_info_(rpc_info) {} + + Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq, + int max_receive_message_size, experimental::ServerRpcInfo* rpc_info) + : call_hook_(call_hook), + cq_(cq), + call_(call), + max_receive_message_size_(max_receive_message_size), + server_rpc_info_(rpc_info) {} + + void PerformOps(CallOpSetInterface* ops) { + call_hook_->PerformOpsOnCall(ops, this); + } + + grpc_call* call() const { return call_; } + CompletionQueue* cq() const { return cq_; } + + int max_receive_message_size() const { return max_receive_message_size_; } + + experimental::ClientRpcInfo* client_rpc_info() const { + return client_rpc_info_; + } + + experimental::ServerRpcInfo* server_rpc_info() const { + return server_rpc_info_; + } + + private: + CallHook* call_hook_; + CompletionQueue* cq_; + grpc_call* call_; + int max_receive_message_size_; + experimental::ClientRpcInfo* client_rpc_info_ = nullptr; + experimental::ServerRpcInfo* server_rpc_info_ = nullptr; +}; +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CALL_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call_hook.h b/third_party/grpc/include/grpcpp/impl/codegen/call_hook.h new file mode 100644 index 00000000000000..4f7d370c4f77e0 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/call_hook.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CALL_HOOK_H +#define GRPCPP_IMPL_CODEGEN_CALL_HOOK_H + +namespace grpc { + +namespace internal { +class CallOpSetInterface; +class Call; + +/// This is an interface that Channel and Server implement to allow them to hook +/// performing ops. +class CallHook { + public: + virtual ~CallHook() {} + virtual void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) = 0; +}; +} // namespace internal + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CALL_HOOK_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call_op_set.h b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set.h new file mode 100644 index 00000000000000..b2100c68b7ff55 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set.h @@ -0,0 +1,923 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H +#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace grpc { + +class CompletionQueue; +extern CoreCodegenInterface* g_core_codegen_interface; + +namespace internal { +class Call; +class CallHook; + +// TODO(yangg) if the map is changed before we send, the pointers will be a +// mess. Make sure it does not happen. +inline grpc_metadata* FillMetadataArray( + const std::multimap& metadata, + size_t* metadata_count, const grpc::string& optional_error_details) { + *metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1); + if (*metadata_count == 0) { + return nullptr; + } + grpc_metadata* metadata_array = + (grpc_metadata*)(g_core_codegen_interface->gpr_malloc( + (*metadata_count) * sizeof(grpc_metadata))); + size_t i = 0; + for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) { + metadata_array[i].key = SliceReferencingString(iter->first); + metadata_array[i].value = SliceReferencingString(iter->second); + } + if (!optional_error_details.empty()) { + metadata_array[i].key = + g_core_codegen_interface->grpc_slice_from_static_buffer( + kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1); + metadata_array[i].value = SliceReferencingString(optional_error_details); + } + return metadata_array; +} +} // namespace internal + +/// Per-message write options. +class WriteOptions { + public: + WriteOptions() : flags_(0), last_message_(false) {} + WriteOptions(const WriteOptions& other) + : flags_(other.flags_), last_message_(other.last_message_) {} + + /// Clear all flags. + inline void Clear() { flags_ = 0; } + + /// Returns raw flags bitset. + inline uint32_t 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); } + + /// corked bit: aliases set_buffer_hint currently, with the intent that + /// set_buffer_hint will be removed in the future + inline WriteOptions& set_corked() { + SetBit(GRPC_WRITE_BUFFER_HINT); + return *this; + } + + inline WriteOptions& clear_corked() { + ClearBit(GRPC_WRITE_BUFFER_HINT); + return *this; + } + + inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); } + + /// last-message bit: indicates this is the last message in a stream + /// client-side: makes Write the equivalent of performing Write, WritesDone + /// in a single step + /// server-side: hold the Write until the service handler returns (sync api) + /// or until Finish is called (async api) + inline WriteOptions& set_last_message() { + last_message_ = true; + return *this; + } + + /// Clears flag indicating that this is the last message in a stream, + /// disabling coalescing. + inline WriteOptions& clear_last_message() { + last_message_ = false; + return *this; + } + + /// Guarantee that all bytes have been written to the socket before completing + /// this write (usually writes are completed when they pass flow control). + inline WriteOptions& set_write_through() { + SetBit(GRPC_WRITE_THROUGH); + return *this; + } + + inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); } + + /// Get value for the flag indicating that this is the last message, and + /// should be coalesced with trailing metadata. + /// + /// \sa GRPC_WRITE_LAST_MESSAGE + bool is_last_message() const { return last_message_; } + + WriteOptions& operator=(const WriteOptions& rhs) { + flags_ = rhs.flags_; + return *this; + } + + private: + void SetBit(const uint32_t mask) { flags_ |= mask; } + + void ClearBit(const uint32_t mask) { flags_ &= ~mask; } + + bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; } + + uint32_t flags_; + bool last_message_; +}; + +namespace internal { + +/// 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) {} + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) {} + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) {} + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) {} +}; + +class CallOpSendInitialMetadata { + public: + CallOpSendInitialMetadata() : send_(false) { + maybe_compression_level_.is_set = false; + } + + void SendInitialMetadata(std::multimap* metadata, + uint32_t flags) { + maybe_compression_level_.is_set = false; + send_ = true; + flags_ = flags; + metadata_map_ = metadata; + } + + void set_compression_level(grpc_compression_level level) { + maybe_compression_level_.is_set = true; + maybe_compression_level_.level = level; + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!send_ || hijacked_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->flags = flags_; + op->reserved = NULL; + initial_metadata_ = + FillMetadataArray(*metadata_map_, &initial_metadata_count_, ""); + op->data.send_initial_metadata.count = initial_metadata_count_; + op->data.send_initial_metadata.metadata = initial_metadata_; + op->data.send_initial_metadata.maybe_compression_level.is_set = + maybe_compression_level_.is_set; + if (maybe_compression_level_.is_set) { + op->data.send_initial_metadata.maybe_compression_level.level = + maybe_compression_level_.level; + } + } + void FinishOp(bool* status) { + if (!send_ || hijacked_) return; + g_core_codegen_interface->gpr_free(initial_metadata_); + send_ = false; + } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (!send_) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA); + interceptor_methods->SetSendInitialMetadata(metadata_map_); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) {} + + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + } + + bool hijacked_ = false; + bool send_; + uint32_t flags_; + size_t initial_metadata_count_; + std::multimap* metadata_map_; + grpc_metadata* initial_metadata_; + struct { + bool is_set; + grpc_compression_level level; + } maybe_compression_level_; +}; + +class CallOpSendMessage { + public: + CallOpSendMessage() : send_buf_() {} + + /// Send \a message using \a options for the write. The \a options are cleared + /// after use. + template + Status SendMessage(const M& message, + 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_.Valid() || hijacked_) 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_message = send_buf_.c_buffer(); + // Flags are per-message: clear them after use. + write_options_.Clear(); + } + void FinishOp(bool* status) { send_buf_.Clear(); } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (!send_buf_.Valid()) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_SEND_MESSAGE); + interceptor_methods->SetSendMessage(&send_buf_); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + // The contents of the SendMessage value that was previously set + // has had its references stolen by core's operations + interceptor_methods->SetSendMessage(nullptr); + } + + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + } + + private: + bool hijacked_ = false; + ByteBuffer send_buf_; + WriteOptions write_options_; +}; + +template +Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) { + write_options_ = options; + bool own_buf; + // TODO(vjpai): Remove the void below when possible + // The void in the template parameter below should not be needed + // (since it should be implicit) but is needed due to an observed + // difference in behavior between clang and gcc for certain internal users + Status result = SerializationTraits::Serialize( + message, send_buf_.bbuf_ptr(), &own_buf); + if (!own_buf) { + send_buf_.Duplicate(); + } + return result; +} + +template +Status CallOpSendMessage::SendMessage(const M& message) { + return SendMessage(message, WriteOptions()); +} + +template +class CallOpRecvMessage { + public: + CallOpRecvMessage() + : got_message(false), + message_(nullptr), + allow_not_getting_message_(false) {} + + void RecvMessage(R* message) { message_ = message; } + + // Do not change status if no message is received. + void AllowNoMessage() { allow_not_getting_message_ = true; } + + bool got_message; + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (message_ == nullptr || hijacked_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_MESSAGE; + op->flags = 0; + op->reserved = NULL; + op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr(); + } + + void FinishOp(bool* status) { + if (message_ == nullptr || hijacked_) return; + if (recv_buf_.Valid()) { + if (*status) { + got_message = *status = + SerializationTraits::Deserialize(recv_buf_.bbuf_ptr(), message_) + .ok(); + recv_buf_.Release(); + } else { + got_message = false; + recv_buf_.Clear(); + } + } else { + got_message = false; + if (!allow_not_getting_message_) { + *status = false; + } + } + message_ = nullptr; + } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + interceptor_methods->SetRecvMessage(message_); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (!got_message) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_MESSAGE); + } + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + if (message_ == nullptr) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_RECV_MESSAGE); + got_message = true; + } + + private: + R* message_; + ByteBuffer recv_buf_; + bool allow_not_getting_message_; + bool hijacked_ = false; +}; + +class DeserializeFunc { + public: + virtual Status Deserialize(ByteBuffer* buf) = 0; + virtual ~DeserializeFunc() {} +}; + +template +class DeserializeFuncType final : public DeserializeFunc { + public: + DeserializeFuncType(R* message) : message_(message) {} + Status Deserialize(ByteBuffer* buf) override { + return SerializationTraits::Deserialize(buf->bbuf_ptr(), message_); + } + + ~DeserializeFuncType() override {} + + private: + R* message_; // Not a managed pointer because management is external to this +}; + +class CallOpGenericRecvMessage { + public: + CallOpGenericRecvMessage() + : got_message(false), allow_not_getting_message_(false) {} + + template + void RecvMessage(R* message) { + // Use an explicit base class pointer to avoid resolution error in the + // following unique_ptr::reset for some old implementations. + DeserializeFunc* func = new DeserializeFuncType(message); + deserialize_.reset(func); + message_ = message; + } + + // Do not change status if no message is received. + void AllowNoMessage() { allow_not_getting_message_ = true; } + + bool got_message; + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!deserialize_ || hijacked_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_MESSAGE; + op->flags = 0; + op->reserved = NULL; + op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr(); + } + + void FinishOp(bool* status) { + if (!deserialize_ || hijacked_) return; + if (recv_buf_.Valid()) { + if (*status) { + got_message = true; + *status = deserialize_->Deserialize(&recv_buf_).ok(); + recv_buf_.Release(); + } else { + got_message = false; + recv_buf_.Clear(); + } + } else { + got_message = false; + if (!allow_not_getting_message_) { + *status = false; + } + } + deserialize_.reset(); + } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + interceptor_methods->SetRecvMessage(message_); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (!got_message) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_MESSAGE); + } + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + if (!deserialize_) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_RECV_MESSAGE); + } + + private: + void* message_; + bool hijacked_ = false; + std::unique_ptr deserialize_; + ByteBuffer recv_buf_; + bool allow_not_getting_message_; +}; + +class CallOpClientSendClose { + public: + CallOpClientSendClose() : send_(false) {} + + void ClientSendClose() { send_ = true; } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!send_ || hijacked_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op->flags = 0; + op->reserved = NULL; + } + void FinishOp(bool* status) { send_ = false; } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (!send_) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_SEND_CLOSE); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) {} + + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + } + + private: + bool hijacked_ = false; + bool send_; +}; + +class CallOpServerSendStatus { + public: + CallOpServerSendStatus() : send_status_available_(false) {} + + void ServerSendStatus( + std::multimap* trailing_metadata, + const Status& status) { + send_error_details_ = status.error_details(); + metadata_map_ = trailing_metadata; + send_status_available_ = true; + send_status_code_ = static_cast(status.error_code()); + send_error_message_ = status.error_message(); + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (!send_status_available_ || hijacked_) return; + trailing_metadata_ = FillMetadataArray( + *metadata_map_, &trailing_metadata_count_, send_error_details_); + 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_; + error_message_slice_ = SliceReferencingString(send_error_message_); + op->data.send_status_from_server.status_details = + send_error_message_.empty() ? nullptr : &error_message_slice_; + op->flags = 0; + op->reserved = NULL; + } + + void FinishOp(bool* status) { + if (!send_status_available_ || hijacked_) return; + g_core_codegen_interface->gpr_free(trailing_metadata_); + send_status_available_ = false; + } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (!send_status_available_) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_SEND_STATUS); + interceptor_methods->SetSendTrailingMetadata(metadata_map_); + interceptor_methods->SetSendStatus(&send_status_code_, &send_error_details_, + &send_error_message_); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) {} + + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + } + + private: + bool hijacked_ = false; + bool send_status_available_; + grpc_status_code send_status_code_; + grpc::string send_error_details_; + grpc::string send_error_message_; + size_t trailing_metadata_count_; + std::multimap* metadata_map_; + grpc_metadata* trailing_metadata_; + grpc_slice error_message_slice_; +}; + +class CallOpRecvInitialMetadata { + public: + CallOpRecvInitialMetadata() : metadata_map_(nullptr) {} + + void RecvInitialMetadata(ClientContext* context) { + context->initial_metadata_received_ = true; + metadata_map_ = &context->recv_initial_metadata_; + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (metadata_map_ == nullptr || hijacked_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr(); + op->flags = 0; + op->reserved = NULL; + } + + void FinishOp(bool* status) { + if (metadata_map_ == nullptr || hijacked_) return; + } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + interceptor_methods->SetRecvInitialMetadata(metadata_map_); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (metadata_map_ == nullptr) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA); + metadata_map_ = nullptr; + } + + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + if (metadata_map_ == nullptr) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_RECV_INITIAL_METADATA); + } + + private: + bool hijacked_ = false; + MetadataMap* metadata_map_; +}; + +class CallOpClientRecvStatus { + public: + CallOpClientRecvStatus() + : recv_status_(nullptr), debug_error_string_(nullptr) {} + + void ClientRecvStatus(ClientContext* context, Status* status) { + client_context_ = context; + metadata_map_ = &client_context_->trailing_metadata_; + recv_status_ = status; + error_message_ = g_core_codegen_interface->grpc_empty_slice(); + } + + protected: + void AddOp(grpc_op* ops, size_t* nops) { + if (recv_status_ == nullptr || hijacked_) return; + grpc_op* op = &ops[(*nops)++]; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr(); + op->data.recv_status_on_client.status = &status_code_; + op->data.recv_status_on_client.status_details = &error_message_; + op->data.recv_status_on_client.error_string = &debug_error_string_; + op->flags = 0; + op->reserved = NULL; + } + + void FinishOp(bool* status) { + if (recv_status_ == nullptr || hijacked_) return; + grpc::string binary_error_details = metadata_map_->GetBinaryErrorDetails(); + *recv_status_ = + Status(static_cast(status_code_), + GRPC_SLICE_IS_EMPTY(error_message_) + ? grpc::string() + : grpc::string(GRPC_SLICE_START_PTR(error_message_), + GRPC_SLICE_END_PTR(error_message_)), + binary_error_details); + client_context_->set_debug_error_string( + debug_error_string_ != nullptr ? debug_error_string_ : ""); + g_core_codegen_interface->grpc_slice_unref(error_message_); + if (debug_error_string_ != nullptr) { + g_core_codegen_interface->gpr_free((void*)debug_error_string_); + } + } + + void SetInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + interceptor_methods->SetRecvStatus(recv_status_); + interceptor_methods->SetRecvTrailingMetadata(metadata_map_); + } + + void SetFinishInterceptionHookPoint( + InterceptorBatchMethodsImpl* interceptor_methods) { + if (recv_status_ == nullptr) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_STATUS); + recv_status_ = nullptr; + } + + void SetHijackingState(InterceptorBatchMethodsImpl* interceptor_methods) { + hijacked_ = true; + if (recv_status_ == nullptr) return; + interceptor_methods->AddInterceptionHookPoint( + experimental::InterceptionHookPoints::PRE_RECV_STATUS); + } + + private: + bool hijacked_ = false; + ClientContext* client_context_; + MetadataMap* metadata_map_; + Status* recv_status_; + const char* debug_error_string_; + grpc_status_code status_code_; + grpc_slice error_message_; +}; + +template , class Op2 = CallNoOp<2>, + class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>, + class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>> +class CallOpSet; + +/// Primary implementation 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 CallOpSet : public CallOpSetInterface, + public Op1, + public Op2, + public Op3, + public Op4, + public Op5, + public Op6 { + public: + CallOpSet() : core_cq_tag_(this), return_tag_(this) {} + // The copy constructor and assignment operator reset the value of + // core_cq_tag_, return_tag_, done_intercepting_ and interceptor_methods_ + // since those are only meaningful on a specific object, not across objects. + CallOpSet(const CallOpSet& other) + : core_cq_tag_(this), + return_tag_(this), + call_(other.call_), + done_intercepting_(false), + interceptor_methods_(InterceptorBatchMethodsImpl()) {} + + CallOpSet& operator=(const CallOpSet& other) { + core_cq_tag_ = this; + return_tag_ = this; + call_ = other.call_; + done_intercepting_ = false; + interceptor_methods_ = InterceptorBatchMethodsImpl(); + return *this; + } + + void FillOps(Call* call) override { + done_intercepting_ = false; + g_core_codegen_interface->grpc_call_ref(call->call()); + call_ = + *call; // It's fine to create a copy of call since it's just pointers + + if (RunInterceptors()) { + ContinueFillOpsAfterInterception(); + } else { + // After the interceptors are run, ContinueFillOpsAfterInterception will + // be run + } + } + + bool FinalizeResult(void** tag, bool* status) override { + if (done_intercepting_) { + // We have already finished intercepting and filling in the results. This + // round trip from the core needed to be made because interceptors were + // run + *tag = return_tag_; + *status = saved_status_; + g_core_codegen_interface->grpc_call_unref(call_.call()); + return true; + } + + this->Op1::FinishOp(status); + this->Op2::FinishOp(status); + this->Op3::FinishOp(status); + this->Op4::FinishOp(status); + this->Op5::FinishOp(status); + this->Op6::FinishOp(status); + saved_status_ = *status; + if (RunInterceptorsPostRecv()) { + *tag = return_tag_; + g_core_codegen_interface->grpc_call_unref(call_.call()); + return true; + } + // Interceptors are going to be run, so we can't return the tag just yet. + // After the interceptors are run, ContinueFinalizeResultAfterInterception + return false; + } + + void set_output_tag(void* return_tag) { return_tag_ = return_tag; } + + void* core_cq_tag() override { return core_cq_tag_; } + + /// set_core_cq_tag is used to provide a different core CQ tag than "this". + /// This is used for callback-based tags, where the core tag is the core + /// callback function. It does not change the use or behavior of any other + /// function (such as FinalizeResult) + void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; } + + // This will be called while interceptors are run if the RPC is a hijacked + // RPC. This should set hijacking state for each of the ops. + void SetHijackingState() override { + this->Op1::SetHijackingState(&interceptor_methods_); + this->Op2::SetHijackingState(&interceptor_methods_); + this->Op3::SetHijackingState(&interceptor_methods_); + this->Op4::SetHijackingState(&interceptor_methods_); + this->Op5::SetHijackingState(&interceptor_methods_); + this->Op6::SetHijackingState(&interceptor_methods_); + } + + // Should be called after interceptors are done running + void ContinueFillOpsAfterInterception() override { + static const size_t MAX_OPS = 6; + grpc_op ops[MAX_OPS]; + size_t nops = 0; + 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); + GPR_CODEGEN_ASSERT(GRPC_CALL_OK == + g_core_codegen_interface->grpc_call_start_batch( + call_.call(), ops, nops, core_cq_tag(), nullptr)); + } + + // Should be called after interceptors are done running on the finalize result + // path + void ContinueFinalizeResultAfterInterception() override { + done_intercepting_ = true; + GPR_CODEGEN_ASSERT(GRPC_CALL_OK == + g_core_codegen_interface->grpc_call_start_batch( + call_.call(), nullptr, 0, core_cq_tag(), nullptr)); + } + + private: + // Returns true if no interceptors need to be run + bool RunInterceptors() { + interceptor_methods_.ClearState(); + interceptor_methods_.SetCallOpSetInterface(this); + interceptor_methods_.SetCall(&call_); + this->Op1::SetInterceptionHookPoint(&interceptor_methods_); + this->Op2::SetInterceptionHookPoint(&interceptor_methods_); + this->Op3::SetInterceptionHookPoint(&interceptor_methods_); + this->Op4::SetInterceptionHookPoint(&interceptor_methods_); + this->Op5::SetInterceptionHookPoint(&interceptor_methods_); + this->Op6::SetInterceptionHookPoint(&interceptor_methods_); + return interceptor_methods_.RunInterceptors(); + } + // Returns true if no interceptors need to be run + bool RunInterceptorsPostRecv() { + // Call and OpSet had already been set on the set state. + // SetReverse also clears previously set hook points + interceptor_methods_.SetReverse(); + this->Op1::SetFinishInterceptionHookPoint(&interceptor_methods_); + this->Op2::SetFinishInterceptionHookPoint(&interceptor_methods_); + this->Op3::SetFinishInterceptionHookPoint(&interceptor_methods_); + this->Op4::SetFinishInterceptionHookPoint(&interceptor_methods_); + this->Op5::SetFinishInterceptionHookPoint(&interceptor_methods_); + this->Op6::SetFinishInterceptionHookPoint(&interceptor_methods_); + return interceptor_methods_.RunInterceptors(); + } + + void* core_cq_tag_; + void* return_tag_; + Call call_; + bool done_intercepting_ = false; + InterceptorBatchMethodsImpl interceptor_methods_; + bool saved_status_; +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/call_op_set_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set_interface.h new file mode 100644 index 00000000000000..3b74566a6d35c5 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/call_op_set_interface.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H +#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H + +#include + +namespace grpc { +namespace internal { + +class Call; + +/// 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: + /// Fills in grpc_op, starting from ops[*nops] and moving + /// upwards. + virtual void FillOps(internal::Call* call) = 0; + + /// Get the tag to be used at the core completion queue. Generally, the + /// value of core_cq_tag will be "this". However, it can be overridden if we + /// want core to process the tag differently (e.g., as a core callback) + virtual void* core_cq_tag() = 0; + + // This will be called while interceptors are run if the RPC is a hijacked + // RPC. This should set hijacking state for each of the ops. + virtual void SetHijackingState() = 0; + + // Should be called after interceptors are done running + virtual void ContinueFillOpsAfterInterception() = 0; + + // Should be called after interceptors are done running on the finalize result + // path + virtual void ContinueFinalizeResultAfterInterception() = 0; +}; +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_INTERFACE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/callback_common.h b/third_party/grpc/include/grpcpp/impl/codegen/callback_common.h new file mode 100644 index 00000000000000..a3c8c41246c3dc --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/callback_common.h @@ -0,0 +1,217 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H +#define GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H + +#include + +#include +#include +#include +#include +#include +#include + +namespace grpc { +namespace internal { + +/// An exception-safe way of invoking a user-specified callback function +// TODO(vjpai): decide whether it is better for this to take a const lvalue +// parameter or an rvalue parameter, or if it even matters +template +void CatchingCallback(Func&& func, Args&&... args) { +#if GRPC_ALLOW_EXCEPTIONS + try { + func(std::forward(args)...); + } catch (...) { + // nothing to return or change here, just don't crash the library + } +#else // GRPC_ALLOW_EXCEPTIONS + func(std::forward(args)...); +#endif // GRPC_ALLOW_EXCEPTIONS +} + +template +ReturnType* CatchingReactorCreator(Func&& func, Args&&... args) { +#if GRPC_ALLOW_EXCEPTIONS + try { + return func(std::forward(args)...); + } catch (...) { + // fail the RPC, don't crash the library + return nullptr; + } +#else // GRPC_ALLOW_EXCEPTIONS + return func(std::forward(args)...); +#endif // GRPC_ALLOW_EXCEPTIONS +} + +// The contract on these tags is that they are single-shot. They must be +// constructed and then fired at exactly one point. There is no expectation +// that they can be reused without reconstruction. + +class CallbackWithStatusTag + : public grpc_experimental_completion_queue_functor { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(CallbackWithStatusTag)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + CallbackWithStatusTag(grpc_call* call, std::function f, + CompletionQueueTag* ops) + : call_(call), func_(std::move(f)), ops_(ops) { + g_core_codegen_interface->grpc_call_ref(call); + functor_run = &CallbackWithStatusTag::StaticRun; + } + ~CallbackWithStatusTag() {} + Status* status_ptr() { return &status_; } + + // force_run can not be performed on a tag if operations using this tag + // have been sent to PerformOpsOnCall. It is intended for error conditions + // that are detected before the operations are internally processed. + void force_run(Status s) { + status_ = std::move(s); + Run(true); + } + + private: + grpc_call* call_; + std::function func_; + CompletionQueueTag* ops_; + Status status_; + + static void StaticRun(grpc_experimental_completion_queue_functor* cb, + int ok) { + static_cast(cb)->Run(static_cast(ok)); + } + void Run(bool ok) { + void* ignored = ops_; + + if (!ops_->FinalizeResult(&ignored, &ok)) { + // The tag was swallowed + return; + } + GPR_CODEGEN_ASSERT(ignored == ops_); + + // Last use of func_ or status_, so ok to move them out + auto func = std::move(func_); + auto status = std::move(status_); + func_ = nullptr; // reset to clear this out for sure + status_ = Status(); // reset to clear this out for sure + CatchingCallback(std::move(func), std::move(status)); + g_core_codegen_interface->grpc_call_unref(call_); + } +}; + +/// CallbackWithSuccessTag can be reused multiple times, and will be used in +/// this fashion for streaming operations. As a result, it shouldn't clear +/// anything up until its destructor +class CallbackWithSuccessTag + : public grpc_experimental_completion_queue_functor { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(CallbackWithSuccessTag)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + CallbackWithSuccessTag() : call_(nullptr) {} + + CallbackWithSuccessTag(grpc_call* call, std::function f, + CompletionQueueTag* ops) { + Set(call, f, ops); + } + + CallbackWithSuccessTag(const CallbackWithSuccessTag&) = delete; + CallbackWithSuccessTag& operator=(const CallbackWithSuccessTag&) = delete; + + ~CallbackWithSuccessTag() { Clear(); } + + // Set can only be called on a default-constructed or Clear'ed tag. + // It should never be called on a tag that was constructed with arguments + // or on a tag that has been Set before unless the tag has been cleared. + void Set(grpc_call* call, std::function f, + CompletionQueueTag* ops) { + GPR_CODEGEN_ASSERT(call_ == nullptr); + g_core_codegen_interface->grpc_call_ref(call); + call_ = call; + func_ = std::move(f); + ops_ = ops; + functor_run = &CallbackWithSuccessTag::StaticRun; + } + + void Clear() { + if (call_ != nullptr) { + grpc_call* call = call_; + call_ = nullptr; + func_ = nullptr; + g_core_codegen_interface->grpc_call_unref(call); + } + } + + CompletionQueueTag* ops() { return ops_; } + + // force_run can not be performed on a tag if operations using this tag + // have been sent to PerformOpsOnCall. It is intended for error conditions + // that are detected before the operations are internally processed. + void force_run(bool ok) { Run(ok); } + + /// check if this tag is currently set + operator bool() const { return call_ != nullptr; } + + private: + grpc_call* call_; + std::function func_; + CompletionQueueTag* ops_; + + static void StaticRun(grpc_experimental_completion_queue_functor* cb, + int ok) { + static_cast(cb)->Run(static_cast(ok)); + } + void Run(bool ok) { + void* ignored = ops_; + // Allow a "false" return value from FinalizeResult to silence the + // callback, just as it silences a CQ tag in the async cases + auto* ops = ops_; + bool do_callback = ops_->FinalizeResult(&ignored, &ok); + GPR_CODEGEN_ASSERT(ignored == ops); + + if (do_callback) { + CatchingCallback(func_, ok); + } + } +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/channel_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/channel_interface.h new file mode 100644 index 00000000000000..5353f5feaa6db4 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/channel_interface.h @@ -0,0 +1,164 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H +#define GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H + +#include +#include +#include +#include + +namespace grpc { +class ChannelInterface; +class ClientContext; +class CompletionQueue; + +template +class ClientReader; +template +class ClientWriter; +template +class ClientReaderWriter; + +namespace internal { +class Call; +class CallOpSetInterface; +class RpcMethod; +template +class BlockingUnaryCallImpl; +template +class CallbackUnaryCallImpl; +template +class ClientAsyncReaderFactory; +template +class ClientAsyncWriterFactory; +template +class ClientAsyncReaderWriterFactory; +template +class ClientAsyncResponseReaderFactory; +template +class ClientCallbackReaderWriterFactory; +template +class ClientCallbackReaderFactory; +template +class ClientCallbackWriterFactory; +class InterceptedChannel; +} // namespace internal + +/// Codegen interface for \a grpc::Channel. +class ChannelInterface { + public: + virtual ~ChannelInterface() {} + /// Get the current channel state. If the channel is in IDLE and + /// \a try_to_connect is set to true, try to connect. + virtual grpc_connectivity_state GetState(bool try_to_connect) = 0; + + /// 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()); + } + + /// Wait for this channel to be connected + template + bool WaitForConnected(T deadline) { + grpc_connectivity_state state; + while ((state = GetState(true)) != GRPC_CHANNEL_READY) { + if (!WaitForStateChange(state, deadline)) return false; + } + return true; + } + + private: + template + friend class ::grpc::ClientReader; + template + friend class ::grpc::ClientWriter; + template + friend class ::grpc::ClientReaderWriter; + template + friend class ::grpc::internal::ClientAsyncReaderFactory; + template + friend class ::grpc::internal::ClientAsyncWriterFactory; + template + friend class ::grpc::internal::ClientAsyncReaderWriterFactory; + template + friend class ::grpc::internal::ClientAsyncResponseReaderFactory; + template + friend class ::grpc::internal::ClientCallbackReaderWriterFactory; + template + friend class ::grpc::internal::ClientCallbackReaderFactory; + template + friend class ::grpc::internal::ClientCallbackWriterFactory; + template + friend class ::grpc::internal::BlockingUnaryCallImpl; + template + friend class ::grpc::internal::CallbackUnaryCallImpl; + friend class ::grpc::internal::RpcMethod; + friend class ::grpc::internal::InterceptedChannel; + virtual internal::Call CreateCall(const internal::RpcMethod& method, + ClientContext* context, + CompletionQueue* cq) = 0; + virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops, + internal::Call* call) = 0; + virtual void* RegisterMethod(const char* method) = 0; + virtual void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline, + CompletionQueue* cq, void* tag) = 0; + virtual bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline) = 0; + + // EXPERIMENTAL + // This is needed to keep codegen_test_minimal happy. InterceptedChannel needs + // to make use of this but can't directly call Channel's implementation + // because of the test. + // Returns an empty Call object (rather than being pure) since this is a new + // method and adding a new pure method to an interface would be a breaking + // change (even though this is private and non-API) + virtual internal::Call CreateCallInternal(const internal::RpcMethod& method, + ClientContext* context, + CompletionQueue* cq, + size_t interceptor_pos) { + return internal::Call(); + } + + // EXPERIMENTAL + // A method to get the callbackable completion queue associated with this + // channel. If the return value is nullptr, this channel doesn't support + // callback operations. + // TODO(vjpai): Consider a better default like using a global CQ + // Returns nullptr (rather than being pure) since this is a post-1.0 method + // and adding a new pure method to an interface would be a breaking change + // (even though this is private and non-API) + virtual CompletionQueue* CallbackCQ() { return nullptr; } +}; +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CHANNEL_INTERFACE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_callback.h b/third_party/grpc/include/grpcpp/impl/codegen/client_callback.h new file mode 100644 index 00000000000000..66cf9b7754cbfd --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/client_callback.h @@ -0,0 +1,750 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H +#define GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +class Channel; +class ClientContext; +class CompletionQueue; + +namespace internal { +class RpcMethod; + +/// Perform a callback-based unary call +/// TODO(vjpai): Combine as much as possible with the blocking unary call code +template +void CallbackUnaryCall(ChannelInterface* channel, const RpcMethod& method, + ClientContext* context, const InputMessage* request, + OutputMessage* result, + std::function on_completion) { + CallbackUnaryCallImpl x( + channel, method, context, request, result, on_completion); +} + +template +class CallbackUnaryCallImpl { + public: + CallbackUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method, + ClientContext* context, const InputMessage* request, + OutputMessage* result, + std::function on_completion) { + CompletionQueue* cq = channel->CallbackCQ(); + GPR_CODEGEN_ASSERT(cq != nullptr); + Call call(channel->CreateCall(method, context, cq)); + + using FullCallOpSet = + CallOpSet, + CallOpClientSendClose, CallOpClientRecvStatus>; + + auto* ops = new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(FullCallOpSet))) FullCallOpSet; + + auto* tag = new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(CallbackWithStatusTag))) + CallbackWithStatusTag(call.call(), on_completion, ops); + + // TODO(vjpai): Unify code with sync API as much as possible + Status s = ops->SendMessage(*request); + if (!s.ok()) { + tag->force_run(s); + return; + } + ops->SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + ops->RecvInitialMetadata(context); + ops->RecvMessage(result); + ops->AllowNoMessage(); + ops->ClientSendClose(); + ops->ClientRecvStatus(context, tag->status_ptr()); + ops->set_core_cq_tag(tag); + call.PerformOps(ops); + } +}; +} // namespace internal + +namespace experimental { + +// Forward declarations +template +class ClientBidiReactor; +template +class ClientReadReactor; +template +class ClientWriteReactor; + +// NOTE: The streaming objects are not actually implemented in the public API. +// These interfaces are provided for mocking only. Typical applications +// will interact exclusively with the reactors that they define. +template +class ClientCallbackReaderWriter { + public: + virtual ~ClientCallbackReaderWriter() {} + virtual void StartCall() = 0; + virtual void Write(const Request* req, WriteOptions options) = 0; + virtual void WritesDone() = 0; + virtual void Read(Response* resp) = 0; + + protected: + void BindReactor(ClientBidiReactor* reactor) { + reactor->BindStream(this); + } +}; + +template +class ClientCallbackReader { + public: + virtual ~ClientCallbackReader() {} + virtual void StartCall() = 0; + virtual void Read(Response* resp) = 0; + + protected: + void BindReactor(ClientReadReactor* reactor) { + reactor->BindReader(this); + } +}; + +template +class ClientCallbackWriter { + public: + virtual ~ClientCallbackWriter() {} + virtual void StartCall() = 0; + void Write(const Request* req) { Write(req, WriteOptions()); } + virtual void Write(const Request* req, WriteOptions options) = 0; + void WriteLast(const Request* req, WriteOptions options) { + Write(req, options.set_last_message()); + } + virtual void WritesDone() = 0; + + protected: + void BindReactor(ClientWriteReactor* reactor) { + reactor->BindWriter(this); + } +}; + +// The user must implement this reactor interface with reactions to each event +// type that gets called by the library. An empty reaction is provided by +// default +template +class ClientBidiReactor { + public: + virtual ~ClientBidiReactor() {} + virtual void OnDone(const Status& s) {} + virtual void OnReadInitialMetadataDone(bool ok) {} + virtual void OnReadDone(bool ok) {} + virtual void OnWriteDone(bool ok) {} + virtual void OnWritesDoneDone(bool ok) {} + + void StartCall() { stream_->StartCall(); } + void StartRead(Response* resp) { stream_->Read(resp); } + void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); } + void StartWrite(const Request* req, WriteOptions options) { + stream_->Write(req, std::move(options)); + } + void StartWriteLast(const Request* req, WriteOptions options) { + StartWrite(req, std::move(options.set_last_message())); + } + void StartWritesDone() { stream_->WritesDone(); } + + private: + friend class ClientCallbackReaderWriter; + void BindStream(ClientCallbackReaderWriter* stream) { + stream_ = stream; + } + ClientCallbackReaderWriter* stream_; +}; + +template +class ClientReadReactor { + public: + virtual ~ClientReadReactor() {} + virtual void OnDone(const Status& s) {} + virtual void OnReadInitialMetadataDone(bool ok) {} + virtual void OnReadDone(bool ok) {} + + void StartCall() { reader_->StartCall(); } + void StartRead(Response* resp) { reader_->Read(resp); } + + private: + friend class ClientCallbackReader; + void BindReader(ClientCallbackReader* reader) { reader_ = reader; } + ClientCallbackReader* reader_; +}; + +template +class ClientWriteReactor { + public: + virtual ~ClientWriteReactor() {} + virtual void OnDone(const Status& s) {} + virtual void OnReadInitialMetadataDone(bool ok) {} + virtual void OnWriteDone(bool ok) {} + virtual void OnWritesDoneDone(bool ok) {} + + void StartCall() { writer_->StartCall(); } + void StartWrite(const Request* req) { StartWrite(req, WriteOptions()); } + void StartWrite(const Request* req, WriteOptions options) { + writer_->Write(req, std::move(options)); + } + void StartWriteLast(const Request* req, WriteOptions options) { + StartWrite(req, std::move(options.set_last_message())); + } + void StartWritesDone() { writer_->WritesDone(); } + + private: + friend class ClientCallbackWriter; + void BindWriter(ClientCallbackWriter* writer) { writer_ = writer; } + ClientCallbackWriter* writer_; +}; + +} // namespace experimental + +namespace internal { + +// Forward declare factory classes for friendship +template +class ClientCallbackReaderWriterFactory; +template +class ClientCallbackReaderFactory; +template +class ClientCallbackWriterFactory; + +template +class ClientCallbackReaderWriterImpl + : public ::grpc::experimental::ClientCallbackReaderWriter { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientCallbackReaderWriterImpl)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void MaybeFinish() { + if (--callbacks_outstanding_ == 0) { + Status s = std::move(finish_status_); + auto* reactor = reactor_; + auto* call = call_.call(); + this->~ClientCallbackReaderWriterImpl(); + g_core_codegen_interface->grpc_call_unref(call); + reactor->OnDone(s); + } + } + + void StartCall() override { + // This call initiates two batches, plus any backlog, each with a callback + // 1. Send initial metadata (unless corked) + recv initial metadata + // 2. Any read backlog + // 3. Recv trailing metadata, on_completion callback + // 4. Any write backlog + // 5. See if the call can finish (if other callbacks were triggered already) + started_ = true; + + start_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadInitialMetadataDone(ok); + MaybeFinish(); + }, + &start_ops_); + if (!start_corked_) { + start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + } + start_ops_.RecvInitialMetadata(context_); + start_ops_.set_core_cq_tag(&start_tag_); + call_.PerformOps(&start_ops_); + + // Also set up the read and write tags so that they don't have to be set up + // each time + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeFinish(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeFinish(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + if (read_ops_at_start_) { + call_.PerformOps(&read_ops_); + } + + finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, + &finish_ops_); + finish_ops_.ClientRecvStatus(context_, &finish_status_); + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + + if (write_ops_at_start_) { + call_.PerformOps(&write_ops_); + } + + if (writes_done_ops_at_start_) { + call_.PerformOps(&writes_done_ops_); + } + MaybeFinish(); + } + + void Read(Response* msg) override { + read_ops_.RecvMessage(msg); + callbacks_outstanding_++; + if (started_) { + call_.PerformOps(&read_ops_); + } else { + read_ops_at_start_ = true; + } + } + + void Write(const Request* msg, WriteOptions options) override { + if (start_corked_) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok()); + + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + callbacks_outstanding_++; + if (started_) { + call_.PerformOps(&write_ops_); + } else { + write_ops_at_start_ = true; + } + } + void WritesDone() override { + if (start_corked_) { + writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + writes_done_ops_.ClientSendClose(); + writes_done_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWritesDoneDone(ok); + MaybeFinish(); + }, + &writes_done_ops_); + writes_done_ops_.set_core_cq_tag(&writes_done_tag_); + callbacks_outstanding_++; + if (started_) { + call_.PerformOps(&writes_done_ops_); + } else { + writes_done_ops_at_start_ = true; + } + } + + private: + friend class ClientCallbackReaderWriterFactory; + + ClientCallbackReaderWriterImpl( + Call call, ClientContext* context, + ::grpc::experimental::ClientBidiReactor* reactor) + : context_(context), + call_(call), + reactor_(reactor), + start_corked_(context_->initial_metadata_corked_) { + this->BindReactor(reactor); + } + + ClientContext* context_; + Call call_; + ::grpc::experimental::ClientBidiReactor* reactor_; + + CallOpSet start_ops_; + CallbackWithSuccessTag start_tag_; + bool start_corked_; + + CallOpSet finish_ops_; + CallbackWithSuccessTag finish_tag_; + Status finish_status_; + + CallOpSet + write_ops_; + CallbackWithSuccessTag write_tag_; + bool write_ops_at_start_{false}; + + CallOpSet writes_done_ops_; + CallbackWithSuccessTag writes_done_tag_; + bool writes_done_ops_at_start_{false}; + + CallOpSet> read_ops_; + CallbackWithSuccessTag read_tag_; + bool read_ops_at_start_{false}; + + // Minimum of 3 callbacks to pre-register for StartCall, start, and finish + std::atomic_int callbacks_outstanding_{3}; + bool started_{false}; +}; + +template +class ClientCallbackReaderWriterFactory { + public: + static void Create( + ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, + ClientContext* context, + ::grpc::experimental::ClientBidiReactor* reactor) { + Call call = channel->CreateCall(method, context, channel->CallbackCQ()); + + g_core_codegen_interface->grpc_call_ref(call.call()); + new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientCallbackReaderWriterImpl))) + ClientCallbackReaderWriterImpl(call, context, + reactor); + } +}; + +template +class ClientCallbackReaderImpl + : public ::grpc::experimental::ClientCallbackReader { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientCallbackReaderImpl)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void MaybeFinish() { + if (--callbacks_outstanding_ == 0) { + Status s = std::move(finish_status_); + auto* reactor = reactor_; + auto* call = call_.call(); + this->~ClientCallbackReaderImpl(); + g_core_codegen_interface->grpc_call_unref(call); + reactor->OnDone(s); + } + } + + void StartCall() override { + // This call initiates two batches, plus any backlog, each with a callback + // 1. Send initial metadata (unless corked) + recv initial metadata + // 2. Any backlog + // 3. Recv trailing metadata, on_completion callback + // 4. See if the call can finish (if other callbacks were triggered already) + started_ = true; + + start_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadInitialMetadataDone(ok); + MaybeFinish(); + }, + &start_ops_); + start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_ops_.RecvInitialMetadata(context_); + start_ops_.set_core_cq_tag(&start_tag_); + call_.PerformOps(&start_ops_); + + // Also set up the read tag so it doesn't have to be set up each time + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeFinish(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + if (read_ops_at_start_) { + call_.PerformOps(&read_ops_); + } + + finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, + &finish_ops_); + finish_ops_.ClientRecvStatus(context_, &finish_status_); + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + + MaybeFinish(); + } + + void Read(Response* msg) override { + read_ops_.RecvMessage(msg); + callbacks_outstanding_++; + if (started_) { + call_.PerformOps(&read_ops_); + } else { + read_ops_at_start_ = true; + } + } + + private: + friend class ClientCallbackReaderFactory; + + template + ClientCallbackReaderImpl( + Call call, ClientContext* context, Request* request, + ::grpc::experimental::ClientReadReactor* reactor) + : context_(context), call_(call), reactor_(reactor) { + this->BindReactor(reactor); + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(start_ops_.SendMessage(*request).ok()); + start_ops_.ClientSendClose(); + } + + ClientContext* context_; + Call call_; + ::grpc::experimental::ClientReadReactor* reactor_; + + CallOpSet + start_ops_; + CallbackWithSuccessTag start_tag_; + + CallOpSet finish_ops_; + CallbackWithSuccessTag finish_tag_; + Status finish_status_; + + CallOpSet> read_ops_; + CallbackWithSuccessTag read_tag_; + bool read_ops_at_start_{false}; + + // Minimum of 3 callbacks to pre-register for StartCall, start, and finish + std::atomic_int callbacks_outstanding_{3}; + bool started_{false}; +}; + +template +class ClientCallbackReaderFactory { + public: + template + static void Create( + ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, + ClientContext* context, const Request* request, + ::grpc::experimental::ClientReadReactor* reactor) { + Call call = channel->CreateCall(method, context, channel->CallbackCQ()); + + g_core_codegen_interface->grpc_call_ref(call.call()); + new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientCallbackReaderImpl))) + ClientCallbackReaderImpl(call, context, request, reactor); + } +}; + +template +class ClientCallbackWriterImpl + : public ::grpc::experimental::ClientCallbackWriter { + public: + // always allocated against a call arena, no memory free required + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(ClientCallbackWriterImpl)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + void MaybeFinish() { + if (--callbacks_outstanding_ == 0) { + Status s = std::move(finish_status_); + auto* reactor = reactor_; + auto* call = call_.call(); + this->~ClientCallbackWriterImpl(); + g_core_codegen_interface->grpc_call_unref(call); + reactor->OnDone(s); + } + } + + void StartCall() override { + // This call initiates two batches, plus any backlog, each with a callback + // 1. Send initial metadata (unless corked) + recv initial metadata + // 2. Recv trailing metadata, on_completion callback + // 3. Any backlog + // 4. See if the call can finish (if other callbacks were triggered already) + started_ = true; + + start_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadInitialMetadataDone(ok); + MaybeFinish(); + }, + &start_ops_); + if (!start_corked_) { + start_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + } + start_ops_.RecvInitialMetadata(context_); + start_ops_.set_core_cq_tag(&start_tag_); + call_.PerformOps(&start_ops_); + + // Also set up the read and write tags so that they don't have to be set up + // each time + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeFinish(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + + finish_tag_.Set(call_.call(), [this](bool ok) { MaybeFinish(); }, + &finish_ops_); + finish_ops_.ClientRecvStatus(context_, &finish_status_); + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + + if (write_ops_at_start_) { + call_.PerformOps(&write_ops_); + } + + if (writes_done_ops_at_start_) { + call_.PerformOps(&writes_done_ops_); + } + + MaybeFinish(); + } + + void Write(const Request* msg, WriteOptions options) override { + if (start_corked_) { + write_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*msg).ok()); + + if (options.is_last_message()) { + options.set_buffer_hint(); + write_ops_.ClientSendClose(); + } + callbacks_outstanding_++; + if (started_) { + call_.PerformOps(&write_ops_); + } else { + write_ops_at_start_ = true; + } + } + void WritesDone() override { + if (start_corked_) { + writes_done_ops_.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + start_corked_ = false; + } + writes_done_ops_.ClientSendClose(); + writes_done_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWritesDoneDone(ok); + MaybeFinish(); + }, + &writes_done_ops_); + writes_done_ops_.set_core_cq_tag(&writes_done_tag_); + callbacks_outstanding_++; + if (started_) { + call_.PerformOps(&writes_done_ops_); + } else { + writes_done_ops_at_start_ = true; + } + } + + private: + friend class ClientCallbackWriterFactory; + + template + ClientCallbackWriterImpl( + Call call, ClientContext* context, Response* response, + ::grpc::experimental::ClientWriteReactor* reactor) + : context_(context), + call_(call), + reactor_(reactor), + start_corked_(context_->initial_metadata_corked_) { + this->BindReactor(reactor); + finish_ops_.RecvMessage(response); + finish_ops_.AllowNoMessage(); + } + + ClientContext* context_; + Call call_; + ::grpc::experimental::ClientWriteReactor* reactor_; + + CallOpSet start_ops_; + CallbackWithSuccessTag start_tag_; + bool start_corked_; + + CallOpSet finish_ops_; + CallbackWithSuccessTag finish_tag_; + Status finish_status_; + + CallOpSet + write_ops_; + CallbackWithSuccessTag write_tag_; + bool write_ops_at_start_{false}; + + CallOpSet writes_done_ops_; + CallbackWithSuccessTag writes_done_tag_; + bool writes_done_ops_at_start_{false}; + + // Minimum of 3 callbacks to pre-register for StartCall, start, and finish + std::atomic_int callbacks_outstanding_{3}; + bool started_{false}; +}; + +template +class ClientCallbackWriterFactory { + public: + template + static void Create( + ChannelInterface* channel, const ::grpc::internal::RpcMethod& method, + ClientContext* context, Response* response, + ::grpc::experimental::ClientWriteReactor* reactor) { + Call call = channel->CreateCall(method, context, channel->CallbackCQ()); + + g_core_codegen_interface->grpc_call_ref(call.call()); + new (g_core_codegen_interface->grpc_call_arena_alloc( + call.call(), sizeof(ClientCallbackWriterImpl))) + ClientCallbackWriterImpl(call, context, response, reactor); + } +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CLIENT_CALLBACK_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_context.h b/third_party/grpc/include/grpcpp/impl/codegen/client_context.h new file mode 100644 index 00000000000000..5946488566eefa --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/client_context.h @@ -0,0 +1,485 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// 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 persistent at channel construction time +/// (see \a grpc::CreateCustomChannel). +/// +/// \warning ClientContext instances should \em not be reused across rpcs. + +#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H +#define GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct census_context; +struct grpc_call; + +namespace grpc { + +class Channel; +class ChannelInterface; +class CompletionQueue; +class CallCredentials; +class ClientContext; + +namespace internal { +class RpcMethod; +class CallOpClientRecvStatus; +class CallOpRecvInitialMetadata; +template +class BlockingUnaryCallImpl; +template +class CallbackUnaryCallImpl; +template +class ClientCallbackReaderWriterImpl; +template +class ClientCallbackReaderImpl; +template +class ClientCallbackWriterImpl; +} // namespace internal + +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; + } + + uint32_t c_bitmask() const { return propagate_; } + + private: + uint32_t propagate_; +}; + +namespace testing { +class InteropClientContextInspector; +} // namespace testing + +/// 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 persistent at channel construction time +/// (see \a grpc::CreateCustomChannel). +/// +/// \warning ClientContext instances should \em not be reused across rpcs. +/// \warning The ClientContext instance used for creating an rpc must remain +/// alive and valid for the lifetime of the rpc. +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, the key name + /// must end in "-bin". + /// + /// Metadata must conform to the following format: + /// Custom-Metadata -> Binary-Header / ASCII-Header + /// Binary-Header -> {Header-Name "-bin" } {binary value} + /// ASCII-Header -> Header-Name ASCII-Value + /// Header-Name -> 1*( %x30-39 / %x61-7A / "_" / "-" / ".") ; 0-9 a-z _ - . + /// ASCII-Value -> 1*( %x20-%x7E ) ; space and printable ASCII + 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() const { + GPR_CODEGEN_ASSERT(initial_metadata_received_); + return *recv_initial_metadata_.map(); + } + + /// 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() const { + // TODO(yangg) check finished + return *trailing_metadata_.map(); + } + + /// 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. The deadline is an absolute (not relative) time. + template + void set_deadline(const T& deadline) { + TimePoint deadline_tp(deadline); + deadline_ = deadline_tp.raw_time(); + } + + /// EXPERIMENTAL: Indicate that this request is idempotent. + /// By default, RPCs are assumed to not be idempotent. + /// + /// If true, the gRPC library assumes that it's safe to initiate + /// this RPC multiple times. + void set_idempotent(bool idempotent) { idempotent_ = idempotent; } + + /// EXPERIMENTAL: Set this request to be cacheable. + /// If set, grpc is free to use the HTTP GET verb for sending the request, + /// with the possibility of receiving a cached response. + void set_cacheable(bool cacheable) { cacheable_ = cacheable; } + + /// EXPERIMENTAL: Trigger wait-for-ready or not on this request. + /// See https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md. + /// If set, if an RPC is made when a channel's connectivity state is + /// TRANSIENT_FAILURE or CONNECTING, the call will not "fail fast", + /// and the channel will wait until the channel is READY before making the + /// call. + void set_wait_for_ready(bool wait_for_ready) { + wait_for_ready_ = wait_for_ready; + wait_for_ready_explicitly_set_ = true; + } + + /// DEPRECATED: Use set_wait_for_ready() instead. + void set_fail_fast(bool fail_fast) { set_wait_for_ready(!fail_fast); } + + /// Return the deadline for the client call. + std::chrono::system_clock::time_point deadline() const { + return Timespec2Timepoint(deadline_); + } + + /// Return a \a gpr_timespec representation of the client call's deadline. + gpr_timespec raw_deadline() const { 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 { + if (auth_context_.get() == nullptr) { + auth_context_ = CreateAuthContext(call_); + } + return auth_context_; + } + + /// 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://grpc.io/docs/guides/auth.html + void set_credentials(const std::shared_ptr& creds) { + creds_ = creds; + } + + /// Return the compression algorithm the client call will request be used. + /// Note that the gRPC runtime may decide to ignore this request, for example, + /// due to resource constraints. + grpc_compression_algorithm compression_algorithm() const { + return compression_algorithm_; + } + + /// Set \a algorithm to be the compression algorithm used for the client call. + /// + /// \param algorithm The compression algorithm used for the client call. + void set_compression_algorithm(grpc_compression_algorithm algorithm); + + /// Flag whether the initial metadata should be \a corked + /// + /// If \a corked is true, then the initial metadata will be coalesced with the + /// write of first message in the stream. As a result, any tag set for the + /// initial metadata operation (starting a client-streaming or bidi-streaming + /// RPC) will not actually be sent to the completion queue or delivered + /// via Next. + /// + /// \param corked The flag indicating whether the initial metadata is to be + /// corked or not. + void set_initial_metadata_corked(bool corked) { + initial_metadata_corked_ = corked; + } + + /// 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 on the call associated with + /// this client context. 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. + /// + /// Note that TryCancel() does not change any of the tags that are pending + /// on the completion queue. All pending tags will still be delivered + /// (though their ok result may reflect the effect of cancellation). + void TryCancel(); + + /// Global Callbacks + /// + /// Can be set exactly once per application to install hooks whenever + /// a client context is constructed and destructed. + class GlobalCallbacks { + public: + virtual ~GlobalCallbacks() {} + virtual void DefaultConstructor(ClientContext* context) = 0; + virtual void Destructor(ClientContext* context) = 0; + }; + static void SetGlobalCallbacks(GlobalCallbacks* callbacks); + + /// Should be used for framework-level extensions only. + /// Applications never need to call this method. + grpc_call* c_call() { return call_; } + + /// EXPERIMENTAL debugging API + /// + /// if status is not ok() for an RPC, this will return a detailed string + /// of the gRPC Core error that led to the failure. It should not be relied + /// upon for anything other than gaining more debug data in failure cases. + grpc::string debug_error_string() const { return debug_error_string_; } + + private: + // Disallow copy and assign. + ClientContext(const ClientContext&); + ClientContext& operator=(const ClientContext&); + + friend class ::grpc::testing::InteropClientContextInspector; + friend class ::grpc::internal::CallOpClientRecvStatus; + friend class ::grpc::internal::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 class ::grpc::internal::BlockingUnaryCallImpl; + template + friend class ::grpc::internal::CallbackUnaryCallImpl; + template + friend class ::grpc::internal::ClientCallbackReaderWriterImpl; + template + friend class ::grpc::internal::ClientCallbackReaderImpl; + template + friend class ::grpc::internal::ClientCallbackWriterImpl; + + // Used by friend class CallOpClientRecvStatus + void set_debug_error_string(const grpc::string& debug_error_string) { + debug_error_string_ = debug_error_string; + } + + grpc_call* call() const { return call_; } + void set_call(grpc_call* call, const std::shared_ptr& channel); + + experimental::ClientRpcInfo* set_client_rpc_info( + const char* method, internal::RpcMethod::RpcType type, + grpc::ChannelInterface* channel, + const std::vector< + std::unique_ptr>& + creators, + size_t interceptor_pos) { + rpc_info_ = experimental::ClientRpcInfo(this, type, method, channel); + rpc_info_.RegisterInterceptors(creators, interceptor_pos); + return &rpc_info_; + } + + uint32_t initial_metadata_flags() const { + return (idempotent_ ? GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST : 0) | + (wait_for_ready_ ? GRPC_INITIAL_METADATA_WAIT_FOR_READY : 0) | + (cacheable_ ? GRPC_INITIAL_METADATA_CACHEABLE_REQUEST : 0) | + (wait_for_ready_explicitly_set_ + ? GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET + : 0) | + (initial_metadata_corked_ ? GRPC_INITIAL_METADATA_CORKED : 0); + } + + grpc::string authority() { return authority_; } + + void SendCancelToInterceptors(); + + bool initial_metadata_received_; + bool wait_for_ready_; + bool wait_for_ready_explicitly_set_; + bool idempotent_; + bool cacheable_; + std::shared_ptr channel_; + std::mutex mu_; + grpc_call* call_; + bool call_canceled_; + 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_; + mutable internal::MetadataMap recv_initial_metadata_; + mutable internal::MetadataMap trailing_metadata_; + + grpc_call* propagate_from_call_; + PropagationOptions propagation_options_; + + grpc_compression_algorithm compression_algorithm_; + bool initial_metadata_corked_; + + grpc::string debug_error_string_; + + experimental::ClientRpcInfo rpc_info_; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CLIENT_CONTEXT_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_interceptor.h b/third_party/grpc/include/grpcpp/impl/codegen/client_interceptor.h new file mode 100644 index 00000000000000..7dfe2290a3f0ac --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/client_interceptor.h @@ -0,0 +1,189 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H +#define GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H + +#include +#include + +#include +#include +#include + +namespace grpc { + +class ClientContext; +class Channel; + +namespace internal { +class InterceptorBatchMethodsImpl; +} + +namespace experimental { +class ClientRpcInfo; + +// A factory interface for creation of client interceptors. A vector of +// factories can be provided at channel creation which will be used to create a +// new vector of client interceptors per RPC. Client interceptor authors should +// create a subclass of ClientInterceptorFactorInterface which creates objects +// of their interceptors. +class ClientInterceptorFactoryInterface { + public: + virtual ~ClientInterceptorFactoryInterface() {} + // Returns a pointer to an Interceptor object on successful creation, nullptr + // otherwise. If nullptr is returned, this server interceptor factory is + // ignored for the purposes of that RPC. + virtual Interceptor* CreateClientInterceptor(ClientRpcInfo* info) = 0; +}; +} // namespace experimental + +namespace internal { +extern experimental::ClientInterceptorFactoryInterface* + g_global_client_interceptor_factory; +} + +/// ClientRpcInfo represents the state of a particular RPC as it +/// appears to an interceptor. It is created and owned by the library and +/// passed to the CreateClientInterceptor method of the application's +/// ClientInterceptorFactoryInterface implementation +namespace experimental { +class ClientRpcInfo { + public: + // TODO(yashykt): Stop default-constructing ClientRpcInfo and remove UNKNOWN + // from the list of possible Types. + /// Type categorizes RPCs by unary or streaming type + enum class Type { + UNARY, + CLIENT_STREAMING, + SERVER_STREAMING, + BIDI_STREAMING, + UNKNOWN // UNKNOWN is not API and will be removed later + }; + + ~ClientRpcInfo(){}; + + // Delete copy constructor but allow default move constructor + ClientRpcInfo(const ClientRpcInfo&) = delete; + ClientRpcInfo(ClientRpcInfo&&) = default; + + // Getter methods + + /// Return the fully-specified method name + const char* method() const { return method_; } + + /// Return a pointer to the channel on which the RPC is being sent + ChannelInterface* channel() { return channel_; } + + /// Return a pointer to the underlying ClientContext structure associated + /// with the RPC to support features that apply to it + grpc::ClientContext* client_context() { return ctx_; } + + /// Return the type of the RPC (unary or a streaming flavor) + Type type() const { return type_; } + + private: + static_assert(Type::UNARY == + static_cast(internal::RpcMethod::NORMAL_RPC), + "violated expectation about Type enum"); + static_assert(Type::CLIENT_STREAMING == + static_cast(internal::RpcMethod::CLIENT_STREAMING), + "violated expectation about Type enum"); + static_assert(Type::SERVER_STREAMING == + static_cast(internal::RpcMethod::SERVER_STREAMING), + "violated expectation about Type enum"); + static_assert(Type::BIDI_STREAMING == + static_cast(internal::RpcMethod::BIDI_STREAMING), + "violated expectation about Type enum"); + + // Default constructor should only be used by ClientContext + ClientRpcInfo() = default; + + // Constructor will only be called from ClientContext + ClientRpcInfo(grpc::ClientContext* ctx, internal::RpcMethod::RpcType type, + const char* method, grpc::ChannelInterface* channel) + : ctx_(ctx), + type_(static_cast(type)), + method_(method), + channel_(channel) {} + + // Move assignment should only be used by ClientContext + // TODO(yashykt): Delete move assignment + ClientRpcInfo& operator=(ClientRpcInfo&&) = default; + + // Runs interceptor at pos \a pos. + void RunInterceptor( + experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) { + GPR_CODEGEN_ASSERT(pos < interceptors_.size()); + interceptors_[pos]->Intercept(interceptor_methods); + } + + void RegisterInterceptors( + const std::vector>& creators, + size_t interceptor_pos) { + if (interceptor_pos > creators.size()) { + // No interceptors to register + return; + } + for (auto it = creators.begin() + interceptor_pos; it != creators.end(); + ++it) { + auto* interceptor = (*it)->CreateClientInterceptor(this); + if (interceptor != nullptr) { + interceptors_.push_back( + std::unique_ptr(interceptor)); + } + } + if (internal::g_global_client_interceptor_factory != nullptr) { + interceptors_.push_back(std::unique_ptr( + internal::g_global_client_interceptor_factory + ->CreateClientInterceptor(this))); + } + } + + grpc::ClientContext* ctx_ = nullptr; + // TODO(yashykt): make type_ const once move-assignment is deleted + Type type_{Type::UNKNOWN}; + const char* method_ = nullptr; + grpc::ChannelInterface* channel_ = nullptr; + std::vector> interceptors_; + bool hijacked_ = false; + size_t hijacked_interceptor_ = 0; + + friend class internal::InterceptorBatchMethodsImpl; + friend class grpc::ClientContext; +}; + +// PLEASE DO NOT USE THIS. ALWAYS PREFER PER CHANNEL INTERCEPTORS OVER A GLOBAL +// INTERCEPTOR. IF USAGE IS ABSOLUTELY NECESSARY, PLEASE READ THE SAFETY NOTES. +// Registers a global client interceptor factory object, which is used for all +// RPCs made in this process. If the argument is nullptr, the global +// interceptor factory is deregistered. The application is responsible for +// maintaining the life of the object while gRPC operations are in progress. It +// is unsafe to try to register/deregister if any gRPC operation is in progress. +// For safety, it is in the best interests of the developer to register the +// global interceptor factory once at the start of the process before any gRPC +// operations have begun. Deregistration is optional since gRPC does not +// maintain any references to the object. +void RegisterGlobalClientInterceptorFactory( + ClientInterceptorFactoryInterface* factory); + +} // namespace experimental +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CLIENT_INTERCEPTOR_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/client_unary_call.h b/third_party/grpc/include/grpcpp/impl/codegen/client_unary_call.h new file mode 100644 index 00000000000000..5151839412b695 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/client_unary_call.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H +#define GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H + +#include +#include +#include +#include +#include + +namespace grpc { + +class Channel; +class ClientContext; +class CompletionQueue; + +namespace internal { +class RpcMethod; +/// Wrapper that performs a blocking unary call +template +Status BlockingUnaryCall(ChannelInterface* channel, const RpcMethod& method, + ClientContext* context, const InputMessage& request, + OutputMessage* result) { + return BlockingUnaryCallImpl( + channel, method, context, request, result) + .status(); +} + +template +class BlockingUnaryCallImpl { + public: + BlockingUnaryCallImpl(ChannelInterface* channel, const RpcMethod& method, + ClientContext* context, const InputMessage& request, + OutputMessage* result) { + CompletionQueue cq(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, + nullptr}); // Pluckable completion queue + Call call(channel->CreateCall(method, context, &cq)); + CallOpSet, + CallOpClientSendClose, CallOpClientRecvStatus> + ops; + status_ = ops.SendMessage(request); + if (!status_.ok()) { + return; + } + ops.SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + ops.RecvInitialMetadata(context); + ops.RecvMessage(result); + ops.AllowNoMessage(); + ops.ClientSendClose(); + ops.ClientRecvStatus(context, &status_); + call.PerformOps(&ops); + cq.Pluck(&ops); + // Some of the ops might fail. If the ops fail in the core layer, status + // would reflect the error. But, if the ops fail in the C++ layer, the + // status would still be the same as the one returned by gRPC Core. This can + // happen if deserialization of the message fails. + // TODO(yashykt): If deserialization fails, but the status received is OK, + // then it might be a good idea to change the status to something better + // than StatusCode::UNIMPLEMENTED to reflect this. + if (!ops.got_message && status_.ok()) { + status_ = Status(StatusCode::UNIMPLEMENTED, + "No message returned for unary request"); + } + } + Status status() { return status_; } + + private: + Status status_; +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CLIENT_UNARY_CALL_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/completion_queue.h b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue.h new file mode 100644 index 00000000000000..fb38788f7d63a3 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue.h @@ -0,0 +1,403 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// A completion queue implements a concurrent producer-consumer queue, with +/// two main API-exposed methods: \a Next and \a AsyncNext. These +/// methods are the essential component of the gRPC C++ asynchronous API. +/// There is also a \a Shutdown method to indicate that a given completion queue +/// will no longer have regular events. This must be called before the +/// completion queue is destroyed. +/// All completion queue APIs are thread-safe and may be used concurrently with +/// any other completion queue API invocation; it is acceptable to have +/// multiple threads calling \a Next or \a AsyncNext on the same or different +/// completion queues, or to call these methods concurrently with a \a Shutdown +/// elsewhere. +/// \remark{All other API calls on completion queue should be completed before +/// a completion queue destructor is called.} +#ifndef GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H +#define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H + +#include +#include +#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; +namespace internal { +template +class ServerReaderWriterBody; +} // namespace internal + +class Channel; +class ChannelInterface; +class ClientContext; +class CompletionQueue; +class Server; +class ServerBuilder; +class ServerContext; +class ServerInterface; + +namespace internal { +class CompletionQueueTag; +class RpcMethod; +template +class RpcMethodHandler; +template +class ClientStreamingHandler; +template +class ServerStreamingHandler; +template +class BidiStreamingHandler; +template +class TemplatedBidiStreamingHandler; +template +class ErrorMethodHandler; +template +class BlockingUnaryCallImpl; +} // namespace internal + +extern CoreCodegenInterface* g_core_codegen_interface; + +/// A thin wrapper around \ref grpc_completion_queue (see \ref +/// src/core/lib/surface/completion_queue.h). +/// See \ref doc/cpp/perf_notes.md for notes on best practices for high +/// performance servers. +class CompletionQueue : private GrpcLibraryCodegen { + public: + /// Default constructor. Implicitly creates a \a grpc_completion_queue + /// instance. + CompletionQueue() + : CompletionQueue(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_NEXT, GRPC_CQ_DEFAULT_POLLING, + nullptr}) {} + + /// 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() { + g_core_codegen_interface->grpc_completion_queue_destroy(cq_); + } + + /// Tri-state return for AsyncNext: SHUTDOWN, GOT_EVENT, TIMEOUT. + enum NextStatus { + SHUTDOWN, ///< The completion queue has been shutdown and fully-drained + 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 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 successful event, false otherwise. + /// + /// Note that each tag sent to the completion queue (through RPC operations + /// or alarms) will be delivered out of the completion queue by a call to + /// Next (or a related method), regardless of whether the operation succeeded + /// or not. Success here means that this operation completed in the normal + /// valid manner. + /// + /// Server-side RPC request: \a ok indicates that the RPC has indeed + /// been started. If it is false, the server has been Shutdown + /// before this particular call got matched to an incoming RPC. + /// + /// Client-side StartCall/RPC invocation: \a ok indicates that the RPC is + /// going to go to the wire. If it is false, it not going to the wire. This + /// would happen if the channel is either permanently broken or + /// transiently broken but with the fail-fast option. (Note that async unary + /// RPCs don't post a CQ tag at this point, nor do client-streaming + /// or bidi-streaming RPCs that have the initial metadata corked option set.) + /// + /// Client-side Write, Client-side WritesDone, Server-side Write, + /// Server-side Finish, Server-side SendInitialMetadata (which is + /// typically included in Write or Finish when not done explicitly): + /// \a ok means that the data/metadata/status/etc is going to go to the + /// wire. If it is false, it not going to the wire because the call + /// is already dead (i.e., canceled, deadline expired, other side + /// dropped the channel, etc). + /// + /// Client-side Read, Server-side Read, Client-side + /// RecvInitialMetadata (which is typically included in Read if not + /// done explicitly): \a ok indicates whether there is a valid message + /// that got read. If not, you know that there are certainly no more + /// messages that can ever be read from this stream. For the client-side + /// operations, this only happens because the call is dead. For the + /// server-sider operation, though, this could happen because the client + /// has done a WritesDone already. + /// + /// Client-side Finish: \a ok should always be true + /// + /// Server-side AsyncNotifyWhenDone: \a ok should always be true + /// + /// Alarm: \a ok is true if it expired, false if it was canceled + /// + /// \return true if got an event, false if the queue is fully drained and + /// shut down. + bool Next(void** tag, bool* ok) { + return (AsyncNextInternal(tag, ok, + g_core_codegen_interface->gpr_inf_future( + GPR_CLOCK_REALTIME)) != SHUTDOWN); + } + + /// 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 a successful event, false otherwise + /// See documentation for CompletionQueue::Next for explanation of ok + /// \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()); + } + + /// EXPERIMENTAL + /// First executes \a F, then reads 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 f [in] Function to execute before calling AsyncNext on this queue. + /// \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 DoThenAsyncNext(F&& f, void** tag, bool* ok, const T& deadline) { + CompletionQueueTLSCache cache = CompletionQueueTLSCache(this); + f(); + if (cache.Flush(tag, ok)) { + return GOT_EVENT; + } else { + return AsyncNext(tag, ok, deadline); + } + } + + /// Request the shutdown of the queue. + /// + /// \warning This method must be called at some point if this completion queue + /// is accessed with Next or AsyncNext. \a Next will not return false + /// until this method has been called and all pending tags have been drained. + /// (Likewise for \a AsyncNext returning \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. + /// Also note that applications must ensure that no work is enqueued on this + /// completion queue after this method is called. + 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_; } + + protected: + /// Private constructor of CompletionQueue only visible to friend classes + CompletionQueue(const grpc_completion_queue_attributes& attributes) { + cq_ = g_core_codegen_interface->grpc_completion_queue_create( + g_core_codegen_interface->grpc_completion_queue_factory_lookup( + &attributes), + &attributes, NULL); + InitialAvalanching(); // reserve this for the future shutdown + } + + 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::internal::ServerReaderWriterBody; + template + friend class ::grpc::internal::RpcMethodHandler; + template + friend class ::grpc::internal::ClientStreamingHandler; + template + friend class ::grpc::internal::ServerStreamingHandler; + template + friend class ::grpc::internal::TemplatedBidiStreamingHandler; + template + friend class ::grpc::internal::ErrorMethodHandler; + friend class ::grpc::Server; + friend class ::grpc::ServerContext; + friend class ::grpc::ServerInterface; + template + friend class ::grpc::internal::BlockingUnaryCallImpl; + + // Friends that need access to constructor for callback CQ + friend class ::grpc::Channel; + + /// EXPERIMENTAL + /// Creates a Thread Local cache to store the first event + /// On this completion queue queued from this thread. Once + /// initialized, it must be flushed on the same thread. + class CompletionQueueTLSCache { + public: + CompletionQueueTLSCache(CompletionQueue* cq); + ~CompletionQueueTLSCache(); + bool Flush(void** tag, bool* ok); + + private: + CompletionQueue* cq_; + bool flushed_; + }; + + 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(internal::CompletionQueueTag* tag) { + auto deadline = + g_core_codegen_interface->gpr_inf_future(GPR_CLOCK_REALTIME); + while (true) { + auto ev = g_core_codegen_interface->grpc_completion_queue_pluck( + cq_, tag, deadline, nullptr); + bool ok = ev.success != 0; + void* ignored = tag; + if (tag->FinalizeResult(&ignored, &ok)) { + GPR_CODEGEN_ASSERT(ignored == tag); + return ok; + } + } + } + + /// Performs a single polling pluck on \a tag. + /// \warning Must not be mixed with calls to \a Next. + /// + /// TODO: sreek - This calls tag->FinalizeResult() even if the cq_ is already + /// shutdown. This is most likely a bug and if it is a bug, then change this + /// implementation to simple call the other TryPluck function with a zero + /// timeout. i.e: + /// TryPluck(tag, gpr_time_0(GPR_CLOCK_REALTIME)) + void TryPluck(internal::CompletionQueueTag* tag) { + auto deadline = g_core_codegen_interface->gpr_time_0(GPR_CLOCK_REALTIME); + auto ev = g_core_codegen_interface->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_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok)); + } + + /// Performs a single polling pluck on \a tag. Calls tag->FinalizeResult if + /// the pluck() was successful and returned the tag. + /// + /// This exects tag->FinalizeResult (if called) to return 'false' i.e expects + /// that the tag is internal not something that is returned to the user. + void TryPluck(internal::CompletionQueueTag* tag, gpr_timespec deadline) { + auto ev = g_core_codegen_interface->grpc_completion_queue_pluck( + cq_, tag, deadline, nullptr); + if (ev.type == GRPC_QUEUE_TIMEOUT || ev.type == GRPC_QUEUE_SHUTDOWN) { + return; + } + + bool ok = ev.success != 0; + void* ignored = tag; + GPR_CODEGEN_ASSERT(!tag->FinalizeResult(&ignored, &ok)); + } + + /// Manage state of avalanching operations : completion queue tags that + /// trigger other completion queue operations. The underlying core completion + /// queue should not really shutdown until all avalanching operations have + /// been finalized. Note that we maintain the requirement that an avalanche + /// registration must take place before CQ shutdown (which must be maintained + /// elsehwere) + void InitialAvalanching() { + gpr_atm_rel_store(&avalanches_in_flight_, static_cast(1)); + } + void RegisterAvalanching() { + gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_, + static_cast(1)); + } + void CompleteAvalanching(); + + grpc_completion_queue* cq_; // owned + + gpr_atm avalanches_in_flight_; +}; + +/// A specific type of completion queue used by the processing of notifications +/// by servers. Instantiated by \a ServerBuilder. +class ServerCompletionQueue : public CompletionQueue { + public: + bool IsFrequentlyPolled() { return polling_type_ != GRPC_CQ_NON_LISTENING; } + + protected: + /// Default constructor + ServerCompletionQueue() : polling_type_(GRPC_CQ_DEFAULT_POLLING) {} + + private: + /// \param completion_type indicates whether this is a NEXT or CALLBACK + /// completion queue. + /// \param polling_type Informs the GRPC library about the type of polling + /// allowed on this completion queue. See grpc_cq_polling_type's description + /// in grpc_types.h for more details. + /// \param shutdown_cb is the shutdown callback used for CALLBACK api queues + ServerCompletionQueue(grpc_cq_completion_type completion_type, + grpc_cq_polling_type polling_type, + grpc_experimental_completion_queue_functor* shutdown_cb) + : CompletionQueue(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, completion_type, polling_type, + shutdown_cb}), + polling_type_(polling_type) {} + + grpc_cq_polling_type polling_type_; + friend class ServerBuilder; + friend class Server; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/completion_queue_tag.h b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue_tag.h new file mode 100644 index 00000000000000..304386a9eccd6a --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/completion_queue_tag.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H +#define GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H + +namespace grpc { + +namespace internal { +/// An interface allowing implementors to process and filter event tags. +class CompletionQueueTag { + public: + virtual ~CompletionQueueTag() {} + + /// FinalizeResult must be called before informing user code that the + /// operation bound to the underlying core completion queue tag has + /// completed. In practice, this means: + /// + /// 1. For the sync API - before returning from Pluck + /// 2. For the CQ-based async API - before returning from Next + /// 3. For the callback-based API - before invoking the user callback + /// + /// This is the method that translates from core-side tag/status to + /// C++ API-observable tag/status. + /// + /// The return value is the status of the operation (returning status is the + /// general behavior of this function). If this function returns false, the + /// tag is dropped and not returned from the completion queue: this concept is + /// for events that are observed at core but not requested by the user + /// application (e.g., server shutdown, for server unimplemented method + /// responses, or for cases where a server-side RPC doesn't have a completion + /// notification registered using AsyncNotifyWhenDone) + virtual bool FinalizeResult(void** tag, bool* status) = 0; +}; +} // namespace internal + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_COMPLETION_QUEUE_TAG_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/config.h b/third_party/grpc/include/grpcpp/impl/codegen/config.h new file mode 100644 index 00000000000000..37f0fd1faf86cc --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/config.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CONFIG_H +#define GRPCPP_IMPL_CODEGEN_CONFIG_H + +#ifndef GRPC_CUSTOM_STRING +#include +#define GRPC_CUSTOM_STRING std::string +#endif + +/// The following macros are deprecated and appear only for users +/// with PB files generated using gRPC 1.0.x plugins. They should +/// not be used in new code +#define GRPC_OVERRIDE override // deprecated +#define GRPC_FINAL final // deprecated + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +using std::to_string; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CONFIG_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/config_protobuf.h b/third_party/grpc/include/grpcpp/impl/codegen/config_protobuf.h new file mode 100644 index 00000000000000..8c2e9e6792781e --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/config_protobuf.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H +#define GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H + +#define GRPC_OPEN_SOURCE_PROTO + +#ifndef GRPC_CUSTOM_PROTOBUF_INT64 +#include +#define GRPC_CUSTOM_PROTOBUF_INT64 ::google::protobuf::int64 +#endif + +#ifndef GRPC_CUSTOM_MESSAGE +#ifdef GRPC_USE_PROTO_LITE +#include +#define GRPC_CUSTOM_MESSAGE ::google::protobuf::MessageLite +#else +#include +#define GRPC_CUSTOM_MESSAGE ::google::protobuf::Message +#endif +#endif + +#ifndef GRPC_CUSTOM_DESCRIPTOR +#include +#include +#define GRPC_CUSTOM_DESCRIPTOR ::google::protobuf::Descriptor +#define GRPC_CUSTOM_DESCRIPTORPOOL ::google::protobuf::DescriptorPool +#define GRPC_CUSTOM_FIELDDESCRIPTOR ::google::protobuf::FieldDescriptor +#define GRPC_CUSTOM_FILEDESCRIPTOR ::google::protobuf::FileDescriptor +#define GRPC_CUSTOM_FILEDESCRIPTORPROTO ::google::protobuf::FileDescriptorProto +#define GRPC_CUSTOM_METHODDESCRIPTOR ::google::protobuf::MethodDescriptor +#define GRPC_CUSTOM_SERVICEDESCRIPTOR ::google::protobuf::ServiceDescriptor +#define GRPC_CUSTOM_SOURCELOCATION ::google::protobuf::SourceLocation +#endif + +#ifndef GRPC_CUSTOM_DESCRIPTORDATABASE +#include +#define GRPC_CUSTOM_DESCRIPTORDATABASE ::google::protobuf::DescriptorDatabase +#define GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE \ + ::google::protobuf::SimpleDescriptorDatabase +#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 + +#ifndef GRPC_CUSTOM_JSONUTIL +#include +#define GRPC_CUSTOM_JSONUTIL ::google::protobuf::util +#define GRPC_CUSTOM_UTIL_STATUS ::google::protobuf::util::Status +#endif + +namespace grpc { +namespace protobuf { + +typedef GRPC_CUSTOM_MESSAGE Message; +typedef GRPC_CUSTOM_PROTOBUF_INT64 int64; + +typedef GRPC_CUSTOM_DESCRIPTOR Descriptor; +typedef GRPC_CUSTOM_DESCRIPTORPOOL DescriptorPool; +typedef GRPC_CUSTOM_DESCRIPTORDATABASE DescriptorDatabase; +typedef GRPC_CUSTOM_FIELDDESCRIPTOR FieldDescriptor; +typedef GRPC_CUSTOM_FILEDESCRIPTOR FileDescriptor; +typedef GRPC_CUSTOM_FILEDESCRIPTORPROTO FileDescriptorProto; +typedef GRPC_CUSTOM_METHODDESCRIPTOR MethodDescriptor; +typedef GRPC_CUSTOM_SERVICEDESCRIPTOR ServiceDescriptor; +typedef GRPC_CUSTOM_SIMPLEDESCRIPTORDATABASE SimpleDescriptorDatabase; +typedef GRPC_CUSTOM_SOURCELOCATION SourceLocation; + +namespace util { +typedef GRPC_CUSTOM_UTIL_STATUS Status; +} // namespace util + +namespace json = GRPC_CUSTOM_JSONUTIL; + +namespace io { +typedef GRPC_CUSTOM_ZEROCOPYOUTPUTSTREAM ZeroCopyOutputStream; +typedef GRPC_CUSTOM_ZEROCOPYINPUTSTREAM ZeroCopyInputStream; +typedef GRPC_CUSTOM_CODEDINPUTSTREAM CodedInputStream; +} // namespace io + +} // namespace protobuf +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CONFIG_PROTOBUF_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/core_codegen.h b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen.h new file mode 100644 index 00000000000000..6ef184d01ab793 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen.h @@ -0,0 +1,123 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H +#define GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H + +// This file should be compiled as part of grpcpp. + +#include +#include +#include +#include + +namespace grpc { + +/// Implementation of the core codegen interface. +class CoreCodegen final : public CoreCodegenInterface { + private: + virtual const grpc_completion_queue_factory* + grpc_completion_queue_factory_lookup( + const grpc_completion_queue_attributes* attributes) override; + virtual grpc_completion_queue* grpc_completion_queue_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attributes, + void* reserved) override; + grpc_completion_queue* grpc_completion_queue_create_for_next( + void* reserved) override; + grpc_completion_queue* grpc_completion_queue_create_for_pluck( + void* reserved) override; + void grpc_completion_queue_destroy(grpc_completion_queue* cq) override; + grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq, void* tag, + gpr_timespec deadline, + void* reserved) override; + + void* gpr_malloc(size_t size) override; + void gpr_free(void* p) override; + + void grpc_init() override; + void grpc_shutdown() override; + + void gpr_mu_init(gpr_mu* mu) override; + void gpr_mu_destroy(gpr_mu* mu) override; + void gpr_mu_lock(gpr_mu* mu) override; + void gpr_mu_unlock(gpr_mu* mu) override; + void gpr_cv_init(gpr_cv* cv) override; + void gpr_cv_destroy(gpr_cv* cv) override; + int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) override; + void gpr_cv_signal(gpr_cv* cv) override; + void gpr_cv_broadcast(gpr_cv* cv) override; + + grpc_call_error grpc_call_start_batch(grpc_call* call, const grpc_op* ops, + size_t nops, void* tag, + void* reserved) override; + grpc_call_error grpc_call_cancel_with_status(grpc_call* call, + grpc_status_code status, + const char* description, + void* reserved) override; + void grpc_call_ref(grpc_call* call) override; + void grpc_call_unref(grpc_call* call) override; + virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) override; + + grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) override; + void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) override; + size_t grpc_byte_buffer_length(grpc_byte_buffer* bb) override; + + int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader, + grpc_byte_buffer* buffer) override; + void grpc_byte_buffer_reader_destroy( + grpc_byte_buffer_reader* reader) override; + int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader, + grpc_slice* slice) override; + + grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice, + size_t nslices) override; + grpc_slice grpc_slice_new_with_user_data(void* p, size_t len, + void (*destroy)(void*), + void* user_data) override; + grpc_slice grpc_slice_new_with_len(void* p, size_t len, + void (*destroy)(void*, size_t)) override; + grpc_slice grpc_empty_slice() override; + grpc_slice grpc_slice_malloc(size_t length) override; + void grpc_slice_unref(grpc_slice slice) override; + grpc_slice grpc_slice_ref(grpc_slice slice) override; + grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) override; + grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) override; + grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) override; + void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_slice slice) override; + void grpc_slice_buffer_pop(grpc_slice_buffer* sb) override; + grpc_slice grpc_slice_from_static_buffer(const void* buffer, + size_t length) override; + grpc_slice grpc_slice_from_copied_buffer(const void* buffer, + size_t length) override; + void grpc_metadata_array_init(grpc_metadata_array* array) override; + void grpc_metadata_array_destroy(grpc_metadata_array* array) override; + + gpr_timespec gpr_inf_future(gpr_clock_type type) override; + gpr_timespec gpr_time_0(gpr_clock_type type) override; + + virtual const Status& ok() override; + virtual const Status& cancelled() override; + + void assert_fail(const char* failed_assertion, const char* file, + int line) override; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/core_codegen_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen_interface.h new file mode 100644 index 00000000000000..20a5b3300c4221 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/core_codegen_interface.h @@ -0,0 +1,159 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H +#define GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H + +#include +#include +#include +#include +#include + +namespace grpc { + +/// Interface between the codegen library and the minimal subset of core +/// features required by the generated code. +/// +/// All undocumented methods are simply forwarding the call to their namesakes. +/// Please refer to their corresponding documentation for details. +/// +/// \warning This interface should be considered internal and private. +class CoreCodegenInterface { + public: + virtual ~CoreCodegenInterface() = default; + + /// Upon a failed assertion, log the error. + virtual void assert_fail(const char* failed_assertion, const char* file, + int line) = 0; + + virtual const grpc_completion_queue_factory* + grpc_completion_queue_factory_lookup( + const grpc_completion_queue_attributes* attributes) = 0; + virtual grpc_completion_queue* grpc_completion_queue_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attributes, void* reserved) = 0; + virtual grpc_completion_queue* grpc_completion_queue_create_for_next( + void* reserved) = 0; + virtual grpc_completion_queue* grpc_completion_queue_create_for_pluck( + void* reserved) = 0; + virtual void grpc_completion_queue_destroy(grpc_completion_queue* cq) = 0; + virtual grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq, + void* tag, + gpr_timespec deadline, + void* reserved) = 0; + + virtual void* gpr_malloc(size_t size) = 0; + virtual void gpr_free(void* p) = 0; + + // These are only to be used to fix edge cases involving grpc_init and + // grpc_shutdown. Calling grpc_init from the codegen interface before + // the real grpc_init is called will cause a crash, so if you use this + // function, ensure that it is not the first call to grpc_init. + virtual void grpc_init() = 0; + virtual void grpc_shutdown() = 0; + + virtual void gpr_mu_init(gpr_mu* mu) = 0; + virtual void gpr_mu_destroy(gpr_mu* mu) = 0; + virtual void gpr_mu_lock(gpr_mu* mu) = 0; + virtual void gpr_mu_unlock(gpr_mu* mu) = 0; + virtual void gpr_cv_init(gpr_cv* cv) = 0; + virtual void gpr_cv_destroy(gpr_cv* cv) = 0; + virtual int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, + gpr_timespec abs_deadline) = 0; + virtual void gpr_cv_signal(gpr_cv* cv) = 0; + virtual void gpr_cv_broadcast(gpr_cv* cv) = 0; + + virtual grpc_byte_buffer* grpc_byte_buffer_copy(grpc_byte_buffer* bb) = 0; + virtual void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) = 0; + virtual size_t grpc_byte_buffer_length(grpc_byte_buffer* bb) + GRPC_MUST_USE_RESULT = 0; + + virtual int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader, + grpc_byte_buffer* buffer) + GRPC_MUST_USE_RESULT = 0; + virtual void grpc_byte_buffer_reader_destroy( + grpc_byte_buffer_reader* reader) = 0; + virtual int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader, + grpc_slice* slice) = 0; + + virtual grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_slice* slice, + size_t nslices) = 0; + virtual grpc_slice grpc_slice_new_with_user_data(void* p, size_t len, + void (*destroy)(void*), + void* user_data) = 0; + virtual grpc_slice grpc_slice_new_with_len(void* p, size_t len, + void (*destroy)(void*, + size_t)) = 0; + virtual grpc_call_error grpc_call_start_batch(grpc_call* call, + const grpc_op* ops, size_t nops, + void* tag, void* reserved) = 0; + virtual grpc_call_error grpc_call_cancel_with_status(grpc_call* call, + grpc_status_code status, + const char* description, + void* reserved) = 0; + virtual void grpc_call_ref(grpc_call* call) = 0; + virtual void grpc_call_unref(grpc_call* call) = 0; + virtual void* grpc_call_arena_alloc(grpc_call* call, size_t length) = 0; + virtual grpc_slice grpc_empty_slice() = 0; + virtual grpc_slice grpc_slice_malloc(size_t length) = 0; + virtual void grpc_slice_unref(grpc_slice slice) = 0; + virtual grpc_slice grpc_slice_ref(grpc_slice slice) = 0; + virtual grpc_slice grpc_slice_split_tail(grpc_slice* s, size_t split) = 0; + virtual grpc_slice grpc_slice_split_head(grpc_slice* s, size_t split) = 0; + virtual grpc_slice grpc_slice_sub(grpc_slice s, size_t begin, size_t end) = 0; + virtual void grpc_slice_buffer_add(grpc_slice_buffer* sb, + grpc_slice slice) = 0; + virtual void grpc_slice_buffer_pop(grpc_slice_buffer* sb) = 0; + virtual grpc_slice grpc_slice_from_static_buffer(const void* buffer, + size_t length) = 0; + virtual grpc_slice grpc_slice_from_copied_buffer(const void* buffer, + size_t length) = 0; + + virtual void grpc_metadata_array_init(grpc_metadata_array* array) = 0; + virtual void grpc_metadata_array_destroy(grpc_metadata_array* array) = 0; + + virtual const Status& ok() = 0; + virtual const Status& cancelled() = 0; + + virtual gpr_timespec gpr_inf_future(gpr_clock_type type) = 0; + virtual gpr_timespec gpr_time_0(gpr_clock_type type) = 0; +}; + +extern CoreCodegenInterface* g_core_codegen_interface; + +/// Codegen specific version of \a GPR_ASSERT. +#define GPR_CODEGEN_ASSERT(x) \ + do { \ + if (!(x)) { \ + grpc::g_core_codegen_interface->assert_fail(#x, __FILE__, __LINE__); \ + } \ + } while (0) + +/// Codegen specific version of \a GPR_DEBUG_ASSERT. +#ifndef NDEBUG +#define GPR_CODEGEN_DEBUG_ASSERT(x) GPR_CODEGEN_ASSERT(x) +#else +#define GPR_CODEGEN_DEBUG_ASSERT(x) \ + do { \ + } while (0) +#endif + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CORE_CODEGEN_INTERFACE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/create_auth_context.h b/third_party/grpc/include/grpcpp/impl/codegen/create_auth_context.h new file mode 100644 index 00000000000000..cb6095c3a5af08 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/create_auth_context.h @@ -0,0 +1,33 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H +#define GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H + +#include + +#include +#include + +namespace grpc { + +std::shared_ptr CreateAuthContext(grpc_call* call); + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_CREATE_AUTH_CONTEXT_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/grpc_library.h b/third_party/grpc/include/grpcpp/impl/codegen/grpc_library.h new file mode 100644 index 00000000000000..17c904d71a1cf9 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/grpc_library.h @@ -0,0 +1,64 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H +#define GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H + +#include + +namespace grpc { + +class GrpcLibraryInterface { + public: + virtual ~GrpcLibraryInterface() = default; + virtual void init() = 0; + virtual void shutdown() = 0; +}; + +/// Initialized by \a grpc::GrpcLibraryInitializer from +/// +extern GrpcLibraryInterface* g_glip; + +/// Classes that require gRPC to be initialized should inherit from this class. +class GrpcLibraryCodegen { + public: + GrpcLibraryCodegen(bool call_grpc_init = true) : grpc_init_called_(false) { + if (call_grpc_init) { + GPR_CODEGEN_ASSERT(g_glip && + "gRPC library not initialized. See " + "grpc::internal::GrpcLibraryInitializer."); + g_glip->init(); + grpc_init_called_ = true; + } + } + virtual ~GrpcLibraryCodegen() { + if (grpc_init_called_) { + GPR_CODEGEN_ASSERT(g_glip && + "gRPC library not initialized. See " + "grpc::internal::GrpcLibraryInitializer."); + g_glip->shutdown(); + } + } + + private: + bool grpc_init_called_; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_GRPC_LIBRARY_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/intercepted_channel.h b/third_party/grpc/include/grpcpp/impl/codegen/intercepted_channel.h new file mode 100644 index 00000000000000..5255a6d14726dd --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/intercepted_channel.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H +#define GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H + +#include + +namespace grpc { + +namespace internal { + +class InterceptorBatchMethodsImpl; + +/// An InterceptedChannel is available to client Interceptors. An +/// InterceptedChannel is unique to an interceptor, and when an RPC is started +/// on this channel, only those interceptors that come after this interceptor +/// see the RPC. +class InterceptedChannel : public ChannelInterface { + public: + virtual ~InterceptedChannel() { channel_ = nullptr; } + + /// 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) override { + return channel_->GetState(try_to_connect); + } + + private: + InterceptedChannel(ChannelInterface* channel, size_t pos) + : channel_(channel), interceptor_pos_(pos) {} + + Call CreateCall(const RpcMethod& method, ClientContext* context, + CompletionQueue* cq) override { + return channel_->CreateCallInternal(method, context, cq, interceptor_pos_); + } + + void PerformOpsOnCall(CallOpSetInterface* ops, Call* call) override { + return channel_->PerformOpsOnCall(ops, call); + } + void* RegisterMethod(const char* method) override { + return channel_->RegisterMethod(method); + } + + void NotifyOnStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline, CompletionQueue* cq, + void* tag) override { + return channel_->NotifyOnStateChangeImpl(last_observed, deadline, cq, tag); + } + bool WaitForStateChangeImpl(grpc_connectivity_state last_observed, + gpr_timespec deadline) override { + return channel_->WaitForStateChangeImpl(last_observed, deadline); + } + + CompletionQueue* CallbackCQ() override { return channel_->CallbackCQ(); } + + ChannelInterface* channel_; + size_t interceptor_pos_; + + friend class InterceptorBatchMethodsImpl; +}; +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_INTERCEPTED_CHANNEL_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/interceptor.h b/third_party/grpc/include/grpcpp/impl/codegen/interceptor.h new file mode 100644 index 00000000000000..46175cd73b8514 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/interceptor.h @@ -0,0 +1,175 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H +#define GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H + +#include +#include +#include +#include +#include + +namespace grpc { + +class ChannelInterface; +class Status; + +namespace experimental { + +/// An enumeration of different possible points at which the \a Intercept +/// method of the \a Interceptor interface may be called. Any given call +/// to \a Intercept will include one or more of these hook points, and +/// each hook point makes certain types of information available to the +/// interceptor. +/// In these enumeration names, PRE_SEND means that an interception has taken +/// place between the time the application provided a certain type of data +/// (e.g., initial metadata, status) and the time that that data goes to the +/// other side. POST_SEND means that the data has been committed for going to +/// the other side (even if it has not yet been received at the other side). +/// PRE_RECV means an interception between the time that a certain +/// operation has been requested and it is available. POST_RECV means that a +/// result is available but has not yet been passed back to the application. +enum class InterceptionHookPoints { + /// The first two in this list are for clients and servers + PRE_SEND_INITIAL_METADATA, + PRE_SEND_MESSAGE, + PRE_SEND_STATUS, // server only + PRE_SEND_CLOSE, // client only: WritesDone for stream; after write in unary + /// The following three are for hijacked clients only and can only be + /// registered by the global interceptor + PRE_RECV_INITIAL_METADATA, + PRE_RECV_MESSAGE, + PRE_RECV_STATUS, + /// The following two are for all clients and servers + POST_RECV_INITIAL_METADATA, + POST_RECV_MESSAGE, + POST_RECV_STATUS, // client only + POST_RECV_CLOSE, // server only + /// This is a special hook point available to both clients and servers when + /// TryCancel() is performed. + /// - No other hook points will be present along with this. + /// - It is illegal for an interceptor to block/delay this operation. + /// - ALL interceptors see this hook point irrespective of whether the + /// RPC was hijacked or not. + PRE_SEND_CANCEL, + NUM_INTERCEPTION_HOOKS +}; + +/// Class that is passed as an argument to the \a Intercept method +/// of the application's \a Interceptor interface implementation. It has five +/// purposes: +/// 1. Indicate which hook points are present at a specific interception +/// 2. Allow an interceptor to inform the library that an RPC should +/// continue to the next stage of its processing (which may be another +/// interceptor or the main path of the library) +/// 3. Allow an interceptor to hijack the processing of the RPC (only for +/// client-side RPCs with PRE_SEND_INITIAL_METADATA) so that it does not +/// proceed with normal processing beyond that stage +/// 4. Access the relevant fields of an RPC at each interception point +/// 5. Set some fields of an RPC at each interception point, when possible +class InterceptorBatchMethods { + public: + virtual ~InterceptorBatchMethods(){}; + /// Determine whether the current batch has an interception hook point + /// of type \a type + virtual bool QueryInterceptionHookPoint(InterceptionHookPoints type) = 0; + /// Signal that the interceptor is done intercepting the current batch of the + /// RPC. Every interceptor must either call Proceed or Hijack on each + /// interception. In most cases, only Proceed will be used. Explicit use of + /// Proceed is what enables interceptors to delay the processing of RPCs + /// while they perform other work. + /// Proceed is a no-op if the batch contains PRE_SEND_CANCEL. Simply returning + /// from the Intercept method does the job of continuing the RPC in this case. + /// This is because PRE_SEND_CANCEL is always in a separate batch and is not + /// allowed to be delayed. + virtual void Proceed() = 0; + /// Indicate that the interceptor has hijacked the RPC (only valid if the + /// batch contains send_initial_metadata on the client side). Later + /// interceptors in the interceptor list will not be called. Later batches + /// on the same RPC will go through interception, but only up to the point + /// of the hijacking interceptor. + virtual void Hijack() = 0; + + /// Returns a modifable ByteBuffer holding the serialized form of the message + /// that is going to be sent. Valid for PRE_SEND_MESSAGE interceptions. + /// A return value of nullptr indicates that this ByteBuffer is not valid. + virtual ByteBuffer* GetSendMessage() = 0; + + /// Returns a modifiable multimap of the initial metadata to be sent. Valid + /// for PRE_SEND_INITIAL_METADATA interceptions. A value of nullptr indicates + /// that this field is not valid. + virtual std::multimap* + GetSendInitialMetadata() = 0; + + /// Returns the status to be sent. Valid for PRE_SEND_STATUS interceptions. + virtual Status GetSendStatus() = 0; + + /// Overwrites the status with \a status. Valid for PRE_SEND_STATUS + /// interceptions. + virtual void ModifySendStatus(const Status& status) = 0; + + /// Returns a modifiable multimap of the trailing metadata to be sent. Valid + /// for PRE_SEND_STATUS interceptions. A value of nullptr indicates + /// that this field is not valid. + virtual std::multimap* + GetSendTrailingMetadata() = 0; + + /// Returns a pointer to the modifiable received message. Note that the + /// message is already deserialized but the type is not set; the interceptor + /// should static_cast to the appropriate type before using it. This is valid + /// for POST_RECV_MESSAGE interceptions; nullptr for not valid + virtual void* GetRecvMessage() = 0; + + /// Returns a modifiable multimap of the received initial metadata. + /// Valid for POST_RECV_INITIAL_METADATA interceptions; nullptr if not valid + virtual std::multimap* + GetRecvInitialMetadata() = 0; + + /// Returns a modifiable view of the received status on POST_RECV_STATUS + /// interceptions; nullptr if not valid. + virtual Status* GetRecvStatus() = 0; + + /// Returns a modifiable multimap of the received trailing metadata on + /// POST_RECV_STATUS interceptions; nullptr if not valid + virtual std::multimap* + GetRecvTrailingMetadata() = 0; + + /// Gets an intercepted channel. When a call is started on this interceptor, + /// only interceptors after the current interceptor are created from the + /// factory objects registered with the channel. This allows calls to be + /// started from interceptors without infinite regress through the interceptor + /// list. + virtual std::unique_ptr GetInterceptedChannel() = 0; +}; + +/// Interface for an interceptor. Interceptor authors must create a class +/// that derives from this parent class. +class Interceptor { + public: + virtual ~Interceptor() {} + + /// The one public method of an Interceptor interface. Override this to + /// trigger the desired actions at the hook points described above. + virtual void Intercept(InterceptorBatchMethods* methods) = 0; +}; + +} // namespace experimental +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_INTERCEPTOR_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/interceptor_common.h b/third_party/grpc/include/grpcpp/impl/codegen/interceptor_common.h new file mode 100644 index 00000000000000..d0aa23cb0a0dba --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/interceptor_common.h @@ -0,0 +1,458 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H +#define GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H + +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace grpc { +namespace internal { + +class InterceptorBatchMethodsImpl + : public experimental::InterceptorBatchMethods { + public: + InterceptorBatchMethodsImpl() { + for (auto i = static_cast(0); + i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS; + i = static_cast( + static_cast(i) + 1)) { + hooks_[static_cast(i)] = false; + } + } + + ~InterceptorBatchMethodsImpl() {} + + bool QueryInterceptionHookPoint( + experimental::InterceptionHookPoints type) override { + return hooks_[static_cast(type)]; + } + + void Proceed() override { + if (call_->client_rpc_info() != nullptr) { + return ProceedClient(); + } + GPR_CODEGEN_ASSERT(call_->server_rpc_info() != nullptr); + ProceedServer(); + } + + void Hijack() override { + // Only the client can hijack when sending down initial metadata + GPR_CODEGEN_ASSERT(!reverse_ && ops_ != nullptr && + call_->client_rpc_info() != nullptr); + // It is illegal to call Hijack twice + GPR_CODEGEN_ASSERT(!ran_hijacking_interceptor_); + auto* rpc_info = call_->client_rpc_info(); + rpc_info->hijacked_ = true; + rpc_info->hijacked_interceptor_ = current_interceptor_index_; + ClearHookPoints(); + ops_->SetHijackingState(); + ran_hijacking_interceptor_ = true; + rpc_info->RunInterceptor(this, current_interceptor_index_); + } + + void AddInterceptionHookPoint(experimental::InterceptionHookPoints type) { + hooks_[static_cast(type)] = true; + } + + ByteBuffer* GetSendMessage() override { return send_message_; } + + std::multimap* GetSendInitialMetadata() override { + return send_initial_metadata_; + } + + Status GetSendStatus() override { + return Status(static_cast(*code_), *error_message_, + *error_details_); + } + + void ModifySendStatus(const Status& status) override { + *code_ = static_cast(status.error_code()); + *error_details_ = status.error_details(); + *error_message_ = status.error_message(); + } + + std::multimap* GetSendTrailingMetadata() + override { + return send_trailing_metadata_; + } + + void* GetRecvMessage() override { return recv_message_; } + + std::multimap* GetRecvInitialMetadata() + override { + return recv_initial_metadata_->map(); + } + + Status* GetRecvStatus() override { return recv_status_; } + + std::multimap* GetRecvTrailingMetadata() + override { + return recv_trailing_metadata_->map(); + } + + void SetSendMessage(ByteBuffer* buf) { send_message_ = buf; } + + void SetSendInitialMetadata( + std::multimap* metadata) { + send_initial_metadata_ = metadata; + } + + void SetSendStatus(grpc_status_code* code, grpc::string* error_details, + grpc::string* error_message) { + code_ = code; + error_details_ = error_details; + error_message_ = error_message; + } + + void SetSendTrailingMetadata( + std::multimap* metadata) { + send_trailing_metadata_ = metadata; + } + + void SetRecvMessage(void* message) { recv_message_ = message; } + + void SetRecvInitialMetadata(MetadataMap* map) { + recv_initial_metadata_ = map; + } + + void SetRecvStatus(Status* status) { recv_status_ = status; } + + void SetRecvTrailingMetadata(MetadataMap* map) { + recv_trailing_metadata_ = map; + } + + std::unique_ptr GetInterceptedChannel() override { + auto* info = call_->client_rpc_info(); + if (info == nullptr) { + return std::unique_ptr(nullptr); + } + // The intercepted channel starts from the interceptor just after the + // current interceptor + return std::unique_ptr(new InterceptedChannel( + info->channel(), current_interceptor_index_ + 1)); + } + + // Clears all state + void ClearState() { + reverse_ = false; + ran_hijacking_interceptor_ = false; + ClearHookPoints(); + } + + // Prepares for Post_recv operations + void SetReverse() { + reverse_ = true; + ran_hijacking_interceptor_ = false; + ClearHookPoints(); + } + + // This needs to be set before interceptors are run + void SetCall(Call* call) { call_ = call; } + + // This needs to be set before interceptors are run using RunInterceptors(). + // Alternatively, RunInterceptors(std::function f) can be used. + void SetCallOpSetInterface(CallOpSetInterface* ops) { ops_ = ops; } + + // Returns true if no interceptors are run. This should be used only by + // subclasses of CallOpSetInterface. SetCall and SetCallOpSetInterface should + // have been called before this. After all the interceptors are done running, + // either ContinueFillOpsAfterInterception or + // ContinueFinalizeOpsAfterInterception will be called. Note that neither of + // them is invoked if there were no interceptors registered. + bool RunInterceptors() { + GPR_CODEGEN_ASSERT(ops_); + auto* client_rpc_info = call_->client_rpc_info(); + if (client_rpc_info != nullptr) { + if (client_rpc_info->interceptors_.size() == 0) { + return true; + } else { + RunClientInterceptors(); + return false; + } + } + + auto* server_rpc_info = call_->server_rpc_info(); + if (server_rpc_info == nullptr || + server_rpc_info->interceptors_.size() == 0) { + return true; + } + RunServerInterceptors(); + return false; + } + + // Returns true if no interceptors are run. Returns false otherwise if there + // are interceptors registered. After the interceptors are done running \a f + // will be invoked. This is to be used only by BaseAsyncRequest and + // SyncRequest. + bool RunInterceptors(std::function f) { + // This is used only by the server for initial call request + GPR_CODEGEN_ASSERT(reverse_ == true); + GPR_CODEGEN_ASSERT(call_->client_rpc_info() == nullptr); + auto* server_rpc_info = call_->server_rpc_info(); + if (server_rpc_info == nullptr || + server_rpc_info->interceptors_.size() == 0) { + return true; + } + callback_ = std::move(f); + RunServerInterceptors(); + return false; + } + + private: + void RunClientInterceptors() { + auto* rpc_info = call_->client_rpc_info(); + if (!reverse_) { + current_interceptor_index_ = 0; + } else { + if (rpc_info->hijacked_) { + current_interceptor_index_ = rpc_info->hijacked_interceptor_; + } else { + current_interceptor_index_ = rpc_info->interceptors_.size() - 1; + } + } + rpc_info->RunInterceptor(this, current_interceptor_index_); + } + + void RunServerInterceptors() { + auto* rpc_info = call_->server_rpc_info(); + if (!reverse_) { + current_interceptor_index_ = 0; + } else { + current_interceptor_index_ = rpc_info->interceptors_.size() - 1; + } + rpc_info->RunInterceptor(this, current_interceptor_index_); + } + + void ProceedClient() { + auto* rpc_info = call_->client_rpc_info(); + if (rpc_info->hijacked_ && !reverse_ && + current_interceptor_index_ == rpc_info->hijacked_interceptor_ && + !ran_hijacking_interceptor_) { + // We now need to provide hijacked recv ops to this interceptor + ClearHookPoints(); + ops_->SetHijackingState(); + ran_hijacking_interceptor_ = true; + rpc_info->RunInterceptor(this, current_interceptor_index_); + return; + } + if (!reverse_) { + current_interceptor_index_++; + // We are going down the stack of interceptors + if (current_interceptor_index_ < rpc_info->interceptors_.size()) { + if (rpc_info->hijacked_ && + current_interceptor_index_ > rpc_info->hijacked_interceptor_) { + // This is a hijacked RPC and we are done with hijacking + ops_->ContinueFillOpsAfterInterception(); + } else { + rpc_info->RunInterceptor(this, current_interceptor_index_); + } + } else { + // we are done running all the interceptors without any hijacking + ops_->ContinueFillOpsAfterInterception(); + } + } else { + // We are going up the stack of interceptors + if (current_interceptor_index_ > 0) { + // Continue running interceptors + current_interceptor_index_--; + rpc_info->RunInterceptor(this, current_interceptor_index_); + } else { + // we are done running all the interceptors without any hijacking + ops_->ContinueFinalizeResultAfterInterception(); + } + } + } + + void ProceedServer() { + auto* rpc_info = call_->server_rpc_info(); + if (!reverse_) { + current_interceptor_index_++; + if (current_interceptor_index_ < rpc_info->interceptors_.size()) { + return rpc_info->RunInterceptor(this, current_interceptor_index_); + } else if (ops_) { + return ops_->ContinueFillOpsAfterInterception(); + } + } else { + // We are going up the stack of interceptors + if (current_interceptor_index_ > 0) { + // Continue running interceptors + current_interceptor_index_--; + return rpc_info->RunInterceptor(this, current_interceptor_index_); + } else if (ops_) { + return ops_->ContinueFinalizeResultAfterInterception(); + } + } + GPR_CODEGEN_ASSERT(callback_); + callback_(); + } + + void ClearHookPoints() { + for (auto i = static_cast(0); + i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS; + i = static_cast( + static_cast(i) + 1)) { + hooks_[static_cast(i)] = false; + } + } + + std::array( + experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS)> + hooks_; + + size_t current_interceptor_index_ = 0; // Current iterator + bool reverse_ = false; + bool ran_hijacking_interceptor_ = false; + Call* call_ = nullptr; // The Call object is present along with CallOpSet + // object/callback + CallOpSetInterface* ops_ = nullptr; + std::function callback_; + + ByteBuffer* send_message_ = nullptr; + + std::multimap* send_initial_metadata_; + + grpc_status_code* code_ = nullptr; + grpc::string* error_details_ = nullptr; + grpc::string* error_message_ = nullptr; + Status send_status_; + + std::multimap* send_trailing_metadata_ = nullptr; + + void* recv_message_ = nullptr; + + MetadataMap* recv_initial_metadata_ = nullptr; + + Status* recv_status_ = nullptr; + + MetadataMap* recv_trailing_metadata_ = nullptr; +}; + +// A special implementation of InterceptorBatchMethods to send a Cancel +// notification down the interceptor stack +class CancelInterceptorBatchMethods + : public experimental::InterceptorBatchMethods { + public: + bool QueryInterceptionHookPoint( + experimental::InterceptionHookPoints type) override { + if (type == experimental::InterceptionHookPoints::PRE_SEND_CANCEL) { + return true; + } else { + return false; + } + } + + void Proceed() override { + // This is a no-op. For actual continuation of the RPC simply needs to + // return from the Intercept method + } + + void Hijack() override { + // Only the client can hijack when sending down initial metadata + GPR_CODEGEN_ASSERT(false && + "It is illegal to call Hijack on a method which has a " + "Cancel notification"); + } + + ByteBuffer* GetSendMessage() override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetSendMessage on a method which " + "has a Cancel notification"); + return nullptr; + } + + std::multimap* GetSendInitialMetadata() override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetSendInitialMetadata on a " + "method which has a Cancel notification"); + return nullptr; + } + + Status GetSendStatus() override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetSendStatus on a method which " + "has a Cancel notification"); + return Status(); + } + + void ModifySendStatus(const Status& status) override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call ModifySendStatus on a method " + "which has a Cancel notification"); + return; + } + + std::multimap* GetSendTrailingMetadata() + override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetSendTrailingMetadata on a " + "method which has a Cancel notification"); + return nullptr; + } + + void* GetRecvMessage() override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetRecvMessage on a method which " + "has a Cancel notification"); + return nullptr; + } + + std::multimap* GetRecvInitialMetadata() + override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetRecvInitialMetadata on a " + "method which has a Cancel notification"); + return nullptr; + } + + Status* GetRecvStatus() override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetRecvStatus on a method which " + "has a Cancel notification"); + return nullptr; + } + + std::multimap* GetRecvTrailingMetadata() + override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetRecvTrailingMetadata on a " + "method which has a Cancel notification"); + return nullptr; + } + + std::unique_ptr GetInterceptedChannel() override { + GPR_CODEGEN_ASSERT(false && + "It is illegal to call GetInterceptedChannel on a " + "method which has a Cancel notification"); + return std::unique_ptr(nullptr); + } +}; +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/metadata_map.h b/third_party/grpc/include/grpcpp/impl/codegen/metadata_map.h new file mode 100644 index 00000000000000..9cec54d9f01c08 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/metadata_map.h @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_METADATA_MAP_H +#define GRPCPP_IMPL_CODEGEN_METADATA_MAP_H + +#include + +#include +#include + +namespace grpc { + +namespace internal { + +const char kBinaryErrorDetailsKey[] = "grpc-status-details-bin"; + +class MetadataMap { + public: + MetadataMap() { Setup(); } + + ~MetadataMap() { Destroy(); } + + grpc::string GetBinaryErrorDetails() { + // if filled_, extract from the multimap for O(log(n)) + if (filled_) { + auto iter = map_.find(kBinaryErrorDetailsKey); + if (iter != map_.end()) { + return grpc::string(iter->second.begin(), iter->second.length()); + } + } + // if not yet filled, take the O(n) lookup to avoid allocating the + // multimap until it is requested. + // TODO(ncteisen): plumb this through core as a first class object, just + // like code and message. + else { + for (size_t i = 0; i < arr_.count; i++) { + if (strncmp(reinterpret_cast( + GRPC_SLICE_START_PTR(arr_.metadata[i].key)), + kBinaryErrorDetailsKey, + GRPC_SLICE_LENGTH(arr_.metadata[i].key)) == 0) { + return grpc::string(reinterpret_cast( + GRPC_SLICE_START_PTR(arr_.metadata[i].value)), + GRPC_SLICE_LENGTH(arr_.metadata[i].value)); + } + } + } + return grpc::string(); + } + + std::multimap* map() { + FillMap(); + return &map_; + } + grpc_metadata_array* arr() { return &arr_; } + + void Reset() { + filled_ = false; + map_.clear(); + Destroy(); + Setup(); + } + + private: + bool filled_ = false; + grpc_metadata_array arr_; + std::multimap map_; + + void Destroy() { + g_core_codegen_interface->grpc_metadata_array_destroy(&arr_); + } + + void Setup() { memset(&arr_, 0, sizeof(arr_)); } + + void FillMap() { + if (filled_) return; + filled_ = true; + for (size_t i = 0; i < arr_.count; i++) { + // TODO(yangg) handle duplicates? + map_.insert(std::pair( + StringRefFromSlice(&arr_.metadata[i].key), + StringRefFromSlice(&arr_.metadata[i].value))); + } + } +}; +} // namespace internal + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_METADATA_MAP_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/method_handler_impl.h b/third_party/grpc/include/grpcpp/impl/codegen/method_handler_impl.h new file mode 100644 index 00000000000000..dd53f975f68dd5 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/method_handler_impl.h @@ -0,0 +1,347 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H +#define GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H + +#include +#include +#include +#include + +namespace grpc { + +namespace internal { + +// Invoke the method handler, fill in the status, and +// return whether or not we finished safely (without an exception). +// Note that exception handling is 0-cost in most compiler/library +// implementations (except when an exception is actually thrown), +// so this process doesn't require additional overhead in the common case. +// Additionally, we don't need to return if we caught an exception or not; +// the handling is the same in either case. +template +Status CatchingFunctionHandler(Callable&& handler) { +#if GRPC_ALLOW_EXCEPTIONS + try { + return handler(); + } catch (...) { + return Status(StatusCode::UNKNOWN, "Unexpected error in RPC handling"); + } +#else // GRPC_ALLOW_EXCEPTIONS + return handler(); +#endif // GRPC_ALLOW_EXCEPTIONS +} + +/// 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) final { + ResponseType rsp; + Status status = param.status; + if (status.ok()) { + status = CatchingFunctionHandler([this, ¶m, &rsp] { + return func_(service_, param.server_context, + static_cast(param.request), &rsp); + }); + static_cast(param.request)->~RequestType(); + } + + GPR_CODEGEN_ASSERT(!param.server_context->sent_initial_metadata_); + CallOpSet + ops; + ops.SendInitialMetadata(¶m.server_context->initial_metadata_, + param.server_context->initial_metadata_flags()); + if (param.server_context->compression_level_set()) { + ops.set_compression_level(param.server_context->compression_level()); + } + if (status.ok()) { + status = ops.SendMessage(rsp); + } + ops.ServerSendStatus(¶m.server_context->trailing_metadata_, status); + param.call->PerformOps(&ops); + param.call->cq()->Pluck(&ops); + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + Status* status) final { + ByteBuffer buf; + buf.set_buffer(req); + auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(RequestType))) RequestType(); + *status = SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + request->~RequestType(); + return nullptr; + } + + 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) final { + ServerReader reader(param.call, param.server_context); + ResponseType rsp; + Status status = CatchingFunctionHandler([this, ¶m, &reader, &rsp] { + return func_(service_, param.server_context, &reader, &rsp); + }); + + CallOpSet + ops; + if (!param.server_context->sent_initial_metadata_) { + ops.SendInitialMetadata(¶m.server_context->initial_metadata_, + param.server_context->initial_metadata_flags()); + if (param.server_context->compression_level_set()) { + ops.set_compression_level(param.server_context->compression_level()); + } + } + if (status.ok()) { + status = ops.SendMessage(rsp); + } + ops.ServerSendStatus(¶m.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) final { + Status status = param.status; + if (status.ok()) { + ServerWriter writer(param.call, param.server_context); + status = CatchingFunctionHandler([this, ¶m, &writer] { + return func_(service_, param.server_context, + static_cast(param.request), &writer); + }); + static_cast(param.request)->~RequestType(); + } + + CallOpSet ops; + if (!param.server_context->sent_initial_metadata_) { + ops.SendInitialMetadata(¶m.server_context->initial_metadata_, + param.server_context->initial_metadata_flags()); + if (param.server_context->compression_level_set()) { + ops.set_compression_level(param.server_context->compression_level()); + } + } + ops.ServerSendStatus(¶m.server_context->trailing_metadata_, status); + param.call->PerformOps(&ops); + if (param.server_context->has_pending_ops_) { + param.call->cq()->Pluck(¶m.server_context->pending_ops_); + } + param.call->cq()->Pluck(&ops); + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + Status* status) final { + ByteBuffer buf; + buf.set_buffer(req); + auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(RequestType))) RequestType(); + *status = SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + request->~RequestType(); + return nullptr; + } + + private: + std::function*)> + func_; + ServiceType* service_; +}; + +/// A wrapper class of an application provided bidi-streaming handler. +/// This also applies to server-streamed implementation of a unary method +/// with the additional requirement that such methods must have done a +/// write for status to be ok +/// Since this is used by more than 1 class, the service is not passed in. +/// Instead, it is expected to be an implicitly-captured argument of func +/// (through bind or something along those lines) +template +class TemplatedBidiStreamingHandler : public MethodHandler { + public: + TemplatedBidiStreamingHandler( + std::function func) + : func_(func), write_needed_(WriteNeeded) {} + + void RunHandler(const HandlerParameter& param) final { + Streamer stream(param.call, param.server_context); + Status status = CatchingFunctionHandler([this, ¶m, &stream] { + return func_(param.server_context, &stream); + }); + + CallOpSet ops; + if (!param.server_context->sent_initial_metadata_) { + ops.SendInitialMetadata(¶m.server_context->initial_metadata_, + param.server_context->initial_metadata_flags()); + if (param.server_context->compression_level_set()) { + ops.set_compression_level(param.server_context->compression_level()); + } + if (write_needed_ && status.ok()) { + // If we needed a write but never did one, we need to mark the + // status as a fail + status = Status(StatusCode::INTERNAL, + "Service did not provide response message"); + } + } + ops.ServerSendStatus(¶m.server_context->trailing_metadata_, status); + param.call->PerformOps(&ops); + if (param.server_context->has_pending_ops_) { + param.call->cq()->Pluck(¶m.server_context->pending_ops_); + } + param.call->cq()->Pluck(&ops); + } + + private: + std::function func_; + const bool write_needed_; +}; + +template +class BidiStreamingHandler + : public TemplatedBidiStreamingHandler< + ServerReaderWriter, false> { + public: + BidiStreamingHandler( + std::function*)> + func, + ServiceType* service) + : TemplatedBidiStreamingHandler< + ServerReaderWriter, false>(std::bind( + func, service, std::placeholders::_1, std::placeholders::_2)) {} +}; + +template +class StreamedUnaryHandler + : public TemplatedBidiStreamingHandler< + ServerUnaryStreamer, true> { + public: + explicit StreamedUnaryHandler( + std::function*)> + func) + : TemplatedBidiStreamingHandler< + ServerUnaryStreamer, true>(func) {} +}; + +template +class SplitServerStreamingHandler + : public TemplatedBidiStreamingHandler< + ServerSplitStreamer, false> { + public: + explicit SplitServerStreamingHandler( + std::function*)> + func) + : TemplatedBidiStreamingHandler< + ServerSplitStreamer, false>(func) {} +}; + +/// General method handler class for errors that prevent real method use +/// e.g., handle unknown method by returning UNIMPLEMENTED error. +template +class ErrorMethodHandler : public MethodHandler { + public: + template + static void FillOps(ServerContext* context, T* ops) { + Status status(code, ""); + if (!context->sent_initial_metadata_) { + ops->SendInitialMetadata(&context->initial_metadata_, + context->initial_metadata_flags()); + if (context->compression_level_set()) { + ops->set_compression_level(context->compression_level()); + } + context->sent_initial_metadata_ = true; + } + ops->ServerSendStatus(&context->trailing_metadata_, status); + } + + void RunHandler(const HandlerParameter& param) final { + CallOpSet ops; + FillOps(param.server_context, &ops); + param.call->PerformOps(&ops); + param.call->cq()->Pluck(&ops); + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + Status* status) final { + // We have to destroy any request payload + if (req != nullptr) { + g_core_codegen_interface->grpc_byte_buffer_destroy(req); + } + return nullptr; + } +}; + +typedef ErrorMethodHandler UnknownMethodHandler; +typedef ErrorMethodHandler + ResourceExhaustedHandler; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_METHOD_HANDLER_IMPL_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_reader.h b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_reader.h new file mode 100644 index 00000000000000..9acae476b116e7 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_reader.h @@ -0,0 +1,151 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_READER_H +#define GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_READER_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/// This header provides an object that reads bytes directly from a +/// grpc::ByteBuffer, via the ZeroCopyInputStream interface + +namespace grpc { + +extern CoreCodegenInterface* g_core_codegen_interface; + +/// This is a specialization of the protobuf class ZeroCopyInputStream +/// The principle is to get one chunk of data at a time from the proto layer, +/// with options to backup (re-see some bytes) or skip (forward past some bytes) +/// +/// Read more about ZeroCopyInputStream interface here: +/// https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyInputStream +class ProtoBufferReader : public ::grpc::protobuf::io::ZeroCopyInputStream { + public: + /// Constructs buffer reader from \a buffer. Will set \a status() to non ok + /// if \a buffer is invalid (the internal buffer has not been initialized). + explicit ProtoBufferReader(ByteBuffer* buffer) + : byte_count_(0), backup_count_(0), status_() { + /// Implemented through a grpc_byte_buffer_reader which iterates + /// over the slices that make up a byte buffer + if (!buffer->Valid() || + !g_core_codegen_interface->grpc_byte_buffer_reader_init( + &reader_, buffer->c_buffer())) { + status_ = Status(StatusCode::INTERNAL, + "Couldn't initialize byte buffer reader"); + } + } + + ~ProtoBufferReader() { + if (status_.ok()) { + g_core_codegen_interface->grpc_byte_buffer_reader_destroy(&reader_); + } + } + + /// Give the proto library a chunk of data from the stream. The caller + /// may safely read from data[0, size - 1]. + bool Next(const void** data, int* size) override { + if (!status_.ok()) { + return false; + } + /// If we have backed up previously, we need to return the backed-up slice + if (backup_count_ > 0) { + *data = GRPC_SLICE_START_PTR(slice_) + GRPC_SLICE_LENGTH(slice_) - + backup_count_; + GPR_CODEGEN_ASSERT(backup_count_ <= INT_MAX); + *size = (int)backup_count_; + backup_count_ = 0; + return true; + } + /// Otherwise get the next slice from the byte buffer reader + if (!g_core_codegen_interface->grpc_byte_buffer_reader_next(&reader_, + &slice_)) { + return false; + } + g_core_codegen_interface->grpc_slice_unref(slice_); + *data = GRPC_SLICE_START_PTR(slice_); + // On win x64, int is only 32bit + GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX); + byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_); + return true; + } + + /// Returns the status of the buffer reader. + Status status() const { return status_; } + + /// The proto library calls this to indicate that we should back up \a count + /// bytes that have already been returned by the last call of Next. + /// So do the backup and have that ready for a later Next. + void BackUp(int count) override { + GPR_CODEGEN_ASSERT(count <= static_cast(GRPC_SLICE_LENGTH(slice_))); + backup_count_ = count; + } + + /// The proto library calls this to skip over \a count bytes. Implement this + /// using Next and BackUp combined. + bool Skip(int count) 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; + } + + /// Returns the total number of bytes read since this object was created. + grpc::protobuf::int64 ByteCount() const override { + return byte_count_ - backup_count_; + } + + // These protected members are needed to support internal optimizations. + // they expose internal bits of grpc core that are NOT stable. If you have + // a use case needs to use one of these functions, please send an email to + // https://groups.google.com/forum/#!forum/grpc-io. + protected: + void set_byte_count(int64_t byte_count) { byte_count_ = byte_count; } + int64_t backup_count() { return backup_count_; } + void set_backup_count(int64_t backup_count) { backup_count_ = backup_count; } + grpc_byte_buffer_reader* reader() { return &reader_; } + grpc_slice* slice() { return &slice_; } + + private: + int64_t byte_count_; ///< total bytes read since object creation + int64_t backup_count_; ///< how far backed up in the stream we are + grpc_byte_buffer_reader reader_; ///< internal object to read \a grpc_slice + ///< from the \a grpc_byte_buffer + grpc_slice slice_; ///< current slice passed back to the caller + Status status_; ///< status of the entire object +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_READER_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_writer.h b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_writer.h new file mode 100644 index 00000000000000..fdff4676b04881 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/proto_buffer_writer.h @@ -0,0 +1,167 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_WRITER_H +#define GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_WRITER_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +/// This header provides an object that writes bytes directly into a +/// grpc::ByteBuffer, via the ZeroCopyOutputStream interface + +namespace grpc { + +extern CoreCodegenInterface* g_core_codegen_interface; + +// Forward declaration for testing use only +namespace internal { +class ProtoBufferWriterPeer; +} // namespace internal + +const int kProtoBufferWriterMaxBufferLength = 1024 * 1024; + +/// This is a specialization of the protobuf class ZeroCopyOutputStream. +/// The principle is to give the proto layer one buffer of bytes at a time +/// that it can use to serialize the next portion of the message, with the +/// option to "backup" if more buffer is given than required at the last buffer. +/// +/// Read more about ZeroCopyOutputStream interface here: +/// https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.io.zero_copy_stream#ZeroCopyOutputStream +class ProtoBufferWriter : public ::grpc::protobuf::io::ZeroCopyOutputStream { + public: + /// Constructor for this derived class + /// + /// \param[out] byte_buffer A pointer to the grpc::ByteBuffer created + /// \param block_size How big are the chunks to allocate at a time + /// \param total_size How many total bytes are required for this proto + ProtoBufferWriter(ByteBuffer* byte_buffer, int block_size, int total_size) + : block_size_(block_size), + total_size_(total_size), + byte_count_(0), + have_backup_(false) { + GPR_CODEGEN_ASSERT(!byte_buffer->Valid()); + /// Create an empty raw byte buffer and look at its underlying slice buffer + grpc_byte_buffer* bp = + g_core_codegen_interface->grpc_raw_byte_buffer_create(NULL, 0); + byte_buffer->set_buffer(bp); + slice_buffer_ = &bp->data.raw.slice_buffer; + } + + ~ProtoBufferWriter() { + if (have_backup_) { + g_core_codegen_interface->grpc_slice_unref(backup_slice_); + } + } + + /// Give the proto library the next buffer of bytes and its size. It is + /// safe for the caller to write from data[0, size - 1]. + bool Next(void** data, int* size) override { + // Protobuf should not ask for more memory than total_size_. + GPR_CODEGEN_ASSERT(byte_count_ < total_size_); + // 1. Use the remaining backup slice if we have one + // 2. Otherwise allocate a slice, up to the remaining length needed + // or our maximum allocation size + // 3. Provide the slice start and size available + // 4. Add the slice being returned to the slice buffer + size_t remain = total_size_ - byte_count_; + if (have_backup_) { + /// If we have a backup slice, we should use it first + slice_ = backup_slice_; + have_backup_ = false; + if (GRPC_SLICE_LENGTH(slice_) > remain) { + GRPC_SLICE_SET_LENGTH(slice_, remain); + } + } else { + // When less than a whole block is needed, only allocate that much. + // But make sure the allocated slice is not inlined. + size_t allocate_length = + remain > static_cast(block_size_) ? block_size_ : remain; + slice_ = g_core_codegen_interface->grpc_slice_malloc( + allocate_length > GRPC_SLICE_INLINED_SIZE + ? allocate_length + : GRPC_SLICE_INLINED_SIZE + 1); + } + *data = GRPC_SLICE_START_PTR(slice_); + // On win x64, int is only 32bit + GPR_CODEGEN_ASSERT(GRPC_SLICE_LENGTH(slice_) <= INT_MAX); + byte_count_ += * size = (int)GRPC_SLICE_LENGTH(slice_); + g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_); + return true; + } + + /// Backup by \a count bytes because Next returned more bytes than needed + /// (only used in the last buffer). \a count must be less than or equal too + /// the last buffer returned from next. + void BackUp(int count) override { + /// 1. Remove the partially-used last slice from the slice buffer + /// 2. Split it into the needed (if any) and unneeded part + /// 3. Add the needed part back to the slice buffer + /// 4. Mark that we still have the remaining part (for later use/unref) + GPR_CODEGEN_ASSERT(count <= static_cast(GRPC_SLICE_LENGTH(slice_))); + g_core_codegen_interface->grpc_slice_buffer_pop(slice_buffer_); + if ((size_t)count == GRPC_SLICE_LENGTH(slice_)) { + backup_slice_ = slice_; + } else { + backup_slice_ = g_core_codegen_interface->grpc_slice_split_tail( + &slice_, GRPC_SLICE_LENGTH(slice_) - count); + g_core_codegen_interface->grpc_slice_buffer_add(slice_buffer_, slice_); + } + // It's dangerous to keep an inlined grpc_slice as the backup slice, since + // on a following Next() call, a reference will be returned to this slice + // via GRPC_SLICE_START_PTR, which will not be an address held by + // slice_buffer_. + have_backup_ = backup_slice_.refcount != NULL; + byte_count_ -= count; + } + + /// Returns the total number of bytes written since this object was created. + grpc::protobuf::int64 ByteCount() const override { return byte_count_; } + + // These protected members are needed to support internal optimizations. + // they expose internal bits of grpc core that are NOT stable. If you have + // a use case needs to use one of these functions, please send an email to + // https://groups.google.com/forum/#!forum/grpc-io. + protected: + grpc_slice_buffer* slice_buffer() { return slice_buffer_; } + void set_byte_count(int64_t byte_count) { byte_count_ = byte_count; } + + private: + // friend for testing purposes only + friend class internal::ProtoBufferWriterPeer; + const int block_size_; ///< size to alloc for each new \a grpc_slice needed + const int total_size_; ///< byte size of proto being serialized + int64_t byte_count_; ///< bytes written since this object was created + grpc_slice_buffer* + slice_buffer_; ///< internal buffer of slices holding the serialized data + bool have_backup_; ///< if we are holding a backup slice or not + grpc_slice backup_slice_; ///< holds space we can still write to, if the + ///< caller has called BackUp + grpc_slice slice_; ///< current slice passed back to the caller +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_PROTO_BUFFER_WRITER_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/proto_utils.h b/third_party/grpc/include/grpcpp/impl/codegen/proto_utils.h new file mode 100644 index 00000000000000..d9db6de05c0cca --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/proto_utils.h @@ -0,0 +1,121 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H +#define GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// This header provides serialization and deserialization between gRPC +/// messages serialized using protobuf and the C++ objects they represent. + +namespace grpc { + +extern CoreCodegenInterface* g_core_codegen_interface; + +// ProtoBufferWriter must be a subclass of ::protobuf::io::ZeroCopyOutputStream. +template +Status GenericSerialize(const grpc::protobuf::Message& msg, ByteBuffer* bb, + bool* own_buffer) { + static_assert(std::is_base_of::value, + "ProtoBufferWriter must be a subclass of " + "::protobuf::io::ZeroCopyOutputStream"); + *own_buffer = true; + int byte_size = msg.ByteSize(); + if ((size_t)byte_size <= GRPC_SLICE_INLINED_SIZE) { + Slice slice(byte_size); + // We serialize directly into the allocated slices memory + GPR_CODEGEN_ASSERT(slice.end() == msg.SerializeWithCachedSizesToArray( + const_cast(slice.begin()))); + ByteBuffer tmp(&slice, 1); + bb->Swap(&tmp); + + return g_core_codegen_interface->ok(); + } + ProtoBufferWriter writer(bb, kProtoBufferWriterMaxBufferLength, byte_size); + return msg.SerializeToZeroCopyStream(&writer) + ? g_core_codegen_interface->ok() + : Status(StatusCode::INTERNAL, "Failed to serialize message"); +} + +// BufferReader must be a subclass of ::protobuf::io::ZeroCopyInputStream. +template +Status GenericDeserialize(ByteBuffer* buffer, grpc::protobuf::Message* msg) { + static_assert(std::is_base_of::value, + "ProtoBufferReader must be a subclass of " + "::protobuf::io::ZeroCopyInputStream"); + if (buffer == nullptr) { + return Status(StatusCode::INTERNAL, "No payload"); + } + Status result = g_core_codegen_interface->ok(); + { + ProtoBufferReader reader(buffer); + if (!reader.status().ok()) { + return reader.status(); + } + ::grpc::protobuf::io::CodedInputStream decoder(&reader); + decoder.SetTotalBytesLimit(INT_MAX, INT_MAX); + if (!msg->ParseFromCodedStream(&decoder)) { + result = Status(StatusCode::INTERNAL, msg->InitializationErrorString()); + } + if (!decoder.ConsumedEntireMessage()) { + result = Status(StatusCode::INTERNAL, "Did not read entire message"); + } + } + buffer->Clear(); + return result; +} + +// this is needed so the following class does not conflict with protobuf +// serializers that utilize internal-only tools. +#ifdef GRPC_OPEN_SOURCE_PROTO +// This class provides a protobuf serializer. It translates between protobuf +// objects and grpc_byte_buffers. More information about SerializationTraits can +// be found in include/grpcpp/impl/codegen/serialization_traits.h. +template +class SerializationTraits::value>::type> { + public: + static Status Serialize(const grpc::protobuf::Message& msg, ByteBuffer* bb, + bool* own_buffer) { + return GenericSerialize(msg, bb, own_buffer); + } + + static Status Deserialize(ByteBuffer* buffer, grpc::protobuf::Message* msg) { + return GenericDeserialize(buffer, msg); + } +}; +#endif + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_PROTO_UTILS_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/rpc_method.h b/third_party/grpc/include/grpcpp/impl/codegen/rpc_method.h new file mode 100644 index 00000000000000..9dcde954f1dac5 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/rpc_method.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_RPC_METHOD_H +#define GRPCPP_IMPL_CODEGEN_RPC_METHOD_H + +#include + +#include + +namespace grpc { +namespace internal { +/// Descriptor of an RPC method +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 SetMethodType(RpcType type) { method_type_ = type; } + void* channel_tag() const { return channel_tag_; } + + private: + const char* const name_; + RpcType method_type_; + void* const channel_tag_; +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_RPC_METHOD_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/rpc_service_method.h b/third_party/grpc/include/grpcpp/impl/codegen/rpc_service_method.h new file mode 100644 index 00000000000000..f465c5fc2f91f7 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/rpc_service_method.h @@ -0,0 +1,150 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H +#define GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace grpc { +class ServerContext; + +namespace internal { +/// Base class for running an RPC handler. +class MethodHandler { + public: + virtual ~MethodHandler() {} + struct HandlerParameter { + /// Constructor for HandlerParameter + /// + /// \param c : the gRPC Call structure for this server call + /// \param context : the ServerContext structure for this server call + /// \param req : the request payload, if appropriate for this RPC + /// \param req_status : the request status after any interceptors have run + /// \param rpc_requester : used only by the callback API. It is a function + /// called by the RPC Controller to request another RPC (and also + /// to set up the state required to make that request possible) + HandlerParameter(Call* c, ServerContext* context, void* req, + Status req_status, std::function requester) + : call(c), + server_context(context), + request(req), + status(req_status), + call_requester(std::move(requester)) {} + ~HandlerParameter() {} + Call* call; + ServerContext* server_context; + void* request; + Status status; + std::function call_requester; + }; + virtual void RunHandler(const HandlerParameter& param) = 0; + + /* Returns a pointer to the deserialized request. \a status reflects the + result of deserialization. This pointer and the status should be filled in + a HandlerParameter and passed to RunHandler. It is illegal to access the + pointer after calling RunHandler. Ownership of the deserialized request is + retained by the handler. Returns nullptr if deserialization failed. */ + virtual void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + Status* status) { + GPR_CODEGEN_ASSERT(req == nullptr); + return nullptr; + } +}; + +/// 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), + server_tag_(nullptr), + api_type_(ApiType::SYNC), + handler_(handler) {} + + enum class ApiType { + SYNC, + ASYNC, + RAW, + CALL_BACK, // not CALLBACK because that is reserved in Windows + RAW_CALL_BACK, + }; + + void set_server_tag(void* tag) { server_tag_ = tag; } + void* server_tag() const { return server_tag_; } + /// if MethodHandler is nullptr, then this is an async method + MethodHandler* handler() const { return handler_.get(); } + ApiType api_type() const { return api_type_; } + void SetHandler(MethodHandler* handler) { handler_.reset(handler); } + void SetServerApiType(RpcServiceMethod::ApiType type) { + if ((api_type_ == ApiType::SYNC) && + (type == ApiType::ASYNC || type == ApiType::RAW)) { + // this marks this method as async + handler_.reset(); + } else if (api_type_ != ApiType::SYNC) { + // this is not an error condition, as it allows users to declare a server + // like WithRawMethod_foo. However since it + // overwrites behavior, it should be logged. + gpr_log( + GPR_INFO, + "You are marking method %s as '%s', even though it was " + "previously marked '%s'. This behavior will overwrite the original " + "behavior. If you expected this then ignore this message.", + name(), TypeToString(api_type_), TypeToString(type)); + } + api_type_ = type; + } + + private: + void* server_tag_; + ApiType api_type_; + std::unique_ptr handler_; + + const char* TypeToString(RpcServiceMethod::ApiType type) { + switch (type) { + case ApiType::SYNC: + return "sync"; + case ApiType::ASYNC: + return "async"; + case ApiType::RAW: + return "raw"; + case ApiType::CALL_BACK: + return "callback"; + case ApiType::RAW_CALL_BACK: + return "raw_callback"; + default: + GPR_UNREACHABLE_CODE(return "unknown"); + } + } +}; +} // namespace internal + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_RPC_SERVICE_METHOD_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/security/auth_context.h b/third_party/grpc/include/grpcpp/impl/codegen/security/auth_context.h new file mode 100644 index 00000000000000..0e30f7cc3f4ba1 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/security/auth_context.h @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H +#define GRPCPP_IMPL_CODEGEN_SECURITY_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 // GRPCPP_IMPL_CODEGEN_SECURITY_AUTH_CONTEXT_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/serialization_traits.h b/third_party/grpc/include/grpcpp/impl/codegen/serialization_traits.h new file mode 100644 index 00000000000000..8f792232909871 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/serialization_traits.h @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H +#define GRPCPP_IMPL_CODEGEN_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: +/// 1. static Status Serialize(const Message& msg, +/// ByteBuffer* buffer, +/// bool* own_buffer); +/// OR +/// static Status Serialize(const Message& msg, +/// grpc_byte_buffer** buffer, +/// bool* own_buffer); +/// The former is preferred; the latter is deprecated +/// +/// 2. static Status Deserialize(ByteBuffer* buffer, +/// Message* msg); +/// OR +/// static Status Deserialize(grpc_byte_buffer* buffer, +/// Message* msg); +/// The former is preferred; the latter is deprecated +/// +/// Serialize is required to convert message to a ByteBuffer, and +/// return that byte buffer through *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_receive_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 // GRPCPP_IMPL_CODEGEN_SERIALIZATION_TRAITS_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_callback.h b/third_party/grpc/include/grpcpp/impl/codegen/server_callback.h new file mode 100644 index 00000000000000..1854f6ef2f5d34 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/server_callback.h @@ -0,0 +1,890 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H +#define GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +// Declare base class of all reactors as internal +namespace internal { + +class ServerReactor { + public: + virtual ~ServerReactor() = default; + virtual void OnDone() {} + virtual void OnCancel() {} +}; + +} // namespace internal + +namespace experimental { + +// Forward declarations +template +class ServerReadReactor; +template +class ServerWriteReactor; +template +class ServerBidiReactor; + +// For unary RPCs, the exposed controller class is only an interface +// and the actual implementation is an internal class. +class ServerCallbackRpcController { + public: + virtual ~ServerCallbackRpcController() = default; + + // The method handler must call this function when it is done so that + // the library knows to free its resources + virtual void Finish(Status s) = 0; + + // Allow the method handler to push out the initial metadata before + // the response and status are ready + virtual void SendInitialMetadata(std::function) = 0; +}; + +// NOTE: The actual streaming object classes are provided +// as API only to support mocking. There are no implementations of +// these class interfaces in the API. +template +class ServerCallbackReader { + public: + virtual ~ServerCallbackReader() {} + virtual void Finish(Status s) = 0; + virtual void SendInitialMetadata() = 0; + virtual void Read(Request* msg) = 0; + + protected: + template + void BindReactor(ServerReadReactor* reactor) { + reactor->BindReader(this); + } +}; + +template +class ServerCallbackWriter { + public: + virtual ~ServerCallbackWriter() {} + + virtual void Finish(Status s) = 0; + virtual void SendInitialMetadata() = 0; + virtual void Write(const Response* msg, WriteOptions options) = 0; + virtual void WriteAndFinish(const Response* msg, WriteOptions options, + Status s) { + // Default implementation that can/should be overridden + Write(msg, std::move(options)); + Finish(std::move(s)); + }; + + protected: + template + void BindReactor(ServerWriteReactor* reactor) { + reactor->BindWriter(this); + } +}; + +template +class ServerCallbackReaderWriter { + public: + virtual ~ServerCallbackReaderWriter() {} + + virtual void Finish(Status s) = 0; + virtual void SendInitialMetadata() = 0; + virtual void Read(Request* msg) = 0; + virtual void Write(const Response* msg, WriteOptions options) = 0; + virtual void WriteAndFinish(const Response* msg, WriteOptions options, + Status s) { + // Default implementation that can/should be overridden + Write(msg, std::move(options)); + Finish(std::move(s)); + }; + + protected: + void BindReactor(ServerBidiReactor* reactor) { + reactor->BindStream(this); + } +}; + +// The following classes are reactors that are to be implemented +// by the user, returned as the result of the method handler for +// a callback method, and activated by the call to OnStarted +template +class ServerBidiReactor : public internal::ServerReactor { + public: + ~ServerBidiReactor() = default; + virtual void OnStarted(ServerContext*) {} + virtual void OnSendInitialMetadataDone(bool ok) {} + virtual void OnReadDone(bool ok) {} + virtual void OnWriteDone(bool ok) {} + + void StartSendInitialMetadata() { stream_->SendInitialMetadata(); } + void StartRead(Request* msg) { stream_->Read(msg); } + void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); } + void StartWrite(const Response* msg, WriteOptions options) { + stream_->Write(msg, std::move(options)); + } + void StartWriteAndFinish(const Response* msg, WriteOptions options, + Status s) { + stream_->WriteAndFinish(msg, std::move(options), std::move(s)); + } + void StartWriteLast(const Response* msg, WriteOptions options) { + StartWrite(msg, std::move(options.set_last_message())); + } + void Finish(Status s) { stream_->Finish(std::move(s)); } + + private: + friend class ServerCallbackReaderWriter; + void BindStream(ServerCallbackReaderWriter* stream) { + stream_ = stream; + } + + ServerCallbackReaderWriter* stream_; +}; + +template +class ServerReadReactor : public internal::ServerReactor { + public: + ~ServerReadReactor() = default; + virtual void OnStarted(ServerContext*, Response* resp) {} + virtual void OnSendInitialMetadataDone(bool ok) {} + virtual void OnReadDone(bool ok) {} + + void StartSendInitialMetadata() { reader_->SendInitialMetadata(); } + void StartRead(Request* msg) { reader_->Read(msg); } + void Finish(Status s) { reader_->Finish(std::move(s)); } + + private: + friend class ServerCallbackReader; + void BindReader(ServerCallbackReader* reader) { reader_ = reader; } + + ServerCallbackReader* reader_; +}; + +template +class ServerWriteReactor : public internal::ServerReactor { + public: + ~ServerWriteReactor() = default; + virtual void OnStarted(ServerContext*, const Request* req) {} + virtual void OnSendInitialMetadataDone(bool ok) {} + virtual void OnWriteDone(bool ok) {} + + void StartSendInitialMetadata() { writer_->SendInitialMetadata(); } + void StartWrite(const Response* msg) { StartWrite(msg, WriteOptions()); } + void StartWrite(const Response* msg, WriteOptions options) { + writer_->Write(msg, std::move(options)); + } + void StartWriteAndFinish(const Response* msg, WriteOptions options, + Status s) { + writer_->WriteAndFinish(msg, std::move(options), std::move(s)); + } + void StartWriteLast(const Response* msg, WriteOptions options) { + StartWrite(msg, std::move(options.set_last_message())); + } + void Finish(Status s) { writer_->Finish(std::move(s)); } + + private: + friend class ServerCallbackWriter; + void BindWriter(ServerCallbackWriter* writer) { writer_ = writer; } + + ServerCallbackWriter* writer_; +}; + +} // namespace experimental + +namespace internal { + +template +class UnimplementedReadReactor + : public experimental::ServerReadReactor { + public: + void OnDone() override { delete this; } + void OnStarted(ServerContext*, Response*) override { + this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); + } +}; + +template +class UnimplementedWriteReactor + : public experimental::ServerWriteReactor { + public: + void OnDone() override { delete this; } + void OnStarted(ServerContext*, const Request*) override { + this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); + } +}; + +template +class UnimplementedBidiReactor + : public experimental::ServerBidiReactor { + public: + void OnDone() override { delete this; } + void OnStarted(ServerContext*) override { + this->Finish(Status(StatusCode::UNIMPLEMENTED, "")); + } +}; + +template +class CallbackUnaryHandler : public MethodHandler { + public: + CallbackUnaryHandler( + std::function + func) + : func_(func) {} + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a controller structure (that includes request/response) + g_core_codegen_interface->grpc_call_ref(param.call->call()); + auto* controller = new (g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackRpcControllerImpl))) + ServerCallbackRpcControllerImpl( + param.server_context, param.call, + static_cast(param.request), + std::move(param.call_requester)); + Status status = param.status; + + if (status.ok()) { + // Call the actual function handler and expect the user to call finish + CatchingCallback(func_, param.server_context, controller->request(), + controller->response(), controller); + } else { + // if deserialization failed, we need to fail the call + controller->Finish(status); + } + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + Status* status) final { + ByteBuffer buf; + buf.set_buffer(req); + auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(RequestType))) RequestType(); + *status = SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + request->~RequestType(); + return nullptr; + } + + private: + std::function + func_; + + // The implementation class of ServerCallbackRpcController is a private member + // of CallbackUnaryHandler since it is never exposed anywhere, and this allows + // it to take advantage of CallbackUnaryHandler's friendships. + class ServerCallbackRpcControllerImpl + : public experimental::ServerCallbackRpcController { + public: + void Finish(Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (s.ok()) { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_ops_.SendMessage(resp_)); + } else { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + } + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata(std::function f) override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_++; + // TODO(vjpai): Consider taking f as a move-capture if we adopt C++14 + // and if performance of this operation matters + meta_tag_.Set(call_.call(), + [this, f](bool ok) { + f(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + private: + friend class CallbackUnaryHandler; + + ServerCallbackRpcControllerImpl(ServerContext* ctx, Call* call, + const RequestType* req, + std::function call_requester) + : ctx_(ctx), + call_(*call), + req_(req), + call_requester_(std::move(call_requester)) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, nullptr); + } + + ~ServerCallbackRpcControllerImpl() { req_->~RequestType(); } + + const RequestType* request() { return req_; } + ResponseType* response() { return &resp_; } + + void MaybeDone() { + if (--callbacks_outstanding_ == 0) { + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackRpcControllerImpl(); // explicitly call destructor + g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + CallOpSet meta_ops_; + CallbackWithSuccessTag meta_tag_; + CallOpSet + finish_ops_; + CallbackWithSuccessTag finish_tag_; + + ServerContext* ctx_; + Call call_; + const RequestType* req_; + ResponseType resp_; + std::function call_requester_; + std::atomic_int callbacks_outstanding_{ + 2}; // reserve for Finish and CompletionOp + }; +}; + +template +class CallbackClientStreamingHandler : public MethodHandler { + public: + CallbackClientStreamingHandler( + std::function< + experimental::ServerReadReactor*()> + func) + : func_(std::move(func)) {} + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a reader structure (that includes response) + g_core_codegen_interface->grpc_call_ref(param.call->call()); + + experimental::ServerReadReactor* reactor = + param.status.ok() + ? CatchingReactorCreator< + experimental::ServerReadReactor>( + func_) + : nullptr; + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new UnimplementedReadReactor; + } + + auto* reader = new (g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackReaderImpl))) + ServerCallbackReaderImpl(param.server_context, param.call, + std::move(param.call_requester), reactor); + + reader->BindReactor(reactor); + reactor->OnStarted(param.server_context, reader->response()); + reader->MaybeDone(); + } + + private: + std::function*()> + func_; + + class ServerCallbackReaderImpl + : public experimental::ServerCallbackReader { + public: + void Finish(Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // The response is dropped if the status is not OK. + if (s.ok()) { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, + finish_ops_.SendMessage(resp_)); + } else { + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + } + finish_ops_.set_core_cq_tag(&finish_tag_); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_++; + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Read(RequestType* req) override { + callbacks_outstanding_++; + read_ops_.RecvMessage(req); + call_.PerformOps(&read_ops_); + } + + private: + friend class CallbackClientStreamingHandler; + + ServerCallbackReaderImpl( + ServerContext* ctx, Call* call, std::function call_requester, + experimental::ServerReadReactor* reactor) + : ctx_(ctx), + call_(*call), + call_requester_(std::move(call_requester)), + reactor_(reactor) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeDone(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + } + + ~ServerCallbackReaderImpl() {} + + ResponseType* response() { return &resp_; } + + void MaybeDone() { + if (--callbacks_outstanding_ == 0) { + reactor_->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackReaderImpl(); // explicitly call destructor + g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + CallOpSet meta_ops_; + CallbackWithSuccessTag meta_tag_; + CallOpSet + finish_ops_; + CallbackWithSuccessTag finish_tag_; + CallOpSet> read_ops_; + CallbackWithSuccessTag read_tag_; + + ServerContext* ctx_; + Call call_; + ResponseType resp_; + std::function call_requester_; + experimental::ServerReadReactor* reactor_; + std::atomic_int callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +template +class CallbackServerStreamingHandler : public MethodHandler { + public: + CallbackServerStreamingHandler( + std::function< + experimental::ServerWriteReactor*()> + func) + : func_(std::move(func)) {} + void RunHandler(const HandlerParameter& param) final { + // Arena allocate a writer structure + g_core_codegen_interface->grpc_call_ref(param.call->call()); + + experimental::ServerWriteReactor* reactor = + param.status.ok() + ? CatchingReactorCreator< + experimental::ServerWriteReactor>( + func_) + : nullptr; + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new UnimplementedWriteReactor; + } + + auto* writer = new (g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackWriterImpl))) + ServerCallbackWriterImpl(param.server_context, param.call, + static_cast(param.request), + std::move(param.call_requester), reactor); + writer->BindReactor(reactor); + reactor->OnStarted(param.server_context, writer->request()); + writer->MaybeDone(); + } + + void* Deserialize(grpc_call* call, grpc_byte_buffer* req, + Status* status) final { + ByteBuffer buf; + buf.set_buffer(req); + auto* request = new (g_core_codegen_interface->grpc_call_arena_alloc( + call, sizeof(RequestType))) RequestType(); + *status = SerializationTraits::Deserialize(&buf, request); + buf.Release(); + if (status->ok()) { + return request; + } + request->~RequestType(); + return nullptr; + } + + private: + std::function*()> + func_; + + class ServerCallbackWriterImpl + : public experimental::ServerCallbackWriter { + public: + void Finish(Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + finish_ops_.set_core_cq_tag(&finish_tag_); + + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_++; + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Write(const ResponseType* resp, WriteOptions options) override { + callbacks_outstanding_++; + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + write_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WriteAndFinish(const ResponseType* resp, WriteOptions options, + Status s) override { + // This combines the write into the finish callback + // Don't send any message if the status is bad + if (s.ok()) { + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok()); + } + Finish(std::move(s)); + } + + private: + friend class CallbackServerStreamingHandler; + + ServerCallbackWriterImpl( + ServerContext* ctx, Call* call, const RequestType* req, + std::function call_requester, + experimental::ServerWriteReactor* reactor) + : ctx_(ctx), + call_(*call), + req_(req), + call_requester_(std::move(call_requester)), + reactor_(reactor) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeDone(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + } + ~ServerCallbackWriterImpl() { req_->~RequestType(); } + + const RequestType* request() { return req_; } + + void MaybeDone() { + if (--callbacks_outstanding_ == 0) { + reactor_->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackWriterImpl(); // explicitly call destructor + g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + CallOpSet meta_ops_; + CallbackWithSuccessTag meta_tag_; + CallOpSet + finish_ops_; + CallbackWithSuccessTag finish_tag_; + CallOpSet write_ops_; + CallbackWithSuccessTag write_tag_; + + ServerContext* ctx_; + Call call_; + const RequestType* req_; + std::function call_requester_; + experimental::ServerWriteReactor* reactor_; + std::atomic_int callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +template +class CallbackBidiHandler : public MethodHandler { + public: + CallbackBidiHandler( + std::function< + experimental::ServerBidiReactor*()> + func) + : func_(std::move(func)) {} + void RunHandler(const HandlerParameter& param) final { + g_core_codegen_interface->grpc_call_ref(param.call->call()); + + experimental::ServerBidiReactor* reactor = + param.status.ok() + ? CatchingReactorCreator< + experimental::ServerBidiReactor>( + func_) + : nullptr; + + if (reactor == nullptr) { + // if deserialization or reactor creator failed, we need to fail the call + reactor = new UnimplementedBidiReactor; + } + + auto* stream = new (g_core_codegen_interface->grpc_call_arena_alloc( + param.call->call(), sizeof(ServerCallbackReaderWriterImpl))) + ServerCallbackReaderWriterImpl(param.server_context, param.call, + std::move(param.call_requester), + reactor); + + stream->BindReactor(reactor); + reactor->OnStarted(param.server_context); + stream->MaybeDone(); + } + + private: + std::function*()> + func_; + + class ServerCallbackReaderWriterImpl + : public experimental::ServerCallbackReaderWriter { + public: + void Finish(Status s) override { + finish_tag_.Set(call_.call(), [this](bool) { MaybeDone(); }, + &finish_ops_); + finish_ops_.set_core_cq_tag(&finish_tag_); + + if (!ctx_->sent_initial_metadata_) { + finish_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + finish_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + finish_ops_.ServerSendStatus(&ctx_->trailing_metadata_, s); + call_.PerformOps(&finish_ops_); + } + + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + callbacks_outstanding_++; + meta_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnSendInitialMetadataDone(ok); + MaybeDone(); + }, + &meta_ops_); + meta_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + meta_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + meta_ops_.set_core_cq_tag(&meta_tag_); + call_.PerformOps(&meta_ops_); + } + + void Write(const ResponseType* resp, WriteOptions options) override { + callbacks_outstanding_++; + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->sent_initial_metadata_) { + write_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + write_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(write_ops_.SendMessage(*resp, options).ok()); + call_.PerformOps(&write_ops_); + } + + void WriteAndFinish(const ResponseType* resp, WriteOptions options, + Status s) override { + // Don't send any message if the status is bad + if (s.ok()) { + // TODO(vjpai): don't assert + GPR_CODEGEN_ASSERT(finish_ops_.SendMessage(*resp, options).ok()); + } + Finish(std::move(s)); + } + + void Read(RequestType* req) override { + callbacks_outstanding_++; + read_ops_.RecvMessage(req); + call_.PerformOps(&read_ops_); + } + + private: + friend class CallbackBidiHandler; + + ServerCallbackReaderWriterImpl( + ServerContext* ctx, Call* call, std::function call_requester, + experimental::ServerBidiReactor* reactor) + : ctx_(ctx), + call_(*call), + call_requester_(std::move(call_requester)), + reactor_(reactor) { + ctx_->BeginCompletionOp(call, [this](bool) { MaybeDone(); }, reactor); + write_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnWriteDone(ok); + MaybeDone(); + }, + &write_ops_); + write_ops_.set_core_cq_tag(&write_tag_); + read_tag_.Set(call_.call(), + [this](bool ok) { + reactor_->OnReadDone(ok); + MaybeDone(); + }, + &read_ops_); + read_ops_.set_core_cq_tag(&read_tag_); + } + ~ServerCallbackReaderWriterImpl() {} + + void MaybeDone() { + if (--callbacks_outstanding_ == 0) { + reactor_->OnDone(); + grpc_call* call = call_.call(); + auto call_requester = std::move(call_requester_); + this->~ServerCallbackReaderWriterImpl(); // explicitly call destructor + g_core_codegen_interface->grpc_call_unref(call); + call_requester(); + } + } + + CallOpSet meta_ops_; + CallbackWithSuccessTag meta_tag_; + CallOpSet + finish_ops_; + CallbackWithSuccessTag finish_tag_; + CallOpSet write_ops_; + CallbackWithSuccessTag write_tag_; + CallOpSet> read_ops_; + CallbackWithSuccessTag read_tag_; + + ServerContext* ctx_; + Call call_; + std::function call_requester_; + experimental::ServerBidiReactor* reactor_; + std::atomic_int callbacks_outstanding_{ + 3}; // reserve for OnStarted, Finish, and CompletionOp + }; +}; + +} // namespace internal + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_SERVER_CALLBACK_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_context.h b/third_party/grpc/include/grpcpp/impl/codegen/server_context.h new file mode 100644 index 00000000000000..affe61b547bb65 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/server_context.h @@ -0,0 +1,370 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H +#define GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +namespace internal { +template +class ServerReaderWriterBody; +template +class RpcMethodHandler; +template +class ClientStreamingHandler; +template +class ServerStreamingHandler; +template +class BidiStreamingHandler; +template +class CallbackUnaryHandler; +template +class CallbackClientStreamingHandler; +template +class CallbackServerStreamingHandler; +template +class CallbackBidiHandler; +template +class TemplatedBidiStreamingHandler; +template +class ErrorMethodHandler; +class Call; +class ServerReactor; +} // namespace internal + +class CompletionQueue; +class Server; +class ServerInterface; + +namespace testing { +class InteropServerContextInspector; +class ServerContextTestSpouse; +} // namespace testing + +/// A ServerContext allows the person implementing a service handler to: +/// +/// - Add custom initial and trailing metadata key-value pairs that will +/// propagated to the client side. +/// - Control call settings such as compression and authentication. +/// - Access metadata coming from the client. +/// - Get performance metrics (ie, census). +/// +/// Context settings are only relevant to the call handler they are supplied to, +/// that is to say, they aren't sticky across multiple calls. Some of these +/// settings, such as the compression options, can be made persistent at server +/// construction time by specifying the appropriate \a ChannelArguments +/// to a \a grpc::ServerBuilder, via \a ServerBuilder::AddChannelArgument. +/// +/// \warning ServerContext instances should \em not be reused across rpcs. +class ServerContext { + public: + ServerContext(); // for async calls + ~ServerContext(); + + /// Return the deadline for the server call. + std::chrono::system_clock::time_point deadline() const { + return Timespec2Timepoint(deadline_); + } + + /// Return a \a gpr_timespec representation of the server call's deadline. + gpr_timespec raw_deadline() const { return deadline_; } + + /// Add the (\a key, \a value) pair to the initial metadata + /// associated with a server call. These are made available at the client side + /// by the \a grpc::ClientContext::GetServerInitialMetadata() method. + /// + /// \warning This method should only be called before sending initial metadata + /// to the client (which can happen explicitly, or implicitly when sending a + /// a response message or status to the client). + /// + /// \param key The metadata key. If \a value is binary data, it must + /// end in "-bin". + /// \param value The metadata value. If its value is binary, the key name + /// must end in "-bin". + /// + /// Metadata must conform to the following format: + /// Custom-Metadata -> Binary-Header / ASCII-Header + /// Binary-Header -> {Header-Name "-bin" } {binary value} + /// ASCII-Header -> Header-Name ASCII-Value + /// Header-Name -> 1*( %x30-39 / %x61-7A / "_" / "-" / ".") ; 0-9 a-z _ - . + /// ASCII-Value -> 1*( %x20-%x7E ) ; space and printable ASCII + void AddInitialMetadata(const grpc::string& key, const grpc::string& value); + + /// Add the (\a key, \a value) pair to the initial metadata + /// associated with a server call. These are made available at the client + /// side by the \a grpc::ClientContext::GetServerTrailingMetadata() method. + /// + /// \warning This method should only be called before sending trailing + /// metadata to the client (which happens when the call is finished and a + /// status is sent to the client). + /// + /// \param key The metadata key. If \a value is binary data, + /// it must end in "-bin". + /// \param value The metadata value. If its value is binary, the key name + /// must end in "-bin". + /// + /// Metadata must conform to the following format: + /// Custom-Metadata -> Binary-Header / ASCII-Header + /// Binary-Header -> {Header-Name "-bin" } {binary value} + /// ASCII-Header -> Header-Name ASCII-Value + /// Header-Name -> 1*( %x30-39 / %x61-7A / "_" / "-" / ".") ; 0-9 a-z _ - . + /// ASCII-Value -> 1*( %x20-%x7E ) ; space and printable ASCII + void AddTrailingMetadata(const grpc::string& key, const grpc::string& value); + + /// IsCancelled is always safe to call when using sync or callback API. + /// When using async API, it is only safe to call IsCancelled after + /// the AsyncNotifyWhenDone tag has been delivered. + bool IsCancelled() const; + + /// Cancel the Call from the server. This is a best-effort API and + /// depending on when it is called, the RPC may still appear successful to + /// the client. + /// For example, if TryCancel() is called on a separate thread, it might race + /// with the server handler which might return success to the client before + /// TryCancel() was even started by the thread. + /// + /// It is the caller's responsibility to prevent such races and ensure that if + /// TryCancel() is called, the serverhandler must return Status::CANCELLED. + /// The only exception is that if the serverhandler is already returning an + /// error status code, it is ok to not return Status::CANCELLED even if + /// TryCancel() was called. + /// + /// Note that TryCancel() does not change any of the tags that are pending + /// on the completion queue. All pending tags will still be delivered + /// (though their ok result may reflect the effect of cancellation). + void TryCancel() const; + + /// Return a collection of initial metadata key-value pairs sent from the + /// client. Note that keys may happen more than + /// once (ie, a \a std::multimap is returned). + /// + /// It is safe to use this method after initial metadata has been received, + /// Calls always begin with the client sending initial metadata, so this is + /// safe to access as soon as the call has begun on the server side. + /// + /// \return A multimap of initial metadata key-value pairs from the server. + const std::multimap& client_metadata() + const { + return *client_metadata_.map(); + } + + /// Return the compression algorithm to be used by the server call. + grpc_compression_level compression_level() const { + return compression_level_; + } + + /// Set \a level to be the compression level used for the server call. + /// + /// \param level The compression level used for the server call. + void set_compression_level(grpc_compression_level level) { + compression_level_set_ = true; + compression_level_ = level; + } + + /// Return a bool indicating whether the compression level for this call + /// has been set (either implicitly or through a previous call to + /// \a set_compression_level. + bool compression_level_set() const { return compression_level_set_; } + + /// Return the compression algorithm the server call will request be used. + /// Note that the gRPC runtime may decide to ignore this request, for example, + /// due to resource constraints, or if the server is aware the client doesn't + /// support the requested algorithm. + grpc_compression_algorithm compression_algorithm() const { + return compression_algorithm_; + } + /// Set \a algorithm to be the compression algorithm used for the server call. + /// + /// \param algorithm The compression algorithm used for the server call. + void set_compression_algorithm(grpc_compression_algorithm algorithm); + + /// Set the serialized load reporting costs in \a cost_data for the call. + void SetLoadReportingCosts(const std::vector& cost_data); + + /// Return the authentication context for this server call. + /// + /// \see grpc::AuthContext. + std::shared_ptr auth_context() const { + if (auth_context_.get() == nullptr) { + auth_context_ = CreateAuthContext(call_); + } + return auth_context_; + } + + /// 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; + + /// Get the census context associated with this server call. + 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. + /// TODO(vjpai): Fix this so that the tag is returned even if the call never + /// starts (https://github.com/grpc/grpc/issues/10136). + void AsyncNotifyWhenDone(void* tag) { + has_notify_when_done_tag_ = true; + async_notify_when_done_tag_ = tag; + } + + /// Should be used for framework-level extensions only. + /// Applications never need to call this method. + grpc_call* c_call() { return call_; } + + private: + friend class ::grpc::testing::InteropServerContextInspector; + friend class ::grpc::testing::ServerContextTestSpouse; + friend class ::grpc::ServerInterface; + 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::internal::ServerReaderWriterBody; + template + friend class ::grpc::internal::RpcMethodHandler; + template + friend class ::grpc::internal::ClientStreamingHandler; + template + friend class ::grpc::internal::ServerStreamingHandler; + template + friend class ::grpc::internal::TemplatedBidiStreamingHandler; + template + friend class ::grpc::internal::CallbackUnaryHandler; + template + friend class ::grpc::internal::CallbackClientStreamingHandler; + template + friend class ::grpc::internal::CallbackServerStreamingHandler; + template + friend class ::grpc::internal::CallbackBidiHandler; + template + friend class internal::ErrorMethodHandler; + friend class ::grpc::ClientContext; + + /// Prevent copying. + ServerContext(const ServerContext&); + ServerContext& operator=(const ServerContext&); + + class CompletionOp; + + void BeginCompletionOp(internal::Call* call, + std::function callback, + internal::ServerReactor* reactor); + /// Return the tag queued by BeginCompletionOp() + internal::CompletionQueueTag* GetCompletionOpTag(); + + ServerContext(gpr_timespec deadline, grpc_metadata_array* arr); + + void set_call(grpc_call* call) { call_ = call; } + + void BindDeadlineAndMetadata(gpr_timespec deadline, grpc_metadata_array* arr); + + void Clear(); + + void Setup(gpr_timespec deadline); + + uint32_t initial_metadata_flags() const { return 0; } + + experimental::ServerRpcInfo* set_server_rpc_info( + const char* method, internal::RpcMethod::RpcType type, + const std::vector< + std::unique_ptr>& + creators) { + if (creators.size() != 0) { + rpc_info_ = new experimental::ServerRpcInfo(this, method, type); + rpc_info_->RegisterInterceptors(creators); + } + return rpc_info_; + } + + CompletionOp* completion_op_; + bool has_notify_when_done_tag_; + void* async_notify_when_done_tag_; + internal::CallbackWithSuccessTag completion_tag_; + + gpr_timespec deadline_; + grpc_call* call_; + CompletionQueue* cq_; + bool sent_initial_metadata_; + mutable std::shared_ptr auth_context_; + mutable internal::MetadataMap client_metadata_; + std::multimap initial_metadata_; + std::multimap trailing_metadata_; + + bool compression_level_set_; + grpc_compression_level compression_level_; + grpc_compression_algorithm compression_algorithm_; + + internal::CallOpSet + pending_ops_; + bool has_pending_ops_; + + experimental::ServerRpcInfo* rpc_info_; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_SERVER_CONTEXT_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_interceptor.h b/third_party/grpc/include/grpcpp/impl/codegen/server_interceptor.h new file mode 100644 index 00000000000000..3e71b3fc55e159 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/server_interceptor.h @@ -0,0 +1,143 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H +#define GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H + +#include +#include + +#include +#include +#include + +namespace grpc { + +class ServerContext; + +namespace internal { +class InterceptorBatchMethodsImpl; +} + +namespace experimental { +class ServerRpcInfo; + +// A factory interface for creation of server interceptors. A vector of +// factories can be provided to ServerBuilder which will be used to create a new +// vector of server interceptors per RPC. Server interceptor authors should +// create a subclass of ServerInterceptorFactorInterface which creates objects +// of their interceptors. +class ServerInterceptorFactoryInterface { + public: + virtual ~ServerInterceptorFactoryInterface() {} + // Returns a pointer to an Interceptor object on successful creation, nullptr + // otherwise. If nullptr is returned, this server interceptor factory is + // ignored for the purposes of that RPC. + virtual Interceptor* CreateServerInterceptor(ServerRpcInfo* info) = 0; +}; + +/// ServerRpcInfo represents the state of a particular RPC as it +/// appears to an interceptor. It is created and owned by the library and +/// passed to the CreateServerInterceptor method of the application's +/// ServerInterceptorFactoryInterface implementation +class ServerRpcInfo { + public: + /// Type categorizes RPCs by unary or streaming type + enum class Type { UNARY, CLIENT_STREAMING, SERVER_STREAMING, BIDI_STREAMING }; + + ~ServerRpcInfo(){}; + + // Delete all copy and move constructors and assignments + ServerRpcInfo(const ServerRpcInfo&) = delete; + ServerRpcInfo& operator=(const ServerRpcInfo&) = delete; + ServerRpcInfo(ServerRpcInfo&&) = delete; + ServerRpcInfo& operator=(ServerRpcInfo&&) = delete; + + // Getter methods + + /// Return the fully-specified method name + const char* method() const { return method_; } + + /// Return the type of the RPC (unary or a streaming flavor) + Type type() const { return type_; } + + /// Return a pointer to the underlying ServerContext structure associated + /// with the RPC to support features that apply to it + grpc::ServerContext* server_context() { return ctx_; } + + private: + static_assert(Type::UNARY == + static_cast(internal::RpcMethod::NORMAL_RPC), + "violated expectation about Type enum"); + static_assert(Type::CLIENT_STREAMING == + static_cast(internal::RpcMethod::CLIENT_STREAMING), + "violated expectation about Type enum"); + static_assert(Type::SERVER_STREAMING == + static_cast(internal::RpcMethod::SERVER_STREAMING), + "violated expectation about Type enum"); + static_assert(Type::BIDI_STREAMING == + static_cast(internal::RpcMethod::BIDI_STREAMING), + "violated expectation about Type enum"); + + ServerRpcInfo(grpc::ServerContext* ctx, const char* method, + internal::RpcMethod::RpcType type) + : ctx_(ctx), method_(method), type_(static_cast(type)) { + ref_.store(1); + } + + // Runs interceptor at pos \a pos. + void RunInterceptor( + experimental::InterceptorBatchMethods* interceptor_methods, size_t pos) { + GPR_CODEGEN_ASSERT(pos < interceptors_.size()); + interceptors_[pos]->Intercept(interceptor_methods); + } + + void RegisterInterceptors( + const std::vector< + std::unique_ptr>& + creators) { + for (const auto& creator : creators) { + auto* interceptor = creator->CreateServerInterceptor(this); + if (interceptor != nullptr) { + interceptors_.push_back( + std::unique_ptr(interceptor)); + } + } + } + + void Ref() { ref_++; } + void Unref() { + if (--ref_ == 0) { + delete this; + } + } + + grpc::ServerContext* ctx_ = nullptr; + const char* method_ = nullptr; + const Type type_; + std::atomic_int ref_; + std::vector> interceptors_; + + friend class internal::InterceptorBatchMethodsImpl; + friend class grpc::ServerContext; +}; + +} // namespace experimental +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_SERVER_INTERCEPTOR_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/server_interface.h b/third_party/grpc/include/grpcpp/impl/codegen/server_interface.h new file mode 100644 index 00000000000000..e0e2629827052d --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/server_interface.h @@ -0,0 +1,362 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H +#define GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +class AsyncGenericService; +class Channel; +class GenericServerContext; +class ServerCompletionQueue; +class ServerContext; +class ServerCredentials; +class Service; + +extern CoreCodegenInterface* g_core_codegen_interface; + +/// Models a gRPC server. +/// +/// Servers are configured and started via \a grpc::ServerBuilder. +namespace internal { +class ServerAsyncStreamingInterface; +} // namespace internal + +class ServerInterface : public internal::CallHook { + public: + virtual ~ServerInterface() {} + + /// \a Shutdown does the following things: + /// + /// 1. Shutdown the server: deactivate all listening ports, mark it in + /// "shutdown mode" so that further call Request's or incoming RPC matches + /// are no longer allowed. Also return all Request'ed-but-not-yet-active + /// calls as failed (!ok). This refers to calls that have been requested + /// at the server by the server-side library or application code but that + /// have not yet been matched to incoming RPCs from the client. Note that + /// this would even include default calls added automatically by the gRPC + /// C++ API without the user's input (e.g., "Unimplemented RPC method") + /// + /// 2. Block until all rpc method handlers invoked automatically by the sync + /// API finish. + /// + /// 3. If all pending calls complete (and all their operations are + /// retrieved by Next) before \a deadline expires, this finishes + /// gracefully. Otherwise, forcefully cancel all pending calls associated + /// with the server after \a deadline expires. In the case of the sync API, + /// if the RPC function for a streaming call has already been started and + /// takes a week to complete, the RPC function won't be forcefully + /// terminated (since that would leave state corrupt and incomplete) and + /// the method handler will just keep running (which will prevent the + /// server from completing the "join" operation that it needs to do at + /// shutdown time). + /// + /// All completion queue associated with the server (for example, for async + /// serving) must be shutdown *after* this method has returned: + /// See \a ServerBuilder::AddCompletionQueue for details. + /// They must also be drained (by repeated Next) after being shutdown. + /// + /// \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 without a deadline and forced cancellation. + /// + /// All completion queue associated with the server (for example, for async + /// serving) must be shutdown *after* this method has returned: + /// See \a ServerBuilder::AddCompletionQueue for details. + void Shutdown() { + ShutdownInternal( + g_core_codegen_interface->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. + virtual void Wait() = 0; + + protected: + friend class ::grpc::Service; + + /// Register a service. This call does not take ownership of the service. + /// The service must exist for the lifetime of the Server instance. + virtual bool RegisterService(const grpc::string* host, Service* service) = 0; + + /// Register a generic service. This call does not take ownership of the + /// service. The service must exist for the lifetime of the Server instance. + virtual void RegisterAsyncGenericService(AsyncGenericService* service) = 0; + + /// 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. + virtual int AddListeningPort(const grpc::string& addr, + ServerCredentials* creds) = 0; + + /// 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. + virtual void Start(ServerCompletionQueue** cqs, size_t num_cqs) = 0; + + virtual void ShutdownInternal(gpr_timespec deadline) = 0; + + virtual int max_receive_message_size() const = 0; + + virtual grpc_server* server() = 0; + + virtual void PerformOpsOnCall(internal::CallOpSetInterface* ops, + internal::Call* call) = 0; + + class BaseAsyncRequest : public internal::CompletionQueueTag { + public: + BaseAsyncRequest(ServerInterface* server, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + bool delete_on_finalize); + virtual ~BaseAsyncRequest(); + + bool FinalizeResult(void** tag, bool* status) override; + + private: + void ContinueFinalizeResultAfterInterception(); + + protected: + ServerInterface* const server_; + ServerContext* const context_; + internal::ServerAsyncStreamingInterface* const stream_; + CompletionQueue* const call_cq_; + ServerCompletionQueue* const notification_cq_; + void* const tag_; + const bool delete_on_finalize_; + grpc_call* call_; + internal::Call call_wrapper_; + internal::InterceptorBatchMethodsImpl interceptor_methods_; + bool done_intercepting_; + }; + + /// RegisteredAsyncRequest is not part of the C++ API + class RegisteredAsyncRequest : public BaseAsyncRequest { + public: + RegisteredAsyncRequest(ServerInterface* server, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + const char* name, internal::RpcMethod::RpcType type); + + virtual bool FinalizeResult(void** tag, bool* status) override { + /* If we are done intercepting, then there is nothing more for us to do */ + if (done_intercepting_) { + return BaseAsyncRequest::FinalizeResult(tag, status); + } + call_wrapper_ = internal::Call( + call_, server_, call_cq_, server_->max_receive_message_size(), + context_->set_server_rpc_info(name_, type_, + *server_->interceptor_creators())); + return BaseAsyncRequest::FinalizeResult(tag, status); + } + + protected: + void IssueRequest(void* registered_method, grpc_byte_buffer** payload, + ServerCompletionQueue* notification_cq); + const char* name_; + const internal::RpcMethod::RpcType type_; + }; + + class NoPayloadAsyncRequest final : public RegisteredAsyncRequest { + public: + NoPayloadAsyncRequest(internal::RpcServiceMethod* registered_method, + ServerInterface* server, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag) + : RegisteredAsyncRequest( + server, context, stream, call_cq, notification_cq, tag, + registered_method->name(), registered_method->method_type()) { + IssueRequest(registered_method->server_tag(), nullptr, notification_cq); + } + + // uses RegisteredAsyncRequest::FinalizeResult + }; + + template + class PayloadAsyncRequest final : public RegisteredAsyncRequest { + public: + PayloadAsyncRequest(internal::RpcServiceMethod* registered_method, + ServerInterface* server, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + Message* request) + : RegisteredAsyncRequest( + server, context, stream, call_cq, notification_cq, tag, + registered_method->name(), registered_method->method_type()), + registered_method_(registered_method), + server_(server), + context_(context), + stream_(stream), + call_cq_(call_cq), + notification_cq_(notification_cq), + tag_(tag), + request_(request) { + IssueRequest(registered_method->server_tag(), payload_.bbuf_ptr(), + notification_cq); + } + + ~PayloadAsyncRequest() { + payload_.Release(); // We do not own the payload_ + } + + bool FinalizeResult(void** tag, bool* status) override { + /* If we are done intercepting, then there is nothing more for us to do */ + if (done_intercepting_) { + return RegisteredAsyncRequest::FinalizeResult(tag, status); + } + if (*status) { + if (!payload_.Valid() || !SerializationTraits::Deserialize( + payload_.bbuf_ptr(), request_) + .ok()) { + // If deserialization fails, we cancel the call and instantiate + // a new instance of ourselves to request another call. We then + // return false, which prevents the call from being returned to + // the application. + g_core_codegen_interface->grpc_call_cancel_with_status( + call_, GRPC_STATUS_INTERNAL, "Unable to parse request", nullptr); + g_core_codegen_interface->grpc_call_unref(call_); + new PayloadAsyncRequest(registered_method_, server_, context_, + stream_, call_cq_, notification_cq_, tag_, + request_); + delete this; + return false; + } + } + /* Set interception point for recv message */ + interceptor_methods_.AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_MESSAGE); + interceptor_methods_.SetRecvMessage(request_); + return RegisteredAsyncRequest::FinalizeResult(tag, status); + } + + private: + internal::RpcServiceMethod* const registered_method_; + ServerInterface* const server_; + ServerContext* const context_; + internal::ServerAsyncStreamingInterface* const stream_; + CompletionQueue* const call_cq_; + + ServerCompletionQueue* const notification_cq_; + void* const tag_; + Message* const request_; + ByteBuffer payload_; + }; + + class GenericAsyncRequest : public BaseAsyncRequest { + public: + GenericAsyncRequest(ServerInterface* server, GenericServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + bool delete_on_finalize); + + bool FinalizeResult(void** tag, bool* status) override; + + private: + grpc_call_details call_details_; + }; + + template + void RequestAsyncCall(internal::RpcServiceMethod* method, + ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, + Message* message) { + GPR_CODEGEN_ASSERT(method); + new PayloadAsyncRequest(method, this, context, stream, call_cq, + notification_cq, tag, message); + } + + void RequestAsyncCall(internal::RpcServiceMethod* method, + ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag) { + GPR_CODEGEN_ASSERT(method); + new NoPayloadAsyncRequest(method, this, context, stream, call_cq, + notification_cq, tag); + } + + void RequestAsyncGenericCall(GenericServerContext* context, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, + void* tag) { + new GenericAsyncRequest(this, context, stream, call_cq, notification_cq, + tag, true); + } + + private: + // EXPERIMENTAL + // Getter method for the vector of interceptor factory objects. + // Returns a nullptr (rather than being pure) since this is a post-1.0 method + // and adding a new pure method to an interface would be a breaking change + // (even though this is private and non-API) + virtual std::vector< + std::unique_ptr>* + interceptor_creators() { + return nullptr; + } + + // EXPERIMENTAL + // A method to get the callbackable completion queue associated with this + // server. If the return value is nullptr, this server doesn't support + // callback operations. + // TODO(vjpai): Consider a better default like using a global CQ + // Returns nullptr (rather than being pure) since this is a post-1.0 method + // and adding a new pure method to an interface would be a breaking change + // (even though this is private and non-API) + virtual CompletionQueue* CallbackCQ() { return nullptr; } +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_SERVER_INTERFACE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/service_type.h b/third_party/grpc/include/grpcpp/impl/codegen/service_type.h new file mode 100644 index 00000000000000..332a04c294fdb2 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/service_type.h @@ -0,0 +1,239 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H +#define GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H + +#include +#include +#include +#include +#include +#include + +namespace grpc { + +class CompletionQueue; +class Server; +class ServerInterface; +class ServerCompletionQueue; +class ServerContext; + +namespace internal { +class Call; +class ServerAsyncStreamingInterface { + public: + virtual ~ServerAsyncStreamingInterface() {} + + /// Request notification of the sending of initial metadata to the client. + /// Completion will be notified by \a tag on the associated completion + /// queue. This call is optional, but if it is used, it cannot be used + /// concurrently with or after the \a Finish method. + /// + /// \param[in] tag Tag identifying this request. + virtual void SendInitialMetadata(void* tag) = 0; + + private: + friend class ::grpc::ServerInterface; + virtual void BindCall(Call* call) = 0; +}; +} // namespace internal + +/// Desriptor of an RPC service and its various RPC methods +class Service { + public: + Service() : server_(nullptr) {} + virtual ~Service() {} + + bool has_async_methods() const { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (*it && (*it)->handler() == nullptr) { + return true; + } + } + return false; + } + + bool has_synchronous_methods() const { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (*it && + (*it)->api_type() == internal::RpcServiceMethod::ApiType::SYNC) { + return true; + } + } + return false; + } + + bool has_callback_methods() const { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (*it && ((*it)->api_type() == + internal::RpcServiceMethod::ApiType::CALL_BACK || + (*it)->api_type() == + internal::RpcServiceMethod::ApiType::RAW_CALL_BACK)) { + return true; + } + } + return false; + } + + bool has_generic_methods() const { + for (auto it = methods_.begin(); it != methods_.end(); ++it) { + if (it->get() == nullptr) { + return true; + } + } + return false; + } + + protected: + // TODO(vjpai): Promote experimental contents once callback API is accepted + class experimental_type { + public: + explicit experimental_type(Service* service) : service_(service) {} + + void MarkMethodCallback(int index, internal::MethodHandler* handler) { + // This does not have to be a hard error, however no one has approached us + // with a use case yet. Please file an issue if you believe you have one. + size_t idx = static_cast(index); + GPR_CODEGEN_ASSERT( + service_->methods_[idx].get() != nullptr && + "Cannot mark the method as 'callback' because it has already been " + "marked as 'generic'."); + service_->methods_[idx]->SetHandler(handler); + service_->methods_[idx]->SetServerApiType( + internal::RpcServiceMethod::ApiType::CALL_BACK); + } + + void MarkMethodRawCallback(int index, internal::MethodHandler* handler) { + // This does not have to be a hard error, however no one has approached us + // with a use case yet. Please file an issue if you believe you have one. + size_t idx = static_cast(index); + GPR_CODEGEN_ASSERT( + service_->methods_[idx].get() != nullptr && + "Cannot mark the method as 'raw callback' because it has already " + "been marked as 'generic'."); + service_->methods_[idx]->SetHandler(handler); + service_->methods_[idx]->SetServerApiType( + internal::RpcServiceMethod::ApiType::RAW_CALL_BACK); + } + + private: + Service* service_; + }; + + experimental_type experimental() { return experimental_type(this); } + + template + void RequestAsyncUnary(int index, ServerContext* context, Message* request, + internal::ServerAsyncStreamingInterface* stream, + CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag) { + // Typecast the index to size_t for indexing into a vector + // while preserving the API that existed before a compiler + // warning was first seen (grpc/grpc#11664) + size_t idx = static_cast(index); + server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq, + notification_cq, tag, request); + } + void RequestAsyncClientStreaming( + int index, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag) { + size_t idx = static_cast(index); + server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq, + notification_cq, tag); + } + template + void RequestAsyncServerStreaming( + int index, ServerContext* context, Message* request, + internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag) { + size_t idx = static_cast(index); + server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq, + notification_cq, tag, request); + } + void RequestAsyncBidiStreaming( + int index, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag) { + size_t idx = static_cast(index); + server_->RequestAsyncCall(methods_[idx].get(), context, stream, call_cq, + notification_cq, tag); + } + + void AddMethod(internal::RpcServiceMethod* method) { + methods_.emplace_back(method); + } + + void MarkMethodAsync(int index) { + // This does not have to be a hard error, however no one has approached us + // with a use case yet. Please file an issue if you believe you have one. + size_t idx = static_cast(index); + GPR_CODEGEN_ASSERT( + methods_[idx].get() != nullptr && + "Cannot mark the method as 'async' because it has already been " + "marked as 'generic'."); + methods_[idx]->SetServerApiType(internal::RpcServiceMethod::ApiType::ASYNC); + } + + void MarkMethodRaw(int index) { + // This does not have to be a hard error, however no one has approached us + // with a use case yet. Please file an issue if you believe you have one. + size_t idx = static_cast(index); + GPR_CODEGEN_ASSERT(methods_[idx].get() != nullptr && + "Cannot mark the method as 'raw' because it has already " + "been marked as 'generic'."); + methods_[idx]->SetServerApiType(internal::RpcServiceMethod::ApiType::RAW); + } + + void MarkMethodGeneric(int index) { + // This does not have to be a hard error, however no one has approached us + // with a use case yet. Please file an issue if you believe you have one. + size_t idx = static_cast(index); + GPR_CODEGEN_ASSERT( + methods_[idx]->handler() != nullptr && + "Cannot mark the method as 'generic' because it has already been " + "marked as 'async' or 'raw'."); + methods_[idx].reset(); + } + + void MarkMethodStreamed(int index, internal::MethodHandler* streamed_method) { + // This does not have to be a hard error, however no one has approached us + // with a use case yet. Please file an issue if you believe you have one. + size_t idx = static_cast(index); + GPR_CODEGEN_ASSERT(methods_[idx] && methods_[idx]->handler() && + "Cannot mark an async or generic method Streamed"); + methods_[idx]->SetHandler(streamed_method); + + // From the server's point of view, streamed unary is a special + // case of BIDI_STREAMING that has 1 read and 1 write, in that order, + // and split server-side streaming is BIDI_STREAMING with 1 read and + // any number of writes, in that order. + methods_[idx]->SetMethodType(internal::RpcMethod::BIDI_STREAMING); + } + + private: + friend class Server; + friend class ServerInterface; + ServerInterface* server_; + std::vector> methods_; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_SERVICE_TYPE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/slice.h b/third_party/grpc/include/grpcpp/impl/codegen/slice.h new file mode 100644 index 00000000000000..8966559dc85349 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/slice.h @@ -0,0 +1,143 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SLICE_H +#define GRPCPP_IMPL_CODEGEN_SLICE_H + +#include +#include +#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 final { + public: + /// Construct an empty slice. + Slice() : slice_(g_core_codegen_interface->grpc_empty_slice()) {} + /// Destructor - drops one reference. + ~Slice() { g_core_codegen_interface->grpc_slice_unref(slice_); } + + enum AddRef { ADD_REF }; + /// Construct a slice from \a slice, adding a reference. + Slice(grpc_slice slice, AddRef) + : slice_(g_core_codegen_interface->grpc_slice_ref(slice)) {} + + enum StealRef { STEAL_REF }; + /// Construct a slice from \a slice, stealing a reference. + Slice(grpc_slice slice, StealRef) : slice_(slice) {} + + /// Allocate a slice of specified size + Slice(size_t len) + : slice_(g_core_codegen_interface->grpc_slice_malloc(len)) {} + + /// Construct a slice from a copied buffer + Slice(const void* buf, size_t len) + : slice_(g_core_codegen_interface->grpc_slice_from_copied_buffer( + reinterpret_cast(buf), len)) {} + + /// Construct a slice from a copied string + Slice(const grpc::string& str) + : slice_(g_core_codegen_interface->grpc_slice_from_copied_buffer( + str.c_str(), str.length())) {} + + enum StaticSlice { STATIC_SLICE }; + + /// Construct a slice from a static buffer + Slice(const void* buf, size_t len, StaticSlice) + : slice_(g_core_codegen_interface->grpc_slice_from_static_buffer( + reinterpret_cast(buf), len)) {} + + /// Copy constructor, adds a reference. + Slice(const Slice& other) + : slice_(g_core_codegen_interface->grpc_slice_ref(other.slice_)) {} + + /// Assignment, reference count is unchanged. + Slice& operator=(Slice other) { + std::swap(slice_, other.slice_); + return *this; + } + + /// 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 + /// user data pointer passed in at destruction. Can be the same as buf or + /// different (e.g., if data is part of a larger structure that must be + /// destroyed when the data is no longer needed) + Slice(void* buf, size_t len, void (*destroy)(void*), void* user_data) + : slice_(g_core_codegen_interface->grpc_slice_new_with_user_data( + buf, len, destroy, user_data)) {} + + /// Specialization of above for common case where buf == user_data + Slice(void* buf, size_t len, void (*destroy)(void*)) + : Slice(buf, len, destroy, buf) {} + + /// Similar to the above but has a destroy that also takes slice length + Slice(void* buf, size_t len, void (*destroy)(void*, size_t)) + : slice_(g_core_codegen_interface->grpc_slice_new_with_len(buf, len, + destroy)) {} + + /// Byte size. + size_t size() const { return GRPC_SLICE_LENGTH(slice_); } + + /// Raw pointer to the beginning (first element) of the slice. + const uint8_t* begin() const { return GRPC_SLICE_START_PTR(slice_); } + + /// Raw pointer to the end (one byte \em past the last element) of the slice. + const uint8_t* end() const { return GRPC_SLICE_END_PTR(slice_); } + + /// Raw C slice. Caller needs to call grpc_slice_unref when done. + grpc_slice c_slice() const { + return g_core_codegen_interface->grpc_slice_ref(slice_); + } + + private: + friend class ByteBuffer; + + grpc_slice slice_; +}; + +inline grpc::string_ref StringRefFromSlice(const grpc_slice* slice) { + return grpc::string_ref( + reinterpret_cast(GRPC_SLICE_START_PTR(*slice)), + GRPC_SLICE_LENGTH(*slice)); +} + +inline grpc::string StringFromCopiedSlice(grpc_slice slice) { + return grpc::string(reinterpret_cast(GRPC_SLICE_START_PTR(slice)), + GRPC_SLICE_LENGTH(slice)); +} + +inline grpc_slice SliceReferencingString(const grpc::string& str) { + return g_core_codegen_interface->grpc_slice_from_static_buffer(str.data(), + str.length()); +} + +inline grpc_slice SliceFromCopiedString(const grpc::string& str) { + return g_core_codegen_interface->grpc_slice_from_copied_buffer(str.data(), + str.length()); +} + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_SLICE_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/status.h b/third_party/grpc/include/grpcpp/impl/codegen/status.h new file mode 100644 index 00000000000000..e625a76b15da8f --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/status.h @@ -0,0 +1,133 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_STATUS_H +#define GRPCPP_IMPL_CODEGEN_STATUS_H + +#include +#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) { + // Static assertions to make sure that the C++ API value correctly + // maps to the core surface API value + static_assert(StatusCode::OK == static_cast(GRPC_STATUS_OK), + "Mismatched status code"); + static_assert( + StatusCode::CANCELLED == static_cast(GRPC_STATUS_CANCELLED), + "Mismatched status code"); + static_assert( + StatusCode::UNKNOWN == static_cast(GRPC_STATUS_UNKNOWN), + "Mismatched status code"); + static_assert(StatusCode::INVALID_ARGUMENT == + static_cast(GRPC_STATUS_INVALID_ARGUMENT), + "Mismatched status code"); + static_assert(StatusCode::DEADLINE_EXCEEDED == + static_cast(GRPC_STATUS_DEADLINE_EXCEEDED), + "Mismatched status code"); + static_assert( + StatusCode::NOT_FOUND == static_cast(GRPC_STATUS_NOT_FOUND), + "Mismatched status code"); + static_assert(StatusCode::ALREADY_EXISTS == + static_cast(GRPC_STATUS_ALREADY_EXISTS), + "Mismatched status code"); + static_assert(StatusCode::PERMISSION_DENIED == + static_cast(GRPC_STATUS_PERMISSION_DENIED), + "Mismatched status code"); + static_assert(StatusCode::UNAUTHENTICATED == + static_cast(GRPC_STATUS_UNAUTHENTICATED), + "Mismatched status code"); + static_assert(StatusCode::RESOURCE_EXHAUSTED == + static_cast(GRPC_STATUS_RESOURCE_EXHAUSTED), + "Mismatched status code"); + static_assert(StatusCode::FAILED_PRECONDITION == + static_cast(GRPC_STATUS_FAILED_PRECONDITION), + "Mismatched status code"); + static_assert( + StatusCode::ABORTED == static_cast(GRPC_STATUS_ABORTED), + "Mismatched status code"); + static_assert(StatusCode::OUT_OF_RANGE == + static_cast(GRPC_STATUS_OUT_OF_RANGE), + "Mismatched status code"); + static_assert(StatusCode::UNIMPLEMENTED == + static_cast(GRPC_STATUS_UNIMPLEMENTED), + "Mismatched status code"); + static_assert( + StatusCode::INTERNAL == static_cast(GRPC_STATUS_INTERNAL), + "Mismatched status code"); + static_assert(StatusCode::UNAVAILABLE == + static_cast(GRPC_STATUS_UNAVAILABLE), + "Mismatched status code"); + static_assert( + StatusCode::DATA_LOSS == static_cast(GRPC_STATUS_DATA_LOSS), + "Mismatched status code"); + } + + /// Construct an instance with associated \a code and \a error_message. + /// It is an error to construct an OK status with non-empty \a error_message. + Status(StatusCode code, const grpc::string& error_message) + : code_(code), error_message_(error_message) {} + + /// Construct an instance with \a code, \a error_message and + /// \a error_details. It is an error to construct an OK status with non-empty + /// \a error_message and/or \a error_details. + Status(StatusCode code, const grpc::string& error_message, + const grpc::string& error_details) + : code_(code), + error_message_(error_message), + binary_error_details_(error_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 error_message_; } + /// Return the (binary) error details. + // Usually it contains a serialized google.rpc.Status proto. + grpc::string error_details() const { return binary_error_details_; } + + /// Is the status OK? + bool ok() const { return code_ == StatusCode::OK; } + + // Ignores any errors. This method does nothing except potentially suppress + // complaints from any tools that are checking that errors are not dropped on + // the floor. + void IgnoreError() const {} + + private: + StatusCode code_; + grpc::string error_message_; + grpc::string binary_error_details_; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_STATUS_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/status_code_enum.h b/third_party/grpc/include/grpcpp/impl/codegen/status_code_enum.h new file mode 100644 index 00000000000000..09943f10de8060 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/status_code_enum.h @@ -0,0 +1,142 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H +#define GRPCPP_IMPL_CODEGEN_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. + /// + /// \warning Although data MIGHT not have been transmitted when this + /// status occurs, there is NOT A GUARANTEE that the server has not seen + /// anything. So in general it is unsafe to retry on this status code + /// if the call is non-idempotent. + /// + /// 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 // GRPCPP_IMPL_CODEGEN_STATUS_CODE_ENUM_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/string_ref.h b/third_party/grpc/include/grpcpp/impl/codegen/string_ref.h new file mode 100644 index 00000000000000..5d55fc4180576e --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/string_ref.h @@ -0,0 +1,146 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_STRING_REF_H +#define GRPCPP_IMPL_CODEGEN_STRING_REF_H + +#include + +#include +#include +#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; + + /// 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) { + data_ = rhs.data_; + length_ = rhs.length_; + return *this; + } + + string_ref(const char* s) : data_(s), length_(strlen(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 { + 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 starts_with(string_ref x) const { + return length_ >= x.length_ && (memcmp(data_, x.data_, x.length_) == 0); + } + + bool ends_with(string_ref x) const { + return length_ >= x.length_ && + (memcmp(data_ + (length_ - x.length_), x.data_, x.length_) == 0); + } + + size_t 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 find(char c) const { + auto it = std::find(cbegin(), cend(), c); + return it == cend() ? npos : std::distance(cbegin(), it); + } + + string_ref substr(size_t pos, size_t n = npos) const { + if (pos > length_) pos = length_; + if (n > (length_ - pos)) n = length_ - pos; + return string_ref(data_ + pos, n); + } + + private: + const char* data_; + size_t length_; +}; + +/// Comparison operators +inline bool operator==(string_ref x, string_ref y) { return x.compare(y) == 0; } +inline bool operator!=(string_ref x, string_ref y) { return x.compare(y) != 0; } +inline bool operator<(string_ref x, string_ref y) { return x.compare(y) < 0; } +inline bool operator<=(string_ref x, string_ref y) { return x.compare(y) <= 0; } +inline bool operator>(string_ref x, string_ref y) { return x.compare(y) > 0; } +inline bool operator>=(string_ref x, string_ref y) { return x.compare(y) >= 0; } + +inline std::ostream& operator<<(std::ostream& out, const string_ref& string) { + return out << grpc::string(string.begin(), string.end()); +} + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_STRING_REF_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/stub_options.h b/third_party/grpc/include/grpcpp/impl/codegen/stub_options.h new file mode 100644 index 00000000000000..a56695a8f82bce --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/stub_options.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H +#define GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H + +namespace grpc { + +/// Useful interface for generated stubs +class StubOptions {}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_STUB_OPTIONS_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/sync_stream.h b/third_party/grpc/include/grpcpp/impl/codegen/sync_stream.h new file mode 100644 index 00000000000000..6981076f04efc5 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/sync_stream.h @@ -0,0 +1,934 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H +#define GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace grpc { + +namespace internal { +/// Common interface for all synchronous client side streaming. +class ClientStreamingInterface { + public: + virtual ~ClientStreamingInterface() {} + + /// Block waiting until the stream finishes and a final status of the call is + /// available. + /// + /// It is appropriate to call this method when both: + /// * the calling code (client-side) has no more message to send + /// (this can be declared implicitly by calling this method, or + /// explicitly through an earlier call to WritesDone method of the + /// class in use, e.g. \a ClientWriterInterface::WritesDone or + /// \a ClientReaderWriterInterface::WritesDone). + /// * there are no more messages to be received from the server (which can + /// be known implicitly, or explicitly from an earlier call to \a + /// ReaderInterface::Read that returned "false"). + /// + /// This function will return either: + /// - when all incoming messages have been read and the server has + /// returned status. + /// - when the server has returned a non-OK status. + /// - OR when the call failed for some reason and the library generated a + /// status. + /// + /// Return values: + /// - \a Status contains the status code, message and details for the call + /// - the \a ClientContext associated with this call is updated with + /// possible trailing metadata sent from the server. + virtual Status Finish() = 0; +}; + +/// Common interface for all synchronous server side streaming. +class ServerStreamingInterface { + public: + virtual ~ServerStreamingInterface() {} + + /// Block to send initial metadata to client. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a Finish method. + /// + /// The initial metadata that will be sent to the client will be + /// taken from the \a ServerContext associated with the call. + virtual void SendInitialMetadata() = 0; +}; + +/// An interface that yields a sequence of messages of type \a R. +template +class ReaderInterface { + public: + virtual ~ReaderInterface() {} + + /// Get an upper bound on the next message size available for reading on this + /// stream. + virtual bool NextMessageSize(uint32_t* sz) = 0; + + /// Block to read a message and parse to \a msg. Returns \a true on success. + /// This is thread-safe with respect to \a Write or \WritesDone methods on + /// the same stream. It should not be called concurrently with another \a + /// Read on the same stream as the order of delivery will not be defined. + /// + /// \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() {} + + /// Block to write \a msg to the stream with WriteOptions \a options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \param msg The message to be written to the stream. + /// \param options The WriteOptions affecting the write operation. + /// + /// \return \a true on success, \a false when the stream has been closed. + virtual bool Write(const W& msg, WriteOptions options) = 0; + + /// Block to write \a msg to the stream with default write options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \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()); } + + /// Write \a msg and coalesce it with the writing of trailing metadata, using + /// WriteOptions \a options. + /// + /// For client, WriteLast is equivalent of performing Write and WritesDone in + /// a single step. \a msg and trailing metadata are coalesced and sent on wire + /// by calling this function. For server, WriteLast buffers the \a msg. + /// The writing of \a msg is held until the service handler returns, + /// where \a msg and trailing metadata are coalesced and sent on wire. + /// Note that WriteLast can only buffer \a msg up to the flow control window + /// size. If \a msg size is larger than the window size, it will be sent on + /// wire without buffering. + /// + /// \param[in] msg The message to be written to the stream. + /// \param[in] options The WriteOptions to be used to write this message. + void WriteLast(const W& msg, WriteOptions options) { + Write(msg, options.set_last_message()); + } +}; + +} // namespace internal + +/// Client-side interface for streaming reads of message of type \a R. +template +class ClientReaderInterface : public internal::ClientStreamingInterface, + public internal::ReaderInterface { + public: + /// Block to 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; +}; + +namespace internal { +template +class ClientReaderFactory { + public: + template + static ClientReader* Create(ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ClientContext* context, const W& request) { + return new ClientReader(channel, method, context, request); + } +}; +} // namespace internal + +/// Synchronous (blocking) client-side API for doing server-streaming RPCs, +/// where the stream of messages coming from the server has messages +/// of type \a R. +template +class ClientReader final : public ClientReaderInterface { + public: + /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for + /// semantics. + /// + // Side effect: + /// Once complete, the initial metadata read from + /// the server will be accessable through the \a ClientContext used to + /// construct this object. + void WaitForInitialMetadata() override { + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); /// status ignored + } + + bool NextMessageSize(uint32_t* sz) override { + *sz = call_.max_receive_message_size(); + return true; + } + + /// See the \a ReaderInterface.Read method for semantics. + /// Side effect: + /// This also receives initial metadata from the server, if not + /// already received (if initial metadata is received, it can be then + /// accessed through the \a ClientContext associated with this call). + bool Read(R* msg) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + ops.RecvMessage(msg); + call_.PerformOps(&ops); + return cq_.Pluck(&ops) && ops.got_message; + } + + /// See the \a ClientStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// The \a ClientContext associated with this call is updated with + /// possible metadata received from the server. + Status Finish() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientRecvStatus> ops; + Status status; + ops.ClientRecvStatus(context_, &status); + call_.PerformOps(&ops); + GPR_CODEGEN_ASSERT(cq_.Pluck(&ops)); + return status; + } + + private: + friend class internal::ClientReaderFactory; + ClientContext* context_; + CompletionQueue cq_; + ::grpc::internal::Call call_; + + /// Block to create a stream and write the initial metadata and \a request + /// out. Note that \a context will be used to fill in custom initial + /// metadata used to send to the server when starting the call. + template + ClientReader(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ClientContext* context, const W& request) + : context_(context), + cq_(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, + nullptr}), // Pluckable cq + call_(channel->CreateCall(method, context, &cq_)) { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + ops; + ops.SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + // TODO(ctiller): don't assert + GPR_CODEGEN_ASSERT(ops.SendMessage(request).ok()); + ops.ClientSendClose(); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } +}; + +/// Client-side interface for streaming writes of message type \a W. +template +class ClientWriterInterface : public internal::ClientStreamingInterface, + public internal::WriterInterface { + public: + /// Half close writing from the client. (signal that the stream of messages + /// coming from the client is complete). + /// Blocks until currently-pending writes are completed. + /// Thread safe with respect to \a ReaderInterface::Read operations only + /// + /// \return Whether the writes were successful. + virtual bool WritesDone() = 0; +}; + +namespace internal { +template +class ClientWriterFactory { + public: + template + static ClientWriter* Create(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ClientContext* context, R* response) { + return new ClientWriter(channel, method, context, response); + } +}; +} // namespace internal + +/// Synchronous (blocking) client-side API for doing client-streaming RPCs, +/// where the outgoing message stream coming from the client has messages of +/// type \a W. +template +class ClientWriter : public ClientWriterInterface { + public: + /// See the \a ClientStreamingInterface.WaitForInitialMetadata method for + /// semantics. + /// + // Side effect: + /// Once complete, the initial metadata read from the server will be + /// accessable through the \a ClientContext used to construct this object. + void WaitForInitialMetadata() { + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); // status ignored + } + + /// See the WriterInterface.Write(const W& msg, WriteOptions options) method + /// for semantics. + /// + /// Side effect: + /// Also sends initial metadata if not already sent (using the + /// \a ClientContext associated with this call). + using ::grpc::internal::WriterInterface::Write; + bool Write(const W& msg, WriteOptions options) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + ops; + + if (options.is_last_message()) { + options.set_buffer_hint(); + ops.ClientSendClose(); + } + if (context_->initial_metadata_corked_) { + ops.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + context_->set_initial_metadata_corked(false); + } + if (!ops.SendMessage(msg, options).ok()) { + return false; + } + + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + bool WritesDone() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops; + ops.ClientSendClose(); + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + /// See the ClientStreamingInterface.Finish method for semantics. + /// Side effects: + /// - Also receives initial metadata if not already received. + /// - Attempts to fill in the \a response parameter passed + /// to the constructor of this instance with the response + /// message from the server. + Status Finish() override { + Status status; + if (!context_->initial_metadata_received_) { + finish_ops_.RecvInitialMetadata(context_); + } + finish_ops_.ClientRecvStatus(context_, &status); + call_.PerformOps(&finish_ops_); + GPR_CODEGEN_ASSERT(cq_.Pluck(&finish_ops_)); + return status; + } + + private: + friend class internal::ClientWriterFactory; + + /// Block to create a stream (i.e. send request headers and other initial + /// metadata to the server). Note that \a context will be used to fill + /// in custom initial metadata. \a response will be filled in with the + /// single expected response message from the server upon a successful + /// call to the \a Finish method of this instance. + template + ClientWriter(ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ClientContext* context, R* response) + : context_(context), + cq_(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, + nullptr}), // Pluckable cq + call_(channel->CreateCall(method, context, &cq_)) { + finish_ops_.RecvMessage(response); + finish_ops_.AllowNoMessage(); + + if (!context_->initial_metadata_corked_) { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + ops; + ops.SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } + } + + ClientContext* context_; + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpGenericRecvMessage, + ::grpc::internal::CallOpClientRecvStatus> + finish_ops_; + CompletionQueue cq_; + ::grpc::internal::Call call_; +}; + +/// Client-side interface for bi-directional streaming with +/// client-to-server stream messages of type \a W and +/// server-to-client stream messages of type \a R. +template +class ClientReaderWriterInterface : public internal::ClientStreamingInterface, + public internal::WriterInterface, + public internal::ReaderInterface { + public: + /// Block to 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; + + /// Half close writing from the client. (signal that the stream of messages + /// coming from the clinet is complete). + /// Blocks until currently-pending writes are completed. + /// Thread-safe with respect to \a ReaderInterface::Read + /// + /// \return Whether the writes were successful. + virtual bool WritesDone() = 0; +}; + +namespace internal { +template +class ClientReaderWriterFactory { + public: + static ClientReaderWriter* Create( + ::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, ClientContext* context) { + return new ClientReaderWriter(channel, method, context); + } +}; +} // namespace internal + +/// Synchronous (blocking) client-side API for bi-directional streaming RPCs, +/// where the outgoing message stream coming from the client has messages of +/// type \a W, and the incoming messages stream coming from the server has +/// messages of type \a R. +template +class ClientReaderWriter final : public ClientReaderWriterInterface { + public: + /// Block waiting to read initial metadata from the server. + /// This call is optional, but if it is used, it cannot be used concurrently + /// with or after the \a Finish method. + /// + /// Once complete, the initial metadata read from the server will be + /// accessable through the \a ClientContext used to construct this object. + void WaitForInitialMetadata() override { + GPR_CODEGEN_ASSERT(!context_->initial_metadata_received_); + + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata> + ops; + ops.RecvInitialMetadata(context_); + call_.PerformOps(&ops); + cq_.Pluck(&ops); // status ignored + } + + bool NextMessageSize(uint32_t* sz) override { + *sz = call_.max_receive_message_size(); + return true; + } + + /// See the \a ReaderInterface.Read method for semantics. + /// Side effect: + /// Also receives initial metadata if not already received (updates the \a + /// ClientContext associated with this call in that case). + bool Read(R* msg) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpRecvMessage> + ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + ops.RecvMessage(msg); + call_.PerformOps(&ops); + return cq_.Pluck(&ops) && ops.got_message; + } + + /// See the \a WriterInterface.Write method for semantics. + /// + /// Side effect: + /// Also sends initial metadata if not already sent (using the + /// \a ClientContext associated with this call to fill in values). + using ::grpc::internal::WriterInterface::Write; + bool Write(const W& msg, WriteOptions options) override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata, + ::grpc::internal::CallOpSendMessage, + ::grpc::internal::CallOpClientSendClose> + ops; + + if (options.is_last_message()) { + options.set_buffer_hint(); + ops.ClientSendClose(); + } + if (context_->initial_metadata_corked_) { + ops.SendInitialMetadata(&context_->send_initial_metadata_, + context_->initial_metadata_flags()); + context_->set_initial_metadata_corked(false); + } + if (!ops.SendMessage(msg, options).ok()) { + return false; + } + + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + bool WritesDone() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpClientSendClose> ops; + ops.ClientSendClose(); + call_.PerformOps(&ops); + return cq_.Pluck(&ops); + } + + /// See the ClientStreamingInterface.Finish method for semantics. + /// + /// Side effect: + /// - the \a ClientContext associated with this call is updated with + /// possible trailing metadata sent from the server. + Status Finish() override { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpRecvInitialMetadata, + ::grpc::internal::CallOpClientRecvStatus> + ops; + if (!context_->initial_metadata_received_) { + ops.RecvInitialMetadata(context_); + } + Status status; + ops.ClientRecvStatus(context_, &status); + call_.PerformOps(&ops); + GPR_CODEGEN_ASSERT(cq_.Pluck(&ops)); + return status; + } + + private: + friend class internal::ClientReaderWriterFactory; + + ClientContext* context_; + CompletionQueue cq_; + ::grpc::internal::Call call_; + + /// Block to create a stream and write the initial metadata and \a request + /// out. Note that \a context will be used to fill in custom initial metadata + /// used to send to the server when starting the call. + ClientReaderWriter(::grpc::ChannelInterface* channel, + const ::grpc::internal::RpcMethod& method, + ClientContext* context) + : context_(context), + cq_(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_PLUCK, GRPC_CQ_DEFAULT_POLLING, + nullptr}), // Pluckable cq + call_(channel->CreateCall(method, context, &cq_)) { + if (!context_->initial_metadata_corked_) { + ::grpc::internal::CallOpSet<::grpc::internal::CallOpSendInitialMetadata> + ops; + ops.SendInitialMetadata(&context->send_initial_metadata_, + context->initial_metadata_flags()); + call_.PerformOps(&ops); + cq_.Pluck(&ops); + } + } +}; + +/// Server-side interface for streaming reads of message of type \a R. +template +class ServerReaderInterface : public internal::ServerStreamingInterface, + public internal::ReaderInterface {}; + +/// Synchronous (blocking) server-side API for doing client-streaming RPCs, +/// where the incoming message stream coming from the client has messages of +/// type \a R. +template +class ServerReader final : public ServerReaderInterface { + public: + /// See the \a ServerStreamingInterface.SendInitialMetadata method + /// for semantics. Note that initial metadata will be affected by the + /// \a ServerContext associated with this call. + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + internal::CallOpSet ops; + ops.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + bool NextMessageSize(uint32_t* sz) override { + *sz = call_->max_receive_message_size(); + return true; + } + + bool Read(R* msg) override { + internal::CallOpSet> ops; + ops.RecvMessage(msg); + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops) && ops.got_message; + } + + private: + internal::Call* const call_; + ServerContext* const ctx_; + + template + friend class internal::ClientStreamingHandler; + + ServerReader(internal::Call* call, ServerContext* ctx) + : call_(call), ctx_(ctx) {} +}; + +/// Server-side interface for streaming writes of message of type \a W. +template +class ServerWriterInterface : public internal::ServerStreamingInterface, + public internal::WriterInterface {}; + +/// Synchronous (blocking) server-side API for doing for doing a +/// server-streaming RPCs, where the outgoing message stream coming from the +/// server has messages of type \a W. +template +class ServerWriter final : public ServerWriterInterface { + public: + /// See the \a ServerStreamingInterface.SendInitialMetadata method + /// for semantics. + /// Note that initial metadata will be affected by the + /// \a ServerContext associated with this call. + void SendInitialMetadata() override { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + internal::CallOpSet ops; + ops.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + /// See the \a WriterInterface.Write method for semantics. + /// + /// Side effect: + /// Also sends initial metadata if not already sent (using the + /// \a ClientContext associated with this call to fill in values). + using internal::WriterInterface::Write; + bool Write(const W& msg, WriteOptions options) override { + if (options.is_last_message()) { + options.set_buffer_hint(); + } + + if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) { + return false; + } + if (!ctx_->sent_initial_metadata_) { + ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ctx_->pending_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + call_->PerformOps(&ctx_->pending_ops_); + // if this is the last message we defer the pluck until AFTER we start + // the trailing md op. This prevents hangs. See + // https://github.com/grpc/grpc/issues/11546 + if (options.is_last_message()) { + ctx_->has_pending_ops_ = true; + return true; + } + ctx_->has_pending_ops_ = false; + return call_->cq()->Pluck(&ctx_->pending_ops_); + } + + private: + internal::Call* const call_; + ServerContext* const ctx_; + + template + friend class internal::ServerStreamingHandler; + + ServerWriter(internal::Call* call, ServerContext* ctx) + : call_(call), ctx_(ctx) {} +}; + +/// Server-side interface for bi-directional streaming. +template +class ServerReaderWriterInterface : public internal::ServerStreamingInterface, + public internal::WriterInterface, + public internal::ReaderInterface {}; + +/// Actual implementation of bi-directional streaming +namespace internal { +template +class ServerReaderWriterBody final { + public: + ServerReaderWriterBody(Call* call, ServerContext* ctx) + : call_(call), ctx_(ctx) {} + + void SendInitialMetadata() { + GPR_CODEGEN_ASSERT(!ctx_->sent_initial_metadata_); + + CallOpSet ops; + ops.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ops.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + call_->PerformOps(&ops); + call_->cq()->Pluck(&ops); + } + + bool NextMessageSize(uint32_t* sz) { + *sz = call_->max_receive_message_size(); + return true; + } + + bool Read(R* msg) { + CallOpSet> ops; + ops.RecvMessage(msg); + call_->PerformOps(&ops); + return call_->cq()->Pluck(&ops) && ops.got_message; + } + + bool Write(const W& msg, WriteOptions options) { + if (options.is_last_message()) { + options.set_buffer_hint(); + } + if (!ctx_->pending_ops_.SendMessage(msg, options).ok()) { + return false; + } + if (!ctx_->sent_initial_metadata_) { + ctx_->pending_ops_.SendInitialMetadata(&ctx_->initial_metadata_, + ctx_->initial_metadata_flags()); + if (ctx_->compression_level_set()) { + ctx_->pending_ops_.set_compression_level(ctx_->compression_level()); + } + ctx_->sent_initial_metadata_ = true; + } + call_->PerformOps(&ctx_->pending_ops_); + // if this is the last message we defer the pluck until AFTER we start + // the trailing md op. This prevents hangs. See + // https://github.com/grpc/grpc/issues/11546 + if (options.is_last_message()) { + ctx_->has_pending_ops_ = true; + return true; + } + ctx_->has_pending_ops_ = false; + return call_->cq()->Pluck(&ctx_->pending_ops_); + } + + private: + Call* const call_; + ServerContext* const ctx_; +}; + +} // namespace internal + +/// Synchronous (blocking) server-side API for a bidirectional +/// streaming call, where the incoming message stream coming from the client has +/// messages of type \a R, and the outgoing message streaming coming from +/// the server has messages of type \a W. +template +class ServerReaderWriter final : public ServerReaderWriterInterface { + public: + /// See the \a ServerStreamingInterface.SendInitialMetadata method + /// for semantics. Note that initial metadata will be affected by the + /// \a ServerContext associated with this call. + void SendInitialMetadata() override { body_.SendInitialMetadata(); } + + bool NextMessageSize(uint32_t* sz) override { + return body_.NextMessageSize(sz); + } + + bool Read(R* msg) override { return body_.Read(msg); } + + /// See the \a WriterInterface.Write(const W& msg, WriteOptions options) + /// method for semantics. + /// Side effect: + /// Also sends initial metadata if not already sent (using the \a + /// ServerContext associated with this call). + using internal::WriterInterface::Write; + bool Write(const W& msg, WriteOptions options) override { + return body_.Write(msg, options); + } + + private: + internal::ServerReaderWriterBody body_; + + friend class internal::TemplatedBidiStreamingHandler, + false>; + ServerReaderWriter(internal::Call* call, ServerContext* ctx) + : body_(call, ctx) {} +}; + +/// A class to represent a flow-controlled unary call. This is something +/// of a hybrid between conventional unary and streaming. This is invoked +/// through a unary call on the client side, but the server responds to it +/// as though it were a single-ping-pong streaming call. The server can use +/// the \a NextMessageSize method to determine an upper-bound on the size of +/// the message. A key difference relative to streaming: ServerUnaryStreamer +/// must have exactly 1 Read and exactly 1 Write, in that order, to function +/// correctly. Otherwise, the RPC is in error. +template +class ServerUnaryStreamer final + : public ServerReaderWriterInterface { + public: + /// Block to send initial metadata to client. + /// Implicit input parameter: + /// - the \a ServerContext associated with this call will be used for + /// sending initial metadata. + void SendInitialMetadata() override { body_.SendInitialMetadata(); } + + /// Get an upper bound on the request message size from the client. + bool NextMessageSize(uint32_t* sz) override { + return body_.NextMessageSize(sz); + } + + /// Read a message of type \a R into \a msg. Completion will be notified by \a + /// tag on the associated completion queue. + /// This is thread-safe with respect to \a Write or \a WritesDone methods. It + /// should not be called concurrently with other streaming APIs + /// on the same stream. It is not meaningful to call it concurrently + /// with another \a ReaderInterface::Read on the same stream since reads on + /// the same stream are delivered in order. + /// + /// \param[out] msg Where to eventually store the read message. + /// \param[in] tag The tag identifying the operation. + bool Read(RequestType* request) override { + if (read_done_) { + return false; + } + read_done_ = true; + return body_.Read(request); + } + + /// Block to write \a msg to the stream with WriteOptions \a options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \param msg The message to be written to the stream. + /// \param options The WriteOptions affecting the write operation. + /// + /// \return \a true on success, \a false when the stream has been closed. + using internal::WriterInterface::Write; + bool Write(const ResponseType& response, WriteOptions options) override { + if (write_done_ || !read_done_) { + return false; + } + write_done_ = true; + return body_.Write(response, options); + } + + private: + internal::ServerReaderWriterBody body_; + bool read_done_; + bool write_done_; + + friend class internal::TemplatedBidiStreamingHandler< + ServerUnaryStreamer, true>; + ServerUnaryStreamer(internal::Call* call, ServerContext* ctx) + : body_(call, ctx), read_done_(false), write_done_(false) {} +}; + +/// A class to represent a flow-controlled server-side streaming call. +/// This is something of a hybrid between server-side and bidi streaming. +/// This is invoked through a server-side streaming call on the client side, +/// but the server responds to it as though it were a bidi streaming call that +/// must first have exactly 1 Read and then any number of Writes. +template +class ServerSplitStreamer final + : public ServerReaderWriterInterface { + public: + /// Block to send initial metadata to client. + /// Implicit input parameter: + /// - the \a ServerContext associated with this call will be used for + /// sending initial metadata. + void SendInitialMetadata() override { body_.SendInitialMetadata(); } + + /// Get an upper bound on the request message size from the client. + bool NextMessageSize(uint32_t* sz) override { + return body_.NextMessageSize(sz); + } + + /// Read a message of type \a R into \a msg. Completion will be notified by \a + /// tag on the associated completion queue. + /// This is thread-safe with respect to \a Write or \a WritesDone methods. It + /// should not be called concurrently with other streaming APIs + /// on the same stream. It is not meaningful to call it concurrently + /// with another \a ReaderInterface::Read on the same stream since reads on + /// the same stream are delivered in order. + /// + /// \param[out] msg Where to eventually store the read message. + /// \param[in] tag The tag identifying the operation. + bool Read(RequestType* request) override { + if (read_done_) { + return false; + } + read_done_ = true; + return body_.Read(request); + } + + /// Block to write \a msg to the stream with WriteOptions \a options. + /// This is thread-safe with respect to \a ReaderInterface::Read + /// + /// \param msg The message to be written to the stream. + /// \param options The WriteOptions affecting the write operation. + /// + /// \return \a true on success, \a false when the stream has been closed. + using internal::WriterInterface::Write; + bool Write(const ResponseType& response, WriteOptions options) override { + return read_done_ && body_.Write(response, options); + } + + private: + internal::ServerReaderWriterBody body_; + bool read_done_; + + friend class internal::TemplatedBidiStreamingHandler< + ServerSplitStreamer, false>; + ServerSplitStreamer(internal::Call* call, ServerContext* ctx) + : body_(call, ctx), read_done_(false) {} +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_CODEGEN_SYNC_STREAM_H diff --git a/third_party/grpc/include/grpcpp/impl/codegen/time.h b/third_party/grpc/include/grpcpp/impl/codegen/time.h new file mode 100644 index 00000000000000..c32f2544fa9da3 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/codegen/time.h @@ -0,0 +1,89 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_CODEGEN_TIME_H +#define GRPCPP_IMPL_CODEGEN_TIME_H + +#include + +#include +#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 + +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 // GRPCPP_IMPL_CODEGEN_TIME_H diff --git a/third_party/grpc/include/grpcpp/impl/grpc_library.h b/third_party/grpc/include/grpcpp/impl/grpc_library.h new file mode 100644 index 00000000000000..d1f3ff1297bb55 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/grpc_library.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_GRPC_LIBRARY_H +#define GRPCPP_IMPL_GRPC_LIBRARY_H + +#include + +#include +#include +#include +#include + +namespace grpc { + +namespace internal { +class GrpcLibrary final : public GrpcLibraryInterface { + public: + void init() override { grpc_init(); } + void shutdown() override { grpc_shutdown(); } +}; + +static GrpcLibrary g_gli; +static CoreCodegen g_core_codegen; + +/// Instantiating this class ensures the proper initialization of gRPC. +class GrpcLibraryInitializer final { + public: + GrpcLibraryInitializer() { + if (grpc::g_glip == nullptr) { + grpc::g_glip = &g_gli; + } + if (grpc::g_core_codegen_interface == nullptr) { + grpc::g_core_codegen_interface = &g_core_codegen; + } + } + + /// A no-op method to force the linker to reference this class, which will + /// take care of initializing and shutting down the gRPC runtime. + int summon() { return 0; } +}; + +} // namespace internal +} // namespace grpc + +#endif // GRPCPP_IMPL_GRPC_LIBRARY_H diff --git a/third_party/grpc/include/grpcpp/impl/method_handler_impl.h b/third_party/grpc/include/grpcpp/impl/method_handler_impl.h new file mode 100644 index 00000000000000..7f3be6428b30f4 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/method_handler_impl.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_METHOD_HANDLER_IMPL_H +#define GRPCPP_IMPL_METHOD_HANDLER_IMPL_H + +#include + +#endif // GRPCPP_IMPL_METHOD_HANDLER_IMPL_H diff --git a/third_party/grpc/include/grpcpp/impl/rpc_method.h b/third_party/grpc/include/grpcpp/impl/rpc_method.h new file mode 100644 index 00000000000000..5da704167185b2 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/rpc_method.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_RPC_METHOD_H +#define GRPCPP_IMPL_RPC_METHOD_H + +#include + +#endif // GRPCPP_IMPL_RPC_METHOD_H diff --git a/third_party/grpc/include/grpcpp/impl/rpc_service_method.h b/third_party/grpc/include/grpcpp/impl/rpc_service_method.h new file mode 100644 index 00000000000000..ef70a3a1008083 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/rpc_service_method.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_RPC_SERVICE_METHOD_H +#define GRPCPP_IMPL_RPC_SERVICE_METHOD_H + +#include + +#endif // GRPCPP_IMPL_RPC_SERVICE_METHOD_H diff --git a/third_party/grpc/include/grpcpp/impl/serialization_traits.h b/third_party/grpc/include/grpcpp/impl/serialization_traits.h new file mode 100644 index 00000000000000..95194fbd894804 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/serialization_traits.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_SERIALIZATION_TRAITS_H +#define GRPCPP_IMPL_SERIALIZATION_TRAITS_H + +#include + +#endif // GRPCPP_IMPL_SERIALIZATION_TRAITS_H diff --git a/third_party/grpc/include/grpcpp/impl/server_builder_option.h b/third_party/grpc/include/grpcpp/impl/server_builder_option.h new file mode 100644 index 00000000000000..c7b7801dab3409 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/server_builder_option.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_SERVER_BUILDER_OPTION_H +#define GRPCPP_IMPL_SERVER_BUILDER_OPTION_H + +#include +#include + +#include +#include + +namespace grpc { + +/// Interface to pass an option to a \a ServerBuilder. +class ServerBuilderOption { + public: + virtual ~ServerBuilderOption() {} + /// Alter the \a ChannelArguments used to create the gRPC server. + virtual void UpdateArguments(ChannelArguments* args) = 0; + /// Alter the ServerBuilderPlugin map that will be added into ServerBuilder. + virtual void UpdatePlugins( + std::vector>* plugins) = 0; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_SERVER_BUILDER_OPTION_H diff --git a/third_party/grpc/include/grpcpp/impl/server_builder_plugin.h b/third_party/grpc/include/grpcpp/impl/server_builder_plugin.h new file mode 100644 index 00000000000000..39450b42d56d18 --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/server_builder_plugin.h @@ -0,0 +1,66 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H +#define GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H + +#include + +#include + +namespace grpc { + +class ServerBuilder; +class ServerInitializer; +class ChannelArguments; + +/// This interface is meant for internal usage only. Implementations of this +/// interface should add themselves to a \a ServerBuilder instance through the +/// \a InternalAddPluginFactory method. +class ServerBuilderPlugin { + public: + virtual ~ServerBuilderPlugin() {} + virtual grpc::string name() = 0; + + /// UpdateServerBuilder will be called at an early stage in + /// ServerBuilder::BuildAndStart(), right after the ServerBuilderOptions have + /// done their updates. + virtual void UpdateServerBuilder(ServerBuilder* builder) {} + + /// InitServer will be called in ServerBuilder::BuildAndStart(), after the + /// Server instance is created. + virtual void InitServer(ServerInitializer* si) = 0; + + /// Finish will be called at the end of ServerBuilder::BuildAndStart(). + virtual void Finish(ServerInitializer* si) = 0; + + /// ChangeArguments is an interface that can be used in + /// ServerBuilderOption::UpdatePlugins + virtual void ChangeArguments(const grpc::string& name, void* value) = 0; + + /// UpdateChannelArguments will be called in ServerBuilder::BuildAndStart(), + /// before the Server instance is created. + virtual void UpdateChannelArguments(ChannelArguments* args) {} + + virtual bool has_sync_methods() const { return false; } + virtual bool has_async_methods() const { return false; } +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_SERVER_BUILDER_PLUGIN_H diff --git a/third_party/grpc/include/grpcpp/impl/server_initializer.h b/third_party/grpc/include/grpcpp/impl/server_initializer.h new file mode 100644 index 00000000000000..f949fabfe6f69c --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/server_initializer.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_SERVER_INITIALIZER_H +#define GRPCPP_IMPL_SERVER_INITIALIZER_H + +#include +#include + +#include + +namespace grpc { + +class Server; +class Service; + +class ServerInitializer { + public: + ServerInitializer(Server* server) : server_(server) {} + + bool RegisterService(std::shared_ptr service) { + if (!server_->RegisterService(nullptr, service.get())) { + return false; + } + default_services_.push_back(service); + return true; + } + + const std::vector* GetServiceList() { + return &server_->services_; + } + + private: + Server* server_; + std::vector > default_services_; +}; + +} // namespace grpc + +#endif // GRPCPP_IMPL_SERVER_INITIALIZER_H diff --git a/third_party/grpc/include/grpcpp/impl/service_type.h b/third_party/grpc/include/grpcpp/impl/service_type.h new file mode 100644 index 00000000000000..250bc8cd56d1eb --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/service_type.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_SERVICE_TYPE_H +#define GRPCPP_IMPL_SERVICE_TYPE_H + +#include + +#endif // GRPCPP_IMPL_SERVICE_TYPE_H diff --git a/third_party/grpc/include/grpcpp/impl/sync_cxx11.h b/third_party/grpc/include/grpcpp/impl/sync_cxx11.h new file mode 100644 index 00000000000000..76dcfe391c90ee --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/sync_cxx11.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_SYNC_CXX11_H +#define GRPCPP_IMPL_SYNC_CXX11_H + +#include + +#endif // GRPCPP_IMPL_SYNC_CXX11_H diff --git a/third_party/grpc/include/grpcpp/impl/sync_no_cxx11.h b/third_party/grpc/include/grpcpp/impl/sync_no_cxx11.h new file mode 100644 index 00000000000000..cc2d4f104cf86e --- /dev/null +++ b/third_party/grpc/include/grpcpp/impl/sync_no_cxx11.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_IMPL_SYNC_NO_CXX11_H +#define GRPCPP_IMPL_SYNC_NO_CXX11_H + +#include + +#endif // GRPCPP_IMPL_SYNC_NO_CXX11_H diff --git a/third_party/grpc/include/grpcpp/opencensus.h b/third_party/grpc/include/grpcpp/opencensus.h new file mode 100644 index 00000000000000..29b221f76748a3 --- /dev/null +++ b/third_party/grpc/include/grpcpp/opencensus.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_OPENCENSUS_H +#define GRPCPP_OPENCENSUS_H + +#include "opencensus/trace/span.h" + +namespace grpc { +// These symbols in this file will not be included in the binary unless +// grpc_opencensus_plugin build target was added as a dependency. At the moment +// it is only setup to be built with Bazel. + +// Registers the OpenCensus plugin with gRPC, so that it will be used for future +// RPCs. This must be called before any views are created. +void RegisterOpenCensusPlugin(); + +// RPC stats definitions, defined by +// https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/gRPC.md + +// Registers the cumulative gRPC views so that they will be exported by any +// registered stats exporter. For on-task stats, construct a View using the +// ViewDescriptors below. +void RegisterOpenCensusViewsForExport(); + +class ServerContext; + +// Returns the tracing Span for the current RPC. +::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context); + +} // namespace grpc + +#endif // GRPCPP_OPENCENSUS_H diff --git a/third_party/grpc/include/grpcpp/resource_quota.h b/third_party/grpc/include/grpcpp/resource_quota.h new file mode 100644 index 00000000000000..50bd1cb849a188 --- /dev/null +++ b/third_party/grpc/include/grpcpp/resource_quota.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_RESOURCE_QUOTA_H +#define GRPCPP_RESOURCE_QUOTA_H + +struct grpc_resource_quota; + +#include +#include + +namespace grpc { + +/// ResourceQuota represents a bound on memory and thread usage by the gRPC +/// library. A ResourceQuota can be attached to a server (via \a ServerBuilder), +/// or a client channel (via \a ChannelArguments). +/// gRPC will attempt to keep memory and threads used by all attached entities +/// below the ResourceQuota bound. +class ResourceQuota final : private GrpcLibraryCodegen { + public: + /// \param name - a unique name for this ResourceQuota. + explicit ResourceQuota(const grpc::string& name); + ResourceQuota(); + ~ResourceQuota(); + + /// Resize this \a ResourceQuota to a new size. If \a new_size is smaller + /// than the current size of the pool, memory usage will be monotonically + /// decreased until it falls under \a new_size. + /// No time bound is given for this to occur however. + ResourceQuota& Resize(size_t new_size); + + /// Set the max number of threads that can be allocated from this + /// ResourceQuota object. + /// + /// If the new_max_threads value is smaller than the current value, no new + /// threads are allocated until the number of active threads fall below + /// new_max_threads. There is no time bound on when this may happen i.e none + /// of the current threads are forcefully destroyed and all threads run their + /// normal course. + ResourceQuota& SetMaxThreads(int new_max_threads); + + grpc_resource_quota* c_resource_quota() const { return impl_; } + + private: + ResourceQuota(const ResourceQuota& rhs); + ResourceQuota& operator=(const ResourceQuota& rhs); + + grpc_resource_quota* const impl_; +}; + +} // namespace grpc + +#endif // GRPCPP_RESOURCE_QUOTA_H diff --git a/third_party/grpc/include/grpcpp/security/auth_context.h b/third_party/grpc/include/grpcpp/security/auth_context.h new file mode 100644 index 00000000000000..7a6f2cb718bc56 --- /dev/null +++ b/third_party/grpc/include/grpcpp/security/auth_context.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SECURITY_AUTH_CONTEXT_H +#define GRPCPP_SECURITY_AUTH_CONTEXT_H + +#include + +#endif // GRPCPP_SECURITY_AUTH_CONTEXT_H diff --git a/third_party/grpc/include/grpcpp/security/auth_metadata_processor.h b/third_party/grpc/include/grpcpp/security/auth_metadata_processor.h new file mode 100644 index 00000000000000..30e24c9f0bc811 --- /dev/null +++ b/third_party/grpc/include/grpcpp/security/auth_metadata_processor.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H +#define GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H + +#include + +#include +#include +#include + +namespace grpc { + +/// Interface allowing custom server-side authorization based on credentials +/// encoded in metadata. Objects of this type can be passed to +/// \a ServerCredentials::SetAuthMetadataProcessor(). +class AuthMetadataProcessor { + public: + typedef std::multimap InputMetadata; + typedef std::multimap OutputMetadata; + + virtual ~AuthMetadataProcessor() {} + + /// If this method returns true, the \a 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 // GRPCPP_SECURITY_AUTH_METADATA_PROCESSOR_H diff --git a/third_party/grpc/include/grpcpp/security/credentials.h b/third_party/grpc/include/grpcpp/security/credentials.h new file mode 100644 index 00000000000000..d8c9e04d778cd3 --- /dev/null +++ b/third_party/grpc/include/grpcpp/security/credentials.h @@ -0,0 +1,277 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SECURITY_CREDENTIALS_H +#define GRPCPP_SECURITY_CREDENTIALS_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct grpc_call; + +namespace grpc { +class ChannelArguments; +class Channel; +class SecureChannelCredentials; +class CallCredentials; +class SecureCallCredentials; + +class ChannelCredentials; + +namespace experimental { +std::shared_ptr CreateCustomChannelWithInterceptors( + const grpc::string& target, + const std::shared_ptr& creds, + const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators); +} // namespace experimental + +/// A channel credentials object encapsulates all the state needed by a client +/// to authenticate with a server for a given channel. +/// It can make various assertions, e.g., about the client’s identity, role +/// for all the calls on that channel. +/// +/// \see https://grpc.io/docs/guides/auth.html +class ChannelCredentials : private GrpcLibraryCodegen { + public: + ChannelCredentials(); + ~ChannelCredentials(); + + protected: + friend std::shared_ptr CompositeChannelCredentials( + const std::shared_ptr& channel_creds, + const std::shared_ptr& call_creds); + + virtual SecureChannelCredentials* AsSecureCredentials() = 0; + + private: + friend std::shared_ptr CreateCustomChannel( + const grpc::string& target, + const std::shared_ptr& creds, + const ChannelArguments& args); + + friend std::shared_ptr + experimental::CreateCustomChannelWithInterceptors( + const grpc::string& target, + const std::shared_ptr& creds, + const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators); + + virtual std::shared_ptr CreateChannel( + const grpc::string& target, const ChannelArguments& args) = 0; + + // This function should have been a pure virtual function, but it is + // implemented as a virtual function so that it does not break API. + virtual std::shared_ptr CreateChannelWithInterceptors( + const grpc::string& target, const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) { + return nullptr; + }; +}; + +/// A call credentials object encapsulates the state needed by a client to +/// authenticate with a server for a given call on a channel. +/// +/// \see https://grpc.io/docs/guides/auth.html +class CallCredentials : private GrpcLibraryCodegen { + public: + CallCredentials(); + ~CallCredentials(); + + /// Apply this instance's credentials to \a call. + virtual bool ApplyToCall(grpc_call* call) = 0; + + protected: + friend std::shared_ptr CompositeChannelCredentials( + const std::shared_ptr& channel_creds, + const std::shared_ptr& call_creds); + + friend std::shared_ptr CompositeCallCredentials( + const std::shared_ptr& creds1, + const std::shared_ptr& creds2); + + virtual SecureCallCredentials* AsSecureCredentials() = 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(); + +/// Constant for maximum auth token lifetime. +constexpr long kMaxAuthTokenLifetimeSecs = 3600; + +/// 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 +/// \a kMaxAuthTokenLifetimeSecs or will be cropped to this value. +std::shared_ptr ServiceAccountJWTAccessCredentials( + const grpc::string& json_key, + long token_lifetime_seconds = kMaxAuthTokenLifetimeSecs); + +/// 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 a channel credentials and a call credentials into a composite +/// channel credentials. +std::shared_ptr CompositeChannelCredentials( + const std::shared_ptr& channel_creds, + const std::shared_ptr& call_creds); + +/// Combines two call credentials objects into a composite call credentials. +std::shared_ptr CompositeCallCredentials( + const std::shared_ptr& creds1, + const std::shared_ptr& creds2); + +/// Credentials for an unencrypted, unauthenticated channel +std::shared_ptr InsecureChannelCredentials(); + +/// Credentials for a channel using Cronet. +std::shared_ptr CronetChannelCredentials(void* engine); + +/// User defined metadata credentials. +class MetadataCredentialsPlugin { + public: + virtual ~MetadataCredentialsPlugin() {} + + /// 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; } + + /// Type of credentials this plugin is implementing. + virtual const char* GetType() const { return ""; } + + /// Gets the auth metatada produced by this plugin. + /// The fully qualified method name is: + /// service_url + "/" + method_name. + /// The channel_auth_context contains (among other things), the identity of + /// the server. + virtual Status GetMetadata( + grpc::string_ref service_url, grpc::string_ref method_name, + const AuthContext& channel_auth_context, + std::multimap* metadata) = 0; +}; + +std::shared_ptr MetadataCredentialsFromPlugin( + std::unique_ptr plugin); + +namespace experimental { + +/// Options used to build AltsCredentials. +struct AltsCredentialsOptions { + /// service accounts of target endpoint that will be acceptable + /// by the client. If service accounts are provided and none of them matches + /// that of the server, authentication will fail. + std::vector target_service_accounts; +}; + +/// Builds ALTS Credentials given ALTS specific options +std::shared_ptr AltsCredentials( + const AltsCredentialsOptions& options); + +/// Builds Local Credentials. +std::shared_ptr LocalCredentials( + grpc_local_connect_type type); + +} // namespace experimental +} // namespace grpc + +#endif // GRPCPP_SECURITY_CREDENTIALS_H diff --git a/third_party/grpc/include/grpcpp/security/server_credentials.h b/third_party/grpc/include/grpcpp/security/server_credentials.h new file mode 100644 index 00000000000000..bd00a0a1737962 --- /dev/null +++ b/third_party/grpc/include/grpcpp/security/server_credentials.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SECURITY_SERVER_CREDENTIALS_H +#define GRPCPP_SECURITY_SERVER_CREDENTIALS_H + +#include +#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 { + /// \warning Deprecated + SslServerCredentialsOptions() + : force_client_auth(false), + client_certificate_request(GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE) {} + SslServerCredentialsOptions( + grpc_ssl_client_certificate_request_type request_type) + : force_client_auth(false), client_certificate_request(request_type) {} + + struct PemKeyCertPair { + grpc::string private_key; + grpc::string cert_chain; + }; + grpc::string pem_root_certs; + std::vector pem_key_cert_pairs; + /// \warning Deprecated + bool force_client_auth; + + /// If both \a force_client_auth and \a client_certificate_request + /// fields are set, \a force_client_auth takes effect, i.e. + /// \a REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + /// will be enforced. + grpc_ssl_client_certificate_request_type client_certificate_request; +}; + +/// Builds SSL ServerCredentials given SSL specific options +std::shared_ptr SslServerCredentials( + const SslServerCredentialsOptions& options); + +/// Builds insecure server credentials. +std::shared_ptr InsecureServerCredentials(); + +namespace experimental { + +/// Options to create ServerCredentials with ALTS +struct AltsServerCredentialsOptions { + /// Add fields if needed. +}; + +/// Builds ALTS ServerCredentials given ALTS specific options +std::shared_ptr AltsServerCredentials( + const AltsServerCredentialsOptions& options); + +/// Builds Local ServerCredentials. +std::shared_ptr LocalServerCredentials( + grpc_local_connect_type type); + +} // namespace experimental +} // namespace grpc + +#endif // GRPCPP_SECURITY_SERVER_CREDENTIALS_H diff --git a/third_party/grpc/include/grpcpp/server.h b/third_party/grpc/include/grpcpp/server.h new file mode 100644 index 00000000000000..cdcac186cb65a6 --- /dev/null +++ b/third_party/grpc/include/grpcpp/server.h @@ -0,0 +1,288 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SERVER_H +#define GRPCPP_SERVER_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct grpc_server; + +namespace grpc { + +class AsyncGenericService; +class HealthCheckServiceInterface; +class ServerContext; +class ServerInitializer; + +/// Represents a gRPC server. +/// +/// Use a \a grpc::ServerBuilder to create, configure, and start +/// \a Server instances. +class Server : public ServerInterface, private GrpcLibraryCodegen { + public: + ~Server(); + + /// Block until the server shuts down. + /// + /// \warning The server must be either shutting down or some other thread must + /// call \a Shutdown for this function to ever return. + void Wait() override; + + /// Global callbacks are a set of hooks that are called when server + /// events occur. \a SetGlobalCallbacks method is used to register + /// the hooks with gRPC. Note that + /// the \a GlobalCallbacks instance will be shared among all + /// \a Server instances in an application and can be set exactly + /// once per application. + class GlobalCallbacks { + public: + virtual ~GlobalCallbacks() {} + /// Called before server is created. + virtual void UpdateArguments(ChannelArguments* args) {} + /// Called before application callback for each synchronous server request + virtual void PreSynchronousRequest(ServerContext* context) = 0; + /// Called after application callback for each synchronous server request + virtual void PostSynchronousRequest(ServerContext* context) = 0; + /// Called before server is started. + virtual void PreServerStart(Server* server) {} + /// Called after a server port is added. + virtual void AddPort(Server* server, const grpc::string& addr, + ServerCredentials* creds, int port) {} + }; + /// Set the global callback object. Can only be called once per application. + /// Does not take ownership of callbacks, and expects the pointed to object + /// to be alive until all server objects in the process have been destroyed. + /// The same \a GlobalCallbacks object will be used throughout the + /// application and is shared among all \a Server objects. + static void SetGlobalCallbacks(GlobalCallbacks* callbacks); + + /// Returns a \em raw pointer to the underlying \a grpc_server instance. + /// EXPERIMENTAL: for internal/test use only + grpc_server* c_server(); + + /// Returns the health check service. + HealthCheckServiceInterface* GetHealthCheckService() const { + return health_check_service_.get(); + } + + /// Establish a channel for in-process communication + std::shared_ptr InProcessChannel(const ChannelArguments& args); + + /// NOTE: class experimental_type is not part of the public API of this class. + /// TODO(yashykt): Integrate into public API when this is no longer + /// experimental. + class experimental_type { + public: + explicit experimental_type(Server* server) : server_(server) {} + + /// Establish a channel for in-process communication with client + /// interceptors + std::shared_ptr InProcessChannelWithInterceptors( + const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators); + + private: + Server* server_; + }; + + /// NOTE: The function experimental() is not stable public API. It is a view + /// to the experimental components of this class. It may be changed or removed + /// at any time. + experimental_type experimental() { return experimental_type(this); } + + protected: + /// 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, Service* service) override; + + /// Try binding the server to the given \a addr endpoint + /// (port, and optionally including IP address to bind to). + /// + /// It can be invoked multiple times. Should be used before + /// starting the server. + /// + /// \param addr The address to try to bind to the server (eg, localhost:1234, + /// 192.168.1.1:31416, [::1]:27182, etc.). + /// \param creds The credentials associated with the server. + /// + /// \return bound port number on success, 0 on failure. + /// + /// \warning It is an error to call this method on an already started server. + int AddListeningPort(const grpc::string& addr, + ServerCredentials* creds) override; + + /// NOTE: This is *NOT* a public API. The server constructors are supposed to + /// be used by \a ServerBuilder class only. The constructor will be made + /// 'private' very soon. + /// + /// Server constructors. To be used by \a ServerBuilder only. + /// + /// \param max_message_size Maximum message length that the channel can + /// receive. + /// + /// \param args The channel args + /// + /// \param sync_server_cqs The completion queues to use if the server is a + /// synchronous server (or a hybrid server). The server polls for new RPCs on + /// these queues + /// + /// \param min_pollers The minimum number of polling threads per server + /// completion queue (in param sync_server_cqs) to use for listening to + /// incoming requests (used only in case of sync server) + /// + /// \param max_pollers The maximum number of polling threads per server + /// completion queue (in param sync_server_cqs) to use for listening to + /// incoming requests (used only in case of sync server) + /// + /// \param sync_cq_timeout_msec The timeout to use when calling AsyncNext() on + /// server completion queues passed via sync_server_cqs param. + Server(int max_message_size, ChannelArguments* args, + std::shared_ptr>> + sync_server_cqs, + int min_pollers, int max_pollers, int sync_cq_timeout_msec, + grpc_resource_quota* server_rq = nullptr, + std::vector< + std::unique_ptr> + interceptor_creators = std::vector>()); + + /// 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. + void Start(ServerCompletionQueue** cqs, size_t num_cqs) override; + + grpc_server* server() override { return server_; }; + + private: + std::vector>* + interceptor_creators() override { + return &interceptor_creators_; + } + + friend class AsyncGenericService; + friend class ServerBuilder; + friend class ServerInitializer; + + class SyncRequest; + class CallbackRequest; + class UnimplementedAsyncRequest; + class UnimplementedAsyncResponse; + + /// SyncRequestThreadManager is an implementation of ThreadManager. This class + /// is responsible for polling for incoming RPCs and calling the RPC handlers. + /// This is only used in case of a Sync server (i.e a server exposing a sync + /// interface) + class SyncRequestThreadManager; + + /// 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) override; + + void PerformOpsOnCall(internal::CallOpSetInterface* ops, + internal::Call* call) override; + + void ShutdownInternal(gpr_timespec deadline) override; + + int max_receive_message_size() const override { + return max_receive_message_size_; + }; + + CompletionQueue* CallbackCQ() override; + + ServerInitializer* initializer(); + + // A vector of interceptor factory objects. + // This should be destroyed after health_check_service_ and this requirement + // is satisfied by declaring interceptor_creators_ before + // health_check_service_. (C++ mandates that member objects be destroyed in + // the reverse order of initialization.) + std::vector> + interceptor_creators_; + + const int max_receive_message_size_; + + /// The following completion queues are ONLY used in case of Sync API + /// i.e. if the server has any services with sync methods. The server uses + /// these completion queues to poll for new RPCs + std::shared_ptr>> + sync_server_cqs_; + + /// List of \a ThreadManager instances (one for each cq in + /// the \a sync_server_cqs) + std::vector> sync_req_mgrs_; + + /// Outstanding callback requests + std::vector> callback_reqs_; + + // Server status + std::mutex mu_; + bool started_; + bool shutdown_; + bool shutdown_notified_; // Was notify called on the shutdown_cv_ + + std::condition_variable shutdown_cv_; + + std::shared_ptr global_callbacks_; + + std::vector services_; + bool has_generic_service_; + + // Pointer to the wrapped grpc_server. + grpc_server* server_; + + std::unique_ptr server_initializer_; + + std::unique_ptr health_check_service_; + bool health_check_service_disabled_; + + // A special handler for resource exhausted in sync case + std::unique_ptr resource_exhausted_handler_; + + // callback_cq_ references the callbackable completion queue associated + // with this server (if any). It is set on the first call to CallbackCQ(). + // It is _not owned_ by the server; ownership belongs with its internal + // shutdown callback tag (invoked when the CQ is fully shutdown). + // It is protected by mu_ + CompletionQueue* callback_cq_ = nullptr; +}; + +} // namespace grpc + +#endif // GRPCPP_SERVER_H diff --git a/third_party/grpc/include/grpcpp/server_builder.h b/third_party/grpc/include/grpcpp/server_builder.h new file mode 100644 index 00000000000000..028b8cffaa7deb --- /dev/null +++ b/third_party/grpc/include/grpcpp/server_builder.h @@ -0,0 +1,330 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SERVER_BUILDER_H +#define GRPCPP_SERVER_BUILDER_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct grpc_resource_quota; + +namespace grpc { + +class AsyncGenericService; +class ResourceQuota; +class CompletionQueue; +class Server; +class ServerCompletionQueue; +class ServerCredentials; +class Service; + +namespace testing { +class ServerBuilderPluginTest; +} // namespace testing + +/// A builder class for the creation and startup of \a grpc::Server instances. +class ServerBuilder { + public: + ServerBuilder(); + virtual ~ServerBuilder(); + + ////////////////////////////////////////////////////////////////////////////// + // Primary API's + + /// Return a running server which is ready for processing calls. + /// Before calling, one typically needs to ensure that: + /// 1. a service is registered - so that the server knows what to serve + /// (via RegisterService, or RegisterAsyncGenericService) + /// 2. a listening port has been added - so the server knows where to receive + /// traffic (via AddListeningPort) + /// 3. [for async api only] completion queues have been added via + /// AddCompletionQueue + virtual std::unique_ptr BuildAndStart(); + + /// 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 + ServerBuilder& RegisterService(Service* service); + + /// Enlists an endpoint \a addr (port with an optional IP address) to + /// bind the \a grpc::Server object to be created to. + /// + /// It can be invoked multiple times. + /// + /// \param addr_uri The address to try to bind to the server in URI form. If + /// the scheme name is omitted, "dns:///" is assumed. To bind to any address, + /// please use IPv6 any, i.e., [::]:, which also accepts IPv4 + /// connections. Valid values include dns:///localhost:1234, / + /// 192.168.1.1:31416, dns:///[::1]:27182, etc.). + /// \param creds The credentials associated with the server. + /// \param selected_port[out] If not `nullptr`, gets populated with the port + /// number bound to the \a grpc::Server for the corresponding endpoint after + /// it is successfully bound by BuildAndStart(), 0 otherwise. AddListeningPort + /// does not modify this pointer. + ServerBuilder& AddListeningPort(const grpc::string& addr_uri, + std::shared_ptr creds, + int* selected_port = nullptr); + + /// Add a completion queue for handling asynchronous services. + /// + /// Best performance is typically obtained by using one thread per polling + /// completion queue. + /// + /// Caller is required to shutdown the server prior to shutting down the + /// returned completion queue. Caller is also required to drain the + /// completion queue after shutting it down. A typical usage scenario: + /// + /// // While building the server: + /// ServerBuilder builder; + /// ... + /// cq_ = builder.AddCompletionQueue(); + /// server_ = builder.BuildAndStart(); + /// + /// // While shutting down the server; + /// server_->Shutdown(); + /// cq_->Shutdown(); // Always *after* the associated server's Shutdown()! + /// // Drain the cq_ that was created + /// void* ignored_tag; + /// bool ignored_ok; + /// while (cq_->Next(&ignored_tag, &ignored_ok)) { } + /// + /// \param is_frequently_polled This is an optional parameter to inform gRPC + /// library about whether this completion queue would be frequently polled + /// (i.e. by calling \a Next() or \a AsyncNext()). The default value is + /// 'true' and is the recommended setting. Setting this to 'false' (i.e. + /// not polling the completion queue frequently) will have a significantly + /// negative performance impact and hence should not be used in production + /// use cases. + std::unique_ptr AddCompletionQueue( + bool is_frequently_polled = true); + + ////////////////////////////////////////////////////////////////////////////// + // Less commonly used RegisterService variants + + /// 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(). + /// Only matches requests with :authority \a host + ServerBuilder& RegisterService(const grpc::string& host, Service* service); + + /// Register a generic service. + /// Matches requests with any :authority + /// This is mostly useful for writing generic gRPC Proxies where the exact + /// serialization format is unknown + ServerBuilder& RegisterAsyncGenericService(AsyncGenericService* service); + + ////////////////////////////////////////////////////////////////////////////// + // Fine control knobs + + /// Set max receive message size in bytes. + /// The default is GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH. + ServerBuilder& SetMaxReceiveMessageSize(int max_receive_message_size) { + max_receive_message_size_ = max_receive_message_size; + return *this; + } + + /// Set max send message size in bytes. + /// The default is GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH. + ServerBuilder& SetMaxSendMessageSize(int max_send_message_size) { + max_send_message_size_ = max_send_message_size; + return *this; + } + + /// \deprecated For backward compatibility. + ServerBuilder& SetMaxMessageSize(int max_message_size) { + return SetMaxReceiveMessageSize(max_message_size); + } + + /// Set the support status for compression algorithms. All algorithms are + /// enabled by default. + /// + /// Incoming calls compressed with an unsupported algorithm will fail with + /// \a GRPC_STATUS_UNIMPLEMENTED. + ServerBuilder& SetCompressionAlgorithmSupportStatus( + grpc_compression_algorithm algorithm, bool enabled); + + /// The default compression level to use for all channel calls in the + /// absence of a call-specific level. + ServerBuilder& SetDefaultCompressionLevel(grpc_compression_level level); + + /// The default compression algorithm to use for all channel calls in the + /// absence of a call-specific level. Note that it overrides any compression + /// level set by \a SetDefaultCompressionLevel. + ServerBuilder& SetDefaultCompressionAlgorithm( + grpc_compression_algorithm algorithm); + + /// Set the attached buffer pool for this server + ServerBuilder& SetResourceQuota(const ResourceQuota& resource_quota); + + ServerBuilder& SetOption(std::unique_ptr option); + + /// Options for synchronous servers. + enum SyncServerOption { + NUM_CQS, ///< Number of completion queues. + MIN_POLLERS, ///< Minimum number of polling threads. + MAX_POLLERS, ///< Maximum number of polling threads. + CQ_TIMEOUT_MSEC ///< Completion queue timeout in milliseconds. + }; + + /// Only useful if this is a Synchronous server. + ServerBuilder& SetSyncServerOption(SyncServerOption option, int value); + + /// Add a channel argument (an escape hatch to tuning core library parameters + /// directly) + template + ServerBuilder& AddChannelArgument(const grpc::string& arg, const T& value) { + return SetOption(MakeChannelArgumentOption(arg, value)); + } + + /// For internal use only: Register a ServerBuilderPlugin factory function. + static void InternalAddPluginFactory( + std::unique_ptr (*CreatePlugin)()); + + /// Enable a server workaround. Do not use unless you know what the workaround + /// does. For explanation and detailed descriptions of workarounds, see + /// doc/workarounds.md. + ServerBuilder& EnableWorkaround(grpc_workaround_list id); + + /// NOTE: class experimental_type is not part of the public API of this class. + /// TODO(yashykt): Integrate into public API when this is no longer + /// experimental. + class experimental_type { + public: + explicit experimental_type(ServerBuilder* builder) : builder_(builder) {} + + void SetInterceptorCreators( + std::vector< + std::unique_ptr> + interceptor_creators) { + builder_->interceptor_creators_ = std::move(interceptor_creators); + } + + private: + ServerBuilder* builder_; + }; + + /// NOTE: The function experimental() is not stable public API. It is a view + /// to the experimental components of this class. It may be changed or removed + /// at any time. + experimental_type experimental() { return experimental_type(this); } + + protected: + /// Experimental, to be deprecated + struct Port { + grpc::string addr; + std::shared_ptr creds; + int* selected_port; + }; + + /// Experimental, to be deprecated + typedef std::unique_ptr HostString; + struct NamedService { + explicit NamedService(Service* s) : service(s) {} + NamedService(const grpc::string& h, Service* s) + : host(new grpc::string(h)), service(s) {} + HostString host; + Service* service; + }; + + /// Experimental, to be deprecated + std::vector ports() { return ports_; } + + /// Experimental, to be deprecated + std::vector services() { + std::vector service_refs; + for (auto& ptr : services_) { + service_refs.push_back(ptr.get()); + } + return service_refs; + } + + /// Experimental, to be deprecated + std::vector options() { + std::vector option_refs; + for (auto& ptr : options_) { + option_refs.push_back(ptr.get()); + } + return option_refs; + } + + private: + friend class ::grpc::testing::ServerBuilderPluginTest; + + struct SyncServerSettings { + SyncServerSettings() + : num_cqs(1), min_pollers(1), max_pollers(2), cq_timeout_msec(10000) {} + + /// Number of server completion queues to create to listen to incoming RPCs. + int num_cqs; + + /// Minimum number of threads per completion queue that should be listening + /// to incoming RPCs. + int min_pollers; + + /// Maximum number of threads per completion queue that can be listening to + /// incoming RPCs. + int max_pollers; + + /// The timeout for server completion queue's AsyncNext call. + int cq_timeout_msec; + }; + + int max_receive_message_size_; + int max_send_message_size_; + std::vector> options_; + std::vector> services_; + std::vector ports_; + + SyncServerSettings sync_server_settings_; + + /// List of completion queues added via \a AddCompletionQueue method. + std::vector cqs_; + + std::shared_ptr creds_; + std::vector> plugins_; + grpc_resource_quota* resource_quota_; + AsyncGenericService* generic_service_; + struct { + bool is_set; + grpc_compression_level level; + } maybe_default_compression_level_; + struct { + bool is_set; + grpc_compression_algorithm algorithm; + } maybe_default_compression_algorithm_; + uint32_t enabled_compression_algorithms_bitset_; + std::vector> + interceptor_creators_; +}; + +} // namespace grpc + +#endif // GRPCPP_SERVER_BUILDER_H diff --git a/third_party/grpc/include/grpcpp/server_context.h b/third_party/grpc/include/grpcpp/server_context.h new file mode 100644 index 00000000000000..45f2614994173d --- /dev/null +++ b/third_party/grpc/include/grpcpp/server_context.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SERVER_CONTEXT_H +#define GRPCPP_SERVER_CONTEXT_H + +#include + +#endif // GRPCPP_SERVER_CONTEXT_H diff --git a/third_party/grpc/include/grpcpp/server_posix.h b/third_party/grpc/include/grpcpp/server_posix.h new file mode 100644 index 00000000000000..ef3ee01a5c6ba0 --- /dev/null +++ b/third_party/grpc/include/grpcpp/server_posix.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SERVER_POSIX_H +#define GRPCPP_SERVER_POSIX_H + +#include + +#include +#include + +namespace grpc { + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +/// Add a new client to a \a Server communicating over the given +/// file descriptor. +/// +/// \param server The server to add the client to. +/// \param fd The file descriptor representing a socket. +void AddInsecureChannelFromFd(Server* server, int fd); + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD + +} // namespace grpc + +#endif // GRPCPP_SERVER_POSIX_H diff --git a/third_party/grpc/include/grpcpp/support/async_stream.h b/third_party/grpc/include/grpcpp/support/async_stream.h new file mode 100644 index 00000000000000..ff9e455679388f --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/async_stream.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_ASYNC_STREAM_H +#define GRPCPP_SUPPORT_ASYNC_STREAM_H + +#include + +#endif // GRPCPP_SUPPORT_ASYNC_STREAM_H diff --git a/third_party/grpc/include/grpcpp/support/async_unary_call.h b/third_party/grpc/include/grpcpp/support/async_unary_call.h new file mode 100644 index 00000000000000..2e5181c5570397 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/async_unary_call.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H +#define GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H + +#include + +#endif // GRPCPP_SUPPORT_ASYNC_UNARY_CALL_H diff --git a/third_party/grpc/include/grpcpp/support/byte_buffer.h b/third_party/grpc/include/grpcpp/support/byte_buffer.h new file mode 100644 index 00000000000000..53aeff19f77c9c --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/byte_buffer.h @@ -0,0 +1,31 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_BYTE_BUFFER_H +#define GRPCPP_SUPPORT_BYTE_BUFFER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // GRPCPP_SUPPORT_BYTE_BUFFER_H diff --git a/third_party/grpc/include/grpcpp/support/channel_arguments.h b/third_party/grpc/include/grpcpp/support/channel_arguments.h new file mode 100644 index 00000000000000..217929d4acad07 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/channel_arguments.h @@ -0,0 +1,148 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H +#define GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H + +#include +#include + +#include +#include +#include + +namespace grpc { +namespace testing { +class ChannelArgumentsTest; +} // namespace testing + +class ResourceQuota; + +/// 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); + + /// Dump arguments in this instance to \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. This option is for + /// testing only and should never be used in production. + 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); + + /// Set the grpclb fallback timeout (in ms) for the channel. If this amount + /// of time has passed but we have not gotten any non-empty \a serverlist from + /// the balancer, we will fall back to use the backend address(es) returned by + /// the resolver. + void SetGrpclbFallbackTimeout(int fallback_timeout); + + /// For client channel's, the socket mutator operates on + /// "channel" sockets. For server's, the socket mutator operates + /// only on "listen" sockets. + /// TODO(apolcyn): allow socket mutators to also operate + /// on server "channel" sockets, and adjust the socket mutator + /// object to be more speficic about which type of socket + /// it should operate on. + void SetSocketMutator(grpc_socket_mutator* mutator); + + /// Set the string to prepend to the user agent. + void SetUserAgentPrefix(const grpc::string& user_agent_prefix); + + /// Set the buffer pool to be attached to the constructed channel. + void SetResourceQuota(const ResourceQuota& resource_quota); + + /// Set the max receive and send message sizes. + void SetMaxReceiveMessageSize(int size); + void SetMaxSendMessageSize(int size); + + /// Set LB policy name. + /// Note that if the name resolver returns only balancer addresses, the + /// grpclb LB policy will be used, regardless of what is specified here. + void SetLoadBalancingPolicyName(const grpc::string& lb_policy_name); + + /// Set service config in JSON form. + /// Primarily meant for use in unit tests. + void SetServiceConfigJSON(const grpc::string& service_config_json); + + // 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); + + // Generic channel argument setter. Only for advanced use cases. + /// Set a pointer argument \a value under \a key. Owership is not transferred. + void SetPointer(const grpc::string& key, void* value); + + void SetPointerWithVtable(const grpc::string& key, void* value, + const grpc_arg_pointer_vtable* vtable); + + /// Set a textual argument \a value under \a key. + void SetString(const grpc::string& key, const grpc::string& value); + + /// Return (by value) a C \a grpc_channel_args structure which points to + /// arguments owned by this \a ChannelArguments instance + grpc_channel_args c_channel_args() const { + grpc_channel_args out; + out.num_args = args_.size(); + out.args = args_.empty() ? NULL : const_cast(&args_[0]); + return out; + } + + private: + friend class SecureChannelCredentials; + friend class testing::ChannelArgumentsTest; + + /// Default pointer argument operations. + struct PointerVtableMembers { + static void* Copy(void* in) { return in; } + static void Destroy(void* in) {} + static int Compare(void* a, void* b) { + if (a < b) return -1; + if (a > b) return 1; + return 0; + } + }; + + // Returns empty string when it is not set. + grpc::string GetSslTargetNameOverride() const; + + std::vector args_; + std::list strings_; +}; + +} // namespace grpc + +#endif // GRPCPP_SUPPORT_CHANNEL_ARGUMENTS_H diff --git a/third_party/grpc/include/grpcpp/support/client_callback.h b/third_party/grpc/include/grpcpp/support/client_callback.h new file mode 100644 index 00000000000000..063fdc4f8594fa --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/client_callback.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_CLIENT_CALLBACK_H +#define GRPCPP_SUPPORT_CLIENT_CALLBACK_H + +#include + +#endif // GRPCPP_SUPPORT_CLIENT_CALLBACK_H diff --git a/third_party/grpc/include/grpcpp/support/client_interceptor.h b/third_party/grpc/include/grpcpp/support/client_interceptor.h new file mode 100644 index 00000000000000..50810e3fe3c7b9 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/client_interceptor.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H +#define GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H + +#include + +#endif // GRPCPP_SUPPORT_CLIENT_INTERCEPTOR_H diff --git a/third_party/grpc/include/grpcpp/support/config.h b/third_party/grpc/include/grpcpp/support/config.h new file mode 100644 index 00000000000000..16bdab6427576e --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/config.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_CONFIG_H +#define GRPCPP_SUPPORT_CONFIG_H + +#include + +#endif // GRPCPP_SUPPORT_CONFIG_H diff --git a/third_party/grpc/include/grpcpp/support/error_details.h b/third_party/grpc/include/grpcpp/support/error_details.h new file mode 100644 index 00000000000000..84931d98f18e45 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/error_details.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_ERROR_DETAILS_H +#define GRPCPP_SUPPORT_ERROR_DETAILS_H + +#include + +namespace google { +namespace rpc { +class Status; +} // namespace rpc +} // namespace google + +namespace grpc { + +/// Map a \a grpc::Status to a \a google::rpc::Status. +/// The given \a to object will be cleared. +/// On success, returns status with OK. +/// Returns status with \a INVALID_ARGUMENT, if failed to deserialize. +/// Returns status with \a FAILED_PRECONDITION, if \a to is nullptr. +Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to); + +/// Map \a google::rpc::Status to a \a grpc::Status. +/// Returns OK on success. +/// Returns status with \a FAILED_PRECONDITION if \a to is nullptr. +Status SetErrorDetails(const ::google::rpc::Status& from, Status* to); + +} // namespace grpc + +#endif // GRPCPP_SUPPORT_ERROR_DETAILS_H diff --git a/third_party/grpc/include/grpcpp/support/interceptor.h b/third_party/grpc/include/grpcpp/support/interceptor.h new file mode 100644 index 00000000000000..7ff79516bae6ab --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/interceptor.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_INTERCEPTOR_H +#define GRPCPP_SUPPORT_INTERCEPTOR_H + +#include + +#endif // GRPCPP_SUPPORT_INTERCEPTOR_H diff --git a/third_party/grpc/include/grpcpp/support/proto_buffer_reader.h b/third_party/grpc/include/grpcpp/support/proto_buffer_reader.h new file mode 100644 index 00000000000000..4cdb65d531b141 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/proto_buffer_reader.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_PROTO_BUFFER_READER_H +#define GRPCPP_SUPPORT_PROTO_BUFFER_READER_H + +#include + +#endif // GRPCPP_SUPPORT_PROTO_BUFFER_READER_H diff --git a/third_party/grpc/include/grpcpp/support/proto_buffer_writer.h b/third_party/grpc/include/grpcpp/support/proto_buffer_writer.h new file mode 100644 index 00000000000000..01cf29c457eb71 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/proto_buffer_writer.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H +#define GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H + +#include + +#endif // GRPCPP_SUPPORT_PROTO_BUFFER_WRITER_H diff --git a/third_party/grpc/include/grpcpp/support/server_callback.h b/third_party/grpc/include/grpcpp/support/server_callback.h new file mode 100644 index 00000000000000..b0aeeb53c5d40d --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/server_callback.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_SERVER_CALLBACK_H +#define GRPCPP_SUPPORT_SERVER_CALLBACK_H + +#include + +#endif // GRPCPP_SUPPORT_SERVER_CALLBACK_H diff --git a/third_party/grpc/include/grpcpp/support/server_interceptor.h b/third_party/grpc/include/grpcpp/support/server_interceptor.h new file mode 100644 index 00000000000000..b0a6229b667cbc --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/server_interceptor.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H +#define GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H + +#include + +#endif // GRPCPP_SUPPORT_SERVER_INTERCEPTOR_H diff --git a/third_party/grpc/include/grpcpp/support/slice.h b/third_party/grpc/include/grpcpp/support/slice.h new file mode 100644 index 00000000000000..eaeb29a40b29b1 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/slice.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_SLICE_H +#define GRPCPP_SUPPORT_SLICE_H + +#include +#include +#include + +#endif // GRPCPP_SUPPORT_SLICE_H diff --git a/third_party/grpc/include/grpcpp/support/status.h b/third_party/grpc/include/grpcpp/support/status.h new file mode 100644 index 00000000000000..91b629f1676039 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/status.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_STATUS_H +#define GRPCPP_SUPPORT_STATUS_H + +#include + +#endif // GRPCPP_SUPPORT_STATUS_H diff --git a/third_party/grpc/include/grpcpp/support/status_code_enum.h b/third_party/grpc/include/grpcpp/support/status_code_enum.h new file mode 100644 index 00000000000000..bfb47f39c0a55c --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/status_code_enum.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_STATUS_CODE_ENUM_H +#define GRPCPP_SUPPORT_STATUS_CODE_ENUM_H + +#include + +#endif // GRPCPP_SUPPORT_STATUS_CODE_ENUM_H diff --git a/third_party/grpc/include/grpcpp/support/string_ref.h b/third_party/grpc/include/grpcpp/support/string_ref.h new file mode 100644 index 00000000000000..0e0d3d4389e171 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/string_ref.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_STRING_REF_H +#define GRPCPP_SUPPORT_STRING_REF_H + +#include + +#endif // GRPCPP_SUPPORT_STRING_REF_H diff --git a/third_party/grpc/include/grpcpp/support/stub_options.h b/third_party/grpc/include/grpcpp/support/stub_options.h new file mode 100644 index 00000000000000..e9700ea4928127 --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/stub_options.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_STUB_OPTIONS_H +#define GRPCPP_SUPPORT_STUB_OPTIONS_H + +#include + +#endif // GRPCPP_SUPPORT_STUB_OPTIONS_H diff --git a/third_party/grpc/include/grpcpp/support/sync_stream.h b/third_party/grpc/include/grpcpp/support/sync_stream.h new file mode 100644 index 00000000000000..ea60b6da6b9e7b --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/sync_stream.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_SYNC_STREAM_H +#define GRPCPP_SUPPORT_SYNC_STREAM_H + +#include + +#endif // GRPCPP_SUPPORT_SYNC_STREAM_H diff --git a/third_party/grpc/include/grpcpp/support/time.h b/third_party/grpc/include/grpcpp/support/time.h new file mode 100644 index 00000000000000..c7408ff27d199d --- /dev/null +++ b/third_party/grpc/include/grpcpp/support/time.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_SUPPORT_TIME_H +#define GRPCPP_SUPPORT_TIME_H + +#include + +#endif // GRPCPP_SUPPORT_TIME_H diff --git a/third_party/grpc/include/grpcpp/test/mock_stream.h b/third_party/grpc/include/grpcpp/test/mock_stream.h new file mode 100644 index 00000000000000..93963f7dbb71d8 --- /dev/null +++ b/third_party/grpc/include/grpcpp/test/mock_stream.h @@ -0,0 +1,148 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_TEST_MOCK_STREAM_H +#define GRPCPP_TEST_MOCK_STREAM_H + +#include + +#include +#include +#include +#include +#include + +namespace grpc { +namespace testing { + +template +class MockClientReader : public ClientReaderInterface { + public: + MockClientReader() = default; + + /// ClientStreamingInterface + MOCK_METHOD0_T(Finish, Status()); + + /// ReaderInterface + MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*)); + MOCK_METHOD1_T(Read, bool(R*)); + + /// ClientReaderInterface + MOCK_METHOD0_T(WaitForInitialMetadata, void()); +}; + +template +class MockClientWriter : public ClientWriterInterface { + public: + MockClientWriter() = default; + + /// ClientStreamingInterface + MOCK_METHOD0_T(Finish, Status()); + + /// WriterInterface + MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions)); + + /// ClientWriterInterface + MOCK_METHOD0_T(WritesDone, bool()); +}; + +template +class MockClientReaderWriter : public ClientReaderWriterInterface { + public: + MockClientReaderWriter() = default; + + /// ClientStreamingInterface + MOCK_METHOD0_T(Finish, Status()); + + /// ReaderInterface + MOCK_METHOD1_T(NextMessageSize, bool(uint32_t*)); + MOCK_METHOD1_T(Read, bool(R*)); + + /// WriterInterface + MOCK_METHOD2_T(Write, bool(const W&, const WriteOptions)); + + /// ClientReaderWriterInterface + MOCK_METHOD0_T(WaitForInitialMetadata, void()); + MOCK_METHOD0_T(WritesDone, bool()); +}; + +/// TODO: We do not support mocking an async RPC for now. + +template +class MockClientAsyncResponseReader + : public ClientAsyncResponseReaderInterface { + public: + MockClientAsyncResponseReader() = default; + + MOCK_METHOD1_T(ReadInitialMetadata, void(void*)); + MOCK_METHOD3_T(Finish, void(R*, Status*, void*)); +}; + +template +class MockClientAsyncReader : public ClientAsyncReaderInterface { + public: + MockClientAsyncReader() = default; + + /// ClientAsyncStreamingInterface + MOCK_METHOD1_T(ReadInitialMetadata, void(void*)); + MOCK_METHOD2_T(Finish, void(Status*, void*)); + + /// AsyncReaderInterface + MOCK_METHOD2_T(Read, void(R*, void*)); +}; + +template +class MockClientAsyncWriter : public ClientAsyncWriterInterface { + public: + MockClientAsyncWriter() = default; + + /// ClientAsyncStreamingInterface + MOCK_METHOD1_T(ReadInitialMetadata, void(void*)); + MOCK_METHOD2_T(Finish, void(Status*, void*)); + + /// AsyncWriterInterface + MOCK_METHOD2_T(Write, void(const W&, void*)); + + /// ClientAsyncWriterInterface + MOCK_METHOD1_T(WritesDone, void(void*)); +}; + +template +class MockClientAsyncReaderWriter + : public ClientAsyncReaderWriterInterface { + public: + MockClientAsyncReaderWriter() = default; + + /// ClientAsyncStreamingInterface + MOCK_METHOD1_T(ReadInitialMetadata, void(void*)); + MOCK_METHOD2_T(Finish, void(Status*, void*)); + + /// AsyncWriterInterface + MOCK_METHOD2_T(Write, void(const W&, void*)); + + /// AsyncReaderInterface + MOCK_METHOD2_T(Read, void(R*, void*)); + + /// ClientAsyncReaderWriterInterface + MOCK_METHOD1_T(WritesDone, void(void*)); +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPCPP_TEST_MOCK_STREAM_H diff --git a/third_party/grpc/include/grpcpp/test/server_context_test_spouse.h b/third_party/grpc/include/grpcpp/test/server_context_test_spouse.h new file mode 100644 index 00000000000000..f5ca552ea575da --- /dev/null +++ b/third_party/grpc/include/grpcpp/test/server_context_test_spouse.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H +#define GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H + +#include + +#include + +namespace grpc { +namespace testing { + +/// A test-only class to access private members and methods of ServerContext. +class ServerContextTestSpouse { + public: + explicit ServerContextTestSpouse(ServerContext* ctx) : ctx_(ctx) {} + + /// Inject client metadata to the ServerContext for the test. The test spouse + /// must be alive when \a ServerContext::client_metadata is called. + void AddClientMetadata(const grpc::string& key, const grpc::string& value) { + client_metadata_storage_.insert( + std::pair(key, value)); + ctx_->client_metadata_.map()->clear(); + for (auto iter = client_metadata_storage_.begin(); + iter != client_metadata_storage_.end(); ++iter) { + ctx_->client_metadata_.map()->insert( + std::pair( + iter->first.c_str(), + grpc::string_ref(iter->second.data(), iter->second.size()))); + } + } + + std::multimap GetInitialMetadata() const { + return ctx_->initial_metadata_; + } + + std::multimap GetTrailingMetadata() const { + return ctx_->trailing_metadata_; + } + + private: + ServerContext* ctx_; // not owned + std::multimap client_metadata_storage_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPCPP_TEST_SERVER_CONTEXT_TEST_SPOUSE_H diff --git a/third_party/grpc/src/compiler/README.md b/third_party/grpc/src/compiler/README.md new file mode 100644 index 00000000000000..d5684af7ff24e9 --- /dev/null +++ b/third_party/grpc/src/compiler/README.md @@ -0,0 +1,4 @@ +# Overview + +This directory contains source code for gRPC protocol buffer compiler (*protoc*) plugins. Along with `protoc`, +these plugins are used to generate gRPC client and server stubs from `.proto` files. diff --git a/third_party/grpc/src/compiler/config.h b/third_party/grpc/src/compiler/config.h index fea976c3181840..cfdc3036247a0d 100644 --- a/third_party/grpc/src/compiler/config.h +++ b/third_party/grpc/src/compiler/config.h @@ -1,50 +1,25 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #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 +#include #ifndef GRPC_CUSTOM_CODEGENERATOR #include @@ -68,12 +43,23 @@ #define GRPC_CUSTOM_PLUGINMAIN ::google::protobuf::compiler::PluginMain #endif +#ifndef GRPC_CUSTOM_PARSEGENERATORPARAMETER +#include +#define GRPC_CUSTOM_PARSEGENERATORPARAMETER \ + ::google::protobuf::compiler::ParseGeneratorParameter +#endif + +#ifndef GRPC_CUSTOM_STRING +#include +#define GRPC_CUSTOM_STRING std::string +#endif + namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + 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; @@ -81,6 +67,11 @@ static inline int PluginMain(int argc, char* argv[], const CodeGenerator* generator) { return GRPC_CUSTOM_PLUGINMAIN(argc, argv, generator); } +static inline void ParseGeneratorParameter( + const string& parameter, std::vector >* options) { + GRPC_CUSTOM_PARSEGENERATORPARAMETER(parameter, options); +} + } // namespace compiler namespace io { typedef GRPC_CUSTOM_PRINTER Printer; @@ -90,4 +81,11 @@ typedef GRPC_CUSTOM_STRINGOUTPUTSTREAM StringOutputStream; } // namespace protobuf } // namespace grpc +namespace grpc_cpp_generator { + +static const char* const kCppGeneratorMessageHeaderExt = ".pb.h"; +static const char* const kCppGeneratorServiceHeaderExt = ".grpc.pb.h"; + +} // namespace grpc_cpp_generator + #endif // SRC_COMPILER_CONFIG_H diff --git a/third_party/grpc/src/compiler/cpp_generator.cc b/third_party/grpc/src/compiler/cpp_generator.cc index d3d6224de29429..b004687250272e 100644 --- a/third_party/grpc/src/compiler/cpp_generator.cc +++ b/third_party/grpc/src/compiler/cpp_generator.cc @@ -1,42 +1,24 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include #include "src/compiler/cpp_generator.h" -#include "src/compiler/cpp_generator_helpers.h" - -#include "src/compiler/config.h" #include @@ -50,23 +32,15 @@ grpc::string as_string(T 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(); +inline bool ClientOnlyStreaming(const grpc_generator::Method* method) { + return method->ClientStreaming() && !method->ServerStreaming(); } -bool ServerOnlyStreaming(const grpc::protobuf::MethodDescriptor *method) { - return !method->client_streaming() && method->server_streaming(); +inline bool ServerOnlyStreaming(const grpc_generator::Method* method) { + return !method->ClientStreaming() && method->ServerStreaming(); } -bool BidiStreaming(const grpc::protobuf::MethodDescriptor *method) { - return method->client_streaming() && method->server_streaming(); -} - -grpc::string FilenameIdentifier(const grpc::string &filename) { +grpc::string FilenameIdentifier(const grpc::string& filename) { grpc::string result; for (unsigned i = 0; i < filename.size(); i++) { char c = filename[i]; @@ -83,95 +57,152 @@ grpc::string FilenameIdentifier(const grpc::string &filename) { } } // namespace -grpc::string GetHeaderPrologue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms) { +template +T* array_end(T (&array)[N]) { + return array + N; +} + +void PrintIncludes(grpc_generator::Printer* printer, + const std::vector& headers, + bool use_system_headers, const grpc::string& search_path) { + std::map vars; + + vars["l"] = use_system_headers ? '<' : '"'; + vars["r"] = use_system_headers ? '>' : '"'; + + if (!search_path.empty()) { + vars["l"] += search_path; + if (search_path[search_path.size() - 1] != '/') { + vars["l"] += '/'; + } + } + + for (auto i = headers.begin(); i != headers.end(); i++) { + vars["h"] = *i; + printer->Print(vars, "#include $l$$h$$r$\n"); + } +} + +grpc::string GetHeaderPrologue(grpc_generator::File* file, + const Parameters& /*params*/) { 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, '$'); + auto printer = file->CreatePrinter(&output); 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"); + vars["filename"] = file->filename(); + vars["filename_identifier"] = FilenameIdentifier(file->filename()); + vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = kCppGeneratorMessageHeaderExt; + + printer->Print(vars, "// Generated by the gRPC C++ plugin.\n"); + printer->Print(vars, + "// If you make any local change, they will be lost.\n"); + printer->Print(vars, "// source: $filename$\n"); + grpc::string leading_comments = file->GetLeadingComments("//"); + if (!leading_comments.empty()) { + printer->Print(vars, "// Original file comments:\n"); + printer->PrintRaw(leading_comments.c_str()); + } + 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$$message_header_ext$\"\n"); + printer->Print(vars, file->additional_headers().c_str()); + 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 RpcService;\n" - "class ServerCompletionQueue;\n" - "class ServerContext;\n" - "} // namespace grpc\n\n"; +grpc::string GetHeaderIncludes(grpc_generator::File* file, + const Parameters& params) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; - if (!file->package().empty()) { - std::vector parts = - grpc_generator::tokenize(file->package(), "."); + if (!params.additional_header_includes.empty()) { + PrintIncludes(printer.get(), params.additional_header_includes, false, + ""); + } + static const char* headers_strs[] = { + "functional", + "grpcpp/impl/codegen/async_generic_service.h", + "grpcpp/impl/codegen/async_stream.h", + "grpcpp/impl/codegen/async_unary_call.h", + "grpcpp/impl/codegen/client_callback.h", + "grpcpp/impl/codegen/method_handler_impl.h", + "grpcpp/impl/codegen/proto_utils.h", + "grpcpp/impl/codegen/rpc_method.h", + "grpcpp/impl/codegen/server_callback.h", + "grpcpp/impl/codegen/service_type.h", + "grpcpp/impl/codegen/status.h", + "grpcpp/impl/codegen/stub_options.h", + "grpcpp/impl/codegen/sync_stream.h"}; + std::vector headers(headers_strs, array_end(headers_strs)); + PrintIncludes(printer.get(), headers, params.use_system_headers, + params.grpc_search_path); + printer->Print(vars, "\n"); + printer->Print(vars, "namespace grpc {\n"); + printer->Print(vars, "class CompletionQueue;\n"); + printer->Print(vars, "class Channel;\n"); + printer->Print(vars, "class ServerCompletionQueue;\n"); + printer->Print(vars, "class ServerContext;\n"); + printer->Print(vars, "} // namespace grpc\n\n"); - for (auto part = parts.begin(); part != parts.end(); part++) { - temp.append("namespace "); - temp.append(*part); - temp.append(" {\n"); + if (!file->package().empty()) { + std::vector parts = file->package_parts(); + + for (auto part = parts.begin(); part != parts.end(); part++) { + vars["part"] = *part; + printer->Print(vars, "namespace $part$ {\n"); + } + printer->Print(vars, "\n"); } - temp.append("\n"); } - - return temp; + return output; } void PrintHeaderClientMethodInterfaces( - grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map *vars, bool is_public) { + grpc_generator::Printer* printer, const grpc_generator::Method* 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); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + struct { + grpc::string prefix; + grpc::string method_params; // extra arguments to method + grpc::string raw_args; // extra arguments to raw version of method + } async_prefixes[] = {{"Async", ", void* tag", ", tag"}, + {"PrepareAsync", "", ""}}; if (is_public) { - if (NoStreaming(method)) { + if (method->NoStreaming()) { 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"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + printer->Print( + *vars, + "std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>> " + "$AsyncPrefix$$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>>(" + "$AsyncPrefix$$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } } else if (ClientOnlyStreaming(method)) { printer->Print( *vars, @@ -185,19 +216,26 @@ void PrintHeaderClientMethodInterfaces( "($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"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncWriterInterface< $Request$>>" + " $AsyncPrefix$$Method$(::grpc::ClientContext* context, " + "$Response$* " + "response, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncWriterInterface< $Request$>>(" + "$AsyncPrefix$$Method$Raw(context, response, " + "cq$AsyncRawArgs$));\n"); + printer->Outdent(); + printer->Print("}\n"); + } } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, @@ -211,20 +249,26 @@ void PrintHeaderClientMethodInterfaces( "($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)) { + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderInterface< $Response$>> " + "$AsyncPrefix$$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderInterface< $Response$>>(" + "$AsyncPrefix$$Method$Raw(context, request, cq$AsyncRawArgs$));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else if (method->BidiStreaming()) { printer->Print(*vars, "std::unique_ptr< ::grpc::ClientReaderWriterInterface< " "$Request$, $Response$>> " @@ -237,93 +281,124 @@ void PrintHeaderClientMethodInterfaces( "$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"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print( + *vars, + "std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>> " + "$AsyncPrefix$$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriterInterface< $Request$, $Response$>>(" + "$AsyncPrefix$$Method$Raw(context, cq$AsyncRawArgs$));\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"); + if (method->NoStreaming()) { + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncResponseReaderInterface< $Response$>* " + "$AsyncPrefix$$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"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncWriterInterface< $Request$>*" + " $AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) = 0;\n"); + } } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, - "virtual ::grpc::ClientReaderInterface< $Response$>* $Method$Raw(" + "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)) { + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncReaderInterface< $Response$>* " + "$AsyncPrefix$$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) = 0;\n"); + } + } else if (method->BidiStreaming()) { 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"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + printer->Print( + *vars, + "virtual ::grpc::ClientAsyncReaderWriterInterface< " + "$Request$, $Response$>* " + "$AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) = 0;\n"); + } } } } -void PrintHeaderClientMethod(grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map *vars, +void PrintHeaderClientMethod(grpc_generator::Printer* printer, + const grpc_generator::Method* 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); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + struct { + grpc::string prefix; + grpc::string method_params; // extra arguments to method + grpc::string raw_args; // extra arguments to raw version of method + } async_prefixes[] = {{"Async", ", void* tag", ", tag"}, + {"PrepareAsync", "", ""}}; + if (is_public) { - if (NoStreaming(method)) { + if (method->NoStreaming()) { 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"); + "const $Request$& request, $Response$* response) override;\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncResponseReader< $Response$>> " + "$AsyncPrefix$$Method$(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Indent(); + printer->Print(*vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncResponseReader< $Response$>>(" + "$AsyncPrefix$$Method$Raw(context, request, cq));\n"); + printer->Outdent(); + printer->Print("}\n"); + } } else if (ClientOnlyStreaming(method)) { printer->Print( *vars, @@ -336,18 +411,24 @@ void PrintHeaderClientMethod(grpc::protobuf::io::Printer *printer, "($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"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>" + " $AsyncPrefix$$Method$(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncWriter< $Request$>>(" + "$AsyncPrefix$$Method$Raw(context, response, " + "cq$AsyncRawArgs$));\n"); + printer->Outdent(); + printer->Print("}\n"); + } } else if (ServerOnlyStreaming(method)) { printer->Print( *vars, @@ -361,20 +442,25 @@ void PrintHeaderClientMethod(grpc::protobuf::io::Printer *printer, "($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)) { + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print( + *vars, + "std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>> " + "$AsyncPrefix$$Method$(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< ::grpc::ClientAsyncReader< $Response$>>(" + "$AsyncPrefix$$Method$Raw(context, request, cq$AsyncRawArgs$));\n"); + printer->Outdent(); + printer->Print("}\n"); + } + } else if (method->BidiStreaming()) { printer->Print( *vars, "std::unique_ptr< ::grpc::ClientReaderWriter< $Request$, $Response$>>" @@ -386,76 +472,233 @@ void PrintHeaderClientMethod(grpc::protobuf::io::Printer *printer, "$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"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print(*vars, + "std::unique_ptr< ::grpc::ClientAsyncReaderWriter< " + "$Request$, $Response$>> " + "$AsyncPrefix$$Method$(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Indent(); + printer->Print( + *vars, + "return std::unique_ptr< " + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>>(" + "$AsyncPrefix$$Method$Raw(context, cq$AsyncRawArgs$));\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"); + if (method->NoStreaming()) { + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + printer->Print( + *vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "$AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) 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"); + "override;\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print( + *vars, + "::grpc::ClientAsyncWriter< $Request$>* $AsyncPrefix$$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) 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"); + " override;\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print( + *vars, + "::grpc::ClientAsyncReader< $Response$>* $AsyncPrefix$$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) override;\n"); + } + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "::grpc::ClientReaderWriter< $Request$, $Response$>* " + "$Method$Raw(::grpc::ClientContext* context) override;\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncRawArgs"] = async_prefix.raw_args; + printer->Print( + *vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "$AsyncPrefix$$Method$Raw(::grpc::ClientContext* context, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) override;\n"); + } } } } -void PrintHeaderClientMethodData(grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map *vars) { +void PrintHeaderClientMethodCallbackInterfacesStart( + grpc_generator::Printer* printer, + std::map* vars) { + // This declares the interface for the callback-based API. The components + // are pure; even though this is new (post-1.0) API, it can be pure because + // it is an entirely new interface that happens to be scoped within + // StubInterface, not new additions to StubInterface itself + printer->Print("class experimental_async_interface {\n"); + // All methods in this new interface are public. There is no need for private + // "Raw" methods since the callback-based API returns unowned raw pointers + printer->Print(" public:\n"); + printer->Indent(); + printer->Print("virtual ~experimental_async_interface() {}\n"); +} + +void PrintHeaderClientMethodCallbackInterfaces( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars, bool is_public) { + // Reserve is_public for future expansion + assert(is_public); + + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + if (method->NoStreaming()) { + printer->Print(*vars, + "virtual void $Method$(::grpc::ClientContext* context, " + "const $Request$* request, $Response$* response, " + "std::function) = 0;\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "virtual void $Method$(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::experimental::ClientWriteReactor< $Request$>* " + "reactor) = 0;\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print(*vars, + "virtual void $Method$(::grpc::ClientContext* context, " + "$Request$* request, " + "::grpc::experimental::ClientReadReactor< $Response$>* " + "reactor) = 0;\n"); + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "virtual void $Method$(::grpc::ClientContext* context, " + "::grpc::experimental::ClientBidiReactor< " + "$Request$,$Response$>* reactor) = 0;\n"); + } +} + +void PrintHeaderClientMethodCallbackInterfacesEnd( + grpc_generator::Printer* printer, + std::map* vars) { + printer->Outdent(); + printer->Print("};\n"); + + // Declare a function to give the async stub contents. It can't be pure + // since this is a new API in StubInterface, but it is meaningless by default + // (since any stub that wants to use it must have its own implementation of + // the callback functions therein), so make the default return value nullptr. + // Intentionally include the word "class" to avoid possible shadowing. + printer->Print( + "virtual class experimental_async_interface* experimental_async() { " + "return nullptr; }\n"); +} + +void PrintHeaderClientMethodCallbackStart( + grpc_generator::Printer* printer, + std::map* vars) { + // This declares the stub entry for the callback-based API. + printer->Print("class experimental_async final :\n"); + printer->Print(" public StubInterface::experimental_async_interface {\n"); + printer->Print(" public:\n"); + printer->Indent(); +} + +void PrintHeaderClientMethodCallback(grpc_generator::Printer* printer, + const grpc_generator::Method* method, + std::map* vars, + bool is_public) { + // Reserve is_public for future expansion + assert(is_public); + + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + if (method->NoStreaming()) { + printer->Print(*vars, + "void $Method$(::grpc::ClientContext* context, " + "const $Request$* request, $Response$* response, " + "std::function) override;\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print(*vars, + "void $Method$(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::experimental::ClientWriteReactor< $Request$>* " + "reactor) override;\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print(*vars, + "void $Method$(::grpc::ClientContext* context, " + "$Request$* request, " + "::grpc::experimental::ClientReadReactor< $Response$>* " + "reactor) override;\n"); + + } else if (method->BidiStreaming()) { + printer->Print(*vars, + "void $Method$(::grpc::ClientContext* context, " + "::grpc::experimental::ClientBidiReactor< " + "$Request$,$Response$>* reactor) override;\n"); + } +} + +void PrintHeaderClientMethodCallbackEnd( + grpc_generator::Printer* printer, + std::map* vars) { + printer->Outdent(); + printer->Print(" private:\n"); + printer->Indent(); + printer->Print("friend class Stub;\n"); + printer->Print("explicit experimental_async(Stub* stub): stub_(stub) { }\n"); + // include a function with a dummy use of stub_ to avoid an unused + // private member warning for service with no methods + printer->Print("Stub* stub() { return stub_; }\n"); + printer->Print("Stub* stub_;\n"); + printer->Outdent(); + printer->Print("};\n"); + + printer->Print( + "class experimental_async_interface* experimental_async() override { " + "return &async_stub_; }\n"); +} + +void PrintHeaderClientMethodData(grpc_generator::Printer* printer, + const grpc_generator::Method* method, + std::map* vars) { (*vars)["Method"] = method->name(); - printer->Print(*vars, "const ::grpc::RpcMethod rpcmethod_$Method$_;\n"); + printer->Print(*vars, + "const ::grpc::internal::RpcMethod rpcmethod_$Method$_;\n"); } -void PrintHeaderServerMethodSync(grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map *vars) { +void PrintHeaderServerMethodSync(grpc_generator::Printer* printer, + const grpc_generator::Method* 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)) { + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + printer->Print(method->GetLeadingComments("//").c_str()); + if (method->NoStreaming()) { printer->Print(*vars, "virtual ::grpc::Status $Method$(" "::grpc::ServerContext* context, const $Request$* request, " @@ -471,7 +714,7 @@ void PrintHeaderServerMethodSync(grpc::protobuf::io::Printer *printer, "virtual ::grpc::Status $Method$(" "::grpc::ServerContext* context, const $Request$* request, " "::grpc::ServerWriter< $Response$>* writer);\n"); - } else if (BidiStreaming(method)) { + } else if (method->BidiStreaming()) { printer->Print( *vars, "virtual ::grpc::Status $Method$(" @@ -479,48 +722,30 @@ void PrintHeaderServerMethodSync(grpc::protobuf::io::Printer *printer, "::grpc::ServerReaderWriter< $Response$, $Request$>* stream);" "\n"); } + printer->Print(method->GetTrailingComments("//").c_str()); } -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); - printer->Print(*vars, "template \n"); - printer->Print(*vars, - "class WithAsyncMethod_$Method$ : public BaseClass {\n"); - printer->Print( - " private:\n" - " void BaseClassMustBeDerivedFromService(Service *service) {}\n"); - printer->Print(" public:\n"); - printer->Indent(); - printer->Print(*vars, - "WithAsyncMethod_$Method$() {\n" - " ::grpc::Service::MarkMethodAsync($Idx$);\n" - "}\n"); - printer->Print(*vars, - "~WithAsyncMethod_$Method$() GRPC_OVERRIDE {\n" - " BaseClassMustBeDerivedFromService(this);\n" - "}\n"); - if (NoStreaming(method)) { +// Helper generator. Disables the sync API for Request and Response, then adds +// in an async API for RealRequest and RealResponse types. This is to be used +// to generate async and raw async APIs. +void PrintHeaderServerAsyncMethodsHelper( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars) { + if (method->NoStreaming()) { printer->Print( *vars, "// disable synchronous version of this method\n" "::grpc::Status $Method$(" "::grpc::ServerContext* context, const $Request$* request, " - "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + "$Response$* response) override {\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); printer->Print( *vars, "void Request$Method$(" - "::grpc::ServerContext* context, $Request$* request, " - "::grpc::ServerAsyncResponseWriter< $Response$>* response, " + "::grpc::ServerContext* context, $RealRequest$* request, " + "::grpc::ServerAsyncResponseWriter< $RealResponse$>* response, " "::grpc::CompletionQueue* new_call_cq, " "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); printer->Print(*vars, @@ -534,7 +759,7 @@ void PrintHeaderServerMethodAsync( "::grpc::Status $Method$(" "::grpc::ServerContext* context, " "::grpc::ServerReader< $Request$>* reader, " - "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + "$Response$* response) override {\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); @@ -542,7 +767,7 @@ void PrintHeaderServerMethodAsync( *vars, "void Request$Method$(" "::grpc::ServerContext* context, " - "::grpc::ServerAsyncReader< $Response$, $Request$>* reader, " + "::grpc::ServerAsyncReader< $RealResponse$, $RealRequest$>* reader, " "::grpc::CompletionQueue* new_call_cq, " "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); printer->Print(*vars, @@ -555,7 +780,7 @@ void PrintHeaderServerMethodAsync( "// disable synchronous version of this method\n" "::grpc::Status $Method$(" "::grpc::ServerContext* context, const $Request$* request, " - "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "::grpc::ServerWriter< $Response$>* writer) override " "{\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" @@ -563,8 +788,8 @@ void PrintHeaderServerMethodAsync( printer->Print( *vars, "void Request$Method$(" - "::grpc::ServerContext* context, $Request$* request, " - "::grpc::ServerAsyncWriter< $Response$>* writer, " + "::grpc::ServerContext* context, $RealRequest$* request, " + "::grpc::ServerAsyncWriter< $RealResponse$>* writer, " "::grpc::CompletionQueue* new_call_cq, " "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); printer->Print( @@ -572,14 +797,14 @@ void PrintHeaderServerMethodAsync( " ::grpc::Service::RequestAsyncServerStreaming($Idx$, " "context, request, writer, new_call_cq, notification_cq, tag);\n"); printer->Print("}\n"); - } else if (BidiStreaming(method)) { + } else if (method->BidiStreaming()) { printer->Print( *vars, "// disable synchronous version of this method\n" "::grpc::Status $Method$(" "::grpc::ServerContext* context, " "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " - "GRPC_FINAL GRPC_OVERRIDE {\n" + " override {\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); @@ -587,7 +812,8 @@ void PrintHeaderServerMethodAsync( *vars, "void Request$Method$(" "::grpc::ServerContext* context, " - "::grpc::ServerAsyncReaderWriter< $Response$, $Request$>* stream, " + "::grpc::ServerAsyncReaderWriter< $RealResponse$, $RealRequest$>* " + "stream, " "::grpc::CompletionQueue* new_call_cq, " "::grpc::ServerCompletionQueue* notification_cq, void *tag) {\n"); printer->Print(*vars, @@ -595,25 +821,362 @@ void PrintHeaderServerMethodAsync( "context, stream, new_call_cq, notification_cq, tag);\n"); printer->Print("}\n"); } +} + +void PrintHeaderServerMethodAsync(grpc_generator::Printer* printer, + const grpc_generator::Method* method, + std::map* vars) { + (*vars)["Method"] = method->name(); + // These will be disabled + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + // These will be used for the async API + (*vars)["RealRequest"] = method->input_type_name(); + (*vars)["RealResponse"] = method->output_type_name(); + printer->Print(*vars, "template \n"); + printer->Print(*vars, + "class WithAsyncMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithAsyncMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodAsync($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithAsyncMethod_$Method$() override {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + PrintHeaderServerAsyncMethodsHelper(printer, method, vars); printer->Outdent(); printer->Print(*vars, "};\n"); } +// Helper generator. Disables the sync API for Request and Response, then adds +// in a callback API for RealRequest and RealResponse types. This is to be used +// to generate callback and raw callback APIs. +void PrintHeaderServerCallbackMethodsHelper( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars) { + if (method->NoStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) override {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print( + *vars, + "virtual void $Method$(" + "::grpc::ServerContext* context, const $RealRequest$* request, " + "$RealResponse$* response, " + "::grpc::experimental::ServerCallbackRpcController* " + "controller) { controller->Finish(::grpc::Status(" + "::grpc::StatusCode::UNIMPLEMENTED, \"\")); }\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReader< $Request$>* reader, " + "$Response$* response) override {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print(*vars, + "virtual ::grpc::experimental::ServerReadReactor< " + "$RealRequest$, $RealResponse$>* $Method$() {\n" + " return new ::grpc::internal::UnimplementedReadReactor<\n" + " $RealRequest$, $RealResponse$>;}\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) override " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print(*vars, + "virtual ::grpc::experimental::ServerWriteReactor< " + "$RealRequest$, $RealResponse$>* $Method$() {\n" + " return new ::grpc::internal::UnimplementedWriteReactor<\n" + " $RealRequest$, $RealResponse$>;}\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "// disable synchronous version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " + " override {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print(*vars, + "virtual ::grpc::experimental::ServerBidiReactor< " + "$RealRequest$, $RealResponse$>* $Method$() {\n" + " return new ::grpc::internal::UnimplementedBidiReactor<\n" + " $RealRequest$, $RealResponse$>;}\n"); + } +} + +void PrintHeaderServerMethodCallback( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars) { + (*vars)["Method"] = method->name(); + // These will be disabled + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + // These will be used for the callback API + (*vars)["RealRequest"] = method->input_type_name(); + (*vars)["RealResponse"] = method->output_type_name(); + printer->Print(*vars, "template \n"); + printer->Print( + *vars, + "class ExperimentalWithCallbackMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, "ExperimentalWithCallbackMethod_$Method$() {\n"); + if (method->NoStreaming()) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" + " new ::grpc::internal::CallbackUnaryHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this](::grpc::ServerContext* context,\n" + " const $RealRequest$* request,\n" + " $RealResponse$* response,\n" + " ::grpc::experimental::ServerCallbackRpcController* " + "controller) {\n" + " return this->$" + "Method$(context, request, response, controller);\n" + " }));\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" + " new ::grpc::internal::CallbackClientStreamingHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this] { return this->$Method$(); }));\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" + " new ::grpc::internal::CallbackServerStreamingHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this] { return this->$Method$(); }));\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodCallback($Idx$,\n" + " new ::grpc::internal::CallbackBidiHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this] { return this->$Method$(); }));\n"); + } + printer->Print(*vars, "}\n"); + printer->Print(*vars, + "~ExperimentalWithCallbackMethod_$Method$() override {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + PrintHeaderServerCallbackMethodsHelper(printer, method, vars); + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderServerMethodRawCallback( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars) { + (*vars)["Method"] = method->name(); + // These will be disabled + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + // These will be used for raw API + (*vars)["RealRequest"] = "::grpc::ByteBuffer"; + (*vars)["RealResponse"] = "::grpc::ByteBuffer"; + printer->Print(*vars, "template \n"); + printer->Print(*vars, + "class ExperimentalWithRawCallbackMethod_$Method$ : public " + "BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, "ExperimentalWithRawCallbackMethod_$Method$() {\n"); + if (method->NoStreaming()) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" + " new ::grpc::internal::CallbackUnaryHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this](::grpc::ServerContext* context,\n" + " const $RealRequest$* request,\n" + " $RealResponse$* response,\n" + " ::grpc::experimental::ServerCallbackRpcController* " + "controller) {\n" + " this->$" + "Method$(context, request, response, controller);\n" + " }));\n"); + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" + " new ::grpc::internal::CallbackClientStreamingHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this] { return this->$Method$(); }));\n"); + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" + " new ::grpc::internal::CallbackServerStreamingHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this] { return this->$Method$(); }));\n"); + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + " ::grpc::Service::experimental().MarkMethodRawCallback($Idx$,\n" + " new ::grpc::internal::CallbackBidiHandler< " + "$RealRequest$, $RealResponse$>(\n" + " [this] { return this->$Method$(); }));\n"); + } + printer->Print(*vars, "}\n"); + printer->Print(*vars, + "~ExperimentalWithRawCallbackMethod_$Method$() override {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + PrintHeaderServerCallbackMethodsHelper(printer, method, vars); + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderServerMethodStreamedUnary( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { + printer->Print(*vars, "template \n"); + printer->Print(*vars, + "class WithStreamedUnaryMethod_$Method$ : " + "public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) " + "{}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithStreamedUnaryMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodStreamed($Idx$,\n" + " new ::grpc::internal::StreamedUnaryHandler< $Request$, " + "$Response$>(std::bind" + "(&WithStreamedUnaryMethod_$Method$::" + "Streamed$Method$, this, std::placeholders::_1, " + "std::placeholders::_2)));\n" + "}\n"); + printer->Print(*vars, + "~WithStreamedUnaryMethod_$Method$() override {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + printer->Print( + *vars, + "// disable regular version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "$Response$* response) override {\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print(*vars, + "// replace default version of method with streamed unary\n" + "virtual ::grpc::Status Streamed$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerUnaryStreamer< " + "$Request$,$Response$>* server_unary_streamer)" + " = 0;\n"); + printer->Outdent(); + printer->Print(*vars, "};\n"); + } +} + +void PrintHeaderServerMethodSplitStreaming( + grpc_generator::Printer* printer, const grpc_generator::Method* method, + std::map* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (ServerOnlyStreaming(method)) { + printer->Print(*vars, "template \n"); + printer->Print(*vars, + "class WithSplitStreamingMethod_$Method$ : " + "public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) " + "{}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print( + *vars, + "WithSplitStreamingMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodStreamed($Idx$,\n" + " new ::grpc::internal::SplitServerStreamingHandler< $Request$, " + "$Response$>(std::bind" + "(&WithSplitStreamingMethod_$Method$::" + "Streamed$Method$, this, std::placeholders::_1, " + "std::placeholders::_2)));\n" + "}\n"); + printer->Print(*vars, + "~WithSplitStreamingMethod_$Method$() override {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + printer->Print( + *vars, + "// disable regular version of this method\n" + "::grpc::Status $Method$(" + "::grpc::ServerContext* context, const $Request$* request, " + "::grpc::ServerWriter< $Response$>* writer) override " + "{\n" + " abort();\n" + " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" + "}\n"); + printer->Print(*vars, + "// replace default version of method with split streamed\n" + "virtual ::grpc::Status Streamed$Method$(" + "::grpc::ServerContext* context, " + "::grpc::ServerSplitStreamer< " + "$Request$,$Response$>* server_split_streamer)" + " = 0;\n"); + printer->Outdent(); + printer->Print(*vars, "};\n"); + } +} + void PrintHeaderServerMethodGeneric( - grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map *vars) { + grpc_generator::Printer* printer, const grpc_generator::Method* 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); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); printer->Print(*vars, "template \n"); printer->Print(*vars, "class WithGenericMethod_$Method$ : public BaseClass {\n"); printer->Print( " private:\n" - " void BaseClassMustBeDerivedFromService(Service *service) {}\n"); + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); printer->Print(" public:\n"); printer->Indent(); printer->Print(*vars, @@ -621,16 +1184,16 @@ void PrintHeaderServerMethodGeneric( " ::grpc::Service::MarkMethodGeneric($Idx$);\n" "}\n"); printer->Print(*vars, - "~WithGenericMethod_$Method$() GRPC_OVERRIDE {\n" + "~WithGenericMethod_$Method$() override {\n" " BaseClassMustBeDerivedFromService(this);\n" "}\n"); - if (NoStreaming(method)) { + if (method->NoStreaming()) { printer->Print( *vars, "// disable synchronous version of this method\n" "::grpc::Status $Method$(" "::grpc::ServerContext* context, const $Request$* request, " - "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + "$Response$* response) override {\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); @@ -641,7 +1204,7 @@ void PrintHeaderServerMethodGeneric( "::grpc::Status $Method$(" "::grpc::ServerContext* context, " "::grpc::ServerReader< $Request$>* reader, " - "$Response$* response) GRPC_FINAL GRPC_OVERRIDE {\n" + "$Response$* response) override {\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); @@ -651,19 +1214,19 @@ void PrintHeaderServerMethodGeneric( "// disable synchronous version of this method\n" "::grpc::Status $Method$(" "::grpc::ServerContext* context, const $Request$* request, " - "::grpc::ServerWriter< $Response$>* writer) GRPC_FINAL GRPC_OVERRIDE " + "::grpc::ServerWriter< $Response$>* writer) override " "{\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); - } else if (BidiStreaming(method)) { + } else if (method->BidiStreaming()) { printer->Print( *vars, "// disable synchronous version of this method\n" "::grpc::Status $Method$(" "::grpc::ServerContext* context, " "::grpc::ServerReaderWriter< $Response$, $Request$>* stream) " - "GRPC_FINAL GRPC_OVERRIDE {\n" + " override {\n" " abort();\n" " return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, \"\");\n" "}\n"); @@ -672,16 +1235,53 @@ void PrintHeaderServerMethodGeneric( printer->Print(*vars, "};\n"); } -void PrintHeaderService(grpc::protobuf::io::Printer *printer, - const grpc::protobuf::ServiceDescriptor *service, - std::map *vars) { +void PrintHeaderServerMethodRaw(grpc_generator::Printer* printer, + const grpc_generator::Method* method, + std::map* vars) { + (*vars)["Method"] = method->name(); + // These will be disabled + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + // These will be used for raw API + (*vars)["RealRequest"] = "::grpc::ByteBuffer"; + (*vars)["RealResponse"] = "::grpc::ByteBuffer"; + printer->Print(*vars, "template \n"); + printer->Print(*vars, "class WithRawMethod_$Method$ : public BaseClass {\n"); + printer->Print( + " private:\n" + " void BaseClassMustBeDerivedFromService(const Service *service) {}\n"); + printer->Print(" public:\n"); + printer->Indent(); + printer->Print(*vars, + "WithRawMethod_$Method$() {\n" + " ::grpc::Service::MarkMethodRaw($Idx$);\n" + "}\n"); + printer->Print(*vars, + "~WithRawMethod_$Method$() override {\n" + " BaseClassMustBeDerivedFromService(this);\n" + "}\n"); + PrintHeaderServerAsyncMethodsHelper(printer, method, vars); + printer->Outdent(); + printer->Print(*vars, "};\n"); +} + +void PrintHeaderService(grpc_generator::Printer* printer, + const grpc_generator::Service* service, + std::map* vars) { (*vars)["Service"] = service->name(); + printer->Print(service->GetLeadingComments("//").c_str()); printer->Print(*vars, - "class $Service$ GRPC_FINAL {\n" + "class $Service$ final {\n" " public:\n"); printer->Indent(); + // Service metadata + printer->Print(*vars, + "static constexpr char const* service_full_name() {\n" + " return \"$Package$$Service$\";\n" + "}\n"); + // Client side printer->Print( "class StubInterface {\n" @@ -689,33 +1289,54 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, printer->Indent(); printer->Print("virtual ~StubInterface() {}\n"); for (int i = 0; i < service->method_count(); ++i) { - PrintHeaderClientMethodInterfaces(printer, service->method(i), vars, true); + printer->Print(service->method(i)->GetLeadingComments("//").c_str()); + PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars, + true); + printer->Print(service->method(i)->GetTrailingComments("//").c_str()); + } + PrintHeaderClientMethodCallbackInterfacesStart(printer, vars); + for (int i = 0; i < service->method_count(); ++i) { + printer->Print(service->method(i)->GetLeadingComments("//").c_str()); + PrintHeaderClientMethodCallbackInterfaces(printer, service->method(i).get(), + vars, true); + printer->Print(service->method(i)->GetTrailingComments("//").c_str()); } + PrintHeaderClientMethodCallbackInterfacesEnd(printer, vars); printer->Outdent(); printer->Print("private:\n"); printer->Indent(); for (int i = 0; i < service->method_count(); ++i) { - PrintHeaderClientMethodInterfaces(printer, service->method(i), vars, false); + PrintHeaderClientMethodInterfaces(printer, service->method(i).get(), vars, + false); } printer->Outdent(); printer->Print("};\n"); printer->Print( - "class Stub GRPC_FINAL : public StubInterface" + "class Stub final : public StubInterface" " {\n public:\n"); printer->Indent(); - printer->Print("Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);\n"); + printer->Print( + "Stub(const std::shared_ptr< ::grpc::ChannelInterface>& " + "channel);\n"); + for (int i = 0; i < service->method_count(); ++i) { + PrintHeaderClientMethod(printer, service->method(i).get(), vars, true); + } + PrintHeaderClientMethodCallbackStart(printer, vars); for (int i = 0; i < service->method_count(); ++i) { - PrintHeaderClientMethod(printer, service->method(i), vars, true); + PrintHeaderClientMethodCallback(printer, service->method(i).get(), vars, + true); } + PrintHeaderClientMethodCallbackEnd(printer, vars); printer->Outdent(); printer->Print("\n private:\n"); printer->Indent(); printer->Print("std::shared_ptr< ::grpc::ChannelInterface> channel_;\n"); + printer->Print("class experimental_async async_stub_{this};\n"); for (int i = 0; i < service->method_count(); ++i) { - PrintHeaderClientMethod(printer, service->method(i), vars, false); + PrintHeaderClientMethod(printer, service->method(i).get(), vars, false); } for (int i = 0; i < service->method_count(); ++i) { - PrintHeaderClientMethodData(printer, service->method(i), vars); + PrintHeaderClientMethodData(printer, service->method(i).get(), vars); } printer->Outdent(); printer->Print("};\n"); @@ -734,7 +1355,7 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, printer->Print("Service();\n"); printer->Print("virtual ~Service();\n"); for (int i = 0; i < service->method_count(); ++i) { - PrintHeaderServerMethodSync(printer, service->method(i), vars); + PrintHeaderServerMethodSync(printer, service->method(i).get(), vars); } printer->Outdent(); printer->Print("};\n"); @@ -742,7 +1363,7 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, // Server side - Asynchronous for (int i = 0; i < service->method_count(); ++i) { (*vars)["Idx"] = as_string(i); - PrintHeaderServerMethodAsync(printer, service->method(i), vars); + PrintHeaderServerMethodAsync(printer, service->method(i).get(), vars); } printer->Print("typedef "); @@ -757,23 +1378,121 @@ void PrintHeaderService(grpc::protobuf::io::Printer *printer, } printer->Print(" AsyncService;\n"); + // Server side - Callback + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodCallback(printer, service->method(i).get(), vars); + } + + printer->Print("typedef "); + + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i)->name(); + printer->Print(*vars, "ExperimentalWithCallbackMethod_$method_name$<"); + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + printer->Print(" >"); + } + printer->Print(" ExperimentalCallbackService;\n"); + // Server side - Generic for (int i = 0; i < service->method_count(); ++i) { (*vars)["Idx"] = as_string(i); - PrintHeaderServerMethodGeneric(printer, service->method(i), vars); + PrintHeaderServerMethodGeneric(printer, service->method(i).get(), vars); + } + + // Server side - Raw + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodRaw(printer, service->method(i).get(), vars); + } + + // Server side - Raw Callback + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodRawCallback(printer, service->method(i).get(), vars); + } + + // Server side - Streamed Unary + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodStreamedUnary(printer, service->method(i).get(), + vars); + } + + printer->Print("typedef "); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i)->name(); + if (service->method(i)->NoStreaming()) { + printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<"); + } + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + if (service->method(i)->NoStreaming()) { + printer->Print(" >"); + } + } + printer->Print(" StreamedUnaryService;\n"); + + // Server side - controlled server-side streaming + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["Idx"] = as_string(i); + PrintHeaderServerMethodSplitStreaming(printer, service->method(i).get(), + vars); + } + + printer->Print("typedef "); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i)->name(); + auto method = service->method(i); + if (ServerOnlyStreaming(method.get())) { + printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<"); + } + } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + if (ServerOnlyStreaming(method.get())) { + printer->Print(" >"); + } + } + printer->Print(" SplitStreamedService;\n"); + + // Server side - typedef for controlled both unary and server-side streaming + printer->Print("typedef "); + for (int i = 0; i < service->method_count(); ++i) { + (*vars)["method_name"] = service->method(i)->name(); + auto method = service->method(i); + if (ServerOnlyStreaming(method.get())) { + printer->Print(*vars, "WithSplitStreamingMethod_$method_name$<"); + } + if (service->method(i)->NoStreaming()) { + printer->Print(*vars, "WithStreamedUnaryMethod_$method_name$<"); + } } + printer->Print("Service"); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + if (service->method(i)->NoStreaming() || + ServerOnlyStreaming(method.get())) { + printer->Print(" >"); + } + } + printer->Print(" StreamedService;\n"); printer->Outdent(); printer->Print("};\n"); + printer->Print(service->GetTrailingComments("//").c_str()); } -grpc::string GetHeaderServices(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms) { +grpc::string GetHeaderServices(grpc_generator::File* file, + const Parameters& params) { 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, '$'); + auto printer = file->CreatePrinter(&output); std::map vars; // Package string is empty or ends with a dot. It is used to fully qualify // method names. @@ -784,220 +1503,312 @@ grpc::string GetHeaderServices(const grpc::protobuf::FileDescriptor *file, if (!params.services_namespace.empty()) { vars["services_namespace"] = params.services_namespace; - printer.Print(vars, "\nnamespace $services_namespace$ {\n\n"); + 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"); + PrintHeaderService(printer.get(), file->service(i).get(), &vars); + printer->Print("\n"); } if (!params.services_namespace.empty()) { - printer.Print(vars, "} // namespace $services_namespace$\n\n"); + printer->Print(vars, "} // namespace $services_namespace$\n\n"); } } return output; } -grpc::string GetHeaderEpilogue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms) { +grpc::string GetHeaderEpilogue(grpc_generator::File* file, + const Parameters& /*params*/) { 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, '$'); + auto printer = file->CreatePrinter(&output); std::map vars; - vars["filename"] = file->name(); - vars["filename_identifier"] = FilenameIdentifier(file->name()); + vars["filename"] = file->filename(); + vars["filename_identifier"] = FilenameIdentifier(file->filename()); if (!file->package().empty()) { - std::vector parts = - grpc_generator::tokenize(file->package(), "."); + std::vector parts = file->package_parts(); for (auto part = parts.rbegin(); part != parts.rend(); part++) { vars["part"] = *part; - printer.Print(vars, "} // namespace $part$\n"); + printer->Print(vars, "} // namespace $part$\n"); } - printer.Print(vars, "\n"); + printer->Print(vars, "\n"); } - printer.Print(vars, "\n"); - printer.Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n"); + printer->Print(vars, "\n"); + printer->Print(vars, "#endif // GRPC_$filename_identifier$__INCLUDED\n"); + + printer->Print(file->GetTrailingComments("//").c_str()); } return output; } -grpc::string GetSourcePrologue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms) { +grpc::string GetSourcePrologue(grpc_generator::File* file, + const Parameters& /*params*/) { 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, '$'); + auto printer = file->CreatePrinter(&output); std::map vars; - vars["filename"] = file->name(); - vars["filename_base"] = grpc_generator::StripProto(file->name()); + vars["filename"] = file->filename(); + vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = kCppGeneratorMessageHeaderExt; + vars["service_header_ext"] = kCppGeneratorServiceHeaderExt; + + printer->Print(vars, "// Generated by the gRPC C++ 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, "// 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"); + printer->Print(vars, "#include \"$filename_base$$message_header_ext$\"\n"); + printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n"); + printer->Print(vars, "\n"); } return output; } -grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶m) { +grpc::string GetSourceIncludes(grpc_generator::File* file, + const Parameters& params) { 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, '$'); + auto printer = file->CreatePrinter(&output); 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"); - printer.Print(vars, "#include \n"); + static const char* headers_strs[] = { + "functional", + "grpcpp/impl/codegen/async_stream.h", + "grpcpp/impl/codegen/async_unary_call.h", + "grpcpp/impl/codegen/channel_interface.h", + "grpcpp/impl/codegen/client_unary_call.h", + "grpcpp/impl/codegen/client_callback.h", + "grpcpp/impl/codegen/method_handler_impl.h", + "grpcpp/impl/codegen/rpc_service_method.h", + "grpcpp/impl/codegen/server_callback.h", + "grpcpp/impl/codegen/service_type.h", + "grpcpp/impl/codegen/sync_stream.h"}; + std::vector headers(headers_strs, array_end(headers_strs)); + PrintIncludes(printer.get(), headers, params.use_system_headers, + params.grpc_search_path); if (!file->package().empty()) { - std::vector parts = - grpc_generator::tokenize(file->package(), "."); + std::vector parts = file->package_parts(); for (auto part = parts.begin(); part != parts.end(); part++) { vars["part"] = *part; - printer.Print(vars, "namespace $part$ {\n"); + printer->Print(vars, "namespace $part$ {\n"); } } - printer.Print(vars, "\n"); + printer->Print(vars, "\n"); } return output; } -void PrintSourceClientMethod(grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map *vars) { +void PrintSourceClientMethod(grpc_generator::Printer* printer, + const grpc_generator::Method* 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)) { + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + struct { + grpc::string prefix; + grpc::string start; // bool literal expressed as string + grpc::string method_params; // extra arguments to method + grpc::string create_args; // extra arguments to creator + } async_prefixes[] = {{"Async", "true", ", void* tag", ", tag"}, + {"PrepareAsync", "false", "", ", nullptr"}}; + if (method->NoStreaming()) { 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"); + " return ::grpc::internal::BlockingUnaryCall" + "(channel_.get(), rpcmethod_$Method$_, " + "context, request, response);\n}\n\n"); + printer->Print(*vars, - " return new " - "::grpc::ClientAsyncResponseReader< $Response$>(" - "channel_.get(), cq, " - "rpcmethod_$Method$_, " - "context, request);\n" - "}\n\n"); + "void $ns$$Service$::Stub::experimental_async::$Method$(" + "::grpc::ClientContext* context, " + "const $Request$* request, $Response$* response, " + "std::function f) {\n"); + printer->Print(*vars, + " return ::grpc::internal::CallbackUnaryCall" + "(stub_->channel_.get(), stub_->rpcmethod_$Method$_, " + "context, request, response, std::move(f));\n}\n\n"); + + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncStart"] = async_prefix.start; + printer->Print(*vars, + "::grpc::ClientAsyncResponseReader< $Response$>* " + "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(::grpc::" + "ClientContext* context, " + "const $Request$& request, " + "::grpc::CompletionQueue* cq) {\n"); + printer->Print( + *vars, + " return " + "::grpc::internal::ClientAsyncResponseReaderFactory< $Response$>" + "::Create(channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request, $AsyncStart$);\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 ::grpc::internal::ClientWriterFactory< $Request$>::Create(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, response);\n" + "}\n\n"); + + printer->Print( + *vars, + "void $ns$$Service$::" + "Stub::experimental_async::$Method$(::grpc::ClientContext* context, " + "$Response$* response, " + "::grpc::experimental::ClientWriteReactor< $Request$>* reactor) {\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" + " ::grpc::internal::ClientCallbackWriterFactory< " + "$Request$>::Create(" + "stub_->channel_.get(), " + "stub_->rpcmethod_$Method$_, " + "context, response, reactor);\n" "}\n\n"); + + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncStart"] = async_prefix.start; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncCreateArgs"] = async_prefix.create_args; + printer->Print(*vars, + "::grpc::ClientAsyncWriter< $Request$>* " + "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(" + "::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Print( + *vars, + " return ::grpc::internal::ClientAsyncWriterFactory< $Request$>" + "::Create(channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, response, $AsyncStart$$AsyncCreateArgs$);\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 ::grpc::internal::ClientReaderFactory< $Response$>::Create(" + "channel_.get(), " + "rpcmethod_$Method$_, " + "context, request);\n" + "}\n\n"); + + printer->Print( + *vars, + "void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::" + "ClientContext* context, " + "$Request$* request, " + "::grpc::experimental::ClientReadReactor< $Response$>* reactor) {\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" + " ::grpc::internal::ClientCallbackReaderFactory< " + "$Response$>::Create(" + "stub_->channel_.get(), " + "stub_->rpcmethod_$Method$_, " + "context, request, reactor);\n" "}\n\n"); - } else if (BidiStreaming(method)) { + + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncStart"] = async_prefix.start; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncCreateArgs"] = async_prefix.create_args; + printer->Print( + *vars, + "::grpc::ClientAsyncReader< $Response$>* " + "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(" + "::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Print( + *vars, + " return ::grpc::internal::ClientAsyncReaderFactory< $Response$>" + "::Create(channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, request, $AsyncStart$$AsyncCreateArgs$);\n" + "}\n\n"); + } + } else if (method->BidiStreaming()) { 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$>(" + " return ::grpc::internal::ClientReaderWriterFactory< " + "$Request$, $Response$>::Create(" "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"); + "void $ns$$Service$::Stub::experimental_async::$Method$(::grpc::" + "ClientContext* context, " + "::grpc::experimental::ClientBidiReactor< $Request$,$Response$>* " + "reactor) {\n"); printer->Print(*vars, - " return new " - "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>(" - "channel_.get(), cq, " - "rpcmethod_$Method$_, " - "context, tag);\n" + " ::grpc::internal::ClientCallbackReaderWriterFactory< " + "$Request$,$Response$>::Create(" + "stub_->channel_.get(), " + "stub_->rpcmethod_$Method$_, " + "context, reactor);\n" "}\n\n"); + + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncStart"] = async_prefix.start; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["AsyncCreateArgs"] = async_prefix.create_args; + printer->Print(*vars, + "::grpc::ClientAsyncReaderWriter< $Request$, $Response$>* " + "$ns$$Service$::Stub::$AsyncPrefix$$Method$Raw(::grpc::" + "ClientContext* context, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$) {\n"); + printer->Print(*vars, + " return " + "::grpc::internal::ClientAsyncReaderWriterFactory< " + "$Request$, $Response$>::Create(" + "channel_.get(), cq, " + "rpcmethod_$Method$_, " + "context, $AsyncStart$$AsyncCreateArgs$);\n" + "}\n\n"); + } } } -void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer, - const grpc::protobuf::MethodDescriptor *method, - std::map *vars) { +void PrintSourceServerMethod(grpc_generator::Printer* printer, + const grpc_generator::Method* 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)) { + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { printer->Print(*vars, "::grpc::Status $ns$$Service$::Service::$Method$(" "::grpc::ServerContext* context, " @@ -1035,7 +1846,7 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer, " return ::grpc::Status(" "::grpc::StatusCode::UNIMPLEMENTED, \"\");\n"); printer->Print("}\n\n"); - } else if (BidiStreaming(method)) { + } else if (method->BidiStreaming()) { printer->Print(*vars, "::grpc::Status $ns$$Service$::Service::$Method$(" "::grpc::ServerContext* context, " @@ -1050,23 +1861,26 @@ void PrintSourceServerMethod(grpc::protobuf::io::Printer *printer, } } -void PrintSourceService(grpc::protobuf::io::Printer *printer, - const grpc::protobuf::ServiceDescriptor *service, - std::map *vars) { +void PrintSourceService(grpc_generator::Printer* printer, + const grpc_generator::Service* 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"); + if (service->method_count() > 0) { + 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, "};\n\n"); printer->Print(*vars, "std::unique_ptr< $ns$$Service$::Stub> $ns$$Service$::NewStub(" "const std::shared_ptr< ::grpc::ChannelInterface>& channel, " "const ::grpc::StubOptions& options) {\n" + " (void)options;\n" " std::unique_ptr< $ns$$Service$::Stub> stub(new " "$ns$$Service$::Stub(channel));\n" " return stub;\n" @@ -1077,14 +1891,17 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, printer->Indent(); printer->Print(": channel_(channel)"); for (int i = 0; i < service->method_count(); ++i) { - const grpc::protobuf::MethodDescriptor *method = service->method(i); + auto method = service->method(i); (*vars)["Method"] = method->name(); (*vars)["Idx"] = as_string(i); - if (NoStreaming(method)) { + if (method->NoStreaming()) { (*vars)["StreamingType"] = "NORMAL_RPC"; - } else if (ClientOnlyStreaming(method)) { + // NOTE: There is no reason to consider streamed-unary as a separate + // category here since this part is setting up the client-side stub + // and this appears as a NORMAL_RPC from the client-side. + } else if (ClientOnlyStreaming(method.get())) { (*vars)["StreamingType"] = "CLIENT_STREAMING"; - } else if (ServerOnlyStreaming(method)) { + } else if (ServerOnlyStreaming(method.get())) { (*vars)["StreamingType"] = "SERVER_STREAMING"; } else { (*vars)["StreamingType"] = "BIDI_STREAMING"; @@ -1092,7 +1909,7 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, printer->Print(*vars, ", rpcmethod_$Method$_(" "$prefix$$Service$_method_names[$Idx$], " - "::grpc::RpcMethod::$StreamingType$, " + "::grpc::internal::RpcMethod::$StreamingType$, " "channel" ")\n"); } @@ -1101,54 +1918,52 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, for (int i = 0; i < service->method_count(); ++i) { (*vars)["Idx"] = as_string(i); - PrintSourceClientMethod(printer, service->method(i), vars); + PrintSourceClientMethod(printer, service->method(i).get(), vars); } printer->Print(*vars, "$ns$$Service$::Service::Service() {\n"); printer->Indent(); for (int i = 0; i < service->method_count(); ++i) { - const grpc::protobuf::MethodDescriptor *method = service->method(i); + auto 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)) { + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + if (method->NoStreaming()) { printer->Print( *vars, - "AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::internal::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" - " ::grpc::RpcMethod::NORMAL_RPC,\n" - " new ::grpc::RpcMethodHandler< $ns$$Service$::Service, " + " ::grpc::internal::RpcMethod::NORMAL_RPC,\n" + " new ::grpc::internal::RpcMethodHandler< $ns$$Service$::Service, " "$Request$, " "$Response$>(\n" " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); - } else if (ClientOnlyStreaming(method)) { + } else if (ClientOnlyStreaming(method.get())) { printer->Print( *vars, - "AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::internal::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" - " ::grpc::RpcMethod::CLIENT_STREAMING,\n" - " new ::grpc::ClientStreamingHandler< " + " ::grpc::internal::RpcMethod::CLIENT_STREAMING,\n" + " new ::grpc::internal::ClientStreamingHandler< " "$ns$$Service$::Service, $Request$, $Response$>(\n" " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); - } else if (ServerOnlyStreaming(method)) { + } else if (ServerOnlyStreaming(method.get())) { printer->Print( *vars, - "AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::internal::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" - " ::grpc::RpcMethod::SERVER_STREAMING,\n" - " new ::grpc::ServerStreamingHandler< " + " ::grpc::internal::RpcMethod::SERVER_STREAMING,\n" + " new ::grpc::internal::ServerStreamingHandler< " "$ns$$Service$::Service, $Request$, $Response$>(\n" " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); - } else if (BidiStreaming(method)) { + } else if (method->BidiStreaming()) { printer->Print( *vars, - "AddMethod(new ::grpc::RpcServiceMethod(\n" + "AddMethod(new ::grpc::internal::RpcServiceMethod(\n" " $prefix$$Service$_method_names[$Idx$],\n" - " ::grpc::RpcMethod::BIDI_STREAMING,\n" - " new ::grpc::BidiStreamingHandler< " + " ::grpc::internal::RpcMethod::BIDI_STREAMING,\n" + " new ::grpc::internal::BidiStreamingHandler< " "$ns$$Service$::Service, $Request$, $Response$>(\n" " std::mem_fn(&$ns$$Service$::Service::$Method$), this)));\n"); } @@ -1160,17 +1975,16 @@ void PrintSourceService(grpc::protobuf::io::Printer *printer, "}\n\n"); for (int i = 0; i < service->method_count(); ++i) { (*vars)["Idx"] = as_string(i); - PrintSourceServerMethod(printer, service->method(i), vars); + PrintSourceServerMethod(printer, service->method(i).get(), vars); } } -grpc::string GetSourceServices(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms) { +grpc::string GetSourceServices(grpc_generator::File* file, + const Parameters& params) { 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, '$'); + auto printer = file->CreatePrinter(&output); std::map vars; // Package string is empty or ends with a dot. It is used to fully qualify // method names. @@ -1187,20 +2001,19 @@ grpc::string GetSourceServices(const grpc::protobuf::FileDescriptor *file, } for (int i = 0; i < file->service_count(); ++i) { - PrintSourceService(&printer, file->service(i), &vars); - printer.Print("\n"); + PrintSourceService(printer.get(), file->service(i).get(), &vars); + printer->Print("\n"); } } return output; } -grpc::string GetSourceEpilogue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms) { +grpc::string GetSourceEpilogue(grpc_generator::File* file, + const Parameters& /*params*/) { grpc::string temp; if (!file->package().empty()) { - std::vector parts = - grpc_generator::tokenize(file->package(), "."); + std::vector parts = file->package_parts(); for (auto part = parts.begin(); part != parts.end(); part++) { temp.append("} // namespace "); @@ -1213,4 +2026,222 @@ grpc::string GetSourceEpilogue(const grpc::protobuf::FileDescriptor *file, return temp; } +// TODO(mmukhi): Make sure we need parameters or not. +grpc::string GetMockPrologue(grpc_generator::File* file, + const Parameters& /*params*/) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + + vars["filename"] = file->filename(); + vars["filename_base"] = file->filename_without_ext(); + vars["message_header_ext"] = kCppGeneratorMessageHeaderExt; + vars["service_header_ext"] = kCppGeneratorServiceHeaderExt; + + printer->Print(vars, "// Generated by the gRPC C++ 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$$message_header_ext$\"\n"); + printer->Print(vars, "#include \"$filename_base$$service_header_ext$\"\n"); + printer->Print(vars, file->additional_headers().c_str()); + printer->Print(vars, "\n"); + } + return output; +} + +// TODO(mmukhi): Add client-stream and completion-queue headers. +grpc::string GetMockIncludes(grpc_generator::File* file, + const Parameters& params) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + std::map vars; + + static const char* headers_strs[] = { + "grpcpp/impl/codegen/async_stream.h", + "grpcpp/impl/codegen/sync_stream.h", + }; + std::vector headers(headers_strs, array_end(headers_strs)); + PrintIncludes(printer.get(), headers, params.use_system_headers, + params.grpc_search_path); + + std::vector gmock_header; + if (params.gmock_search_path.empty()) { + gmock_header.push_back("gmock/gmock.h"); + PrintIncludes(printer.get(), gmock_header, params.use_system_headers, + params.grpc_search_path); + } else { + gmock_header.push_back("gmock.h"); + // We use local includes when a gmock_search_path is given + PrintIncludes(printer.get(), gmock_header, false, + params.gmock_search_path); + } + + if (!file->package().empty()) { + std::vector parts = file->package_parts(); + + 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 PrintMockClientMethods(grpc_generator::Printer* printer, + const grpc_generator::Method* method, + std::map* vars) { + (*vars)["Method"] = method->name(); + (*vars)["Request"] = method->input_type_name(); + (*vars)["Response"] = method->output_type_name(); + + struct { + grpc::string prefix; + grpc::string method_params; // extra arguments to method + int extra_method_param_count; + } async_prefixes[] = {{"Async", ", void* tag", 1}, {"PrepareAsync", "", 0}}; + + if (method->NoStreaming()) { + printer->Print( + *vars, + "MOCK_METHOD3($Method$, ::grpc::Status(::grpc::ClientContext* context, " + "const $Request$& request, $Response$* response));\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + printer->Print( + *vars, + "MOCK_METHOD3($AsyncPrefix$$Method$Raw, " + "::grpc::ClientAsyncResponseReaderInterface< $Response$>*" + "(::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq));\n"); + } + } else if (ClientOnlyStreaming(method)) { + printer->Print( + *vars, + "MOCK_METHOD2($Method$Raw, " + "::grpc::ClientWriterInterface< $Request$>*" + "(::grpc::ClientContext* context, $Response$* response));\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["MockArgs"] = + std::to_string(3 + async_prefix.extra_method_param_count); + printer->Print(*vars, + "MOCK_METHOD$MockArgs$($AsyncPrefix$$Method$Raw, " + "::grpc::ClientAsyncWriterInterface< $Request$>*" + "(::grpc::ClientContext* context, $Response$* response, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$));\n"); + } + } else if (ServerOnlyStreaming(method)) { + printer->Print( + *vars, + "MOCK_METHOD2($Method$Raw, " + "::grpc::ClientReaderInterface< $Response$>*" + "(::grpc::ClientContext* context, const $Request$& request));\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["MockArgs"] = + std::to_string(3 + async_prefix.extra_method_param_count); + printer->Print( + *vars, + "MOCK_METHOD$MockArgs$($AsyncPrefix$$Method$Raw, " + "::grpc::ClientAsyncReaderInterface< $Response$>*" + "(::grpc::ClientContext* context, const $Request$& request, " + "::grpc::CompletionQueue* cq$AsyncMethodParams$));\n"); + } + } else if (method->BidiStreaming()) { + printer->Print( + *vars, + "MOCK_METHOD1($Method$Raw, " + "::grpc::ClientReaderWriterInterface< $Request$, $Response$>*" + "(::grpc::ClientContext* context));\n"); + for (auto async_prefix : async_prefixes) { + (*vars)["AsyncPrefix"] = async_prefix.prefix; + (*vars)["AsyncMethodParams"] = async_prefix.method_params; + (*vars)["MockArgs"] = + std::to_string(2 + async_prefix.extra_method_param_count); + printer->Print( + *vars, + "MOCK_METHOD$MockArgs$($AsyncPrefix$$Method$Raw, " + "::grpc::ClientAsyncReaderWriterInterface<$Request$, $Response$>*" + "(::grpc::ClientContext* context, ::grpc::CompletionQueue* cq" + "$AsyncMethodParams$));\n"); + } + } +} + +void PrintMockService(grpc_generator::Printer* printer, + const grpc_generator::Service* service, + std::map* vars) { + (*vars)["Service"] = service->name(); + + printer->Print(*vars, + "class Mock$Service$Stub : public $Service$::StubInterface {\n" + " public:\n"); + printer->Indent(); + for (int i = 0; i < service->method_count(); ++i) { + PrintMockClientMethods(printer, service->method(i).get(), vars); + } + printer->Outdent(); + printer->Print("};\n"); +} + +grpc::string GetMockServices(grpc_generator::File* file, + const Parameters& params) { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto printer = file->CreatePrinter(&output); + 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["services_namespace"] = params.services_namespace; + printer->Print(vars, "\nnamespace $services_namespace$ {\n\n"); + } + + for (int i = 0; i < file->service_count(); i++) { + PrintMockService(printer.get(), file->service(i).get(), &vars); + printer->Print("\n"); + } + + if (!params.services_namespace.empty()) { + printer->Print(vars, "} // namespace $services_namespace$\n\n"); + } + } + return output; +} + +grpc::string GetMockEpilogue(grpc_generator::File* file, + const Parameters& /*params*/) { + grpc::string temp; + + if (!file->package().empty()) { + std::vector parts = file->package_parts(); + + 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/third_party/grpc/src/compiler/cpp_generator.h b/third_party/grpc/src/compiler/cpp_generator.h index 70c2e985f6b87e..d88ef75c987c83 100644 --- a/third_party/grpc/src/compiler/cpp_generator.h +++ b/third_party/grpc/src/compiler/cpp_generator.h @@ -1,40 +1,44 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H #define GRPC_INTERNAL_COMPILER_CPP_GENERATOR_H +// cpp_generator.h/.cc do not directly depend on GRPC/ProtoBuf, such that they +// can be used to generate code for other serialization systems, such as +// FlatBuffers. + +#include +#include + #include "src/compiler/config.h" +#include "src/compiler/schema_interface.h" + +#ifndef GRPC_CUSTOM_STRING +#include +#define GRPC_CUSTOM_STRING std::string +#endif + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc namespace grpc_cpp_generator { @@ -42,39 +46,81 @@ namespace grpc_cpp_generator { struct Parameters { // Puts the service into a namespace grpc::string services_namespace; + // Use system includes (<>) or local includes ("") + bool use_system_headers; + // Prefix to any grpc include + grpc::string grpc_search_path; + // Generate Google Mock code to facilitate unit testing. + bool generate_mock_code; + // Google Mock search path, when non-empty, local includes will be used. + grpc::string gmock_search_path; + // *EXPERIMENTAL* Additional include files in grpc.pb.h + std::vector additional_header_includes; }; // Return the prologue of the generated header file. -grpc::string GetHeaderPrologue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetHeaderPrologue(grpc_generator::File* file, + const Parameters& params); // Return the includes needed for generated header file. -grpc::string GetHeaderIncludes(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetHeaderIncludes(grpc_generator::File* file, + const Parameters& params); // Return the includes needed for generated source file. -grpc::string GetSourceIncludes(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetSourceIncludes(grpc_generator::File* file, + const Parameters& params); // Return the epilogue of the generated header file. -grpc::string GetHeaderEpilogue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetHeaderEpilogue(grpc_generator::File* file, + const Parameters& params); // Return the prologue of the generated source file. -grpc::string GetSourcePrologue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetSourcePrologue(grpc_generator::File* file, + const Parameters& params); // Return the services for generated header file. -grpc::string GetHeaderServices(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetHeaderServices(grpc_generator::File* file, + const Parameters& params); // Return the services for generated source file. -grpc::string GetSourceServices(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetSourceServices(grpc_generator::File* file, + const Parameters& params); // Return the epilogue of the generated source file. -grpc::string GetSourceEpilogue(const grpc::protobuf::FileDescriptor *file, - const Parameters ¶ms); +grpc::string GetSourceEpilogue(grpc_generator::File* file, + const Parameters& params); + +// Return the prologue of the generated mock file. +grpc::string GetMockPrologue(grpc_generator::File* file, + const Parameters& params); + +// Return the includes needed for generated mock file. +grpc::string GetMockIncludes(grpc_generator::File* file, + const Parameters& params); + +// Return the services for generated mock file. +grpc::string GetMockServices(grpc_generator::File* file, + const Parameters& params); + +// Return the epilogue of generated mock file. +grpc::string GetMockEpilogue(grpc_generator::File* file, + const Parameters& params); + +// Return the prologue of the generated mock file. +grpc::string GetMockPrologue(grpc_generator::File* file, + const Parameters& params); + +// Return the includes needed for generated mock file. +grpc::string GetMockIncludes(grpc_generator::File* file, + const Parameters& params); + +// Return the services for generated mock file. +grpc::string GetMockServices(grpc_generator::File* file, + const Parameters& params); + +// Return the epilogue of generated mock file. +grpc::string GetMockEpilogue(grpc_generator::File* file, + const Parameters& params); } // namespace grpc_cpp_generator diff --git a/third_party/grpc/src/compiler/cpp_generator_helpers.h b/third_party/grpc/src/compiler/cpp_generator_helpers.h index be68cbe695aeeb..b8efcbdb84313f 100644 --- a/third_party/grpc/src/compiler/cpp_generator_helpers.h +++ b/third_party/grpc/src/compiler/cpp_generator_helpers.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -35,27 +20,28 @@ #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) { +inline grpc::string DotsToColons(const grpc::string& name) { return grpc_generator::StringReplace(name, ".", "::"); } -inline grpc::string DotsToUnderscores(const grpc::string &name) { +inline grpc::string DotsToUnderscores(const grpc::string& name) { return grpc_generator::StringReplace(name, ".", "_"); } -inline grpc::string ClassName(const grpc::protobuf::Descriptor *descriptor, +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; + const grpc::protobuf::Descriptor* outer = descriptor; while (outer->containing_type() != NULL) outer = outer->containing_type(); - const grpc::string &outer_name = outer->full_name(); + const grpc::string& outer_name = outer->full_name(); grpc::string inner_name = descriptor->full_name().substr(outer_name.size()); if (qualified) { @@ -65,6 +51,13 @@ inline grpc::string ClassName(const grpc::protobuf::Descriptor *descriptor, } } +// Get leading or trailing comments in a string. Comment lines start with "// ". +// Leading detached comments are put in in front of leading comments. +template +inline grpc::string GetCppComments(const DescriptorType* desc, bool leading) { + return grpc_generator::GetPrefixedComments(desc, leading, "//"); +} + } // namespace grpc_cpp_generator #endif // GRPC_INTERNAL_COMPILER_CPP_GENERATOR_HELPERS_H diff --git a/third_party/grpc/src/compiler/cpp_plugin.cc b/third_party/grpc/src/compiler/cpp_plugin.cc index 88c704948ecc59..c8ab78863bfbf2 100644 --- a/third_party/grpc/src/compiler/cpp_plugin.cc +++ b/third_party/grpc/src/compiler/cpp_plugin.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -35,21 +20,23 @@ // #include +#include #include "src/compiler/config.h" #include "src/compiler/cpp_generator.h" -#include "src/compiler/cpp_generator_helpers.h" +#include "src/compiler/generator_helpers.h" +#include "src/compiler/protobuf_plugin.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 { + virtual bool Generate(const grpc::protobuf::FileDescriptor* file, + const grpc::string& parameter, + 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 " @@ -59,17 +46,43 @@ class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { } grpc_cpp_generator::Parameters generator_parameters; + generator_parameters.use_system_headers = true; + generator_parameters.generate_mock_code = false; + + ProtoBufFile pbfile(file); if (!parameter.empty()) { std::vector parameters_list = - grpc_generator::tokenize(parameter, ","); + grpc_generator::tokenize(parameter, ","); for (auto parameter_string = parameters_list.begin(); - parameter_string != parameters_list.end(); - parameter_string++) { + parameter_string != parameters_list.end(); parameter_string++) { std::vector param = - grpc_generator::tokenize(*parameter_string, "="); + grpc_generator::tokenize(*parameter_string, "="); if (param[0] == "services_namespace") { generator_parameters.services_namespace = param[1]; + } else if (param[0] == "use_system_headers") { + if (param[1] == "true") { + generator_parameters.use_system_headers = true; + } else if (param[1] == "false") { + generator_parameters.use_system_headers = false; + } else { + *error = grpc::string("Invalid parameter: ") + *parameter_string; + return false; + } + } else if (param[0] == "grpc_search_path") { + generator_parameters.grpc_search_path = param[1]; + } else if (param[0] == "generate_mock_code") { + if (param[1] == "true") { + generator_parameters.generate_mock_code = true; + } else if (param[1] != "false") { + *error = grpc::string("Invalid parameter: ") + *parameter_string; + return false; + } + } else if (param[0] == "gmock_search_path") { + generator_parameters.gmock_search_path = param[1]; + } else if (param[0] == "additional_header_includes") { + generator_parameters.additional_header_includes = + grpc_generator::tokenize(param[1], ":"); } else { *error = grpc::string("Unknown parameter: ") + *parameter_string; return false; @@ -80,35 +93,46 @@ class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { 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); + grpc_cpp_generator::GetHeaderPrologue(&pbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderIncludes(&pbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderServices(&pbfile, generator_parameters) + + grpc_cpp_generator::GetHeaderEpilogue(&pbfile, generator_parameters); std::unique_ptr header_output( context->Open(file_name + ".grpc.pb.h")); - grpc::protobuf::io::CodedOutputStream header_coded_out( - header_output.get()); + 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); + grpc_cpp_generator::GetSourcePrologue(&pbfile, generator_parameters) + + grpc_cpp_generator::GetSourceIncludes(&pbfile, generator_parameters) + + grpc_cpp_generator::GetSourceServices(&pbfile, generator_parameters) + + grpc_cpp_generator::GetSourceEpilogue(&pbfile, generator_parameters); std::unique_ptr source_output( context->Open(file_name + ".grpc.pb.cc")); - grpc::protobuf::io::CodedOutputStream source_coded_out( - source_output.get()); + grpc::protobuf::io::CodedOutputStream source_coded_out(source_output.get()); source_coded_out.WriteRaw(source_code.data(), source_code.size()); + if (!generator_parameters.generate_mock_code) { + return true; + } + grpc::string mock_code = + grpc_cpp_generator::GetMockPrologue(&pbfile, generator_parameters) + + grpc_cpp_generator::GetMockIncludes(&pbfile, generator_parameters) + + grpc_cpp_generator::GetMockServices(&pbfile, generator_parameters) + + grpc_cpp_generator::GetMockEpilogue(&pbfile, generator_parameters); + std::unique_ptr mock_output( + context->Open(file_name + "_mock.grpc.pb.h")); + grpc::protobuf::io::CodedOutputStream mock_coded_out(mock_output.get()); + mock_coded_out.WriteRaw(mock_code.data(), mock_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 { + 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()); @@ -116,7 +140,7 @@ class CppGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { } }; -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { CppGrpcGenerator generator; return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); } diff --git a/third_party/grpc/src/compiler/csharp_generator.cc b/third_party/grpc/src/compiler/csharp_generator.cc index f6079bdc05abfd..59ddbd82f61e92 100644 --- a/third_party/grpc/src/compiler/csharp_generator.cc +++ b/third_party/grpc/src/compiler/csharp_generator.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -36,151 +21,281 @@ #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" +#include "src/compiler/csharp_generator_helpers.h" - -using google::protobuf::compiler::csharp::GetFileNamespace; using google::protobuf::compiler::csharp::GetClassName; +using google::protobuf::compiler::csharp::GetFileNamespace; using google::protobuf::compiler::csharp::GetReflectionClassName; -using grpc::protobuf::FileDescriptor; using grpc::protobuf::Descriptor; -using grpc::protobuf::ServiceDescriptor; +using grpc::protobuf::FileDescriptor; using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::ServiceDescriptor; 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_BIDI_STREAMING; using grpc_generator::METHODTYPE_CLIENT_STREAMING; +using grpc_generator::METHODTYPE_NO_STREAMING; using grpc_generator::METHODTYPE_SERVER_STREAMING; -using grpc_generator::METHODTYPE_BIDI_STREAMING; +using grpc_generator::MethodType; using grpc_generator::StringReplace; using std::map; using std::vector; - namespace grpc_csharp_generator { namespace { -std::string GetServiceClassName(const ServiceDescriptor* service) { - return service->name(); +// This function is a massaged version of +// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/csharp/csharp_doc_comment.cc +// Currently, we cannot easily reuse the functionality as +// google/protobuf/compiler/csharp/csharp_doc_comment.h is not a public header. +// TODO(jtattermusch): reuse the functionality from google/protobuf. +bool GenerateDocCommentBodyImpl(grpc::protobuf::io::Printer* printer, + grpc::protobuf::SourceLocation location) { + grpc::string comments = location.leading_comments.empty() + ? location.trailing_comments + : location.leading_comments; + if (comments.empty()) { + return false; + } + // XML escaping... no need for apostrophes etc as the whole text is going to + // be a child + // node of a summary element, not part of an attribute. + comments = grpc_generator::StringReplace(comments, "&", "&", true); + comments = grpc_generator::StringReplace(comments, "<", "<", true); + + std::vector lines; + grpc_generator::Split(comments, '\n', &lines); + // TODO: We really should work out which part to put in the summary and which + // to put in the remarks... + // but that needs to be part of a bigger effort to understand the markdown + // better anyway. + printer->Print("/// \n"); + bool last_was_empty = false; + // We squash multiple blank lines down to one, and remove any trailing blank + // lines. We need + // to preserve the blank lines themselves, as this is relevant in the + // markdown. + // Note that we can't remove leading or trailing whitespace as *that's* + // relevant in markdown too. + // (We don't skip "just whitespace" lines, either.) + for (std::vector::iterator it = lines.begin(); + it != lines.end(); ++it) { + grpc::string line = *it; + if (line.empty()) { + last_was_empty = true; + } else { + if (last_was_empty) { + printer->Print("///\n"); + } + last_was_empty = false; + printer->Print("///$line$\n", "line", *it); + } + } + printer->Print("/// \n"); + return true; +} + +template +bool GenerateDocCommentBody(grpc::protobuf::io::Printer* printer, + const DescriptorType* descriptor) { + grpc::protobuf::SourceLocation location; + if (!descriptor->GetSourceLocation(&location)) { + return false; + } + return GenerateDocCommentBodyImpl(printer, location); +} + +void GenerateDocCommentServerMethod(grpc::protobuf::io::Printer* printer, + const MethodDescriptor* method) { + if (GenerateDocCommentBody(printer, method)) { + if (method->client_streaming()) { + printer->Print( + "/// Used for reading requests from " + "the client.\n"); + } else { + printer->Print( + "/// The request received from the " + "client.\n"); + } + if (method->server_streaming()) { + printer->Print( + "/// Used for sending responses back " + "to the client.\n"); + } + printer->Print( + "/// The context of the server-side call " + "handler being invoked.\n"); + if (method->server_streaming()) { + printer->Print( + "/// A task indicating completion of the " + "handler.\n"); + } else { + printer->Print( + "/// The response to send back to the client (wrapped by a " + "task).\n"); + } + } +} + +void GenerateDocCommentClientMethod(grpc::protobuf::io::Printer* printer, + const MethodDescriptor* method, + bool is_sync, bool use_call_options) { + if (GenerateDocCommentBody(printer, method)) { + if (!method->client_streaming()) { + printer->Print( + "/// The request to send to the " + "server.\n"); + } + if (!use_call_options) { + printer->Print( + "/// The initial metadata to send with the " + "call. This parameter is optional.\n"); + printer->Print( + "/// An optional deadline for the call. The " + "call will be cancelled if deadline is hit.\n"); + printer->Print( + "/// An optional token for " + "canceling the call.\n"); + } else { + printer->Print( + "/// The options for the call.\n"); + } + if (is_sync) { + printer->Print( + "/// The response received from the server.\n"); + } else { + printer->Print("/// The call object.\n"); + } + } } -std::string GetClientInterfaceName(const ServiceDescriptor* service) { - return "I" + service->name() + "Client"; +std::string GetServiceClassName(const ServiceDescriptor* service) { + return service->name(); } std::string GetClientClassName(const ServiceDescriptor* service) { return service->name() + "Client"; } -std::string GetServerInterfaceName(const ServiceDescriptor* service) { - return "I" + service->name(); +std::string GetServerClassName(const ServiceDescriptor* service) { + return service->name() + "Base"; } std::string GetCSharpMethodType(MethodType method_type) { switch (method_type) { case METHODTYPE_NO_STREAMING: - return "MethodType.Unary"; + return "grpc::MethodType.Unary"; case METHODTYPE_CLIENT_STREAMING: - return "MethodType.ClientStreaming"; + return "grpc::MethodType.ClientStreaming"; case METHODTYPE_SERVER_STREAMING: - return "MethodType.ServerStreaming"; + return "grpc::MethodType.ServerStreaming"; case METHODTYPE_BIDI_STREAMING: - return "MethodType.DuplexStreaming"; + return "grpc::MethodType.DuplexStreaming"; } - GOOGLE_LOG(FATAL)<< "Can't get here."; + GOOGLE_LOG(FATAL) << "Can't get here."; return ""; } -std::string GetServiceNameFieldName() { - return "__ServiceName"; -} +std::string GetServiceNameFieldName() { return "__ServiceName"; } -std::string GetMarshallerFieldName(const Descriptor *message) { - return "__Marshaller_" + message->name(); +std::string GetMarshallerFieldName(const Descriptor* message) { + return "__Marshaller_" + + grpc_generator::StringReplace(message->full_name(), ".", "_", true); } -std::string GetMethodFieldName(const MethodDescriptor *method) { +std::string GetMethodFieldName(const MethodDescriptor* method) { return "__Method_" + method->name(); } -std::string GetMethodRequestParamMaybe(const MethodDescriptor *method) { +std::string GetMethodRequestParamMaybe(const MethodDescriptor* method, + bool invocation_param = false) { if (method->client_streaming()) { return ""; } + if (invocation_param) { + return "request, "; + } return GetClassName(method->input_type()) + " request, "; } -std::string GetMethodReturnTypeClient(const MethodDescriptor *method) { +std::string GetAccessLevel(bool internal_access) { + return internal_access ? "internal" : "public"; +} + +std::string GetMethodReturnTypeClient(const MethodDescriptor* method) { switch (GetMethodType(method)) { case METHODTYPE_NO_STREAMING: - return "AsyncUnaryCall<" + GetClassName(method->output_type()) + ">"; + return "grpc::AsyncUnaryCall<" + GetClassName(method->output_type()) + + ">"; case METHODTYPE_CLIENT_STREAMING: - return "AsyncClientStreamingCall<" + GetClassName(method->input_type()) - + ", " + GetClassName(method->output_type()) + ">"; + return "grpc::AsyncClientStreamingCall<" + + GetClassName(method->input_type()) + ", " + + GetClassName(method->output_type()) + ">"; case METHODTYPE_SERVER_STREAMING: - return "AsyncServerStreamingCall<" + GetClassName(method->output_type()) - + ">"; + return "grpc::AsyncServerStreamingCall<" + + GetClassName(method->output_type()) + ">"; case METHODTYPE_BIDI_STREAMING: - return "AsyncDuplexStreamingCall<" + GetClassName(method->input_type()) - + ", " + GetClassName(method->output_type()) + ">"; + return "grpc::AsyncDuplexStreamingCall<" + + GetClassName(method->input_type()) + ", " + + GetClassName(method->output_type()) + ">"; } - GOOGLE_LOG(FATAL)<< "Can't get here."; + GOOGLE_LOG(FATAL) << "Can't get here."; return ""; } -std::string GetMethodRequestParamServer(const MethodDescriptor *method) { +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"; + return "grpc::IAsyncStreamReader<" + GetClassName(method->input_type()) + + "> requestStream"; } - GOOGLE_LOG(FATAL)<< "Can't get here."; + GOOGLE_LOG(FATAL) << "Can't get here."; return ""; } -std::string GetMethodReturnTypeServer(const MethodDescriptor *method) { +std::string GetMethodReturnTypeServer(const MethodDescriptor* method) { switch (GetMethodType(method)) { case METHODTYPE_NO_STREAMING: case METHODTYPE_CLIENT_STREAMING: - return "Task<" + GetClassName(method->output_type()) + ">"; + return "global::System.Threading.Tasks.Task<" + + GetClassName(method->output_type()) + ">"; case METHODTYPE_SERVER_STREAMING: case METHODTYPE_BIDI_STREAMING: - return "Task"; + return "global::System.Threading.Tasks.Task"; } - GOOGLE_LOG(FATAL)<< "Can't get here."; + GOOGLE_LOG(FATAL) << "Can't get here."; return ""; } -std::string GetMethodResponseStreamMaybe(const MethodDescriptor *method) { +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"; + return ", grpc::IServerStreamWriter<" + + GetClassName(method->output_type()) + "> responseStream"; } - GOOGLE_LOG(FATAL)<< "Can't get here."; + 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) { + const ServiceDescriptor* service) { std::set descriptor_set; - std::vector result; // vector is to maintain stable ordering + std::vector + result; // vector is to maintain stable ordering for (int i = 0; i < service->method_count(); i++) { - const MethodDescriptor *method = service->method(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()); @@ -193,21 +308,25 @@ std::vector GetUsedMessages( return result; } -void GenerateMarshallerFields(Printer* out, const ServiceDescriptor *service) { +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]; + 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", + "static readonly grpc::Marshaller<$type$> $fieldname$ = " + "grpc::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) { +void GenerateStaticMethodField(Printer* out, const MethodDescriptor* method) { out->Print( - "static readonly Method<$request$, $response$> $fieldname$ = new Method<$request$, $response$>(\n", + "static readonly grpc::Method<$request$, $response$> $fieldname$ = new " + "grpc::Method<$request$, $response$>(\n", "fieldname", GetMethodFieldName(method), "request", GetClassName(method->input_type()), "response", GetClassName(method->output_type())); @@ -216,7 +335,7 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) { out->Print("$methodtype$,\n", "methodtype", GetCSharpMethodType(GetMethodType(method))); out->Print("$servicenamefield$,\n", "servicenamefield", - GetServiceNameFieldName()); + GetServiceNameFieldName()); out->Print("\"$methodname$\",\n", "methodname", method->name()); out->Print("$requestmarshaller$,\n", "requestmarshaller", GetMarshallerFieldName(method->input_type())); @@ -227,11 +346,14 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) { out->Outdent(); } -void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *service) { +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("/// 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", GetReflectionClassName(service->file()), "index", @@ -240,121 +362,127 @@ void GenerateServiceDescriptorProperty(Printer* out, const ServiceDescriptor *se 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)); +void GenerateServerClass(Printer* out, const ServiceDescriptor* service) { + out->Print( + "/// Base class for server-side implementations of " + "$servicename$\n", + "servicename", GetServiceClassName(service)); + out->Print("public abstract partial class $name$\n", "name", + GetServerClassName(service)); out->Print("{\n"); out->Indent(); for (int i = 0; i < service->method_count(); i++) { - const MethodDescriptor *method = service->method(i); + const MethodDescriptor* method = service->method(i); + GenerateDocCommentServerMethod(out, method); out->Print( - "$returntype$ $methodname$($request$$response_stream_maybe$, " - "ServerCallContext context);\n", + "public virtual $returntype$ " + "$methodname$($request$$response_stream_maybe$, " + "grpc::ServerCallContext context)\n", "methodname", method->name(), "returntype", GetMethodReturnTypeServer(method), "request", GetMethodRequestParamServer(method), "response_stream_maybe", GetMethodResponseStreamMaybe(method)); + out->Print("{\n"); + out->Indent(); + out->Print( + "throw new grpc::RpcException(" + "new grpc::Status(grpc::StatusCode.Unimplemented, \"\"));\n"); + out->Outdent(); + out->Print("}\n\n"); } 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)); +void GenerateClientStub(Printer* out, const ServiceDescriptor* service) { + out->Print("/// Client for $servicename$\n", "servicename", + GetServiceClassName(service)); + out->Print("public partial class $name$ : grpc::ClientBase<$name$>\n", "name", + GetClientClassName(service)); out->Print("{\n"); out->Indent(); // constructors out->Print( - "public $name$(Channel channel) : base(channel)\n", + "/// Creates a new client for $servicename$\n" + "/// The channel to use to make remote " + "calls.\n", + "servicename", GetServiceClassName(service)); + out->Print("public $name$(grpc::Channel channel) : base(channel)\n", "name", + GetClientClassName(service)); + out->Print("{\n"); + out->Print("}\n"); + out->Print( + "/// Creates a new client for $servicename$ that uses a custom " + "CallInvoker.\n" + "/// The callInvoker to use to make remote " + "calls.\n", + "servicename", GetServiceClassName(service)); + out->Print( + "public $name$(grpc::CallInvoker callInvoker) : base(callInvoker)\n", "name", GetClientClassName(service)); out->Print("{\n"); out->Print("}\n"); + out->Print( + "/// Protected parameterless constructor to allow creation" + " of test doubles.\n"); + out->Print("protected $name$() : base()\n", "name", + GetClientClassName(service)); + out->Print("{\n"); + out->Print("}\n"); + out->Print( + "/// Protected constructor to allow creation of configured " + "clients.\n" + "/// The client configuration.\n"); + out->Print( + "protected $name$(ClientBaseConfiguration configuration)" + " : base(configuration)\n", + "name", GetClientClassName(service)); + out->Print("{\n"); + out->Print("}\n\n"); for (int i = 0; i < service->method_count(); i++) { - const MethodDescriptor *method = service->method(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 + GenerateDocCommentClientMethod(out, method, true, false); out->Print( - "public $response$ $methodname$($request$ request, Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n", + "public virtual $response$ $methodname$($request$ request, " + "grpc::Metadata " + "headers = null, global::System.DateTime? deadline = null, " + "global::System.Threading.CancellationToken " + "cancellationToken = " + "default(global::System.Threading.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->Print( + "return $methodname$(request, new grpc::CallOptions(headers, " + "deadline, " + "cancellationToken));\n", + "methodname", method->name()); out->Outdent(); out->Print("}\n"); // overload taking CallOptions as a param + GenerateDocCommentClientMethod(out, method, true, true); out->Print( - "public $response$ $methodname$($request$ request, CallOptions options)\n", - "methodname", method->name(), "request", - GetClassName(method->input_type()), "response", - GetClassName(method->output_type())); + "public virtual $response$ $methodname$($request$ request, " + "grpc::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->Print( + "return CallInvoker.BlockingUnaryCall($methodfield$, null, options, " + "request);\n", + "methodfield", GetMethodFieldName(method)); out->Outdent(); out->Print("}\n"); } @@ -363,94 +491,116 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { if (method_type == METHODTYPE_NO_STREAMING) { method_name += "Async"; // prevent name clash with synchronous method. } + GenerateDocCommentClientMethod(out, method, false, false); out->Print( - "public $returntype$ $methodname$($request_maybe$Metadata headers = null, DateTime? deadline = null, CancellationToken cancellationToken = default(CancellationToken))\n", + "public virtual $returntype$ " + "$methodname$($request_maybe$grpc::Metadata " + "headers = null, global::System.DateTime? deadline = null, " + "global::System.Threading.CancellationToken " + "cancellationToken = " + "default(global::System.Threading.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->Print( + "return $methodname$($request_maybe$new grpc::CallOptions(headers, " + "deadline, " + "cancellationToken));\n", + "methodname", method_name, "request_maybe", + GetMethodRequestParamMaybe(method, true)); out->Outdent(); out->Print("}\n"); // overload taking CallOptions as a param + GenerateDocCommentClientMethod(out, method, false, true); out->Print( - "public $returntype$ $methodname$($request_maybe$CallOptions options)\n", + "public virtual $returntype$ " + "$methodname$($request_maybe$grpc::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"); + out->Print( + "return CallInvoker.AsyncUnaryCall($methodfield$, null, options, " + "request);\n", + "methodfield", GetMethodFieldName(method)); break; case METHODTYPE_CLIENT_STREAMING: - out->Print("return Calls.AsyncClientStreamingCall(call);\n"); + out->Print( + "return CallInvoker.AsyncClientStreamingCall($methodfield$, null, " + "options);\n", + "methodfield", GetMethodFieldName(method)); break; case METHODTYPE_SERVER_STREAMING: out->Print( - "return Calls.AsyncServerStreamingCall(call, request);\n"); + "return CallInvoker.AsyncServerStreamingCall($methodfield$, null, " + "options, request);\n", + "methodfield", GetMethodFieldName(method)); break; case METHODTYPE_BIDI_STREAMING: - out->Print("return Calls.AsyncDuplexStreamingCall(call);\n"); + out->Print( + "return CallInvoker.AsyncDuplexStreamingCall($methodfield$, null, " + "options);\n", + "methodfield", GetMethodFieldName(method)); break; default: - GOOGLE_LOG(FATAL)<< "Can't get here."; + GOOGLE_LOG(FATAL) << "Can't get here."; } out->Outdent(); out->Print("}\n"); } + + // override NewInstance method + out->Print( + "/// Creates a new instance of client from given " + "ClientBaseConfiguration.\n"); + out->Print( + "protected override $name$ NewInstance(ClientBaseConfiguration " + "configuration)\n", + "name", GetClientClassName(service)); + out->Print("{\n"); + out->Indent(); + out->Print("return new $name$(configuration);\n", "name", + GetClientClassName(service)); + out->Outdent(); + out->Print("}\n"); + out->Outdent(); out->Print("}\n"); out->Print("\n"); } -void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service) { +void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor* service) { + out->Print( + "/// Creates service definition that can be registered with a " + "server\n"); out->Print( - "// creates service definition that can be registered with a server\n"); + "/// An object implementing the server-side" + " handling logic.\n"); out->Print( - "public static ServerServiceDefinition BindService($interface$ serviceImpl)\n", - "interface", GetServerInterfaceName(service)); + "public static grpc::ServerServiceDefinition BindService($implclass$ " + "serviceImpl)\n", + "implclass", GetServerClassName(service)); out->Print("{\n"); out->Indent(); - out->Print( - "return ServerServiceDefinition.CreateBuilder($servicenamefield$)\n", - "servicenamefield", GetServiceNameFieldName()); + out->Print("return grpc::ServerServiceDefinition.CreateBuilder()"); 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$)", + const MethodDescriptor* method = service->method(i); + out->Print("\n.AddMethod($methodfield$, serviceImpl.$methodname$)", "methodfield", GetMethodFieldName(method), "methodname", method->name()); - if (i == service->method_count() - 1) { - out->Print(".Build();"); - } - out->Print("\n"); } + out->Print(".Build();\n"); out->Outdent(); out->Outdent(); @@ -459,21 +609,48 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service) { 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)); +void GenerateBindServiceWithBinderMethod(Printer* out, + const ServiceDescriptor* service) { + out->Print( + "/// Register service method implementations with a service " + "binder. Useful when customizing the service binding logic.\n" + "/// Note: this method is part of an experimental API that can change or " + "be " + "removed without any prior notice.\n"); + out->Print( + "/// Service methods will be bound by " + "calling AddMethod on this object." + "\n"); + out->Print( + "/// An object implementing the server-side" + " handling logic.\n"); + out->Print( + "public static void BindService(grpc::ServiceBinderBase serviceBinder, " + "$implclass$ " + "serviceImpl)\n", + "implclass", GetServerClassName(service)); out->Print("{\n"); out->Indent(); - out->Print("return new $classname$(channel);\n", "classname", - GetClientClassName(service)); + + for (int i = 0; i < service->method_count(); i++) { + const MethodDescriptor* method = service->method(i); + out->Print( + "serviceBinder.AddMethod($methodfield$, serviceImpl.$methodname$);\n", + "methodfield", GetMethodFieldName(method), "methodname", + method->name()); + } + out->Outdent(); out->Print("}\n"); out->Print("\n"); } -void GenerateService(Printer* out, const ServiceDescriptor *service) { - out->Print("public static class $classname$\n", "classname", +void GenerateService(Printer* out, const ServiceDescriptor* service, + bool generate_client, bool generate_server, + bool internal_access) { + GenerateDocCommentBody(out, service); + out->Print("$access_level$ static partial class $classname$\n", + "access_level", GetAccessLevel(internal_access), "classname", GetServiceClassName(service)); out->Print("{\n"); out->Indent(); @@ -487,11 +664,17 @@ void GenerateService(Printer* out, const ServiceDescriptor *service) { GenerateStaticMethodField(out, service->method(i)); } GenerateServiceDescriptorProperty(out, service); - GenerateClientInterface(out, service); - GenerateServerInterface(out, service); - GenerateClientStub(out, service); - GenerateBindServiceMethod(out, service); - GenerateNewStubMethods(out, service); + + if (generate_server) { + GenerateServerClass(out, service); + } + if (generate_client) { + GenerateClientStub(out, service); + } + if (generate_server) { + GenerateBindServiceMethod(out, service); + GenerateBindServiceWithBinderMethod(out, service); + } out->Outdent(); out->Print("}\n"); @@ -499,7 +682,8 @@ void GenerateService(Printer* out, const ServiceDescriptor *service) { } // anonymous namespace -grpc::string GetServices(const FileDescriptor *file) { +grpc::string GetServices(const FileDescriptor* file, bool generate_client, + bool generate_server, bool internal_access) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -514,23 +698,39 @@ grpc::string GetServices(const FileDescriptor *file) { } // 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("// \n"); + out.Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); + out.Print("// source: $filename$\n", "filename", file->name()); + out.Print("// \n"); + + // use C++ style as there are no file-level XML comments in .NET + grpc::string leading_comments = GetCsharpComments(file, true); + if (!leading_comments.empty()) { + out.Print("// Original file comments:\n"); + out.PrintRaw(leading_comments.c_str()); + } + + out.Print("#pragma warning disable 0414, 1591\n"); + 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("using grpc = global::Grpc.Core;\n"); out.Print("\n"); - out.Print("namespace $namespace$ {\n", "namespace", GetFileNamespace(file)); - out.Indent(); + grpc::string file_namespace = GetFileNamespace(file); + if (file_namespace != "") { + out.Print("namespace $namespace$ {\n", "namespace", file_namespace); + out.Indent(); + } for (int i = 0; i < file->service_count(); i++) { - GenerateService(&out, file->service(i)); + GenerateService(&out, file->service(i), generate_client, generate_server, + internal_access); + } + if (file_namespace != "") { + out.Outdent(); + out.Print("}\n"); } - out.Outdent(); - out.Print("}\n"); out.Print("#endregion\n"); } return output; diff --git a/third_party/grpc/src/compiler/csharp_generator.h b/third_party/grpc/src/compiler/csharp_generator.h index 90eb7e298466d3..fd36e11851b0c4 100644 --- a/third_party/grpc/src/compiler/csharp_generator.h +++ b/third_party/grpc/src/compiler/csharp_generator.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -40,7 +25,9 @@ namespace grpc_csharp_generator { -grpc::string GetServices(const grpc::protobuf::FileDescriptor *file); +grpc::string GetServices(const grpc::protobuf::FileDescriptor* file, + bool generate_client, bool generate_server, + bool internal_access); } // namespace grpc_csharp_generator diff --git a/third_party/grpc/src/compiler/csharp_generator_helpers.h b/third_party/grpc/src/compiler/csharp_generator_helpers.h index 5639ea058b80a4..8c89925551cd45 100644 --- a/third_party/grpc/src/compiler/csharp_generator_helpers.h +++ b/third_party/grpc/src/compiler/csharp_generator_helpers.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -39,12 +24,21 @@ 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"; +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; } +// Get leading or trailing comments in a string. Comment lines start with "// ". +// Leading detached comments are put in in front of leading comments. +template +inline grpc::string GetCsharpComments(const DescriptorType* desc, + bool leading) { + return grpc_generator::GetPrefixedComments(desc, leading, "//"); +} + } // namespace grpc_csharp_generator #endif // GRPC_INTERNAL_COMPILER_CSHARP_GENERATOR_HELPERS_H diff --git a/third_party/grpc/src/compiler/csharp_plugin.cc b/third_party/grpc/src/compiler/csharp_plugin.cc index 8b9395f9e2b21a..5f13aa6749e0cd 100644 --- a/third_party/grpc/src/compiler/csharp_plugin.cc +++ b/third_party/grpc/src/compiler/csharp_plugin.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -44,11 +29,31 @@ class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { 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); + bool Generate(const grpc::protobuf::FileDescriptor* file, + const grpc::string& parameter, + grpc::protobuf::compiler::GeneratorContext* context, + grpc::string* error) const { + std::vector > options; + grpc::protobuf::compiler::ParseGeneratorParameter(parameter, &options); + + bool generate_client = true; + bool generate_server = true; + bool internal_access = false; + for (size_t i = 0; i < options.size(); i++) { + if (options[i].first == "no_client") { + generate_client = false; + } else if (options[i].first == "no_server") { + generate_server = false; + } else if (options[i].first == "internal_access") { + internal_access = true; + } else { + *error = "Unknown generator option: " + options[i].first; + return false; + } + } + + grpc::string code = grpc_csharp_generator::GetServices( + file, generate_client, generate_server, internal_access); if (code.size() == 0) { return true; // don't generate a file if there are no services } @@ -66,7 +71,7 @@ class CSharpGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { } }; -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { CSharpGrpcGenerator generator; return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); } diff --git a/third_party/grpc/src/compiler/generator_helpers.h b/third_party/grpc/src/compiler/generator_helpers.h index e1bb66a8753b0c..747096f0657e38 100644 --- a/third_party/grpc/src/compiler/generator_helpers.h +++ b/third_party/grpc/src/compiler/generator_helpers.h @@ -1,46 +1,35 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H #define GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H +#include #include +#include +#include +#include #include "src/compiler/config.h" namespace grpc_generator { -inline bool StripSuffix(grpc::string *filename, const grpc::string &suffix) { +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) { @@ -52,6 +41,16 @@ inline bool StripSuffix(grpc::string *filename, const grpc::string &suffix) { return false; } +inline bool StripPrefix(grpc::string* name, const grpc::string& prefix) { + if (name->length() >= prefix.length()) { + if (name->substr(0, prefix.size()) == prefix) { + *name = name->substr(prefix.size()); + return true; + } + } + return false; +} + inline grpc::string StripProto(grpc::string filename) { if (!StripSuffix(&filename, ".protodevel")) { StripSuffix(&filename, ".proto"); @@ -59,8 +58,8 @@ inline grpc::string StripProto(grpc::string filename) { return filename; } -inline grpc::string StringReplace(grpc::string str, const grpc::string &from, - const grpc::string &to, bool replace_all) { +inline grpc::string StringReplace(grpc::string str, const grpc::string& from, + const grpc::string& to, bool replace_all) { size_t pos = 0; do { @@ -70,18 +69,18 @@ inline grpc::string StringReplace(grpc::string str, const grpc::string &from, } str.replace(pos, from.length(), to); pos += to.length(); - } while(replace_all); + } while (replace_all); return str; } -inline grpc::string StringReplace(grpc::string str, const grpc::string &from, - const grpc::string &to) { +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) { +inline std::vector tokenize(const grpc::string& input, + const grpc::string& delimiters) { std::vector tokens; size_t pos, last_pos = 0; @@ -125,8 +124,8 @@ inline grpc::string LowerUnderscoreToUpperCamel(grpc::string str) { return result; } -inline grpc::string FileNameInUpperCamel(const grpc::protobuf::FileDescriptor *file, - bool include_package_path) { +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) { @@ -138,7 +137,8 @@ inline grpc::string FileNameInUpperCamel(const grpc::protobuf::FileDescriptor *f return result; } -inline grpc::string FileNameInUpperCamel(const grpc::protobuf::FileDescriptor *file) { +inline grpc::string FileNameInUpperCamel( + const grpc::protobuf::FileDescriptor* file) { return FileNameInUpperCamel(file, true); } @@ -149,7 +149,8 @@ enum MethodType { METHODTYPE_BIDI_STREAMING }; -inline MethodType GetMethodType(const grpc::protobuf::MethodDescriptor *method) { +inline MethodType GetMethodType( + const grpc::protobuf::MethodDescriptor* method) { if (method->client_streaming()) { if (method->server_streaming()) { return METHODTYPE_BIDI_STREAMING; @@ -165,6 +166,112 @@ inline MethodType GetMethodType(const grpc::protobuf::MethodDescriptor *method) } } +inline void Split(const grpc::string& s, char delim, + std::vector* append_to) { + std::istringstream iss(s); + grpc::string piece; + while (std::getline(iss, piece)) { + append_to->push_back(piece); + } +} + +enum CommentType { + COMMENTTYPE_LEADING, + COMMENTTYPE_TRAILING, + COMMENTTYPE_LEADING_DETACHED +}; + +// Get all the raw comments and append each line without newline to out. +template +inline void GetComment(const DescriptorType* desc, CommentType type, + std::vector* out) { + grpc::protobuf::SourceLocation location; + if (!desc->GetSourceLocation(&location)) { + return; + } + if (type == COMMENTTYPE_LEADING || type == COMMENTTYPE_TRAILING) { + const grpc::string& comments = type == COMMENTTYPE_LEADING + ? location.leading_comments + : location.trailing_comments; + Split(comments, '\n', out); + } else if (type == COMMENTTYPE_LEADING_DETACHED) { + for (unsigned int i = 0; i < location.leading_detached_comments.size(); + i++) { + Split(location.leading_detached_comments[i], '\n', out); + out->push_back(""); + } + } else { + std::cerr << "Unknown comment type " << type << std::endl; + abort(); + } +} + +// Each raw comment line without newline is appended to out. +// For file level leading and detached leading comments, we return comments +// above syntax line. Return nothing for trailing comments. +template <> +inline void GetComment(const grpc::protobuf::FileDescriptor* desc, + CommentType type, std::vector* out) { + if (type == COMMENTTYPE_TRAILING) { + return; + } + grpc::protobuf::SourceLocation location; + std::vector path; + path.push_back(grpc::protobuf::FileDescriptorProto::kSyntaxFieldNumber); + if (!desc->GetSourceLocation(path, &location)) { + return; + } + if (type == COMMENTTYPE_LEADING) { + Split(location.leading_comments, '\n', out); + } else if (type == COMMENTTYPE_LEADING_DETACHED) { + for (unsigned int i = 0; i < location.leading_detached_comments.size(); + i++) { + Split(location.leading_detached_comments[i], '\n', out); + out->push_back(""); + } + } else { + std::cerr << "Unknown comment type " << type << std::endl; + abort(); + } +} + +// Add prefix and newline to each comment line and concatenate them together. +// Make sure there is a space after the prefix unless the line is empty. +inline grpc::string GenerateCommentsWithPrefix( + const std::vector& in, const grpc::string& prefix) { + std::ostringstream oss; + for (auto it = in.begin(); it != in.end(); it++) { + const grpc::string& elem = *it; + if (elem.empty()) { + oss << prefix << "\n"; + } else if (elem[0] == ' ') { + oss << prefix << elem << "\n"; + } else { + oss << prefix << " " << elem << "\n"; + } + } + return oss.str(); +} + +template +inline grpc::string GetPrefixedComments(const DescriptorType* desc, + bool leading, + const grpc::string& prefix) { + std::vector out; + if (leading) { + grpc_generator::GetComment( + desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &out); + std::vector leading; + grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING, + &leading); + out.insert(out.end(), leading.begin(), leading.end()); + } else { + grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING, + &out); + } + return GenerateCommentsWithPrefix(out, prefix); +} + } // namespace grpc_generator #endif // GRPC_INTERNAL_COMPILER_GENERATOR_HELPERS_H diff --git a/third_party/grpc/src/compiler/node_generator.cc b/third_party/grpc/src/compiler/node_generator.cc new file mode 100644 index 00000000000000..a430628dbc666b --- /dev/null +++ b/third_party/grpc/src/compiler/node_generator.cc @@ -0,0 +1,278 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" +#include "src/compiler/node_generator.h" +#include "src/compiler/node_generator_helpers.h" + +using grpc::protobuf::Descriptor; +using grpc::protobuf::FileDescriptor; +using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::ServiceDescriptor; +using grpc::protobuf::io::Printer; +using grpc::protobuf::io::StringOutputStream; +using std::map; + +namespace grpc_node_generator { +namespace { + +// Returns the alias we assign to the module of the given .proto filename +// when importing. Copied entirely from +// github:google/protobuf/src/google/protobuf/compiler/js/js_generator.cc#L154 +grpc::string ModuleAlias(const grpc::string filename) { + // This scheme could technically cause problems if a file includes any 2 of: + // foo/bar_baz.proto + // foo_bar_baz.proto + // foo_bar/baz.proto + // + // We'll worry about this problem if/when we actually see it. This name isn't + // exposed to users so we can change it later if we need to. + grpc::string basename = grpc_generator::StripProto(filename); + basename = grpc_generator::StringReplace(basename, "-", "$"); + basename = grpc_generator::StringReplace(basename, "/", "_"); + basename = grpc_generator::StringReplace(basename, ".", "_"); + return basename + "_pb"; +} + +// Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript +// message file foo/bar/baz.js +grpc::string GetJSMessageFilename(const grpc::string& filename) { + grpc::string name = filename; + return grpc_generator::StripProto(name) + "_pb.js"; +} + +// Given a filename like foo/bar/baz.proto, returns the root directory +// path ../../ +grpc::string GetRootPath(const grpc::string& from_filename, + const grpc::string& to_filename) { + if (to_filename.find("google/protobuf") == 0) { + // Well-known types (.proto files in the google/protobuf directory) are + // assumed to come from the 'google-protobuf' npm package. We may want to + // generalize this exception later by letting others put generated code in + // their own npm packages. + return "google-protobuf/"; + } + size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/'); + if (slashes == 0) { + return "./"; + } + grpc::string result = ""; + for (size_t i = 0; i < slashes; i++) { + result += "../"; + } + return result; +} + +// Return the relative path to load to_file from the directory containing +// from_file, assuming that both paths are relative to the same directory +grpc::string GetRelativePath(const grpc::string& from_file, + const grpc::string& to_file) { + return GetRootPath(from_file, to_file) + to_file; +} + +/* Finds all message types used in all services in the file, and returns them + * as a map of fully qualified message type name to message descriptor */ +map GetAllMessages( + const FileDescriptor* file) { + map message_types; + for (int service_num = 0; service_num < file->service_count(); + service_num++) { + const ServiceDescriptor* service = file->service(service_num); + for (int method_num = 0; method_num < service->method_count(); + method_num++) { + const MethodDescriptor* method = service->method(method_num); + const Descriptor* input_type = method->input_type(); + const Descriptor* output_type = method->output_type(); + message_types[input_type->full_name()] = input_type; + message_types[output_type->full_name()] = output_type; + } + } + return message_types; +} + +grpc::string MessageIdentifierName(const grpc::string& name) { + return grpc_generator::StringReplace(name, ".", "_"); +} + +grpc::string NodeObjectPath(const Descriptor* descriptor) { + grpc::string module_alias = ModuleAlias(descriptor->file()->name()); + grpc::string name = descriptor->full_name(); + grpc_generator::StripPrefix(&name, descriptor->file()->package() + "."); + return module_alias + "." + name; +} + +// Prints out the message serializer and deserializer functions +void PrintMessageTransformer(const Descriptor* descriptor, Printer* out, + const Parameters& params) { + map template_vars; + grpc::string full_name = descriptor->full_name(); + template_vars["identifier_name"] = MessageIdentifierName(full_name); + template_vars["name"] = full_name; + template_vars["node_name"] = NodeObjectPath(descriptor); + // Print the serializer + out->Print(template_vars, "function serialize_$identifier_name$(arg) {\n"); + out->Indent(); + out->Print(template_vars, "if (!(arg instanceof $node_name$)) {\n"); + out->Indent(); + out->Print(template_vars, + "throw new Error('Expected argument of type $name$');\n"); + out->Outdent(); + out->Print("}\n"); + if (params.minimum_node_version > 5) { + // Node version is > 5, we should use Buffer.from + out->Print("return Buffer.from(arg.serializeBinary());\n"); + } else { + out->Print("return new Buffer(arg.serializeBinary());\n"); + } + out->Outdent(); + out->Print("}\n\n"); + + // Print the deserializer + out->Print(template_vars, + "function deserialize_$identifier_name$(buffer_arg) {\n"); + out->Indent(); + out->Print( + template_vars, + "return $node_name$.deserializeBinary(new Uint8Array(buffer_arg));\n"); + out->Outdent(); + out->Print("}\n\n"); +} + +void PrintMethod(const MethodDescriptor* method, Printer* out) { + const Descriptor* input_type = method->input_type(); + const Descriptor* output_type = method->output_type(); + map vars; + vars["service_name"] = method->service()->full_name(); + vars["name"] = method->name(); + vars["input_type"] = NodeObjectPath(input_type); + vars["input_type_id"] = MessageIdentifierName(input_type->full_name()); + vars["output_type"] = NodeObjectPath(output_type); + vars["output_type_id"] = MessageIdentifierName(output_type->full_name()); + vars["client_stream"] = method->client_streaming() ? "true" : "false"; + vars["server_stream"] = method->server_streaming() ? "true" : "false"; + out->Print("{\n"); + out->Indent(); + out->Print(vars, "path: '/$service_name$/$name$',\n"); + out->Print(vars, "requestStream: $client_stream$,\n"); + out->Print(vars, "responseStream: $server_stream$,\n"); + out->Print(vars, "requestType: $input_type$,\n"); + out->Print(vars, "responseType: $output_type$,\n"); + out->Print(vars, "requestSerialize: serialize_$input_type_id$,\n"); + out->Print(vars, "requestDeserialize: deserialize_$input_type_id$,\n"); + out->Print(vars, "responseSerialize: serialize_$output_type_id$,\n"); + out->Print(vars, "responseDeserialize: deserialize_$output_type_id$,\n"); + out->Outdent(); + out->Print("}"); +} + +// Prints out the service descriptor object +void PrintService(const ServiceDescriptor* service, Printer* out) { + map template_vars; + out->Print(GetNodeComments(service, true).c_str()); + template_vars["name"] = service->name(); + out->Print(template_vars, "var $name$Service = exports.$name$Service = {\n"); + out->Indent(); + for (int i = 0; i < service->method_count(); i++) { + grpc::string method_name = + grpc_generator::LowercaseFirstLetter(service->method(i)->name()); + out->Print(GetNodeComments(service->method(i), true).c_str()); + out->Print("$method_name$: ", "method_name", method_name); + PrintMethod(service->method(i), out); + out->Print(",\n"); + out->Print(GetNodeComments(service->method(i), false).c_str()); + } + out->Outdent(); + out->Print("};\n\n"); + out->Print(template_vars, + "exports.$name$Client = " + "grpc.makeGenericClientConstructor($name$Service);\n"); + out->Print(GetNodeComments(service, false).c_str()); +} + +void PrintImports(const FileDescriptor* file, Printer* out) { + out->Print("var grpc = require('grpc');\n"); + if (file->message_type_count() > 0) { + grpc::string file_path = + GetRelativePath(file->name(), GetJSMessageFilename(file->name())); + out->Print("var $module_alias$ = require('$file_path$');\n", "module_alias", + ModuleAlias(file->name()), "file_path", file_path); + } + + for (int i = 0; i < file->dependency_count(); i++) { + grpc::string file_path = GetRelativePath( + file->name(), GetJSMessageFilename(file->dependency(i)->name())); + out->Print("var $module_alias$ = require('$file_path$');\n", "module_alias", + ModuleAlias(file->dependency(i)->name()), "file_path", + file_path); + } + out->Print("\n"); +} + +void PrintTransformers(const FileDescriptor* file, Printer* out, + const Parameters& params) { + map messages = GetAllMessages(file); + for (std::map::iterator it = + messages.begin(); + it != messages.end(); it++) { + PrintMessageTransformer(it->second, out, params); + } + out->Print("\n"); +} + +void PrintServices(const FileDescriptor* file, Printer* out) { + for (int i = 0; i < file->service_count(); i++) { + PrintService(file->service(i), out); + } +} +} // namespace + +grpc::string GenerateFile(const FileDescriptor* file, + const Parameters& params) { + grpc::string output; + { + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + + if (file->service_count() == 0) { + return output; + } + out.Print("// GENERATED CODE -- DO NOT EDIT!\n\n"); + + grpc::string leading_comments = GetNodeComments(file, true); + if (!leading_comments.empty()) { + out.Print("// Original file comments:\n"); + out.PrintRaw(leading_comments.c_str()); + } + + out.Print("'use strict';\n"); + + PrintImports(file, &out); + + PrintTransformers(file, &out, params); + + PrintServices(file, &out); + + out.Print(GetNodeComments(file, false).c_str()); + } + return output; +} + +} // namespace grpc_node_generator diff --git a/third_party/grpc/src/compiler/node_generator.h b/third_party/grpc/src/compiler/node_generator.h new file mode 100644 index 00000000000000..f3a531597a1bcd --- /dev/null +++ b/third_party/grpc/src/compiler/node_generator.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H + +#include "src/compiler/config.h" + +namespace grpc_node_generator { + +// Contains all the parameters that are parsed from the command line. +struct Parameters { + // Sets the earliest version of nodejs that needs to be supported. + int minimum_node_version; +}; + +grpc::string GenerateFile(const grpc::protobuf::FileDescriptor* file, + const Parameters& params); + +} // namespace grpc_node_generator + +#endif // GRPC_INTERNAL_COMPILER_NODE_GENERATOR_H diff --git a/third_party/grpc/src/compiler/node_generator_helpers.h b/third_party/grpc/src/compiler/node_generator_helpers.h new file mode 100644 index 00000000000000..82d2d8454419f6 --- /dev/null +++ b/third_party/grpc/src/compiler/node_generator_helpers.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H + +#include + +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" + +namespace grpc_node_generator { + +inline grpc::string GetJSServiceFilename(const grpc::string& filename) { + return grpc_generator::StripProto(filename) + "_grpc_pb.js"; +} + +// Get leading or trailing comments in a string. Comment lines start with "// ". +// Leading detached comments are put in in front of leading comments. +template +inline grpc::string GetNodeComments(const DescriptorType* desc, bool leading) { + return grpc_generator::GetPrefixedComments(desc, leading, "//"); +} + +} // namespace grpc_node_generator + +#endif // GRPC_INTERNAL_COMPILER_NODE_GENERATOR_HELPERS_H diff --git a/third_party/grpc/src/compiler/node_plugin.cc b/third_party/grpc/src/compiler/node_plugin.cc new file mode 100644 index 00000000000000..0d19d8e9820b9d --- /dev/null +++ b/third_party/grpc/src/compiler/node_plugin.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Generates Node gRPC service interface out of Protobuf IDL. + +#include + +#include "src/compiler/config.h" +#include "src/compiler/node_generator.h" +#include "src/compiler/node_generator_helpers.h" + +using grpc_node_generator::GenerateFile; +using grpc_node_generator::GetJSServiceFilename; + +class NodeGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + NodeGrpcGenerator() {} + ~NodeGrpcGenerator() {} + + bool Generate(const grpc::protobuf::FileDescriptor* file, + const grpc::string& parameter, + grpc::protobuf::compiler::GeneratorContext* context, + grpc::string* error) const { + grpc_node_generator::Parameters generator_parameters; + generator_parameters.minimum_node_version = 4; + + 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] == "minimum_node_version") { + sscanf(param[1].c_str(), "%d", + &generator_parameters.minimum_node_version); + } else { + *error = grpc::string("Unknown parameter: ") + *parameter_string; + return false; + } + } + } + + grpc::string code = GenerateFile(file, generator_parameters); + if (code.size() == 0) { + return true; + } + + // Get output file name + grpc::string file_name = GetJSServiceFilename(file->name()); + + 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[]) { + NodeGrpcGenerator generator; + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/third_party/grpc/src/compiler/objective_c_generator.cc b/third_party/grpc/src/compiler/objective_c_generator.cc index ff092053ad0938..39f68cb95651f9 100644 --- a/third_party/grpc/src/compiler/objective_c_generator.cc +++ b/third_party/grpc/src/compiler/objective_c_generator.cc @@ -1,37 +1,23 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include +#include #include #include "src/compiler/config.h" @@ -41,17 +27,20 @@ #include using ::google::protobuf::compiler::objectivec::ClassName; -using ::grpc::protobuf::io::Printer; +using ::grpc::protobuf::FileDescriptor; +using ::grpc::protobuf::FileDescriptor; using ::grpc::protobuf::MethodDescriptor; using ::grpc::protobuf::ServiceDescriptor; +using ::grpc::protobuf::io::Printer; using ::std::map; +using ::std::set; namespace grpc_objective_c_generator { namespace { -void PrintProtoRpcDeclarationAsPragma(Printer *printer, - const MethodDescriptor *method, - map< ::grpc::string, ::grpc::string> vars) { +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 " : ""; @@ -60,9 +49,34 @@ void PrintProtoRpcDeclarationAsPragma(Printer *printer, " 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. +template +static void PrintAllComments(const DescriptorType* desc, Printer* printer) { + std::vector comments; + grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED, + &comments); + grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING, + &comments); + grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING, + &comments); + if (comments.empty()) { + return; + } + printer->Print("/**\n"); + for (auto it = comments.begin(); it != comments.end(); ++it) { + printer->Print(" * "); + size_t start_pos = it->find_first_not_of(' '); + if (start_pos != grpc::string::npos) { + printer->PrintRaw(it->c_str() + start_pos); + } + printer->Print("\n"); + } + printer->Print(" */\n"); +} + +void PrintMethodSignature(Printer* printer, const MethodDescriptor* method, + const map< ::grpc::string, ::grpc::string>& vars) { + // Print comment + PrintAllComments(method, printer); printer->Print(vars, "- ($return_type$)$method_name$With"); if (method->client_streaming()) { @@ -75,15 +89,16 @@ void PrintMethodSignature(Printer *printer, const MethodDescriptor *method, if (method->server_streaming()) { printer->Print(vars, " eventHandler:(void(^)(BOOL done, " - "$response_class$ *response, NSError *error))eventHandler"); + "$response_class$ *_Nullable response, NSError *_Nullable " + "error))eventHandler"); } else { printer->Print(vars, - " handler:(void(^)($response_class$ *response, " - "NSError *error))handler"); + " handler:(void(^)($response_class$ *_Nullable response, " + "NSError *_Nullable error))handler"); } } -void PrintSimpleSignature(Printer *printer, const MethodDescriptor *method, +void PrintSimpleSignature(Printer* printer, const MethodDescriptor* method, map< ::grpc::string, ::grpc::string> vars) { vars["method_name"] = grpc_generator::LowercaseFirstLetter(vars["method_name"]); @@ -91,14 +106,15 @@ void PrintSimpleSignature(Printer *printer, const MethodDescriptor *method, PrintMethodSignature(printer, method, vars); } -void PrintAdvancedSignature(Printer *printer, const MethodDescriptor *method, +void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method, map< ::grpc::string, ::grpc::string> vars) { vars["method_name"] = "RPCTo" + vars["method_name"]; - vars["return_type"] = "ProtoRPC *"; + vars["return_type"] = "GRPCProtoCall *"; PrintMethodSignature(printer, method, vars); } -inline map< ::grpc::string, ::grpc::string> GetMethodVars(const MethodDescriptor *method) { +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(); @@ -108,7 +124,7 @@ inline map< ::grpc::string, ::grpc::string> GetMethodVars(const MethodDescriptor return res; } -void PrintMethodDeclarations(Printer *printer, const MethodDescriptor *method) { +void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) { map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method); PrintProtoRpcDeclarationAsPragma(printer, method, vars); @@ -119,7 +135,7 @@ void PrintMethodDeclarations(Printer *printer, const MethodDescriptor *method) { printer->Print(";\n\n\n"); } -void PrintSimpleImplementation(Printer *printer, const MethodDescriptor *method, +void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method, map< ::grpc::string, ::grpc::string> vars) { printer->Print("{\n"); printer->Print(vars, " [[self RPCTo$method_name$With"); @@ -136,8 +152,8 @@ void PrintSimpleImplementation(Printer *printer, const MethodDescriptor *method, printer->Print("}\n"); } -void PrintAdvancedImplementation(Printer *printer, - const MethodDescriptor *method, +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"); @@ -161,8 +177,8 @@ void PrintAdvancedImplementation(Printer *printer, printer->Print("}\n"); } -void PrintMethodImplementations(Printer *printer, - const MethodDescriptor *method) { +void PrintMethodImplementations(Printer* printer, + const MethodDescriptor* method) { map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method); PrintProtoRpcDeclarationAsPragma(printer, method, vars); @@ -178,72 +194,108 @@ void PrintMethodImplementations(Printer *printer, } // namespace -::grpc::string GetHeader(const ServiceDescriptor *service) { +::grpc::string GetAllMessageClasses(const FileDescriptor* file) { ::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, '$'); + set< ::grpc::string> classes; + for (int i = 0; i < file->service_count(); i++) { + const auto service = file->service(i); + for (int i = 0; i < service->method_count(); i++) { + const auto method = service->method(i); + classes.insert(ClassName(method->input_type())); + classes.insert(ClassName(method->output_type())); + } + } + for (auto one_class : classes) { + output += "@class " + one_class + ";\n"; + } - map< ::grpc::string, ::grpc::string> vars = {{"service_class", ServiceClassName(service)}}; + return output; +} - printer.Print(vars, "@protocol $service_class$ \n\n"); +::grpc::string GetProtocol(const ServiceDescriptor* service) { + ::grpc::string output; - for (int i = 0; i < service->method_count(); i++) { - PrintMethodDeclarations(&printer, service->method(i)); - } - printer.Print("@end\n\n"); + // Scope the output stream so it closes and finalizes output to the string. + grpc::protobuf::io::StringOutputStream output_stream(&output); + Printer printer(&output_stream, '$'); - 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("+ (instancetype)serviceWithHost:(NSString *)host;\n"); - printer.Print("@end\n"); + 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"); + return output; } -::grpc::string GetSource(const ServiceDescriptor *service) { - ::grpc::string output; +::grpc::string GetInterface(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, + "/**\n" + " * Basic service implementation, over gRPC, that only does\n" + " * marshalling and parsing.\n" + " */\n"); + printer.Print(vars, + "@interface $service_class$ :" + " GRPCProtoService<$service_class$>\n"); + printer.Print( + "- (instancetype)initWithHost:(NSString *)host" + " NS_DESIGNATED_INITIALIZER;\n"); + printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\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()}}; + 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"); + "@implementation $service_class$\n\n" + "// Designated initializer\n" + "- (instancetype)initWithHost:(NSString *)host {\n" + " self = [super initWithHost:host\n" + " packageName:@\"$package$\"\n" + " serviceName:@\"$service_name$\"];\n" + " return self;\n" + "}\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"); - printer.Print("+ (instancetype)serviceWithHost:(NSString *)host {\n"); - printer.Print(" return [[self alloc] initWithHost:host];\n"); - printer.Print("}\n\n\n"); + " package and service names.\n" + "- (instancetype)initWithHost:(NSString *)host\n" + " packageName:(NSString *)packageName\n" + " serviceName:(NSString *)serviceName {\n" + " return [self initWithHost:host];\n" + "}\n\n"); + + printer.Print( + "#pragma mark - Class Methods\n\n" + "+ (instancetype)serviceWithHost:(NSString *)host {\n" + " return [[self alloc] initWithHost:host];\n" + "}\n\n"); + + printer.Print("#pragma mark - Method Implementations\n\n"); for (int i = 0; i < service->method_count(); i++) { PrintMethodImplementations(&printer, service->method(i)); diff --git a/third_party/grpc/src/compiler/objective_c_generator.h b/third_party/grpc/src/compiler/objective_c_generator.h index 40a0c87f99cef4..eb1c7ff005a1ff 100644 --- a/third_party/grpc/src/compiler/objective_c_generator.h +++ b/third_party/grpc/src/compiler/objective_c_generator.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -38,16 +23,25 @@ namespace grpc_objective_c_generator { +using ::grpc::protobuf::FileDescriptor; +using ::grpc::protobuf::FileDescriptor; 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 forward declaration of classes in the generated header file. +string GetAllMessageClasses(const FileDescriptor* file); + +// Returns the content to be included defining the @protocol segment at the +// insertion point of the generated implementation file. +string GetProtocol(const ServiceDescriptor* service); + +// Returns the content to be included defining the @interface segment at the +// insertion point of the generated implementation file. +string GetInterface(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); +string GetSource(const ServiceDescriptor* service); } // namespace grpc_objective_c_generator diff --git a/third_party/grpc/src/compiler/objective_c_generator_helpers.h b/third_party/grpc/src/compiler/objective_c_generator_helpers.h index 1f8c80014f0fac..a284da97f4b26d 100644 --- a/third_party/grpc/src/compiler/objective_c_generator_helpers.h +++ b/third_party/grpc/src/compiler/objective_c_generator_helpers.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -38,21 +23,62 @@ #include "src/compiler/config.h" #include "src/compiler/generator_helpers.h" +#include + 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 MessageHeaderName(const FileDescriptor* file) { + return google::protobuf::compiler::objectivec::FilePath(file) + ".pbobjc.h"; } -inline string ServiceClassName(const ServiceDescriptor *service) { - const FileDescriptor *file = service->file(); +inline string ServiceClassName(const ServiceDescriptor* service) { + const FileDescriptor* file = service->file(); string prefix = file->options().objc_class_prefix(); return prefix + service->name(); } +inline ::grpc::string LocalImport(const ::grpc::string& import) { + return ::grpc::string("#import \"" + import + "\"\n"); +} + +inline ::grpc::string SystemImport(const ::grpc::string& import) { + return ::grpc::string("#import <" + import + ">\n"); +} + +inline ::grpc::string PreprocConditional(::grpc::string symbol, bool invert) { + return invert ? "!defined(" + symbol + ") || !" + symbol + : "defined(" + symbol + ") && " + symbol; +} + +inline ::grpc::string PreprocIf(const ::grpc::string& symbol, + const ::grpc::string& if_true) { + return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" + + if_true + "#endif\n"); } + +inline ::grpc::string PreprocIfNot(const ::grpc::string& symbol, + const ::grpc::string& if_true) { + return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" + + if_true + "#endif\n"); +} + +inline ::grpc::string PreprocIfElse(const ::grpc::string& symbol, + const ::grpc::string& if_true, + const ::grpc::string& if_false) { + return ::grpc::string("#if " + PreprocConditional(symbol, false) + "\n" + + if_true + "#else\n" + if_false + "#endif\n"); +} + +inline ::grpc::string PreprocIfNotElse(const ::grpc::string& symbol, + const ::grpc::string& if_true, + const ::grpc::string& if_false) { + return ::grpc::string("#if " + PreprocConditional(symbol, true) + "\n" + + if_true + "#else\n" + if_false + "#endif\n"); +} + +} // namespace grpc_objective_c_generator #endif // GRPC_INTERNAL_COMPILER_OBJECTIVE_C_GENERATOR_HELPERS_H diff --git a/third_party/grpc/src/compiler/objective_c_plugin.cc b/third_party/grpc/src/compiler/objective_c_plugin.cc index 17440358bb837e..f0fe3688cca5f4 100644 --- a/third_party/grpc/src/compiler/objective_c_plugin.cc +++ b/third_party/grpc/src/compiler/objective_c_plugin.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -39,67 +24,129 @@ #include "src/compiler/objective_c_generator.h" #include "src/compiler/objective_c_generator_helpers.h" +#include + +using ::google::protobuf::compiler::objectivec:: + IsProtobufLibraryBundledProtoFile; +using ::google::protobuf::compiler::objectivec::ProtobufLibraryFrameworkName; +using ::grpc_objective_c_generator::LocalImport; +using ::grpc_objective_c_generator::PreprocIfElse; +using ::grpc_objective_c_generator::PreprocIfNot; +using ::grpc_objective_c_generator::SystemImport; + +namespace { + +inline ::grpc::string ImportProtoHeaders( + const grpc::protobuf::FileDescriptor* dep, const char* indent) { + ::grpc::string header = grpc_objective_c_generator::MessageHeaderName(dep); + + if (!IsProtobufLibraryBundledProtoFile(dep)) { + return indent + LocalImport(header); + } + + ::grpc::string base_name = header; + grpc_generator::StripPrefix(&base_name, "google/protobuf/"); + // create the import code snippet + ::grpc::string framework_header = + ::grpc::string(ProtobufLibraryFrameworkName) + "/" + base_name; + + static const ::grpc::string kFrameworkImportsCondition = + "GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS"; + return PreprocIfElse(kFrameworkImportsCondition, + indent + SystemImport(framework_header), + indent + LocalImport(header)); +} + +} // namespace + 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 { - + public: + virtual bool Generate(const grpc::protobuf::FileDescriptor* file, + const ::grpc::string& parameter, + 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(); + static const ::grpc::string kNonNullBegin = "NS_ASSUME_NONNULL_BEGIN\n"; + static const ::grpc::string kNonNullEnd = "NS_ASSUME_NONNULL_END\n"; + static const ::grpc::string kProtocolOnly = "GPB_GRPC_PROTOCOL_ONLY"; + static const ::grpc::string kForwardDeclare = + "GPB_GRPC_FORWARD_DECLARE_MESSAGE_PROTO"; + + ::grpc::string file_name = + google::protobuf::compiler::objectivec::FilePath(file); { // Generate .pbrpc.h - ::grpc::string imports = ::grpc::string("#import \"") + file_name + - ".pbobjc.h\"\n\n" - "#import \n" - "#import \n" - "#import \n"; + ::grpc::string imports = LocalImport(file_name + ".pbobjc.h"); + + ::grpc::string system_imports = SystemImport("ProtoRPC/ProtoService.h") + + SystemImport("ProtoRPC/ProtoRPC.h") + + SystemImport("RxLibrary/GRXWriteable.h") + + SystemImport("RxLibrary/GRXWriter.h"); + + ::grpc::string forward_declarations = "@class GRPCProtoCall;\n\n"; - // TODO(jcanizales): Instead forward-declare the input and output types - // and import the files in the .pbrpc.m - ::grpc::string proto_imports; + ::grpc::string class_declarations = + grpc_objective_c_generator::GetAllMessageClasses(file); + + ::grpc::string class_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"; + class_imports += ImportProtoHeaders(file->dependency(i), " "); + } + + ::grpc::string protocols; + for (int i = 0; i < file->service_count(); i++) { + const grpc::protobuf::ServiceDescriptor* service = file->service(i); + protocols += grpc_objective_c_generator::GetProtocol(service); } - ::grpc::string declarations; + ::grpc::string interfaces; for (int i = 0; i < file->service_count(); i++) { - const grpc::protobuf::ServiceDescriptor *service = file->service(i); - declarations += grpc_objective_c_generator::GetHeader(service); + const grpc::protobuf::ServiceDescriptor* service = file->service(i); + interfaces += grpc_objective_c_generator::GetInterface(service); } Write(context, file_name + ".pbrpc.h", - imports + '\n' + proto_imports + '\n' + declarations); + PreprocIfNot(kForwardDeclare, imports) + "\n" + + PreprocIfNot(kProtocolOnly, system_imports) + "\n" + + class_declarations + "\n" + + PreprocIfNot(kForwardDeclare, class_imports) + "\n" + + forward_declarations + "\n" + kNonNullBegin + "\n" + protocols + + "\n" + PreprocIfNot(kProtocolOnly, interfaces) + "\n" + + kNonNullEnd + "\n"); } { // Generate .pbrpc.m - ::grpc::string imports = ::grpc::string("#import \"") + file_name + - ".pbrpc.h\"\n\n" - "#import \n" - "#import \n"; + ::grpc::string imports = LocalImport(file_name + ".pbrpc.h") + + LocalImport(file_name + ".pbobjc.h") + + SystemImport("ProtoRPC/ProtoRPC.h") + + SystemImport("RxLibrary/GRXWriter+Immediate.h"); + + ::grpc::string class_imports; + for (int i = 0; i < file->dependency_count(); i++) { + class_imports += ImportProtoHeaders(file->dependency(i), ""); + } ::grpc::string definitions; for (int i = 0; i < file->service_count(); i++) { - const grpc::protobuf::ServiceDescriptor *service = file->service(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); + Write(context, file_name + ".pbrpc.m", + PreprocIfNot(kProtocolOnly, + imports + "\n" + class_imports + "\n" + definitions)); } return true; @@ -107,8 +154,8 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { 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 { + 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()); @@ -116,7 +163,7 @@ class ObjectiveCGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { } }; -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { ObjectiveCGrpcGenerator generator; return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); } diff --git a/third_party/grpc/src/compiler/php_generator.cc b/third_party/grpc/src/compiler/php_generator.cc new file mode 100644 index 00000000000000..ca084cccbdfb0d --- /dev/null +++ b/third_party/grpc/src/compiler/php_generator.cc @@ -0,0 +1,184 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" +#include "src/compiler/php_generator_helpers.h" + +using google::protobuf::compiler::php::GeneratedClassName; +using grpc::protobuf::Descriptor; +using grpc::protobuf::FileDescriptor; +using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::ServiceDescriptor; +using grpc::protobuf::io::Printer; +using grpc::protobuf::io::StringOutputStream; +using std::map; + +namespace grpc_php_generator { +namespace { + +grpc::string ConvertToPhpNamespace(const grpc::string& name) { + std::vector tokens = grpc_generator::tokenize(name, "."); + std::ostringstream oss; + for (unsigned int i = 0; i < tokens.size(); i++) { + oss << (i == 0 ? "" : "\\") + << grpc_generator::CapitalizeFirstLetter(tokens[i]); + } + return oss.str(); +} + +grpc::string PackageName(const FileDescriptor* file) { + if (file->options().has_php_namespace()) { + return file->options().php_namespace(); + } else { + return ConvertToPhpNamespace(file->package()); + } +} + +grpc::string MessageIdentifierName(const grpc::string& name, + const FileDescriptor* file) { + std::vector tokens = grpc_generator::tokenize(name, "."); + std::ostringstream oss; + if (PackageName(file) != "") { + oss << PackageName(file) << "\\"; + } + oss << grpc_generator::CapitalizeFirstLetter(tokens[tokens.size() - 1]); + return oss.str(); +} + +void PrintMethod(const MethodDescriptor* method, Printer* out) { + const Descriptor* input_type = method->input_type(); + const Descriptor* output_type = method->output_type(); + map vars; + vars["service_name"] = method->service()->full_name(); + vars["name"] = method->name(); + vars["input_type_id"] = + MessageIdentifierName(GeneratedClassName(input_type), input_type->file()); + vars["output_type_id"] = MessageIdentifierName( + GeneratedClassName(output_type), output_type->file()); + + out->Print("/**\n"); + out->Print(GetPHPComments(method, " *").c_str()); + if (method->client_streaming()) { + out->Print(vars, + " * @param array $$metadata metadata\n" + " * @param array $$options call options\n */\n" + "public function $name$($$metadata = [], " + "$$options = []) {\n"); + out->Indent(); + out->Indent(); + if (method->server_streaming()) { + out->Print("return $$this->_bidiRequest("); + } else { + out->Print("return $$this->_clientStreamRequest("); + } + out->Print(vars, + "'/$service_name$/$name$',\n" + "['\\$output_type_id$','decode'],\n" + "$$metadata, $$options);\n"); + } else { + out->Print(vars, + " * @param \\$input_type_id$ $$argument input argument\n" + " * @param array $$metadata metadata\n" + " * @param array $$options call options\n */\n" + "public function $name$(\\$input_type_id$ $$argument,\n" + " $$metadata = [], $$options = []) {\n"); + out->Indent(); + out->Indent(); + if (method->server_streaming()) { + out->Print("return $$this->_serverStreamRequest("); + } else { + out->Print("return $$this->_simpleRequest("); + } + out->Print(vars, + "'/$service_name$/$name$',\n" + "$$argument,\n" + "['\\$output_type_id$', 'decode'],\n" + "$$metadata, $$options);\n"); + } + out->Outdent(); + out->Outdent(); + out->Print("}\n\n"); +} + +// Prints out the service descriptor object +void PrintService(const ServiceDescriptor* service, + const grpc::string& class_suffix, Printer* out) { + map vars; + out->Print("/**\n"); + out->Print(GetPHPComments(service, " *").c_str()); + out->Print(" */\n"); + vars["name"] = GetPHPServiceClassname(service, class_suffix); + out->Print(vars, "class $name$ extends \\Grpc\\BaseStub {\n\n"); + out->Indent(); + out->Indent(); + out->Print( + "/**\n * @param string $$hostname hostname\n" + " * @param array $$opts channel options\n" + " * @param \\Grpc\\Channel $$channel (optional) re-use channel " + "object\n */\n" + "public function __construct($$hostname, $$opts, " + "$$channel = null) {\n"); + out->Indent(); + out->Indent(); + out->Print("parent::__construct($$hostname, $$opts, $$channel);\n"); + out->Outdent(); + out->Outdent(); + out->Print("}\n\n"); + for (int i = 0; i < service->method_count(); i++) { + grpc::string method_name = + grpc_generator::LowercaseFirstLetter(service->method(i)->name()); + PrintMethod(service->method(i), out); + } + out->Outdent(); + out->Outdent(); + out->Print("}\n"); +} +} // namespace + +grpc::string GenerateFile(const FileDescriptor* file, + const ServiceDescriptor* service, + const grpc::string& class_suffix) { + grpc::string output; + { + StringOutputStream output_stream(&output); + Printer out(&output_stream, '$'); + + out.Print(" vars; + grpc::string php_namespace = PackageName(file); + vars["package"] = php_namespace; + out.Print(vars, "namespace $package$;\n\n"); + + PrintService(service, class_suffix, &out); + } + return output; +} + +} // namespace grpc_php_generator diff --git a/third_party/grpc/src/compiler/php_generator.h b/third_party/grpc/src/compiler/php_generator.h new file mode 100644 index 00000000000000..46222b3d8e7a1a --- /dev/null +++ b/third_party/grpc/src/compiler/php_generator.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_PHP_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_PHP_GENERATOR_H + +#include "src/compiler/config.h" + +namespace grpc_php_generator { + +grpc::string GenerateFile(const grpc::protobuf::FileDescriptor* file, + const grpc::protobuf::ServiceDescriptor* service, + const grpc::string& class_suffix); + +} // namespace grpc_php_generator + +#endif // GRPC_INTERNAL_COMPILER_PHP_GENERATOR_H diff --git a/third_party/grpc/src/compiler/php_generator_helpers.h b/third_party/grpc/src/compiler/php_generator_helpers.h new file mode 100644 index 00000000000000..3ad199776412cf --- /dev/null +++ b/third_party/grpc/src/compiler/php_generator_helpers.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_PHP_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_PHP_GENERATOR_HELPERS_H + +#include + +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" + +namespace grpc_php_generator { + +inline grpc::string GetPHPServiceClassname( + const grpc::protobuf::ServiceDescriptor* service, + const grpc::string& class_suffix) { + return service->name() + (class_suffix == "" ? "Client" : class_suffix); +} + +// 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; +} + +inline grpc::string GetPHPServiceFilename( + const grpc::protobuf::FileDescriptor* file, + const grpc::protobuf::ServiceDescriptor* service, + const grpc::string& class_suffix) { + std::ostringstream oss; + if (file->options().has_php_namespace()) { + oss << ReplaceAll(file->options().php_namespace(), "\\", "/"); + } else { + std::vector tokens = + grpc_generator::tokenize(file->package(), "."); + for (unsigned int i = 0; i < tokens.size(); i++) { + oss << (i == 0 ? "" : "/") + << grpc_generator::CapitalizeFirstLetter(tokens[i]); + } + } + return oss.str() + "/" + GetPHPServiceClassname(service, class_suffix) + + ".php"; +} + +// Get leading or trailing comments in a string. Comment lines start with "// ". +// Leading detached comments are put in in front of leading comments. +template +inline grpc::string GetPHPComments(const DescriptorType* desc, + grpc::string prefix) { + return ReplaceAll(grpc_generator::GetPrefixedComments(desc, true, prefix), + "*/", "*/"); +} + +} // namespace grpc_php_generator + +#endif // GRPC_INTERNAL_COMPILER_PHP_GENERATOR_HELPERS_H diff --git a/third_party/grpc/src/compiler/php_plugin.cc b/third_party/grpc/src/compiler/php_plugin.cc new file mode 100644 index 00000000000000..d01cbf87a0f0c2 --- /dev/null +++ b/third_party/grpc/src/compiler/php_plugin.cc @@ -0,0 +1,77 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Generates PHP gRPC service interface out of Protobuf IDL. + +#include + +#include "src/compiler/config.h" +#include "src/compiler/php_generator.h" +#include "src/compiler/php_generator_helpers.h" + +using google::protobuf::compiler::ParseGeneratorParameter; +using grpc_php_generator::GenerateFile; +using grpc_php_generator::GetPHPServiceFilename; + +class PHPGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { + public: + PHPGrpcGenerator() {} + ~PHPGrpcGenerator() {} + + bool Generate(const grpc::protobuf::FileDescriptor* file, + const grpc::string& parameter, + grpc::protobuf::compiler::GeneratorContext* context, + grpc::string* error) const { + if (file->service_count() == 0) { + return true; + } + + std::vector > options; + ParseGeneratorParameter(parameter, &options); + + grpc::string class_suffix; + for (size_t i = 0; i < options.size(); ++i) { + if (options[i].first == "class_suffix") { + class_suffix = options[i].second; + } else { + *error = "unsupported options: " + options[i].first; + return false; + } + } + + for (int i = 0; i < file->service_count(); i++) { + grpc::string code = GenerateFile(file, file->service(i), class_suffix); + + // Get output file name + grpc::string file_name = + GetPHPServiceFilename(file, file->service(i), class_suffix); + + 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[]) { + PHPGrpcGenerator generator; + return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); +} diff --git a/third_party/grpc/src/compiler/protobuf_plugin.h b/third_party/grpc/src/compiler/protobuf_plugin.h new file mode 100644 index 00000000000000..b971af13109150 --- /dev/null +++ b/third_party/grpc/src/compiler/protobuf_plugin.h @@ -0,0 +1,196 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_PROTOBUF_PLUGIN_H +#define GRPC_INTERNAL_COMPILER_PROTOBUF_PLUGIN_H + +#include "src/compiler/config.h" +#include "src/compiler/cpp_generator_helpers.h" +#include "src/compiler/python_generator_helpers.h" +#include "src/compiler/python_private_generator.h" +#include "src/compiler/schema_interface.h" + +#include + +// Get leading or trailing comments in a string. +template +inline grpc::string GetCommentsHelper(const DescriptorType* desc, bool leading, + const grpc::string& prefix) { + return grpc_generator::GetPrefixedComments(desc, leading, prefix); +} + +class ProtoBufMethod : public grpc_generator::Method { + public: + ProtoBufMethod(const grpc::protobuf::MethodDescriptor* method) + : method_(method) {} + + grpc::string name() const { return method_->name(); } + + grpc::string input_type_name() const { + return grpc_cpp_generator::ClassName(method_->input_type(), true); + } + grpc::string output_type_name() const { + return grpc_cpp_generator::ClassName(method_->output_type(), true); + } + + grpc::string get_input_type_name() const { + return method_->input_type()->file()->name(); + } + grpc::string get_output_type_name() const { + return method_->output_type()->file()->name(); + } + + bool get_module_and_message_path_input(grpc::string* str, + grpc::string generator_file_name, + bool generate_in_pb2_grpc, + grpc::string import_prefix) const { + return grpc_python_generator::GetModuleAndMessagePath( + method_->input_type(), str, generator_file_name, generate_in_pb2_grpc, + import_prefix); + } + + bool get_module_and_message_path_output(grpc::string* str, + grpc::string generator_file_name, + bool generate_in_pb2_grpc, + grpc::string import_prefix) const { + return grpc_python_generator::GetModuleAndMessagePath( + method_->output_type(), str, generator_file_name, generate_in_pb2_grpc, + import_prefix); + } + + bool NoStreaming() const { + return !method_->client_streaming() && !method_->server_streaming(); + } + + bool ClientStreaming() const { return method_->client_streaming(); } + + bool ServerStreaming() const { return method_->server_streaming(); } + + bool BidiStreaming() const { + return method_->client_streaming() && method_->server_streaming(); + } + + grpc::string GetLeadingComments(const grpc::string prefix) const { + return GetCommentsHelper(method_, true, prefix); + } + + grpc::string GetTrailingComments(const grpc::string prefix) const { + return GetCommentsHelper(method_, false, prefix); + } + + vector GetAllComments() const { + return grpc_python_generator::get_all_comments(method_); + } + + private: + const grpc::protobuf::MethodDescriptor* method_; +}; + +class ProtoBufService : public grpc_generator::Service { + public: + ProtoBufService(const grpc::protobuf::ServiceDescriptor* service) + : service_(service) {} + + grpc::string name() const { return service_->name(); } + + int method_count() const { return service_->method_count(); }; + std::unique_ptr method(int i) const { + return std::unique_ptr( + new ProtoBufMethod(service_->method(i))); + }; + + grpc::string GetLeadingComments(const grpc::string prefix) const { + return GetCommentsHelper(service_, true, prefix); + } + + grpc::string GetTrailingComments(const grpc::string prefix) const { + return GetCommentsHelper(service_, false, prefix); + } + + vector GetAllComments() const { + return grpc_python_generator::get_all_comments(service_); + } + + private: + const grpc::protobuf::ServiceDescriptor* service_; +}; + +class ProtoBufPrinter : public grpc_generator::Printer { + public: + ProtoBufPrinter(grpc::string* str) + : output_stream_(str), printer_(&output_stream_, '$') {} + + void Print(const std::map& vars, + const char* string_template) { + printer_.Print(vars, string_template); + } + + void Print(const char* string) { printer_.Print(string); } + void PrintRaw(const char* string) { printer_.PrintRaw(string); } + void Indent() { printer_.Indent(); } + void Outdent() { printer_.Outdent(); } + + private: + grpc::protobuf::io::StringOutputStream output_stream_; + grpc::protobuf::io::Printer printer_; +}; + +class ProtoBufFile : public grpc_generator::File { + public: + ProtoBufFile(const grpc::protobuf::FileDescriptor* file) : file_(file) {} + + grpc::string filename() const { return file_->name(); } + grpc::string filename_without_ext() const { + return grpc_generator::StripProto(filename()); + } + + grpc::string package() const { return file_->package(); } + std::vector package_parts() const { + return grpc_generator::tokenize(package(), "."); + } + + grpc::string additional_headers() const { return ""; } + + int service_count() const { return file_->service_count(); }; + std::unique_ptr service(int i) const { + return std::unique_ptr( + new ProtoBufService(file_->service(i))); + } + + std::unique_ptr CreatePrinter( + grpc::string* str) const { + return std::unique_ptr(new ProtoBufPrinter(str)); + } + + grpc::string GetLeadingComments(const grpc::string prefix) const { + return GetCommentsHelper(file_, true, prefix); + } + + grpc::string GetTrailingComments(const grpc::string prefix) const { + return GetCommentsHelper(file_, false, prefix); + } + + vector GetAllComments() const { + return grpc_python_generator::get_all_comments(file_); + } + + private: + const grpc::protobuf::FileDescriptor* file_; +}; + +#endif // GRPC_INTERNAL_COMPILER_PROTOBUF_PLUGIN_H diff --git a/third_party/grpc/src/compiler/python_generator.cc b/third_party/grpc/src/compiler/python_generator.cc index 4fd5dfbecb1688..8a0b88945493d5 100644 --- a/third_party/grpc/src/compiler/python_generator.cc +++ b/third_party/grpc/src/compiler/python_generator.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -35,92 +20,45 @@ #include #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/protobuf_plugin.h" #include "src/compiler/python_generator.h" +#include "src/compiler/python_generator_helpers.h" +#include "src/compiler/python_private_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::set; +using std::tuple; 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; - } -} +grpc::string generator_file_name; 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; -} + +typedef map StringMap; +typedef vector StringVector; +typedef tuple StringPair; +typedef set StringPairSet; // Provides RAII indentation handling. Use as: // { @@ -131,152 +69,160 @@ map ListToDict( // } class IndentScope { public: - explicit IndentScope(Printer* printer) : printer_(printer) { + explicit IndentScope(grpc_generator::Printer* printer) : printer_(printer) { printer_->Indent(); } - ~IndentScope() { - printer_->Outdent(); - } + ~IndentScope() { printer_->Outdent(); } private: - Printer* printer_; + grpc_generator::Printer* printer_; }; -//////////////////////////////// -// END FORMATTING BOILERPLATE // -//////////////////////////////// - -// 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"; -} +PrivateGenerator::PrivateGenerator(const GeneratorConfiguration& config, + const grpc_generator::File* file) + : config(config), file(file) {} -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; +void PrivateGenerator::PrintAllComments(StringVector comments, + grpc_generator::Printer* out) { + if (comments.empty()) { + // Python requires code structures like class and def to have + // a body, even if it is just "pass" or a docstring. We need + // to ensure not to generate empty bodies. We could do something + // smarter and more sophisticated, but at the moment, if there is + // no docstring to print, we simply emit "pass" to ensure validity + // of the generated code. + out->Print("# missing associated documentation comment in .proto file\n"); + out->Print("pass\n"); + return; } - 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() + "."; + out->Print("\"\"\""); + for (StringVector::iterator it = comments.begin(); it != comments.end(); + ++it) { + size_t start_pos = it->find_first_not_of(' '); + if (start_pos != grpc::string::npos) { + out->PrintRaw(it->c_str() + start_pos); + } + out->Print("\n"); } - // no pop_back prior to C++11 - message_type.resize(message_type.size() - 1); - *out = make_pair(module, message_type); - return true; + out->Print("\"\"\"\n"); } -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"); +bool PrivateGenerator::PrintBetaServicer(const grpc_generator::Service* service, + grpc_generator::Printer* out) { + StringMap service_dict; + service_dict["Service"] = service->name(); + out->Print("\n\n"); + out->Print(service_dict, "class Beta$Service$Servicer(object):\n"); { IndentScope raii_class_indent(out); - out->Print(dict, "\"\"\"$Documentation$\"\"\"\n"); - out->Print("__metaclass__ = abc.ABCMeta\n"); + out->Print( + "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" + "\nIt is recommended to use the GA API (classes and functions in this\n" + "file not marked beta) for all further purposes. This class was " + "generated\n" + "only to ease transition from grpcio<0.15.0 to " + "grpcio>=0.15.0.\"\"\"\n"); + StringVector service_comments = service->GetAllComments(); + PrintAllComments(service_comments, out); 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); + auto method = service->method(i); + grpc::string arg_name = + method->ClientStreaming() ? "request_iterator" : "request"; + StringMap method_dict; + method_dict["Method"] = method->name(); + method_dict["ArgName"] = arg_name; + out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n"); { IndentScope raii_method_indent(out); - out->Print("raise NotImplementedError()\n"); + StringVector method_comments = method->GetAllComments(); + PrintAllComments(method_comments, out); + out->Print("context.code(beta_interfaces.StatusCode.UNIMPLEMENTED)\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"); +bool PrivateGenerator::PrintBetaStub(const grpc_generator::Service* service, + grpc_generator::Printer* out) { + StringMap service_dict; + service_dict["Service"] = service->name(); + out->Print("\n\n"); + out->Print(service_dict, "class Beta$Service$Stub(object):\n"); { IndentScope raii_class_indent(out); - out->Print(dict, "\"\"\"$Documentation$\"\"\"\n"); - out->Print("__metaclass__ = abc.ABCMeta\n"); + out->Print( + "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" + "\nIt is recommended to use the GA API (classes and functions in this\n" + "file not marked beta) for all further purposes. This class was " + "generated\n" + "only to ease transition from grpcio<0.15.0 to " + "grpcio>=0.15.0.\"\"\"\n"); + StringVector service_comments = service->GetAllComments(); + PrintAllComments(service_comments, out); 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"); + auto method = service->method(i); + grpc::string arg_name = + method->ClientStreaming() ? "request_iterator" : "request"; + StringMap method_dict; + method_dict["Method"] = method->name(); + method_dict["ArgName"] = arg_name; + out->Print(method_dict, + "def $Method$(self, $ArgName$, timeout, metadata=None, " + "with_call=False, protocol_options=None):\n"); { IndentScope raii_method_indent(out); + StringVector method_comments = method->GetAllComments(); + PrintAllComments(method_comments, out); out->Print("raise NotImplementedError()\n"); } - if (!meth->server_streaming()) { - out->Print(methdict, "$Method$.future = None\n"); + if (!method->ServerStreaming()) { + out->Print(method_dict, "$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()); +bool PrivateGenerator::PrintBetaServerFactory( + const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, grpc_generator::Printer* out) { + StringMap service_dict; + service_dict["Service"] = service->name(); + out->Print("\n\n"); + out->Print(service_dict, + "def beta_create_$Service$_server(servicer, pool=None, " + "pool_size=None, default_timeout=None, maximum_timeout=None):\n"); { IndentScope raii_create_server_indent(out); - map method_implementation_constructors; - map> - input_message_modules_and_classes; - map> - output_message_modules_and_classes; + out->Print( + "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" + "\nIt is recommended to use the GA API (classes and functions in this\n" + "file not marked beta) for all further purposes. This function was\n" + "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0" + "\"\"\"\n"); + StringMap method_implementation_constructors; + StringMap input_message_modules_and_classes; + StringMap output_message_modules_and_classes; for (int i = 0; i < service->method_count(); ++i) { - const MethodDescriptor* method = service->method(i); + auto method = service->method(i); const grpc::string method_implementation_constructor = - grpc::string(method->client_streaming() ? "stream_" : "unary_") + - grpc::string(method->server_streaming() ? "stream_" : "unary_") + + grpc::string(method->ClientStreaming() ? "stream_" : "unary_") + + grpc::string(method->ServerStreaming() ? "stream_" : "unary_") + "inline"; - pair input_message_module_and_class; - if (!GetModuleAndMessagePath(method->input_type(), - &input_message_module_and_class)) { + grpc::string input_message_module_and_class; + if (!method->get_module_and_message_path_input( + &input_message_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { return false; } - pair output_message_module_and_class; - if (!GetModuleAndMessagePath(method->output_type(), - &output_message_module_and_class)) { + grpc::string output_message_module_and_class; + if (!method->get_module_and_message_path_output( + &output_message_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { 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( @@ -284,104 +230,105 @@ bool PrintBetaServerFactory(const grpc::string& package_qualified_service_name, output_message_modules_and_classes.insert( make_pair(method->name(), output_message_module_and_class)); } + StringMap method_dict; + method_dict["PackageQualifiedServiceName"] = package_qualified_service_name; out->Print("request_deserializers = {\n"); - for (auto name_and_input_module_class_pair = - input_message_modules_and_classes.begin(); + for (StringMap::iterator name_and_input_module_class_pair = + input_message_modules_and_classes.begin(); name_and_input_module_class_pair != - input_message_modules_and_classes.end(); + input_message_modules_and_classes.end(); name_and_input_module_class_pair++) { + method_dict["MethodName"] = name_and_input_module_class_pair->first; + method_dict["InputTypeModuleAndClass"] = + name_and_input_module_class_pair->second; 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(method_dict, + "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$InputTypeModuleAndClass$.FromString,\n"); } out->Print("}\n"); out->Print("response_serializers = {\n"); - for (auto name_and_output_module_class_pair = - output_message_modules_and_classes.begin(); + for (StringMap::iterator name_and_output_module_class_pair = + output_message_modules_and_classes.begin(); name_and_output_module_class_pair != - output_message_modules_and_classes.end(); + output_message_modules_and_classes.end(); name_and_output_module_class_pair++) { + method_dict["MethodName"] = name_and_output_module_class_pair->first; + method_dict["OutputTypeModuleAndClass"] = + name_and_output_module_class_pair->second; 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(method_dict, + "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$OutputTypeModuleAndClass$.SerializeToString,\n"); } 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++) { + for (StringMap::iterator name_and_implementation_constructor = + method_implementation_constructors.begin(); + name_and_implementation_constructor != + method_implementation_constructors.end(); + name_and_implementation_constructor++) { + method_dict["Method"] = name_and_implementation_constructor->first; + method_dict["Constructor"] = name_and_implementation_constructor->second; 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(method_dict, + "(\'$PackageQualifiedServiceName$\', \'$Method$\'): " + "face_utilities.$Constructor$(servicer.$Method$),\n"); } 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"); + 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," +bool PrivateGenerator::PrintBetaStubFactory( + const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, grpc_generator::Printer* out) { + StringMap dict; + dict["Service"] = service->name(); + out->Print("\n\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; + out->Print( + "\"\"\"The Beta API is deprecated for 0.15.0 and later.\n" + "\nIt is recommended to use the GA API (classes and functions in this\n" + "file not marked beta) for all further purposes. This function was\n" + "generated only to ease transition from grpcio<0.15.0 to grpcio>=0.15.0" + "\"\"\"\n"); + StringMap method_cardinalities; + StringMap input_message_modules_and_classes; + StringMap output_message_modules_and_classes; for (int i = 0; i < service->method_count(); ++i) { - const MethodDescriptor* method = service->method(i); + auto 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)) { + grpc::string(method->ClientStreaming() ? "STREAM" : "UNARY") + "_" + + grpc::string(method->ServerStreaming() ? "STREAM" : "UNARY"); + grpc::string input_message_module_and_class; + if (!method->get_module_and_message_path_input( + &input_message_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { return false; } - pair output_message_module_and_class; - if (!GetModuleAndMessagePath(method->output_type(), - &output_message_module_and_class)) { + grpc::string output_message_module_and_class; + if (!method->get_module_and_message_path_output( + &output_message_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { 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( @@ -389,101 +336,446 @@ bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name, output_message_modules_and_classes.insert( make_pair(method->name(), output_message_module_and_class)); } + StringMap method_dict; + method_dict["PackageQualifiedServiceName"] = package_qualified_service_name; out->Print("request_serializers = {\n"); - for (auto name_and_input_module_class_pair = - input_message_modules_and_classes.begin(); + for (StringMap::iterator name_and_input_module_class_pair = + input_message_modules_and_classes.begin(); name_and_input_module_class_pair != - input_message_modules_and_classes.end(); + input_message_modules_and_classes.end(); name_and_input_module_class_pair++) { + method_dict["MethodName"] = name_and_input_module_class_pair->first; + method_dict["InputTypeModuleAndClass"] = + name_and_input_module_class_pair->second; 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(method_dict, + "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$InputTypeModuleAndClass$.SerializeToString,\n"); } out->Print("}\n"); out->Print("response_deserializers = {\n"); - for (auto name_and_output_module_class_pair = - output_message_modules_and_classes.begin(); + for (StringMap::iterator name_and_output_module_class_pair = + output_message_modules_and_classes.begin(); name_and_output_module_class_pair != - output_message_modules_and_classes.end(); + output_message_modules_and_classes.end(); name_and_output_module_class_pair++) { + method_dict["MethodName"] = name_and_output_module_class_pair->first; + method_dict["OutputTypeModuleAndClass"] = + name_and_output_module_class_pair->second; 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(method_dict, + "(\'$PackageQualifiedServiceName$\', \'$MethodName$\'): " + "$OutputTypeModuleAndClass$.FromString,\n"); } out->Print("}\n"); out->Print("cardinalities = {\n"); - for (auto name_and_cardinality = method_cardinalities.begin(); + for (StringMap::iterator name_and_cardinality = + method_cardinalities.begin(); name_and_cardinality != method_cardinalities.end(); name_and_cardinality++) { + method_dict["Method"] = name_and_cardinality->first; + method_dict["Cardinality"] = name_and_cardinality->second; IndentScope raii_descriptions_indent(out); - out->Print("\'$Method$\': cardinality.Cardinality.$Cardinality$,\n", - "Method", name_and_cardinality->first, - "Cardinality", name_and_cardinality->second); + out->Print(method_dict, + "\'$Method$\': cardinality.Cardinality.$Cardinality$,\n"); } 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); + "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(method_dict, + "return beta_implementations.dynamic_stub(channel, " + "\'$PackageQualifiedServiceName$\', " + "cardinalities, options=stub_options)\n"); } 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 grpc.framework.common import cardinality\n"); - out->Print("from grpc.framework.interfaces.face import utilities as face_utilities\n"); +bool PrivateGenerator::PrintStub( + const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, grpc_generator::Printer* out) { + StringMap dict; + dict["Service"] = service->name(); + out->Print("\n\n"); + out->Print(dict, "class $Service$Stub(object):\n"); + { + IndentScope raii_class_indent(out); + StringVector service_comments = service->GetAllComments(); + PrintAllComments(service_comments, out); + out->Print("\n"); + out->Print("def __init__(self, channel):\n"); + { + IndentScope raii_init_indent(out); + out->Print("\"\"\"Constructor.\n"); + out->Print("\n"); + out->Print("Args:\n"); + { + IndentScope raii_args_indent(out); + out->Print("channel: A grpc.Channel.\n"); + } + out->Print("\"\"\"\n"); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + grpc::string multi_callable_constructor = + grpc::string(method->ClientStreaming() ? "stream" : "unary") + "_" + + grpc::string(method->ServerStreaming() ? "stream" : "unary"); + grpc::string request_module_and_class; + if (!method->get_module_and_message_path_input( + &request_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { + return false; + } + grpc::string response_module_and_class; + if (!method->get_module_and_message_path_output( + &response_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { + return false; + } + StringMap method_dict; + method_dict["Method"] = method->name(); + method_dict["MultiCallableConstructor"] = multi_callable_constructor; + out->Print(method_dict, + "self.$Method$ = channel.$MultiCallableConstructor$(\n"); + { + method_dict["PackageQualifiedService"] = + package_qualified_service_name; + method_dict["RequestModuleAndClass"] = request_module_and_class; + method_dict["ResponseModuleAndClass"] = response_module_and_class; + IndentScope raii_first_attribute_indent(out); + IndentScope raii_second_attribute_indent(out); + out->Print(method_dict, "'/$PackageQualifiedService$/$Method$',\n"); + out->Print(method_dict, + "request_serializer=$RequestModuleAndClass$." + "SerializeToString,\n"); + out->Print( + method_dict, + "response_deserializer=$ResponseModuleAndClass$.FromString,\n"); + out->Print(")\n"); + } + } + } + } return true; } -} // namespace +bool PrivateGenerator::PrintServicer(const grpc_generator::Service* service, + grpc_generator::Printer* out) { + StringMap service_dict; + service_dict["Service"] = service->name(); + out->Print("\n\n"); + out->Print(service_dict, "class $Service$Servicer(object):\n"); + { + IndentScope raii_class_indent(out); + StringVector service_comments = service->GetAllComments(); + PrintAllComments(service_comments, out); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + grpc::string arg_name = + method->ClientStreaming() ? "request_iterator" : "request"; + StringMap method_dict; + method_dict["Method"] = method->name(); + method_dict["ArgName"] = arg_name; + out->Print("\n"); + out->Print(method_dict, "def $Method$(self, $ArgName$, context):\n"); + { + IndentScope raii_method_indent(out); + StringVector method_comments = method->GetAllComments(); + PrintAllComments(method_comments, out); + out->Print("context.set_code(grpc.StatusCode.UNIMPLEMENTED)\n"); + out->Print("context.set_details('Method not implemented!')\n"); + out->Print("raise NotImplementedError('Method not implemented!')\n"); + } + } + } + return true; +} -pair GetServices(const FileDescriptor* file, - const GeneratorConfiguration& config) { - grpc::string output; +bool PrivateGenerator::PrintAddServicerToServer( + const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, grpc_generator::Printer* out) { + StringMap service_dict; + service_dict["Service"] = service->name(); + out->Print("\n\n"); + out->Print(service_dict, + "def add_$Service$Servicer_to_server(servicer, server):\n"); { - // 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, ""); + IndentScope raii_class_indent(out); + out->Print("rpc_method_handlers = {\n"); + { + IndentScope raii_dict_first_indent(out); + IndentScope raii_dict_second_indent(out); + for (int i = 0; i < service->method_count(); ++i) { + auto method = service->method(i); + grpc::string method_handler_constructor = + grpc::string(method->ClientStreaming() ? "stream" : "unary") + "_" + + grpc::string(method->ServerStreaming() ? "stream" : "unary") + + "_rpc_method_handler"; + grpc::string request_module_and_class; + if (!method->get_module_and_message_path_input( + &request_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { + return false; + } + grpc::string response_module_and_class; + if (!method->get_module_and_message_path_output( + &response_module_and_class, generator_file_name, + generate_in_pb2_grpc, config.import_prefix)) { + return false; + } + StringMap method_dict; + method_dict["Method"] = method->name(); + method_dict["MethodHandlerConstructor"] = method_handler_constructor; + method_dict["RequestModuleAndClass"] = request_module_and_class; + method_dict["ResponseModuleAndClass"] = response_module_and_class; + out->Print(method_dict, + "'$Method$': grpc.$MethodHandlerConstructor$(\n"); + { + IndentScope raii_call_first_indent(out); + IndentScope raii_call_second_indent(out); + out->Print(method_dict, "servicer.$Method$,\n"); + out->Print( + method_dict, + "request_deserializer=$RequestModuleAndClass$.FromString,\n"); + out->Print( + method_dict, + "response_serializer=$ResponseModuleAndClass$.SerializeToString," + "\n"); + } + out->Print("),\n"); + } } - auto package = file->package(); - if (!package.empty()) { - package = package.append("."); + StringMap method_dict; + method_dict["PackageQualifiedServiceName"] = package_qualified_service_name; + out->Print("}\n"); + out->Print("generic_handler = grpc.method_handlers_generic_handler(\n"); + { + IndentScope raii_call_first_indent(out); + IndentScope raii_call_second_indent(out); + out->Print(method_dict, + "'$PackageQualifiedServiceName$', rpc_method_handlers)\n"); } + out->Print("server.add_generic_rpc_handlers((generic_handler,))\n"); + } + return true; +} + +bool PrivateGenerator::PrintBetaPreamble(grpc_generator::Printer* out) { + StringMap var; + var["Package"] = config.beta_package_root; + out->Print(var, + "from $Package$ import implementations as beta_implementations\n"); + out->Print(var, "from $Package$ import interfaces as beta_interfaces\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; +} + +bool PrivateGenerator::PrintPreamble(grpc_generator::Printer* out) { + StringMap var; + var["Package"] = config.grpc_package_root; + out->Print(var, "import $Package$\n"); + if (generate_in_pb2_grpc) { + out->Print("\n"); + StringPairSet imports_set; for (int i = 0; i < file->service_count(); ++i) { auto service = file->service(i); - auto package_qualified_service_name = package + service->name(); - if (!(PrintBetaServicer(service, &out) && - PrintBetaStub(service, &out) && - PrintBetaServerFactory(package_qualified_service_name, service, &out) && - PrintBetaStubFactory(package_qualified_service_name, service, &out))) { + for (int j = 0; j < service->method_count(); ++j) { + auto method = service.get()->method(j); + + grpc::string input_type_file_name = method->get_input_type_name(); + grpc::string input_module_name = + ModuleName(input_type_file_name, config.import_prefix); + grpc::string input_module_alias = + ModuleAlias(input_type_file_name, config.import_prefix); + imports_set.insert( + std::make_tuple(input_module_name, input_module_alias)); + + grpc::string output_type_file_name = method->get_output_type_name(); + grpc::string output_module_name = + ModuleName(output_type_file_name, config.import_prefix); + grpc::string output_module_alias = + ModuleAlias(output_type_file_name, config.import_prefix); + imports_set.insert( + std::make_tuple(output_module_name, output_module_alias)); + } + } + + for (StringPairSet::iterator it = imports_set.begin(); + it != imports_set.end(); ++it) { + auto module_name = std::get<0>(*it); + var["ModuleAlias"] = std::get<1>(*it); + const size_t last_dot_pos = module_name.rfind('.'); + if (last_dot_pos == grpc::string::npos) { + var["ImportStatement"] = "import " + module_name; + } else { + var["ImportStatement"] = "from " + module_name.substr(0, last_dot_pos) + + " import " + + module_name.substr(last_dot_pos + 1); + } + out->Print(var, "$ImportStatement$ as $ModuleAlias$\n"); + } + } + return true; +} + +bool PrivateGenerator::PrintGAServices(grpc_generator::Printer* out) { + grpc::string package = file->package(); + if (!package.empty()) { + package = package.append("."); + } + for (int i = 0; i < file->service_count(); ++i) { + auto service = file->service(i); + grpc::string package_qualified_service_name = package + service->name(); + if (!(PrintStub(package_qualified_service_name, service.get(), out) && + PrintServicer(service.get(), out) && + PrintAddServicerToServer(package_qualified_service_name, + service.get(), out))) { + return false; + } + } + return true; +} + +bool PrivateGenerator::PrintBetaServices(grpc_generator::Printer* out) { + grpc::string package = file->package(); + if (!package.empty()) { + package = package.append("."); + } + for (int i = 0; i < file->service_count(); ++i) { + auto service = file->service(i); + grpc::string package_qualified_service_name = package + service->name(); + if (!(PrintBetaServicer(service.get(), out) && + PrintBetaStub(service.get(), out) && + PrintBetaServerFactory(package_qualified_service_name, service.get(), + out) && + PrintBetaStubFactory(package_qualified_service_name, service.get(), + out))) { + return false; + } + } + return true; +} + +pair PrivateGenerator::GetGrpcServices() { + grpc::string output; + { + // Scope the output stream so it closes and finalizes output to the string. + auto out = file->CreatePrinter(&output); + if (generate_in_pb2_grpc) { + out->Print( + "# Generated by the gRPC Python protocol compiler plugin. " + "DO NOT EDIT!\n"); + if (!PrintPreamble(out.get())) { + return make_pair(false, ""); + } + if (!PrintGAServices(out.get())) { return make_pair(false, ""); } + } else { + out->Print("try:\n"); + { + IndentScope raii_dict_try_indent(out.get()); + out->Print( + "# THESE ELEMENTS WILL BE DEPRECATED.\n" + "# Please use the generated *_pb2_grpc.py files instead.\n"); + if (!PrintPreamble(out.get())) { + return make_pair(false, ""); + } + if (!PrintBetaPreamble(out.get())) { + return make_pair(false, ""); + } + if (!PrintGAServices(out.get())) { + return make_pair(false, ""); + } + if (!PrintBetaServices(out.get())) { + return make_pair(false, ""); + } + } + out->Print("except ImportError:\n"); + { + IndentScope raii_dict_except_indent(out.get()); + out->Print("pass"); + } } } return make_pair(true, std::move(output)); } +} // namespace + +GeneratorConfiguration::GeneratorConfiguration() + : grpc_package_root("grpc"), + beta_package_root("grpc.beta"), + import_prefix("") {} + +PythonGrpcGenerator::PythonGrpcGenerator(const GeneratorConfiguration& config) + : config_(config) {} + +PythonGrpcGenerator::~PythonGrpcGenerator() {} + +static bool GenerateGrpc(GeneratorContext* context, PrivateGenerator& generator, + grpc::string file_name, bool generate_in_pb2_grpc) { + bool success; + std::unique_ptr output; + std::unique_ptr coded_output; + grpc::string grpc_code; + + if (generate_in_pb2_grpc) { + output.reset(context->Open(file_name)); + generator.generate_in_pb2_grpc = true; + } else { + output.reset(context->OpenForInsert(file_name, "module_scope")); + generator.generate_in_pb2_grpc = false; + } + + coded_output.reset(new CodedOutputStream(output.get())); + tie(success, grpc_code) = generator.GetGrpcServices(); + + if (success) { + coded_output->WriteRaw(grpc_code.data(), grpc_code.size()); + return true; + } else { + return false; + } +} + +bool PythonGrpcGenerator::Generate(const FileDescriptor* file, + const grpc::string& parameter, + GeneratorContext* context, + grpc::string* error) const { + // Get output file name. + grpc::string pb2_file_name; + grpc::string pb2_grpc_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) { + grpc::string base = + file->name().substr(0, file->name().size() - proto_suffix_length); + std::replace(base.begin(), base.end(), '-', '_'); + pb2_file_name = base + "_pb2.py"; + pb2_grpc_file_name = base + "_pb2_grpc.py"; + } else { + *error = "Invalid proto file name. Proto file must end with .proto"; + return false; + } + generator_file_name = file->name(); + + ProtoBufFile pbfile(file); + PrivateGenerator generator(config_, &pbfile); + if (parameter == "" || parameter == "grpc_2_0") { + return GenerateGrpc(context, generator, pb2_grpc_file_name, true); + } else if (parameter == "grpc_1_0") { + return GenerateGrpc(context, generator, pb2_grpc_file_name, true) && + GenerateGrpc(context, generator, pb2_file_name, false); + } else { + *error = "Invalid parameter '" + parameter + "'."; + return false; + } +} + } // namespace grpc_python_generator diff --git a/third_party/grpc/src/compiler/python_generator.h b/third_party/grpc/src/compiler/python_generator.h index 68da18f9baa659..9139307e63c2fd 100644 --- a/third_party/grpc/src/compiler/python_generator.h +++ b/third_party/grpc/src/compiler/python_generator.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -37,13 +22,19 @@ #include #include "src/compiler/config.h" +#include "src/compiler/schema_interface.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 { + GeneratorConfiguration(); + grpc::string grpc_package_root; + // TODO(https://github.com/grpc/grpc/issues/8622): Drop this. grpc::string beta_package_root; + // TODO(https://github.com/google/protobuf/issues/888): Drop this. + grpc::string import_prefix; }; class PythonGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { @@ -55,14 +46,11 @@ class PythonGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { 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/third_party/grpc/src/compiler/python_generator_helpers.h b/third_party/grpc/src/compiler/python_generator_helpers.h new file mode 100644 index 00000000000000..b1b58befdf459c --- /dev/null +++ b/third_party/grpc/src/compiler/python_generator_helpers.h @@ -0,0 +1,127 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_HELPERS_H +#define GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_HELPERS_H + +#include +#include +#include +#include + +#include "src/compiler/config.h" +#include "src/compiler/generator_helpers.h" +#include "src/compiler/python_generator.h" +#include "src/compiler/python_private_generator.h" + +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 grpc_generator::StringReplace; +using grpc_generator::StripProto; +using std::vector; + +namespace grpc_python_generator { + +namespace { + +typedef vector DescriptorVector; +typedef vector StringVector; + +// TODO(https://github.com/google/protobuf/issues/888): +// Export `ModuleName` from protobuf's +// `src/google/protobuf/compiler/python/python_generator.cc` file. +grpc::string ModuleName(const grpc::string& filename, + const grpc::string& import_prefix) { + grpc::string basename = StripProto(filename); + basename = StringReplace(basename, "-", "_"); + basename = StringReplace(basename, "/", "."); + return import_prefix + basename + "_pb2"; +} + +// TODO(https://github.com/google/protobuf/issues/888): +// Export `ModuleAlias` from protobuf's +// `src/google/protobuf/compiler/python/python_generator.cc` file. +grpc::string ModuleAlias(const grpc::string& filename, + const grpc::string& import_prefix) { + grpc::string module_name = ModuleName(filename, import_prefix); + // We can't have dots in the module name, so we replace each with _dot_. + // But that could lead to a collision between a.b and a_dot_b, so we also + // duplicate each underscore. + module_name = StringReplace(module_name, "_", "__"); + module_name = StringReplace(module_name, ".", "_dot_"); + return module_name; +} + +bool GetModuleAndMessagePath(const Descriptor* type, grpc::string* out, + grpc::string generator_file_name, + bool generate_in_pb2_grpc, + grpc::string& import_prefix) { + const Descriptor* path_elem_type = type; + DescriptorVector 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; + if (generator_file_name != file_name || generate_in_pb2_grpc) { + module = ModuleAlias(file_name, import_prefix) + "."; + } else { + module = ""; + } + grpc::string message_type; + for (DescriptorVector::reverse_iterator 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 = module + message_type; + return true; +} + +template +StringVector get_all_comments(const DescriptorType* descriptor) { + StringVector comments; + grpc_generator::GetComment( + descriptor, grpc_generator::COMMENTTYPE_LEADING_DETACHED, &comments); + grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_LEADING, + &comments); + grpc_generator::GetComment(descriptor, grpc_generator::COMMENTTYPE_TRAILING, + &comments); + return comments; +} + +} // namespace + +} // namespace grpc_python_generator + +#endif // GRPC_INTERNAL_COMPILER_PYTHON_GENERATOR_HELPERS_H diff --git a/third_party/grpc/src/compiler/python_plugin.cc b/third_party/grpc/src/compiler/python_plugin.cc index d781ddbee589c9..81eb1d4fc2e6a0 100644 --- a/third_party/grpc/src/compiler/python_plugin.cc +++ b/third_party/grpc/src/compiler/python_plugin.cc @@ -1,44 +1,29 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ // Generates a Python gRPC service interface out of Protobuf IDL. #include "src/compiler/config.h" +#include "src/compiler/protobuf_plugin.h" #include "src/compiler/python_generator.h" int main(int argc, char* argv[]) { grpc_python_generator::GeneratorConfiguration config; - config.beta_package_root = "grpc.beta"; grpc_python_generator::PythonGrpcGenerator generator(config); return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); } diff --git a/third_party/grpc/src/compiler/python_private_generator.h b/third_party/grpc/src/compiler/python_private_generator.h new file mode 100644 index 00000000000000..ee5cbbd9ae3e03 --- /dev/null +++ b/third_party/grpc/src/compiler/python_private_generator.h @@ -0,0 +1,84 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H +#define GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H + +#include +#include + +#include "src/compiler/python_generator.h" +#include "src/compiler/schema_interface.h" + +namespace grpc_python_generator { + +namespace { + +// Tucks all generator state in an anonymous namespace away from +// PythonGrpcGenerator and the header file, mostly to encourage future changes +// to not require updates to the grpcio-tools C++ code part. Assumes that it is +// only ever used from a single thread. +struct PrivateGenerator { + const GeneratorConfiguration& config; + const grpc_generator::File* file; + + bool generate_in_pb2_grpc; + + PrivateGenerator(const GeneratorConfiguration& config, + const grpc_generator::File* file); + + std::pair GetGrpcServices(); + + private: + bool PrintPreamble(grpc_generator::Printer* out); + bool PrintBetaPreamble(grpc_generator::Printer* out); + bool PrintGAServices(grpc_generator::Printer* out); + bool PrintBetaServices(grpc_generator::Printer* out); + + bool PrintAddServicerToServer( + const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, grpc_generator::Printer* out); + bool PrintServicer(const grpc_generator::Service* service, + grpc_generator::Printer* out); + bool PrintStub(const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, + grpc_generator::Printer* out); + + bool PrintBetaServicer(const grpc_generator::Service* service, + grpc_generator::Printer* out); + bool PrintBetaServerFactory( + const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, grpc_generator::Printer* out); + bool PrintBetaStub(const grpc_generator::Service* service, + grpc_generator::Printer* out); + bool PrintBetaStubFactory(const grpc::string& package_qualified_service_name, + const grpc_generator::Service* service, + grpc_generator::Printer* out); + + // Get all comments (leading, leading_detached, trailing) and print them as a + // docstring. Any leading space of a line will be removed, but the line + // wrapping will not be changed. + void PrintAllComments(std::vector comments, + grpc_generator::Printer* out); +}; + +} // namespace + +} // namespace grpc_python_generator + +#endif // GRPC_INTERNAL_COMPILER_PYTHON_PRIVATE_GENERATOR_H diff --git a/third_party/grpc/src/compiler/ruby_generator.cc b/third_party/grpc/src/compiler/ruby_generator.cc index 299137519f8d45..e39d8be5d41616 100644 --- a/third_party/grpc/src/compiler/ruby_generator.cc +++ b/third_party/grpc/src/compiler/ruby_generator.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -42,8 +27,8 @@ #include "src/compiler/ruby_generator_string-inl.h" using grpc::protobuf::FileDescriptor; -using grpc::protobuf::ServiceDescriptor; using grpc::protobuf::MethodDescriptor; +using grpc::protobuf::ServiceDescriptor; using grpc::protobuf::io::Printer; using grpc::protobuf::io::StringOutputStream; using std::map; @@ -53,41 +38,47 @@ 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); +void PrintMethod(const MethodDescriptor* method, const grpc::string& package, + Printer* out) { + grpc::string input_type = + RubyTypeOf(method->input_type()->full_name(), package); if (method->client_streaming()) { input_type = "stream(" + input_type + ")"; } - grpc::string output_type = RubyTypeOf(method->output_type()->name(), package); + grpc::string output_type = + RubyTypeOf(method->output_type()->full_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, }); + std::map method_vars = ListToDict({ + "mth.name", + method->name(), + "input.type", + input_type, + "output.type", + output_type, + }); + out->Print(GetRubyComments(method, true).c_str()); out->Print(method_vars, "rpc :$mth.name$, $input.type$, $output.type$\n"); + out->Print(GetRubyComments(method, false).c_str()); } // Prints out the service using the ruby gRPC DSL. -void PrintService(const ServiceDescriptor *service, const grpc::string &package, - Printer *out) { +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()), }); + std::map module_vars = ListToDict({ + "module.name", + Modularize(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(GetRubyComments(service, true).c_str()); out->Print("class Service\n"); // Write the indented class body. @@ -98,8 +89,8 @@ void PrintService(const ServiceDescriptor *service, const grpc::string &package, 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"); + ListToDict({"service_full_name", service->full_name()}); + out->Print(pkg_vars, "self.service_name = '$service_full_name$'\n"); out->Print("\n"); for (int i = 0; i < service->method_count(); ++i) { PrintMethod(service->method(i), package, out); @@ -113,11 +104,49 @@ void PrintService(const ServiceDescriptor *service, const grpc::string &package, // End the service module out->Outdent(); out->Print("end\n"); + out->Print(GetRubyComments(service, false).c_str()); } } // namespace -grpc::string GetServices(const FileDescriptor *file) { +// The following functions are copied directly from the source for the protoc +// ruby generator +// to ensure compatibility (with the exception of int and string type changes). +// See +// https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/ruby/ruby_generator.cc#L250 +// TODO: keep up to date with protoc code generation, though this behavior isn't +// expected to change +bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; } + +char ToUpper(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; } + +// Package names in protobuf are snake_case by convention, but Ruby module +// names must be PascalCased. +// +// foo_bar_baz -> FooBarBaz +grpc::string PackageToModule(const grpc::string& name) { + bool next_upper = true; + grpc::string result; + result.reserve(name.size()); + + for (grpc::string::size_type i = 0; i < name.size(); i++) { + if (name[i] == '_') { + next_upper = true; + } else { + if (next_upper) { + result.push_back(ToUpper(name[i])); + } else { + result.push_back(name[i]); + } + next_upper = false; + } + } + + return result; +} +// end copying of protoc generator for ruby code + +grpc::string GetServices(const FileDescriptor* file) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -131,28 +160,50 @@ grpc::string GetServices(const FileDescriptor *file) { return output; } + std::string package_name; + + if (file->options().has_ruby_package()) { + package_name = file->options().ruby_package(); + } else { + package_name = file->package(); + } + // Write out a file header. - std::map header_comment_vars = ListToDict( - {"file.name", file->name(), "file.package", file->package(), }); + std::map header_comment_vars = ListToDict({ + "file.name", + file->name(), + "file.package", + package_name, + }); 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"); + grpc::string leading_comments = GetRubyComments(file, true); + if (!leading_comments.empty()) { + out.Print("# Original file comments:\n"); + out.PrintRaw(leading_comments.c_str()); + } + 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), }); + 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(), '.'); + std::vector modules = Split(package_name, '.'); for (size_t i = 0; i < modules.size(); ++i) { - std::map module_vars = - ListToDict({"module.name", CapitalizeFirst(modules[i]), }); + std::map module_vars = ListToDict({ + "module.name", + PackageToModule(modules[i]), + }); out.Print(module_vars, "module $module.name$\n"); out.Indent(); } @@ -164,6 +215,8 @@ grpc::string GetServices(const FileDescriptor *file) { out.Outdent(); out.Print("end\n"); } + + out.Print(GetRubyComments(file, false).c_str()); } return output; } diff --git a/third_party/grpc/src/compiler/ruby_generator.h b/third_party/grpc/src/compiler/ruby_generator.h index a2ab36d4d99dad..9a03e0d139605d 100644 --- a/third_party/grpc/src/compiler/ruby_generator.h +++ b/third_party/grpc/src/compiler/ruby_generator.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -38,7 +23,7 @@ namespace grpc_ruby_generator { -grpc::string GetServices(const grpc::protobuf::FileDescriptor *file); +grpc::string GetServices(const grpc::protobuf::FileDescriptor* file); } // namespace grpc_ruby_generator diff --git a/third_party/grpc/src/compiler/ruby_generator_helpers-inl.h b/third_party/grpc/src/compiler/ruby_generator_helpers-inl.h index 9da7cab3c7f3dd..2323770425a936 100644 --- a/third_party/grpc/src/compiler/ruby_generator_helpers-inl.h +++ b/third_party/grpc/src/compiler/ruby_generator_helpers-inl.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -35,19 +20,20 @@ #define GRPC_INTERNAL_COMPILER_RUBY_GENERATOR_HELPERS_INL_H #include "src/compiler/config.h" +#include "src/compiler/generator_helpers.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) { +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"; + "_services_pb.rb"; return true; } else { *file_name_or_error = "Invalid proto file name: must end with .proto"; @@ -56,8 +42,15 @@ inline bool ServicesFilename(const grpc::protobuf::FileDescriptor *file, } inline grpc::string MessagesRequireName( - const grpc::protobuf::FileDescriptor *file) { - return Replace(file->name(), ".proto", ""); + const grpc::protobuf::FileDescriptor* file) { + return Replace(file->name(), ".proto", "_pb"); +} + +// Get leading or trailing comments in a string. Comment lines start with "# ". +// Leading detached comments are put in in front of leading comments. +template +inline grpc::string GetRubyComments(const DescriptorType* desc, bool leading) { + return grpc_generator::GetPrefixedComments(desc, leading, "#"); } } // namespace grpc_ruby_generator diff --git a/third_party/grpc/src/compiler/ruby_generator_map-inl.h b/third_party/grpc/src/compiler/ruby_generator_map-inl.h index 6b87774f2136b5..89a74b1d410744 100644 --- a/third_party/grpc/src/compiler/ruby_generator_map-inl.h +++ b/third_party/grpc/src/compiler/ruby_generator_map-inl.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -36,8 +21,8 @@ #include "src/compiler/config.h" -#include #include +#include #include #include // NOLINT #include @@ -51,10 +36,9 @@ 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) { + const initializer_list& values) { if (values.size() % 2 != 0) { - std::cerr << "Not every 'key' has a value in `values`." - << std::endl; + std::cerr << "Not every 'key' has a value in `values`." << std::endl; } std::map value_map; auto value_iter = values.begin(); diff --git a/third_party/grpc/src/compiler/ruby_generator_string-inl.h b/third_party/grpc/src/compiler/ruby_generator_string-inl.h index 8da3a88da2937d..ecfe796e7a2c36 100644 --- a/third_party/grpc/src/compiler/ruby_generator_string-inl.h +++ b/third_party/grpc/src/compiler/ruby_generator_string-inl.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -46,8 +31,8 @@ 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) { +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)) { @@ -57,15 +42,15 @@ inline std::vector &Split(const grpc::string &s, char delim, } // Split splits a string using char, returning the result in a vector. -inline std::vector Split(const grpc::string &s, char delim) { +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) { +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; @@ -75,8 +60,8 @@ inline grpc::string Replace(grpc::string s, const grpc::string &from, } // 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) { +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); @@ -86,8 +71,8 @@ inline grpc::string ReplaceAll(grpc::string s, const grpc::string &search, } // 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) { +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; @@ -96,18 +81,28 @@ inline bool ReplacePrefix(grpc::string *s, const grpc::string &from, return true; } -// CapitalizeFirst capitalizes the first char in a string. -inline grpc::string CapitalizeFirst(grpc::string s) { +// Modularize converts a string into a ruby module compatible name +inline grpc::string Modularize(grpc::string s) { if (s.empty()) { return s; } - s[0] = ::toupper(s[0]); - return s; + grpc::string new_string = ""; + bool was_last_underscore = false; + new_string.append(1, ::toupper(s[0])); + for (grpc::string::size_type i = 1; i < s.size(); ++i) { + if (was_last_underscore && s[i] != '_') { + new_string.append(1, ::toupper(s[i])); + } else if (s[i] != '_') { + new_string.append(1, s[i]); + } + was_last_underscore = s[i] == '_'; + } + return new_string; } // RubyTypeOf updates a proto type to the required ruby equivalent. -inline grpc::string RubyTypeOf(const grpc::string &a_type, - const grpc::string &package) { +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) @@ -115,12 +110,13 @@ inline grpc::string RubyTypeOf(const grpc::string &a_type, return res; } else { std::vector prefixes_and_type = Split(res, '.'); + res.clear(); 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 + res += Modularize(prefixes_and_type[i]); // capitalize pkgs } else { res += prefixes_and_type[i]; } diff --git a/third_party/grpc/src/compiler/ruby_plugin.cc b/third_party/grpc/src/compiler/ruby_plugin.cc index bd10d46e9ca774..9c234bd7cbc1e9 100644 --- a/third_party/grpc/src/compiler/ruby_plugin.cc +++ b/third_party/grpc/src/compiler/ruby_plugin.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -44,10 +29,10 @@ class RubyGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { RubyGrpcGenerator() {} ~RubyGrpcGenerator() {} - bool Generate(const grpc::protobuf::FileDescriptor *file, - const grpc::string ¶meter, - grpc::protobuf::compiler::GeneratorContext *context, - grpc::string *error) const { + bool Generate(const grpc::protobuf::FileDescriptor* file, + const grpc::string& parameter, + 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 @@ -66,7 +51,7 @@ class RubyGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator { } }; -int main(int argc, char *argv[]) { +int main(int argc, char* argv[]) { RubyGrpcGenerator generator; return grpc::protobuf::compiler::PluginMain(argc, argv, &generator); } diff --git a/third_party/grpc/src/compiler/schema_interface.h b/third_party/grpc/src/compiler/schema_interface.h new file mode 100644 index 00000000000000..c000478e68182a --- /dev/null +++ b/third_party/grpc/src/compiler/schema_interface.h @@ -0,0 +1,112 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H +#define GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H + +#include "src/compiler/config.h" + +#include +#include + +#ifndef GRPC_CUSTOM_STRING +#include +#define GRPC_CUSTOM_STRING std::string +#endif + +namespace grpc { + +typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc + +namespace grpc_generator { + +// A common interface for objects having comments in the source. +// Return formatted comments to be inserted in generated code. +struct CommentHolder { + virtual ~CommentHolder() {} + virtual grpc::string GetLeadingComments(const grpc::string prefix) const = 0; + virtual grpc::string GetTrailingComments(const grpc::string prefix) const = 0; + virtual std::vector GetAllComments() const = 0; +}; + +// An abstract interface representing a method. +struct Method : public CommentHolder { + virtual ~Method() {} + + virtual grpc::string name() const = 0; + + virtual grpc::string input_type_name() const = 0; + virtual grpc::string output_type_name() const = 0; + + virtual bool get_module_and_message_path_input( + grpc::string* str, grpc::string generator_file_name, + bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0; + virtual bool get_module_and_message_path_output( + grpc::string* str, grpc::string generator_file_name, + bool generate_in_pb2_grpc, grpc::string import_prefix) const = 0; + + virtual grpc::string get_input_type_name() const = 0; + virtual grpc::string get_output_type_name() const = 0; + virtual bool NoStreaming() const = 0; + virtual bool ClientStreaming() const = 0; + virtual bool ServerStreaming() const = 0; + virtual bool BidiStreaming() const = 0; +}; + +// An abstract interface representing a service. +struct Service : public CommentHolder { + virtual ~Service() {} + + virtual grpc::string name() const = 0; + + virtual int method_count() const = 0; + virtual std::unique_ptr method(int i) const = 0; +}; + +struct Printer { + virtual ~Printer() {} + + virtual void Print(const std::map& vars, + const char* template_string) = 0; + virtual void Print(const char* string) = 0; + virtual void PrintRaw(const char* string) = 0; + virtual void Indent() = 0; + virtual void Outdent() = 0; +}; + +// An interface that allows the source generated to be output using various +// libraries/idls/serializers. +struct File : public CommentHolder { + virtual ~File() {} + + virtual grpc::string filename() const = 0; + virtual grpc::string filename_without_ext() const = 0; + virtual grpc::string package() const = 0; + virtual std::vector package_parts() const = 0; + virtual grpc::string additional_headers() const = 0; + + virtual int service_count() const = 0; + virtual std::unique_ptr service(int i) const = 0; + + virtual std::unique_ptr CreatePrinter(grpc::string* str) const = 0; +}; +} // namespace grpc_generator + +#endif // GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H diff --git a/third_party/grpc/src/core/README.md b/third_party/grpc/src/core/README.md index 0d8c0d5bd90847..130d2652b3990b 100644 --- a/third_party/grpc/src/core/README.md +++ b/third_party/grpc/src/core/README.md @@ -1,8 +1,4 @@ -#Overview +# Overview -This directory contains source code for shared C library. Libraries in other languages in this repository (C++, Ruby, +This directory contains source code for C library (a.k.a the *gRPC C core*) that provides all gRPC's core functionality through a low level API. Libraries in other languages in this repository (C++, Ruby, Python, PHP, NodeJS, Objective-C) are layered on top of this library. - -#Status - -Beta diff --git a/third_party/grpc/src/core/census/README.md b/third_party/grpc/src/core/census/README.md deleted file mode 100644 index fb615a2194da43..00000000000000 --- a/third_party/grpc/src/core/census/README.md +++ /dev/null @@ -1,76 +0,0 @@ - - -# 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/third_party/grpc/src/core/census/aggregation.h b/third_party/grpc/src/core/census/aggregation.h deleted file mode 100644 index e9bc6ada96085b..00000000000000 --- a/third_party/grpc/src/core/census/aggregation.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/census/context.c b/third_party/grpc/src/core/census/context.c deleted file mode 100644 index e60330de64036d..00000000000000 --- a/third_party/grpc/src/core/census/context.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "src/core/support/string.h" - -// Functions in this file support the public context API, including -// encoding/decoding as part of context propagation across RPC's. The overall -// requirements (in approximate priority order) for the -// context representation: -// 1. Efficient conversion to/from wire format -// 2. Minimal bytes used on-wire -// 3. Efficient context creation -// 4. Efficient lookup of tag value for a key -// 5. Efficient iteration over tags -// 6. Minimal memory footprint -// -// Notes on tradeoffs/decisions: -// * tag includes 1 byte length of key, as well as nil-terminating byte. These -// are to aid in efficient parsing and the ability to directly return key -// strings. This is more important than saving a single byte/tag on the wire. -// * The wire encoding uses only single byte values. This eliminates the need -// to handle endian-ness conversions. It also means there is a hard upper -// limit of 255 for both CENSUS_MAX_TAG_KV_LEN and CENSUS_MAX_PROPAGATED_TAGS. -// * Keep all tag information (keys/values/flags) in a single memory buffer, -// that can be directly copied to the wire. -// * Binary tags share the same structure as, but are encoded separately from, -// non-binary tags. This is primarily because non-binary tags are far more -// likely to be repeated across multiple RPC calls, so are more efficiently -// cached and compressed in any metadata schemes. - -// Structure representing a set of tags. Essentially a count of number of tags -// present, and pointer to a chunk of memory that contains the per-tag details. -struct tag_set { - int ntags; // number of tags. - int ntags_alloc; // ntags + number of deleted tags (total number of tags - // in all of kvm). This will always be == ntags, except during the process - // of building a new tag set. - size_t kvm_size; // number of bytes allocated for key/value storage. - size_t kvm_used; // number of bytes of used key/value memory - char *kvm; // key/value memory. Consists of repeated entries of: - // Offset Size Description - // 0 1 Key length, including trailing 0. (K) - // 1 1 Value length. (V) - // 2 1 Flags - // 3 K Key bytes - // 3 + K V Value bytes - // - // We refer to the first 3 entries as the 'tag header'. If extra values are - // introduced in the header, you will need to modify the TAG_HEADER_SIZE - // constant, the raw_tag structure (and everything that uses it) and the - // encode/decode functions appropriately. -}; - -// Number of bytes in tag header. -#define TAG_HEADER_SIZE 3 // key length (1) + value length (1) + flags (1) -// Offsets to tag header entries. -#define KEY_LEN_OFFSET 0 -#define VALUE_LEN_OFFSET 1 -#define FLAG_OFFSET 2 - -// raw_tag represents the raw-storage form of a tag in the kvm of a tag_set. -struct raw_tag { - uint8_t key_len; - uint8_t value_len; - uint8_t flags; - char *key; - char *value; -}; - -// Use a reserved flag bit for indication of deleted tag. -#define CENSUS_TAG_DELETED CENSUS_TAG_RESERVED -#define CENSUS_TAG_IS_DELETED(flags) (flags & CENSUS_TAG_DELETED) - -// Primary (external) representation of a context. Composed of 3 underlying -// tag_set structs, one for each of the binary/printable propagated tags, and -// one for everything else. This is to efficiently support tag -// encoding/decoding. -struct census_context { - struct tag_set tags[3]; - census_context_status status; -}; - -// Indices into the tags member of census_context -#define PROPAGATED_TAGS 0 -#define PROPAGATED_BINARY_TAGS 1 -#define LOCAL_TAGS 2 - -// Extract a raw tag given a pointer (raw) to the tag header. Allow for some -// extra bytes in the tag header (see encode/decode functions for usage: this -// allows for future expansion of the tag header). -static char *decode_tag(struct raw_tag *tag, char *header, int offset) { - tag->key_len = (uint8_t)(*header++); - tag->value_len = (uint8_t)(*header++); - tag->flags = (uint8_t)(*header++); - header += offset; - tag->key = header; - header += tag->key_len; - tag->value = header; - return header + tag->value_len; -} - -// Make a copy (in 'to') of an existing tag_set. -static void tag_set_copy(struct tag_set *to, const struct tag_set *from) { - memcpy(to, from, sizeof(struct tag_set)); - to->kvm = gpr_malloc(to->kvm_size); - memcpy(to->kvm, from->kvm, from->kvm_used); -} - -// Delete a tag from a tag_set, if it exists (returns true if it did). -static bool tag_set_delete_tag(struct tag_set *tags, const char *key, - size_t key_len) { - char *kvp = tags->kvm; - for (int i = 0; i < tags->ntags_alloc; i++) { - uint8_t *flags = (uint8_t *)(kvp + FLAG_OFFSET); - struct raw_tag tag; - kvp = decode_tag(&tag, kvp, 0); - if (CENSUS_TAG_IS_DELETED(tag.flags)) continue; - if ((key_len == tag.key_len) && (memcmp(key, tag.key, key_len) == 0)) { - *flags |= CENSUS_TAG_DELETED; - tags->ntags--; - return true; - } - } - return false; -} - -// Delete a tag from a context, return true if it existed. -static bool context_delete_tag(census_context *context, const census_tag *tag, - size_t key_len) { - return ( - tag_set_delete_tag(&context->tags[LOCAL_TAGS], tag->key, key_len) || - tag_set_delete_tag(&context->tags[PROPAGATED_TAGS], tag->key, key_len) || - tag_set_delete_tag(&context->tags[PROPAGATED_BINARY_TAGS], tag->key, - key_len)); -} - -// Add a tag to a tag_set. Return true on success, false if the tag could -// not be added because of constraints on tag set size. This function should -// not be called if the tag may already exist (in a non-deleted state) in -// the tag_set, as that would result in two tags with the same key. -static bool tag_set_add_tag(struct tag_set *tags, const census_tag *tag, - size_t key_len) { - if (tags->ntags == CENSUS_MAX_PROPAGATED_TAGS) { - return false; - } - const size_t tag_size = key_len + tag->value_len + TAG_HEADER_SIZE; - if (tags->kvm_used + tag_size > tags->kvm_size) { - // allocate new memory if needed - tags->kvm_size += 2 * CENSUS_MAX_TAG_KV_LEN + TAG_HEADER_SIZE; - char *new_kvm = gpr_malloc(tags->kvm_size); - memcpy(new_kvm, tags->kvm, tags->kvm_used); - gpr_free(tags->kvm); - tags->kvm = new_kvm; - } - char *kvp = tags->kvm + tags->kvm_used; - *kvp++ = (char)key_len; - *kvp++ = (char)tag->value_len; - // ensure reserved flags are not used. - *kvp++ = (char)(tag->flags & (CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS | - CENSUS_TAG_BINARY)); - memcpy(kvp, tag->key, key_len); - kvp += key_len; - memcpy(kvp, tag->value, tag->value_len); - tags->kvm_used += tag_size; - tags->ntags++; - tags->ntags_alloc++; - return true; -} - -// Add/modify/delete a tag to/in a context. Caller must validate that tag key -// etc. are valid. -static void context_modify_tag(census_context *context, const census_tag *tag, - size_t key_len) { - // First delete the tag if it is already present. - bool deleted = context_delete_tag(context, tag, key_len); - // Determine if we need to add it back. - bool call_add = tag->value != NULL && tag->value_len != 0; - bool added = false; - if (call_add) { - if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) { - if (CENSUS_TAG_IS_BINARY(tag->flags)) { - added = tag_set_add_tag(&context->tags[PROPAGATED_BINARY_TAGS], tag, - key_len); - } else { - added = tag_set_add_tag(&context->tags[PROPAGATED_TAGS], tag, key_len); - } - } else { - added = tag_set_add_tag(&context->tags[LOCAL_TAGS], tag, key_len); - } - } - if (deleted) { - if (call_add) { - context->status.n_modified_tags++; - } else { - context->status.n_deleted_tags++; - } - } else { - if (added) { - context->status.n_added_tags++; - } else { - context->status.n_ignored_tags++; - } - } -} - -// Remove memory used for deleted tags from a tag set. Basic algorithm: -// 1) Walk through tag set to find first deleted tag. Record where it is. -// 2) Find the next not-deleted tag. Copy all of kvm from there to the end -// "over" the deleted tags -// 3) repeat #1 and #2 until we have seen all tags -// 4) if we are still looking for a not-deleted tag, then all the end portion -// of the kvm is deleted. Just reduce the used amount of memory by the -// appropriate amount. -static void tag_set_flatten(struct tag_set *tags) { - if (tags->ntags == tags->ntags_alloc) return; - bool found_deleted = false; // found a deleted tag. - char *kvp = tags->kvm; - char *dbase = NULL; // record location of deleted tag - for (int i = 0; i < tags->ntags_alloc; i++) { - struct raw_tag tag; - char *next_kvp = decode_tag(&tag, kvp, 0); - if (found_deleted) { - if (!CENSUS_TAG_IS_DELETED(tag.flags)) { - ptrdiff_t reduce = kvp - dbase; // #bytes in deleted tags - GPR_ASSERT(reduce > 0); - ptrdiff_t copy_size = tags->kvm + tags->kvm_used - kvp; - GPR_ASSERT(copy_size > 0); - memmove(dbase, kvp, (size_t)copy_size); - tags->kvm_used -= (size_t)reduce; - next_kvp -= reduce; - found_deleted = false; - } - } else { - if (CENSUS_TAG_IS_DELETED(tag.flags)) { - dbase = kvp; - found_deleted = true; - } - } - kvp = next_kvp; - } - if (found_deleted) { - GPR_ASSERT(dbase > tags->kvm); - tags->kvm_used = (size_t)(dbase - tags->kvm); - } - tags->ntags_alloc = tags->ntags; -} - -census_context *census_context_create(const census_context *base, - const census_tag *tags, int ntags, - census_context_status const **status) { - census_context *context = gpr_malloc(sizeof(census_context)); - // If we are given a base, copy it into our new tag set. Otherwise set it - // to zero/NULL everything. - if (base == NULL) { - memset(context, 0, sizeof(census_context)); - } else { - tag_set_copy(&context->tags[PROPAGATED_TAGS], &base->tags[PROPAGATED_TAGS]); - tag_set_copy(&context->tags[PROPAGATED_BINARY_TAGS], - &base->tags[PROPAGATED_BINARY_TAGS]); - tag_set_copy(&context->tags[LOCAL_TAGS], &base->tags[LOCAL_TAGS]); - memset(&context->status, 0, sizeof(context->status)); - } - // Walk over the additional tags and, for those that aren't invalid, modify - // the context to add/replace/delete as required. - for (int i = 0; i < ntags; i++) { - const census_tag *tag = &tags[i]; - size_t key_len = strlen(tag->key) + 1; - // ignore the tag if it is too long/short. - if (key_len != 1 && key_len <= CENSUS_MAX_TAG_KV_LEN && - tag->value_len <= CENSUS_MAX_TAG_KV_LEN) { - context_modify_tag(context, tag, key_len); - } else { - context->status.n_invalid_tags++; - } - } - // Remove any deleted tags, update status if needed, and return. - tag_set_flatten(&context->tags[PROPAGATED_TAGS]); - tag_set_flatten(&context->tags[PROPAGATED_BINARY_TAGS]); - tag_set_flatten(&context->tags[LOCAL_TAGS]); - context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; - context->status.n_propagated_binary_tags = - context->tags[PROPAGATED_BINARY_TAGS].ntags; - context->status.n_local_tags = context->tags[LOCAL_TAGS].ntags; - if (status) { - *status = &context->status; - } - return context; -} - -const census_context_status *census_context_get_status( - const census_context *context) { - return &context->status; -} - -void census_context_destroy(census_context *context) { - gpr_free(context->tags[PROPAGATED_TAGS].kvm); - gpr_free(context->tags[PROPAGATED_BINARY_TAGS].kvm); - gpr_free(context->tags[LOCAL_TAGS].kvm); - gpr_free(context); -} - -void census_context_initialize_iterator(const census_context *context, - census_context_iterator *iterator) { - iterator->context = context; - iterator->index = 0; - if (context->tags[PROPAGATED_TAGS].ntags != 0) { - iterator->base = PROPAGATED_TAGS; - iterator->kvm = context->tags[PROPAGATED_TAGS].kvm; - } else if (context->tags[PROPAGATED_BINARY_TAGS].ntags != 0) { - iterator->base = PROPAGATED_BINARY_TAGS; - iterator->kvm = context->tags[PROPAGATED_BINARY_TAGS].kvm; - } else if (context->tags[LOCAL_TAGS].ntags != 0) { - iterator->base = LOCAL_TAGS; - iterator->kvm = context->tags[LOCAL_TAGS].kvm; - } else { - iterator->base = -1; - } -} - -int census_context_next_tag(census_context_iterator *iterator, - census_tag *tag) { - if (iterator->base < 0) { - return 0; - } - struct raw_tag raw; - iterator->kvm = decode_tag(&raw, iterator->kvm, 0); - tag->key = raw.key; - tag->value = raw.value; - tag->value_len = raw.value_len; - tag->flags = raw.flags; - if (++iterator->index == iterator->context->tags[iterator->base].ntags) { - do { - if (iterator->base == LOCAL_TAGS) { - iterator->base = -1; - return 1; - } - } while (iterator->context->tags[++iterator->base].ntags == 0); - iterator->index = 0; - iterator->kvm = iterator->context->tags[iterator->base].kvm; - } - return 1; -} - -// Find a tag in a tag_set by key. Return true if found, false otherwise. -static bool tag_set_get_tag(const struct tag_set *tags, const char *key, - size_t key_len, census_tag *tag) { - char *kvp = tags->kvm; - for (int i = 0; i < tags->ntags; i++) { - struct raw_tag raw; - kvp = decode_tag(&raw, kvp, 0); - if (key_len == raw.key_len && memcmp(raw.key, key, key_len) == 0) { - tag->key = raw.key; - tag->value = raw.value; - tag->value_len = raw.value_len; - tag->flags = raw.flags; - return true; - } - } - return false; -} - -int census_context_get_tag(const census_context *context, const char *key, - census_tag *tag) { - size_t key_len = strlen(key) + 1; - if (key_len == 1) { - return 0; - } - if (tag_set_get_tag(&context->tags[PROPAGATED_TAGS], key, key_len, tag) || - tag_set_get_tag(&context->tags[PROPAGATED_BINARY_TAGS], key, key_len, - tag) || - tag_set_get_tag(&context->tags[LOCAL_TAGS], key, key_len, tag)) { - return 1; - } - return 0; -} - -// Context encoding and decoding functions. -// -// Wire format for tag_set's on the wire: -// -// First, a tag set header: -// -// offset bytes description -// 0 1 version number -// 1 1 number of bytes in this header. This allows for future -// expansion. -// 2 1 number of bytes in each tag header. -// 3 1 ntags value from tag set. -// -// This is followed by the key/value memory from struct tag_set. - -#define ENCODED_VERSION 0 // Version number -#define ENCODED_HEADER_SIZE 4 // size of tag set header - -// Encode a tag set. Returns 0 if buffer is too small. -static size_t tag_set_encode(const struct tag_set *tags, char *buffer, - size_t buf_size) { - if (buf_size < ENCODED_HEADER_SIZE + tags->kvm_used) { - return 0; - } - buf_size -= ENCODED_HEADER_SIZE; - *buffer++ = (char)ENCODED_VERSION; - *buffer++ = (char)ENCODED_HEADER_SIZE; - *buffer++ = (char)TAG_HEADER_SIZE; - *buffer++ = (char)tags->ntags; - if (tags->ntags == 0) { - return ENCODED_HEADER_SIZE; - } - memcpy(buffer, tags->kvm, tags->kvm_used); - return ENCODED_HEADER_SIZE + tags->kvm_used; -} - -char *census_context_encode(const census_context *context, char *buffer, - size_t buf_size, size_t *print_buf_size, - size_t *bin_buf_size) { - *print_buf_size = - tag_set_encode(&context->tags[PROPAGATED_TAGS], buffer, buf_size); - if (*print_buf_size == 0) { - return NULL; - } - char *b_buffer = buffer + *print_buf_size; - *bin_buf_size = tag_set_encode(&context->tags[PROPAGATED_BINARY_TAGS], - b_buffer, buf_size - *print_buf_size); - if (*bin_buf_size == 0) { - return NULL; - } - return b_buffer; -} - -// Decode a tag set. -static void tag_set_decode(struct tag_set *tags, const char *buffer, - size_t size) { - uint8_t version = (uint8_t)(*buffer++); - uint8_t header_size = (uint8_t)(*buffer++); - uint8_t tag_header_size = (uint8_t)(*buffer++); - tags->ntags = tags->ntags_alloc = (int)(*buffer++); - if (tags->ntags == 0) { - tags->ntags_alloc = 0; - tags->kvm_size = 0; - tags->kvm_used = 0; - tags->kvm = NULL; - return; - } - if (header_size != ENCODED_HEADER_SIZE) { - GPR_ASSERT(version != ENCODED_VERSION); - GPR_ASSERT(ENCODED_HEADER_SIZE < header_size); - buffer += (header_size - ENCODED_HEADER_SIZE); - } - tags->kvm_used = size - header_size; - tags->kvm_size = tags->kvm_used + CENSUS_MAX_TAG_KV_LEN; - tags->kvm = gpr_malloc(tags->kvm_size); - if (tag_header_size != TAG_HEADER_SIZE) { - // something new in the tag information. I don't understand it, so - // don't copy it over. - GPR_ASSERT(version != ENCODED_VERSION); - GPR_ASSERT(tag_header_size > TAG_HEADER_SIZE); - char *kvp = tags->kvm; - for (int i = 0; i < tags->ntags; i++) { - memcpy(kvp, buffer, TAG_HEADER_SIZE); - kvp += header_size; - struct raw_tag raw; - buffer = - decode_tag(&raw, (char *)buffer, tag_header_size - TAG_HEADER_SIZE); - memcpy(kvp, raw.key, (size_t)raw.key_len + raw.value_len); - kvp += raw.key_len + raw.value_len; - } - } else { - memcpy(tags->kvm, buffer, tags->kvm_used); - } -} - -census_context *census_context_decode(const char *buffer, size_t size, - const char *bin_buffer, size_t bin_size) { - census_context *context = gpr_malloc(sizeof(census_context)); - memset(&context->tags[LOCAL_TAGS], 0, sizeof(struct tag_set)); - if (buffer == NULL) { - memset(&context->tags[PROPAGATED_TAGS], 0, sizeof(struct tag_set)); - } else { - tag_set_decode(&context->tags[PROPAGATED_TAGS], buffer, size); - } - if (bin_buffer == NULL) { - memset(&context->tags[PROPAGATED_BINARY_TAGS], 0, sizeof(struct tag_set)); - } else { - tag_set_decode(&context->tags[PROPAGATED_BINARY_TAGS], bin_buffer, - bin_size); - } - memset(&context->status, 0, sizeof(context->status)); - context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; - context->status.n_propagated_binary_tags = - context->tags[PROPAGATED_BINARY_TAGS].ntags; - // TODO(aveitch): check that BINARY flag is correct for each type. - return context; -} diff --git a/third_party/grpc/src/core/census/grpc_context.c b/third_party/grpc/src/core/census/grpc_context.c deleted file mode 100644 index 4b61382a2c2fd0..00000000000000 --- a/third_party/grpc/src/core/census/grpc_context.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/api_trace.h" -#include "src/core/surface/call.h" - -void grpc_census_call_set_context(grpc_call *call, census_context *context) { - GRPC_API_TRACE("grpc_census_call_set_context(call=%p, census_context=%p)", 2, - (call, 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) { - GRPC_API_TRACE("grpc_census_call_get_context(call=%p)", 1, (call)); - return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING); -} diff --git a/third_party/grpc/src/core/census/grpc_filter.c b/third_party/grpc/src/core/census/grpc_filter.c deleted file mode 100644 index a8db32b9d54dd0..00000000000000 --- a/third_party/grpc/src/core/census/grpc_filter.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/census/grpc_filter.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/channel/channel_stack.h" -#include "src/core/statistics/census_interface.h" -#include "src/core/statistics/census_rpc_stats.h" -#include "src/core/transport/static_metadata.h" - -typedef struct call_data { - census_op_id op_id; - census_context *ctxt; - gpr_timespec start_ts; - int error; - - /* recv callback */ - grpc_metadata_batch *recv_initial_metadata; - grpc_closure *on_done_recv; - grpc_closure finish_recv; -} call_data; - -typedef struct channel_data { uint8_t unused; } channel_data; - -static void extract_and_annotate_method_tag(grpc_metadata_batch *md, - call_data *calld, - channel_data *chand) { - grpc_linked_mdelem *m; - for (m = md->list.head; m != NULL; m = m->next) { - if (m->md->key == GRPC_MDSTR_PATH) { - 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_initial_metadata) { - extract_and_annotate_method_tag(op->send_initial_metadata, calld, chand); - } -} - -static void client_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - client_mutate_op(elem, op); - grpc_call_next_op(exec_ctx, elem, op); -} - -static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr, - bool 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_initial_metadata, calld, chand); - } - calld->on_done_recv->cb(exec_ctx, 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_initial_metadata) { - /* substitute our callback for the op callback */ - calld->recv_initial_metadata = op->recv_initial_metadata; - calld->on_done_recv = op->on_complete; - op->on_complete = &calld->finish_recv; - } -} - -static void server_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - /* TODO(ctiller): this code fails. I don't know why. I expect it's - incomplete, and someone should look at it soon. - - 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(exec_ctx, elem, op); -} - -static void client_init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_call_element_args *args) { - call_data *d = elem->call_data; - GPR_ASSERT(d != NULL); - memset(d, 0, sizeof(*d)); - d->start_ts = gpr_now(GPR_CLOCK_REALTIME); -} - -static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_call_element_args *args) { - call_data *d = elem->call_data; - GPR_ASSERT(d != NULL); - memset(d, 0, sizeof(*d)); - d->start_ts = gpr_now(GPR_CLOCK_REALTIME); - /* TODO(hongyu): call census_tracing_start_op here. */ - grpc_closure_init(&d->finish_recv, server_on_done_recv, elem); -} - -static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = elem->channel_data; - GPR_ASSERT(chand != NULL); -} - -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = elem->channel_data; - GPR_ASSERT(chand != NULL); -} - -const grpc_channel_filter grpc_client_census_filter = { - client_start_transport_op, grpc_channel_next_op, sizeof(call_data), - client_init_call_elem, grpc_call_stack_ignore_set_pollset, - 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, grpc_call_stack_ignore_set_pollset, - server_destroy_call_elem, sizeof(channel_data), init_channel_elem, - destroy_channel_elem, grpc_call_next_get_peer, "census-server"}; diff --git a/third_party/grpc/src/core/census/grpc_filter.h b/third_party/grpc/src/core/census/grpc_filter.h deleted file mode 100644 index b3de3adc94761a..00000000000000 --- a/third_party/grpc/src/core/census/grpc_filter.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/census/initialize.c b/third_party/grpc/src/core/census/initialize.c deleted file mode 100644 index ce7ec09b89fe66..00000000000000 --- a/third_party/grpc/src/core/census/initialize.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -static int features_enabled = CENSUS_FEATURE_NONE; - -int census_initialize(int features) { - if (features_enabled != CENSUS_FEATURE_NONE) { - // Must have been a previous call to census_initialize; return error - return 1; - } - 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/third_party/grpc/src/core/census/operation.c b/third_party/grpc/src/core/census/operation.c deleted file mode 100644 index 5c587043721e6c..00000000000000 --- a/third_party/grpc/src/core/census/operation.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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, int64_t 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, int64_t 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/third_party/grpc/src/core/census/placeholders.c b/third_party/grpc/src/core/census/placeholders.c deleted file mode 100644 index fe23d13971a0be..00000000000000 --- a/third_party/grpc/src/core/census/placeholders.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * Copyright 2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include - -/* Placeholders for the pending APIs */ - -int census_get_trace_record(census_trace_record *trace_record) { - (void)trace_record; - abort(); -} - -void census_record_values(census_context *context, census_value *values, - size_t nvalues) { - (void)context; - (void)values; - (void)nvalues; - abort(); -} - -void census_set_rpc_client_peer(census_context *context, const char *peer) { - (void)context; - (void)peer; - abort(); -} - -void census_trace_scan_end() { abort(); } - -int census_trace_scan_start(int consume) { - (void)consume; - abort(); -} - -const census_aggregation *census_view_aggregrations(const census_view *view) { - (void)view; - abort(); -} - -census_view *census_view_create(uint32_t metric_id, const census_context *tags, - const census_aggregation *aggregations, - size_t naggregations) { - (void)metric_id; - (void)tags; - (void)aggregations; - (void)naggregations; - abort(); -} - -const census_context *census_view_tags(const census_view *view) { - (void)view; - abort(); -} - -void census_view_delete(census_view *view) { - (void)view; - abort(); -} - -const census_view_data *census_view_get_data(const census_view *view) { - (void)view; - abort(); -} - -size_t census_view_metric(const census_view *view) { - (void)view; - abort(); -} - -size_t census_view_naggregations(const census_view *view) { - (void)view; - abort(); -} - -void census_view_reset(census_view *view) { - (void)view; - abort(); -} diff --git a/third_party/grpc/src/core/census/rpc_metric_id.h b/third_party/grpc/src/core/census/rpc_metric_id.h deleted file mode 100644 index f199839511ce12..00000000000000 --- a/third_party/grpc/src/core/census/rpc_metric_id.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 ((uint32_t)0) -/* Count of server requests sent. */ -#define CENSUS_METRIC_RPC_SERVER_REQUESTS ((uint32_t)1) -/* Client error counts. */ -#define CENSUS_METRIC_RPC_CLIENT_ERRORS ((uint32_t)2) -/* Server error counts. */ -#define CENSUS_METRIC_RPC_SERVER_ERRORS ((uint32_t)3) -/* Client side request latency. */ -#define CENSUS_METRIC_RPC_CLIENT_LATENCY ((uint32_t)4) -/* Server side request latency. */ -#define CENSUS_METRIC_RPC_SERVER_LATENCY ((uint32_t)5) - -#endif /* CENSUS_RPC_METRIC_ID_H */ diff --git a/third_party/grpc/src/core/census/tracing.c b/third_party/grpc/src/core/census/tracing.c deleted file mode 100644 index 3b5d6dab2b8d77..00000000000000 --- a/third_party/grpc/src/core/census/tracing.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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, uint32_t type, - const char *buffer, size_t n) {} diff --git a/third_party/grpc/src/core/channel/channel_args.c b/third_party/grpc/src/core/channel/channel_args.c deleted file mode 100644 index 0427ce0b8dc053..00000000000000 --- a/third_party/grpc/src/core/channel/channel_args.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include "src/core/channel/channel_args.h" -#include "src/core/support/string.h" - -#include -#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 && census_enabled(); - } - } - return census_enabled(); -} - -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 (grpc_compression_algorithm)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((unsigned *)states_arg, algorithm); - } else { - GPR_BITCLEAR((unsigned *)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((unsigned *)&tmp.value.integer, algorithm); - } else { - GPR_BITCLEAR((unsigned *)&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/third_party/grpc/src/core/channel/channel_args.h b/third_party/grpc/src/core/channel/channel_args.h deleted file mode 100644 index 480cc9aec2fc0e..00000000000000 --- a/third_party/grpc/src/core/channel/channel_args.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 with 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/third_party/grpc/src/core/channel/channel_stack.c b/third_party/grpc/src/core/channel/channel_stack.c deleted file mode 100644 index 3e616883640d1f..00000000000000 --- a/third_party/grpc/src/core/channel/channel_stack.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) - -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(grpc_exec_ctx *exec_ctx, int initial_refs, - grpc_iomgr_cb_func destroy, void *destroy_arg, - const grpc_channel_filter **filters, - size_t filter_count, - const grpc_channel_args *channel_args, - const char *name, 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; - grpc_channel_element_args args; - char *user_data; - size_t i; - - stack->count = filter_count; - GRPC_STREAM_REF_INIT(&stack->refcount, initial_refs, destroy, destroy_arg, - name); - 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++) { - args.channel_stack = stack; - args.channel_args = channel_args; - args.is_first = i == 0; - args.is_last = i == (filter_count - 1); - elems[i].filter = filters[i]; - elems[i].channel_data = user_data; - elems[i].filter->init_channel_elem(exec_ctx, &elems[i], &args); - 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((uintptr_t)(user_data - (char *)stack) == - grpc_channel_stack_size(filters, filter_count)); - - stack->call_stack_size = call_size; -} - -void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, &channel_elems[i]); - } -} - -void grpc_call_stack_init(grpc_exec_ctx *exec_ctx, - grpc_channel_stack *channel_stack, int initial_refs, - grpc_iomgr_cb_func destroy, void *destroy_arg, - grpc_call_context_element *context, - const void *transport_server_data, - grpc_call_stack *call_stack) { - grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack); - grpc_call_element_args args; - size_t count = channel_stack->count; - grpc_call_element *call_elems; - char *user_data; - size_t i; - - call_stack->count = count; - GRPC_STREAM_REF_INIT(&call_stack->refcount, initial_refs, destroy, - destroy_arg, "CALL_STACK"); - 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++) { - args.call_stack = call_stack; - args.server_transport_data = transport_server_data; - args.context = context; - 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(exec_ctx, &call_elems[i], &args); - user_data += - ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); - } -} - -void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx, - grpc_call_stack *call_stack, - grpc_pollset *pollset) { - size_t count = call_stack->count; - grpc_call_element *call_elems; - char *user_data; - size_t i; - - 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->set_pollset(exec_ctx, &call_elems[i], pollset); - user_data += - ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); - } -} - -void grpc_call_stack_ignore_set_pollset(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_pollset *pollset) {} - -void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, 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(exec_ctx, &elems[i]); - } -} - -void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op *op) { - grpc_call_element *next_elem = elem + 1; - next_elem->filter->start_transport_stream_op(exec_ctx, next_elem, op); -} - -char *grpc_call_next_get_peer(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - grpc_call_element *next_elem = elem + 1; - return next_elem->filter->get_peer(exec_ctx, next_elem); -} - -void grpc_channel_next_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, - grpc_transport_op *op) { - grpc_channel_element *next_elem = elem + 1; - next_elem->filter->start_transport_op(exec_ctx, 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_exec_ctx *exec_ctx, - 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(exec_ctx, cur_elem, &op); -} diff --git a/third_party/grpc/src/core/channel/channel_stack.h b/third_party/grpc/src/core/channel/channel_stack.h deleted file mode 100644 index c01050e7179aac..00000000000000 --- a/third_party/grpc/src/core/channel/channel_stack.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - -typedef struct grpc_channel_stack grpc_channel_stack; -typedef struct grpc_call_stack grpc_call_stack; - -typedef struct { - grpc_channel_stack *channel_stack; - const grpc_channel_args *channel_args; - int is_first; - int is_last; -} grpc_channel_element_args; - -typedef struct { - grpc_call_stack *call_stack; - const void *server_transport_data; - grpc_call_context_element *context; -} grpc_call_element_args; - -/* 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_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args); - void (*set_pollset)(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_pollset *pollset); - /* Destroy per call data. - The filter does not need to do any chaining */ - void (*destroy_call_elem)(grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, grpc_channel_element *elem, - grpc_channel_element_args *args); - /* Destroy per channel data. - The filter does not need to do any chaining */ - void (*destroy_channel_elem)(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem); - - /* Implement grpc_call_get_peer() */ - char *(*get_peer)(grpc_exec_ctx *exec_ctx, 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 */ -struct grpc_channel_stack { - grpc_stream_refcount refcount; - size_t count; - /* Memory required for a call stack (computed at channel stack - initialization) */ - size_t call_stack_size; -}; - -/* A call stack tracks a set of related filters for one call, and guarantees - they live within a single malloc() allocation */ -struct grpc_call_stack { - /* shared refcount for this channel stack. - MUST be the first element: the underlying code calls destroy - with the address of the refcount, but higher layers prefer to think - about the address of the call stack itself. */ - grpc_stream_refcount refcount; - size_t count; -}; - -/* 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(grpc_exec_ctx *exec_ctx, int initial_refs, - grpc_iomgr_cb_func destroy, void *destroy_arg, - const grpc_channel_filter **filters, - size_t filter_count, const grpc_channel_args *args, - const char *name, grpc_channel_stack *stack); -/* Destroy a channel stack */ -void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_channel_stack *channel_stack, int initial_refs, - grpc_iomgr_cb_func destroy, void *destroy_arg, - grpc_call_context_element *context, - const void *transport_server_data, - grpc_call_stack *call_stack); -/* Set a pollset for a call stack: must occur before the first op is started */ -void grpc_call_stack_set_pollset(grpc_exec_ctx *exec_ctx, - grpc_call_stack *call_stack, - grpc_pollset *pollset); - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -#define GRPC_CALL_STACK_REF(call_stack, reason) \ - grpc_stream_ref(&(call_stack)->refcount, reason) -#define GRPC_CALL_STACK_UNREF(exec_ctx, call_stack, reason) \ - grpc_stream_unref(exec_ctx, &(call_stack)->refcount, reason) -#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \ - grpc_stream_ref(&(channel_stack)->refcount, reason) -#define GRPC_CHANNEL_STACK_UNREF(exec_ctx, channel_stack, reason) \ - grpc_stream_unref(exec_ctx, &(channel_stack)->refcount, reason) -#else -#define GRPC_CALL_STACK_REF(call_stack, reason) \ - grpc_stream_ref(&(call_stack)->refcount) -#define GRPC_CALL_STACK_UNREF(exec_ctx, call_stack, reason) \ - grpc_stream_unref(exec_ctx, &(call_stack)->refcount) -#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \ - grpc_stream_ref(&(channel_stack)->refcount) -#define GRPC_CHANNEL_STACK_UNREF(exec_ctx, channel_stack, reason) \ - grpc_stream_unref(exec_ctx, &(channel_stack)->refcount) -#endif - -/* Destroy a call stack */ -void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack); - -/* Ignore set pollset - used by filters to implement the set_pollset method - if they don't care about pollsets at all. Does nothing. */ -void grpc_call_stack_ignore_set_pollset(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_pollset *pollset); -/* Call the next operation in a call stack */ -void grpc_call_next_op(grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, - 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/third_party/grpc/src/core/channel/client_channel.c b/third_party/grpc/src/core/channel/client_channel.c deleted file mode 100644 index 7176c01b05f11f..00000000000000 --- a/third_party/grpc/src/core/channel/client_channel.c +++ /dev/null @@ -1,528 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/channel/client_channel.h" - -#include -#include - -#include -#include -#include -#include - -#include "src/core/channel/channel_args.h" -#include "src/core/channel/connected_channel.h" -#include "src/core/channel/subchannel_call_holder.h" -#include "src/core/iomgr/iomgr.h" -#include "src/core/profiling/timers.h" -#include "src/core/support/string.h" -#include "src/core/surface/channel.h" -#include "src/core/transport/connectivity_state.h" - -/* Client channel implementation */ - -typedef grpc_subchannel_call_holder call_data; - -typedef struct client_channel_channel_data { - /** resolver for this channel */ - grpc_resolver *resolver; - /** have we started resolving this channel */ - int started_resolving; - - /** 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_closure_list waiting_for_config_closures; - /** resolver callback */ - grpc_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; - /** owning stack */ - grpc_channel_stack *owning_stack; - /** interested parties */ - grpc_pollset_set interested_parties; -} 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_closure on_changed; - grpc_connectivity_state state; - grpc_lb_policy *lb_policy; -} lb_policy_connectivity_watcher; - -typedef struct { - grpc_closure closure; - grpc_call_element *elem; -} waiting_call; - -static char *cc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - return grpc_subchannel_call_holder_get_peer(exec_ctx, elem->call_data); -} - -static void cc_start_transport_stream_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - grpc_subchannel_call_holder_perform_op(exec_ctx, elem->call_data, op); -} - -static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand, - grpc_lb_policy *lb_policy, - grpc_connectivity_state current_state); - -static void on_lb_policy_state_changed_locked( - grpc_exec_ctx *exec_ctx, lb_policy_connectivity_watcher *w) { - grpc_connectivity_state publish_state = w->state; - /* check if the notification is for a stale policy */ - if (w->lb_policy != w->chand->lb_policy) return; - - if (publish_state == GRPC_CHANNEL_FATAL_FAILURE && - w->chand->resolver != NULL) { - publish_state = GRPC_CHANNEL_TRANSIENT_FAILURE; - grpc_resolver_channel_saw_error(exec_ctx, w->chand->resolver); - GRPC_LB_POLICY_UNREF(exec_ctx, w->chand->lb_policy, "channel"); - w->chand->lb_policy = NULL; - } - grpc_connectivity_state_set(exec_ctx, &w->chand->state_tracker, publish_state, - "lb_changed"); - if (w->state != GRPC_CHANNEL_FATAL_FAILURE) { - watch_lb_policy(exec_ctx, w->chand, w->lb_policy, w->state); - } -} - -static void on_lb_policy_state_changed(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - lb_policy_connectivity_watcher *w = arg; - - gpr_mu_lock(&w->chand->mu_config); - on_lb_policy_state_changed_locked(exec_ctx, w); - gpr_mu_unlock(&w->chand->mu_config); - - GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, "watch_lb_policy"); - gpr_free(w); -} - -static void watch_lb_policy(grpc_exec_ctx *exec_ctx, channel_data *chand, - grpc_lb_policy *lb_policy, - grpc_connectivity_state current_state) { - lb_policy_connectivity_watcher *w = gpr_malloc(sizeof(*w)); - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "watch_lb_policy"); - - w->chand = chand; - grpc_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(exec_ctx, lb_policy, &w->state, - &w->on_changed); -} - -static void cc_on_config_changed(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - channel_data *chand = arg; - grpc_lb_policy *lb_policy = NULL; - grpc_lb_policy *old_lb_policy; - grpc_resolver *old_resolver; - 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(exec_ctx, lb_policy); - } - - grpc_client_config_unref(exec_ctx, chand->incoming_configuration); - } - - chand->incoming_configuration = NULL; - - if (lb_policy != NULL) { - grpc_pollset_set_add_pollset_set(exec_ctx, &lb_policy->interested_parties, - &chand->interested_parties); - } - - 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 */) { - grpc_exec_ctx_enqueue_list(exec_ctx, &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(exec_ctx, &chand->state_tracker, state, - "new_lb+resolver"); - if (lb_policy != NULL) { - watch_lb_policy(exec_ctx, chand, lb_policy, state); - } - gpr_mu_unlock(&chand->mu_config); - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); - grpc_resolver_next(exec_ctx, resolver, &chand->incoming_configuration, - &chand->on_config_changed); - GRPC_RESOLVER_UNREF(exec_ctx, resolver, "channel-next"); - } else { - old_resolver = chand->resolver; - chand->resolver = NULL; - grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, - GRPC_CHANNEL_FATAL_FAILURE, "resolver_gone"); - gpr_mu_unlock(&chand->mu_config); - if (old_resolver != NULL) { - grpc_resolver_shutdown(exec_ctx, old_resolver); - GRPC_RESOLVER_UNREF(exec_ctx, old_resolver, "channel"); - } - } - - if (exit_idle) { - grpc_lb_policy_exit_idle(exec_ctx, lb_policy); - GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "exit_idle"); - } - - if (old_lb_policy != NULL) { - grpc_pollset_set_del_pollset_set(exec_ctx, - &old_lb_policy->interested_parties, - &chand->interested_parties); - GRPC_LB_POLICY_UNREF(exec_ctx, old_lb_policy, "channel"); - } - - if (lb_policy != NULL) { - GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "config_change"); - } - - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "resolver"); -} - -static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_transport_op *op) { - channel_data *chand = elem->channel_data; - grpc_resolver *destroy_resolver = NULL; - - grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, true, NULL); - - GPR_ASSERT(op->set_accept_stream == NULL); - if (op->bind_pollset != NULL) { - grpc_pollset_set_add_pollset(exec_ctx, &chand->interested_parties, - op->bind_pollset); - } - - gpr_mu_lock(&chand->mu_config); - if (op->on_connectivity_state_change != NULL) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &chand->state_tracker, op->connectivity_state, - op->on_connectivity_state_change); - op->on_connectivity_state_change = NULL; - op->connectivity_state = NULL; - } - - if (op->send_ping != NULL) { - if (chand->lb_policy == NULL) { - grpc_exec_ctx_enqueue(exec_ctx, op->send_ping, false, NULL); - } else { - grpc_lb_policy_ping_one(exec_ctx, chand->lb_policy, op->send_ping); - op->bind_pollset = NULL; - } - op->send_ping = NULL; - } - - if (op->disconnect && chand->resolver != NULL) { - grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, - GRPC_CHANNEL_FATAL_FAILURE, "disconnect"); - destroy_resolver = chand->resolver; - chand->resolver = NULL; - if (chand->lb_policy != NULL) { - grpc_pollset_set_del_pollset_set(exec_ctx, - &chand->lb_policy->interested_parties, - &chand->interested_parties); - GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); - chand->lb_policy = NULL; - } - } - gpr_mu_unlock(&chand->mu_config); - - if (destroy_resolver) { - grpc_resolver_shutdown(exec_ctx, destroy_resolver); - GRPC_RESOLVER_UNREF(exec_ctx, destroy_resolver, "channel"); - } -} - -typedef struct { - grpc_metadata_batch *initial_metadata; - grpc_connected_subchannel **connected_subchannel; - grpc_closure *on_ready; - grpc_call_element *elem; - grpc_closure closure; -} continue_picking_args; - -static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg, - grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **connected_subchannel, - grpc_closure *on_ready); - -static void continue_picking(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - continue_picking_args *cpa = arg; - if (!success) { - grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, false, NULL); - } else if (cpa->connected_subchannel == NULL) { - /* cancelled, do nothing */ - } else if (cc_pick_subchannel(exec_ctx, cpa->elem, cpa->initial_metadata, - cpa->connected_subchannel, cpa->on_ready)) { - grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, true, NULL); - } - gpr_free(cpa); -} - -static int cc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *elemp, - grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **connected_subchannel, - grpc_closure *on_ready) { - grpc_call_element *elem = elemp; - channel_data *chand = elem->channel_data; - call_data *calld = elem->call_data; - continue_picking_args *cpa; - grpc_closure *closure; - - GPR_ASSERT(connected_subchannel); - - gpr_mu_lock(&chand->mu_config); - if (initial_metadata == NULL) { - if (chand->lb_policy != NULL) { - grpc_lb_policy_cancel_pick(exec_ctx, chand->lb_policy, - connected_subchannel); - } - for (closure = chand->waiting_for_config_closures.head; closure != NULL; - closure = grpc_closure_next(closure)) { - cpa = closure->cb_arg; - if (cpa->connected_subchannel == connected_subchannel) { - cpa->connected_subchannel = NULL; - grpc_exec_ctx_enqueue(exec_ctx, cpa->on_ready, false, NULL); - } - } - gpr_mu_unlock(&chand->mu_config); - return 1; - } - if (chand->lb_policy != NULL) { - grpc_lb_policy *lb_policy = chand->lb_policy; - int r; - GRPC_LB_POLICY_REF(lb_policy, "cc_pick_subchannel"); - gpr_mu_unlock(&chand->mu_config); - r = grpc_lb_policy_pick(exec_ctx, lb_policy, calld->pollset, - initial_metadata, connected_subchannel, on_ready); - GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "cc_pick_subchannel"); - return r; - } - if (chand->resolver != NULL && !chand->started_resolving) { - chand->started_resolving = 1; - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); - grpc_resolver_next(exec_ctx, chand->resolver, - &chand->incoming_configuration, - &chand->on_config_changed); - } - cpa = gpr_malloc(sizeof(*cpa)); - cpa->initial_metadata = initial_metadata; - cpa->connected_subchannel = connected_subchannel; - cpa->on_ready = on_ready; - cpa->elem = elem; - grpc_closure_init(&cpa->closure, continue_picking, cpa); - grpc_closure_list_add(&chand->waiting_for_config_closures, &cpa->closure, 1); - gpr_mu_unlock(&chand->mu_config); - return 0; -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - grpc_subchannel_call_holder_init(elem->call_data, cc_pick_subchannel, elem, - args->call_stack); -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - grpc_subchannel_call_holder_destroy(exec_ctx, elem->call_data); -} - -/* Constructor for channel_data */ -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = elem->channel_data; - - memset(chand, 0, sizeof(*chand)); - - GPR_ASSERT(args->is_last); - GPR_ASSERT(elem->filter == &grpc_client_channel_filter); - - gpr_mu_init(&chand->mu_config); - grpc_closure_init(&chand->on_config_changed, cc_on_config_changed, chand); - chand->owning_stack = args->channel_stack; - - grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, - "client_channel"); - grpc_pollset_set_init(&chand->interested_parties); -} - -/* Destructor for channel_data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = elem->channel_data; - - if (chand->resolver != NULL) { - grpc_resolver_shutdown(exec_ctx, chand->resolver); - GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel"); - } - if (chand->lb_policy != NULL) { - grpc_pollset_set_del_pollset_set(exec_ctx, - &chand->lb_policy->interested_parties, - &chand->interested_parties); - GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); - } - grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); - grpc_pollset_set_destroy(&chand->interested_parties); - gpr_mu_destroy(&chand->mu_config); -} - -static void cc_set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_pollset *pollset) { - call_data *calld = elem->call_data; - calld->pollset = pollset; -} - -const grpc_channel_filter grpc_client_channel_filter = { - cc_start_transport_stream_op, cc_start_transport_op, sizeof(call_data), - init_call_elem, cc_set_pollset, destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, cc_get_peer, "client-channel", -}; - -void grpc_client_channel_set_resolver(grpc_exec_ctx *exec_ctx, - 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 (!grpc_closure_list_empty(chand->waiting_for_config_closures) || - chand->exit_idle_when_lb_policy_arrives) { - chand->started_resolving = 1; - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); - grpc_resolver_next(exec_ctx, resolver, &chand->incoming_configuration, - &chand->on_config_changed); - } - gpr_mu_unlock(&chand->mu_config); -} - -grpc_connectivity_state grpc_client_channel_check_connectivity_state( - grpc_exec_ctx *exec_ctx, 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(exec_ctx, chand->lb_policy); - } else { - chand->exit_idle_when_lb_policy_arrives = 1; - if (!chand->started_resolving && chand->resolver != NULL) { - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); - chand->started_resolving = 1; - grpc_resolver_next(exec_ctx, chand->resolver, - &chand->incoming_configuration, - &chand->on_config_changed); - } - } - } - gpr_mu_unlock(&chand->mu_config); - return out; -} - -typedef struct { - channel_data *chand; - grpc_pollset *pollset; - grpc_closure *on_complete; - grpc_closure my_closure; -} external_connectivity_watcher; - -static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - external_connectivity_watcher *w = arg; - grpc_closure *follow_up = w->on_complete; - grpc_pollset_set_del_pollset(exec_ctx, &w->chand->interested_parties, - w->pollset); - GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, - "external_connectivity_watcher"); - gpr_free(w); - follow_up->cb(exec_ctx, follow_up->cb_arg, iomgr_success); -} - -void grpc_client_channel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, - grpc_connectivity_state *state, grpc_closure *on_complete) { - channel_data *chand = elem->channel_data; - external_connectivity_watcher *w = gpr_malloc(sizeof(*w)); - w->chand = chand; - w->pollset = pollset; - w->on_complete = on_complete; - grpc_pollset_set_add_pollset(exec_ctx, &chand->interested_parties, pollset); - grpc_closure_init(&w->my_closure, on_external_watch_complete, w); - GRPC_CHANNEL_STACK_REF(w->chand->owning_stack, - "external_connectivity_watcher"); - gpr_mu_lock(&chand->mu_config); - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &chand->state_tracker, state, &w->my_closure); - gpr_mu_unlock(&chand->mu_config); -} diff --git a/third_party/grpc/src/core/channel/client_channel.h b/third_party/grpc/src/core/channel/client_channel.h deleted file mode 100644 index d9bc4971f1ae51..00000000000000 --- a/third_party/grpc/src/core/channel/client_channel.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, - grpc_channel_stack *channel_stack, - grpc_resolver *resolver); - -grpc_connectivity_state grpc_client_channel_check_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect); - -void grpc_client_channel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, - grpc_connectivity_state *state, grpc_closure *on_complete); - -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_CHANNEL_H */ diff --git a/third_party/grpc/src/core/channel/client_uchannel.c b/third_party/grpc/src/core/channel/client_uchannel.c deleted file mode 100644 index b1e7155773fd68..00000000000000 --- a/third_party/grpc/src/core/channel/client_uchannel.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/channel/client_uchannel.h" - -#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/subchannel_call_holder.h" -#include "src/core/iomgr/iomgr.h" -#include "src/core/support/string.h" -#include "src/core/surface/channel.h" -#include "src/core/transport/connectivity_state.h" - -#include -#include -#include -#include - -/** Microchannel (uchannel) implementation: a lightweight channel without any - * load-balancing mechanisms meant for communication from within the core. */ - -typedef struct client_uchannel_channel_data { - /** 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_stack *owning_stack; - - /** connectivity state being tracked */ - grpc_connectivity_state_tracker state_tracker; - - /** the subchannel wrapped by the microchannel */ - grpc_connected_subchannel *connected_subchannel; - - /** the callback used to stay subscribed to subchannel connectivity - * notifications */ - grpc_closure connectivity_cb; - - /** the current connectivity state of the wrapped subchannel */ - grpc_connectivity_state subchannel_connectivity; - - gpr_mu mu_state; -} channel_data; - -typedef grpc_subchannel_call_holder call_data; - -static void monitor_subchannel(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - channel_data *chand = arg; - grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, - chand->subchannel_connectivity, - "uchannel_monitor_subchannel"); - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, chand->connected_subchannel, NULL, - &chand->subchannel_connectivity, &chand->connectivity_cb); -} - -static char *cuc_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - return grpc_subchannel_call_holder_get_peer(exec_ctx, elem->call_data); -} - -static void cuc_start_transport_stream_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - grpc_subchannel_call_holder_perform_op(exec_ctx, elem->call_data, op); -} - -static void cuc_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_transport_op *op) { - channel_data *chand = elem->channel_data; - - grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, true, NULL); - - GPR_ASSERT(op->set_accept_stream == NULL); - GPR_ASSERT(op->bind_pollset == NULL); - - if (op->on_connectivity_state_change != NULL) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &chand->state_tracker, op->connectivity_state, - op->on_connectivity_state_change); - op->on_connectivity_state_change = NULL; - op->connectivity_state = NULL; - } - - if (op->disconnect) { - grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, - GRPC_CHANNEL_FATAL_FAILURE, "disconnect"); - } -} - -static int cuc_pick_subchannel(grpc_exec_ctx *exec_ctx, void *arg, - grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **connected_subchannel, - grpc_closure *on_ready) { - channel_data *chand = arg; - GPR_ASSERT(initial_metadata != NULL); - *connected_subchannel = chand->connected_subchannel; - return 1; -} - -/* Constructor for call_data */ -static void cuc_init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - grpc_subchannel_call_holder_init(elem->call_data, cuc_pick_subchannel, - elem->channel_data, args->call_stack); -} - -/* Destructor for call_data */ -static void cuc_destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - grpc_subchannel_call_holder_destroy(exec_ctx, elem->call_data); -} - -/* Constructor for channel_data */ -static void cuc_init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = elem->channel_data; - memset(chand, 0, sizeof(*chand)); - grpc_closure_init(&chand->connectivity_cb, monitor_subchannel, chand); - GPR_ASSERT(args->is_last); - GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); - chand->owning_stack = args->channel_stack; - grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, - "client_uchannel"); - gpr_mu_init(&chand->mu_state); -} - -/* Destructor for channel_data */ -static void cuc_destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = elem->channel_data; - /* cancel subscription */ - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, chand->connected_subchannel, NULL, NULL, - &chand->connectivity_cb); - grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); - gpr_mu_destroy(&chand->mu_state); - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, chand->connected_subchannel, - "uchannel"); -} - -static void cuc_set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_pollset *pollset) { - call_data *calld = elem->call_data; - calld->pollset = pollset; -} - -const grpc_channel_filter grpc_client_uchannel_filter = { - cuc_start_transport_stream_op, cuc_start_transport_op, sizeof(call_data), - cuc_init_call_elem, cuc_set_pollset, cuc_destroy_call_elem, - sizeof(channel_data), cuc_init_channel_elem, cuc_destroy_channel_elem, - cuc_get_peer, "client-uchannel", -}; - -grpc_connectivity_state grpc_client_uchannel_check_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) { - channel_data *chand = elem->channel_data; - grpc_connectivity_state out; - gpr_mu_lock(&chand->mu_state); - out = grpc_connectivity_state_check(&chand->state_tracker); - gpr_mu_unlock(&chand->mu_state); - return out; -} - -void grpc_client_uchannel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, - grpc_connectivity_state *state, grpc_closure *on_complete) { - channel_data *chand = elem->channel_data; - gpr_mu_lock(&chand->mu_state); - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &chand->state_tracker, state, on_complete); - gpr_mu_unlock(&chand->mu_state); -} - -grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel, - grpc_channel_args *args) { - grpc_channel *channel = NULL; -#define MAX_FILTERS 3 - const grpc_channel_filter *filters[MAX_FILTERS]; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - size_t n = 0; - - if (grpc_channel_args_is_census_enabled(args)) { - filters[n++] = &grpc_client_census_filter; - } - filters[n++] = &grpc_compress_filter; - filters[n++] = &grpc_client_uchannel_filter; - GPR_ASSERT(n <= MAX_FILTERS); - - channel = - grpc_channel_create_from_filters(&exec_ctx, NULL, filters, n, args, 1); - - return channel; -} - -void grpc_client_uchannel_set_connected_subchannel( - grpc_channel *uchannel, grpc_connected_subchannel *connected_subchannel) { - grpc_channel_element *elem = - grpc_channel_stack_last_element(grpc_channel_get_channel_stack(uchannel)); - channel_data *chand = elem->channel_data; - GPR_ASSERT(elem->filter == &grpc_client_uchannel_filter); - gpr_mu_lock(&chand->mu_state); - chand->connected_subchannel = connected_subchannel; - GRPC_CONNECTED_SUBCHANNEL_REF(connected_subchannel, "uchannel"); - gpr_mu_unlock(&chand->mu_state); -} diff --git a/third_party/grpc/src/core/channel/client_uchannel.h b/third_party/grpc/src/core/channel/client_uchannel.h deleted file mode 100644 index 92a831493cfbf7..00000000000000 --- a/third_party/grpc/src/core/channel/client_uchannel.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_MICROCHANNEL_H -#define GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H - -#include "src/core/channel/channel_stack.h" -#include "src/core/client_config/resolver.h" - -#define GRPC_MICROCHANNEL_SUBCHANNEL_ARG "grpc.microchannel_subchannel_key" - -/* A client microchannel (aka uchannel) is a channel wrapping a subchannel, for - * the purposes of lightweight RPC communications from within the core.*/ - -extern const grpc_channel_filter grpc_client_uchannel_filter; - -grpc_connectivity_state grpc_client_uchannel_check_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect); - -void grpc_client_uchannel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, grpc_pollset *pollset, - grpc_connectivity_state *state, grpc_closure *on_complete); - -grpc_channel *grpc_client_uchannel_create(grpc_subchannel *subchannel, - grpc_channel_args *args); - -void grpc_client_uchannel_set_connected_subchannel( - grpc_channel *uchannel, grpc_connected_subchannel *connected_subchannel); - -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CLIENT_MICROCHANNEL_H */ diff --git a/third_party/grpc/src/core/channel/compress_filter.c b/third_party/grpc/src/core/channel/compress_filter.c deleted file mode 100644 index 3e7ca08fd29e7b..00000000000000 --- a/third_party/grpc/src/core/channel/compress_filter.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#include -#include -#include -#include - -#include "src/core/channel/channel_args.h" -#include "src/core/channel/compress_filter.h" -#include "src/core/compression/algorithm_metadata.h" -#include "src/core/compression/message_compress.h" -#include "src/core/profiling/timers.h" -#include "src/core/support/string.h" -#include "src/core/transport/static_metadata.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; - uint32_t remaining_slice_bytes; - /** 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; - - grpc_transport_stream_op send_op; - uint32_t send_length; - uint32_t send_flags; - gpr_slice incoming_slice; - grpc_slice_buffer_stream replacement_stream; - grpc_closure *post_send; - grpc_closure send_done; - grpc_closure got_slice; -} call_data; - -typedef struct channel_data { - /** The default, channel-level, compression algorithm */ - grpc_compression_algorithm default_compression_algorithm; - /** Compression options for the channel */ - grpc_compression_options compression_options; - /** Supported compression algorithms */ - uint32_t supported_compression_algorithms; -} channel_data; - -/** 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 == GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST) { - 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' (unknown). Ignoring.", - md_c_str); - calld->compression_algorithm = GRPC_COMPRESS_NONE; - } - if (grpc_compression_options_is_algorithm_enabled( - &channeld->compression_options, calld->compression_algorithm) == - 0) { - gpr_log(GPR_ERROR, - "Invalid compression algorithm: '%s' (previously disabled). " - "Ignoring.", - md_c_str); - calld->compression_algorithm = GRPC_COMPRESS_NONE; - } - calld->has_compression_algorithm = 1; - return NULL; - } - - return md; -} - -static int skip_compression(grpc_call_element *elem) { - call_data *calld = elem->call_data; - channel_data *channeld = elem->channel_data; - 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; -} - -/** Filter initial metadata */ -static void process_send_initial_metadata( - grpc_call_element *elem, grpc_metadata_batch *initial_metadata) { - call_data *calld = elem->call_data; - channel_data *channeld = elem->channel_data; - /* Parse incoming request for compression. If any, it'll be available - * at calld->compression_algorithm */ - grpc_metadata_batch_filter(initial_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( - initial_metadata, &calld->compression_algorithm_storage, - grpc_compression_encoding_mdelem(calld->compression_algorithm)); - - /* convey supported compression algorithms */ - grpc_metadata_batch_add_tail(initial_metadata, - &calld->accept_encoding_storage, - GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS( - channeld->supported_compression_algorithms)); -} - -static void continue_send_message(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem); - -static void send_done(grpc_exec_ctx *exec_ctx, void *elemp, bool success) { - grpc_call_element *elem = elemp; - call_data *calld = elem->call_data; - gpr_slice_buffer_reset_and_unref(&calld->slices); - calld->post_send->cb(exec_ctx, calld->post_send->cb_arg, success); -} - -static void finish_send_message(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - call_data *calld = elem->call_data; - int did_compress; - gpr_slice_buffer tmp; - gpr_slice_buffer_init(&tmp); - did_compress = - grpc_msg_compress(calld->compression_algorithm, &calld->slices, &tmp); - if (did_compress) { - gpr_slice_buffer_swap(&calld->slices, &tmp); - calld->send_flags |= GRPC_WRITE_INTERNAL_COMPRESS; - } - gpr_slice_buffer_destroy(&tmp); - - grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices, - calld->send_flags); - calld->send_op.send_message = &calld->replacement_stream.base; - calld->post_send = calld->send_op.on_complete; - calld->send_op.on_complete = &calld->send_done; - - grpc_call_next_op(exec_ctx, elem, &calld->send_op); -} - -static void got_slice(grpc_exec_ctx *exec_ctx, void *elemp, bool success) { - grpc_call_element *elem = elemp; - call_data *calld = elem->call_data; - gpr_slice_buffer_add(&calld->slices, calld->incoming_slice); - if (calld->send_length == calld->slices.length) { - finish_send_message(exec_ctx, elem); - } else { - continue_send_message(exec_ctx, elem); - } -} - -static void continue_send_message(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - call_data *calld = elem->call_data; - while (grpc_byte_stream_next(exec_ctx, calld->send_op.send_message, - &calld->incoming_slice, ~(size_t)0, - &calld->got_slice)) { - gpr_slice_buffer_add(&calld->slices, calld->incoming_slice); - if (calld->send_length == calld->slices.length) { - finish_send_message(exec_ctx, elem); - break; - } - } -} - -static void compress_start_transport_stream_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - call_data *calld = elem->call_data; - - GPR_TIMER_BEGIN("compress_start_transport_stream_op", 0); - - if (op->send_initial_metadata) { - process_send_initial_metadata(elem, op->send_initial_metadata); - } - if (op->send_message != NULL && !skip_compression(elem) && - 0 == (op->send_message->flags & GRPC_WRITE_NO_COMPRESS)) { - calld->send_op = *op; - calld->send_length = op->send_message->length; - calld->send_flags = op->send_message->flags; - continue_send_message(exec_ctx, elem); - } else { - /* pass control down the stack */ - grpc_call_next_op(exec_ctx, elem, op); - } - - GPR_TIMER_END("compress_start_transport_stream_op", 0); -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - /* 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; - grpc_closure_init(&calld->got_slice, got_slice, elem); - grpc_closure_init(&calld->send_done, send_done, elem); -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *channeld = elem->channel_data; - grpc_compression_algorithm algo_idx; - - grpc_compression_options_init(&channeld->compression_options); - channeld->compression_options.enabled_algorithms_bitset = - (uint32_t)grpc_channel_args_compression_algorithm_get_states( - args->channel_args); - - channeld->default_compression_algorithm = - grpc_channel_args_get_compression_algorithm(args->channel_args); - /* Make sure the default isn't disabled. */ - GPR_ASSERT(grpc_compression_options_is_algorithm_enabled( - &channeld->compression_options, channeld->default_compression_algorithm)); - channeld->compression_options.default_compression_algorithm = - channeld->default_compression_algorithm; - - channeld->supported_compression_algorithms = 0; - for (algo_idx = 0; algo_idx < GRPC_COMPRESS_ALGORITHMS_COUNT; ++algo_idx) { - /* skip disabled algorithms */ - if (grpc_compression_options_is_algorithm_enabled( - &channeld->compression_options, algo_idx) == 0) { - continue; - } - channeld->supported_compression_algorithms |= 1u << algo_idx; - } - - GPR_ASSERT(!args->is_last); -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) {} - -const grpc_channel_filter grpc_compress_filter = { - compress_start_transport_stream_op, grpc_channel_next_op, sizeof(call_data), - init_call_elem, grpc_call_stack_ignore_set_pollset, destroy_call_elem, - sizeof(channel_data), init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, "compress"}; diff --git a/third_party/grpc/src/core/channel/compress_filter.h b/third_party/grpc/src/core/channel/compress_filter.h deleted file mode 100644 index 415459bca60991..00000000000000 --- a/third_party/grpc/src/core/channel/compress_filter.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/channel/connected_channel.c b/third_party/grpc/src/core/channel/connected_channel.c deleted file mode 100644 index e8eb9dcfc5cb2f..00000000000000 --- a/third_party/grpc/src/core/channel/connected_channel.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/profiling/timers.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_exec_ctx *exec_ctx, - 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(exec_ctx, chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld), op); -} - -static void con_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_transport_op *op) { - channel_data *chand = elem->channel_data; - grpc_transport_perform_op(exec_ctx, chand->transport, op); -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - 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( - exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), - &args->call_stack->refcount, args->server_transport_data); - GPR_ASSERT(r == 0); -} - -static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_pollset *pollset) { - call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; - grpc_transport_set_pollset(exec_ctx, chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollset); -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld)); -} - -/* Constructor for channel_data */ -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *cd = (channel_data *)elem->channel_data; - GPR_ASSERT(args->is_last); - GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - cd->transport = NULL; -} - -/* Destructor for channel_data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *cd = (channel_data *)elem->channel_data; - GPR_ASSERT(elem->filter == &grpc_connected_channel_filter); - grpc_transport_destroy(exec_ctx, cd->transport); -} - -static char *con_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - channel_data *chand = elem->channel_data; - return grpc_transport_get_peer(exec_ctx, 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, set_pollset, 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); -} - -grpc_stream *grpc_connected_channel_get_stream(grpc_call_element *elem) { - call_data *calld = elem->call_data; - return TRANSPORT_STREAM_FROM_CALL_DATA(calld); -} diff --git a/third_party/grpc/src/core/channel/connected_channel.h b/third_party/grpc/src/core/channel/connected_channel.h deleted file mode 100644 index 95c1834bfaab49..00000000000000 --- a/third_party/grpc/src/core/channel/connected_channel.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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); - -grpc_stream* grpc_connected_channel_get_stream(grpc_call_element* elem); - -#endif /* GRPC_INTERNAL_CORE_CHANNEL_CONNECTED_CHANNEL_H */ diff --git a/third_party/grpc/src/core/channel/context.h b/third_party/grpc/src/core/channel/context.h deleted file mode 100644 index ac5796b9ef19fc..00000000000000 --- a/third_party/grpc/src/core/channel/context.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/channel/http_client_filter.c b/third_party/grpc/src/core/channel/http_client_filter.c deleted file mode 100644 index 43eee046b888cc..00000000000000 --- a/third_party/grpc/src/core/channel/http_client_filter.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/channel/http_client_filter.h" -#include -#include -#include -#include -#include "src/core/profiling/timers.h" -#include "src/core/support/string.h" -#include "src/core/transport/static_metadata.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; - - grpc_metadata_batch *recv_initial_metadata; - - /** Closure to call when finished with the hc_on_recv hook */ - grpc_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_closure hc_on_recv; -} call_data; - -typedef struct channel_data { - grpc_mdelem *static_scheme; - grpc_mdelem *user_agent; -} channel_data; - -typedef struct { - grpc_call_element *elem; - grpc_exec_ctx *exec_ctx; -} client_recv_filter_args; - -static grpc_mdelem *client_recv_filter(void *user_data, grpc_mdelem *md) { - client_recv_filter_args *a = user_data; - if (md == GRPC_MDELEM_STATUS_200) { - return NULL; - } else if (md->key == GRPC_MDSTR_STATUS) { - grpc_call_element_send_cancel(a->exec_ctx, a->elem); - return NULL; - } else if (md->key == GRPC_MDSTR_CONTENT_TYPE) { - return NULL; - } - return md; -} - -static void hc_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) { - grpc_call_element *elem = user_data; - call_data *calld = elem->call_data; - client_recv_filter_args a; - a.elem = elem; - a.exec_ctx = exec_ctx; - grpc_metadata_batch_filter(calld->recv_initial_metadata, client_recv_filter, - &a); - calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success); -} - -static grpc_mdelem *client_strip_filter(void *user_data, grpc_mdelem *md) { - /* eat the things we'd like to set ourselves */ - if (md->key == GRPC_MDSTR_METHOD) return NULL; - if (md->key == GRPC_MDSTR_SCHEME) return NULL; - if (md->key == GRPC_MDSTR_TE) return NULL; - if (md->key == GRPC_MDSTR_CONTENT_TYPE) return NULL; - if (md->key == GRPC_MDSTR_USER_AGENT) 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; - if (op->send_initial_metadata != NULL) { - grpc_metadata_batch_filter(op->send_initial_metadata, client_strip_filter, - elem); - /* Send : prefixed headers, which have to be before any application - layer headers. */ - grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->method, - GRPC_MDELEM_METHOD_POST); - grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->scheme, - channeld->static_scheme); - grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->te_trailers, - GRPC_MDELEM_TE_TRAILERS); - grpc_metadata_batch_add_tail( - op->send_initial_metadata, &calld->content_type, - GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC); - grpc_metadata_batch_add_tail(op->send_initial_metadata, &calld->user_agent, - GRPC_MDELEM_REF(channeld->user_agent)); - } - - if (op->recv_initial_metadata != NULL) { - /* substitute our callback for the higher callback */ - calld->recv_initial_metadata = op->recv_initial_metadata; - calld->on_done_recv = op->on_complete; - op->on_complete = &calld->hc_on_recv; - } -} - -static void hc_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - GPR_TIMER_BEGIN("hc_start_transport_op", 0); - GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - hc_mutate_op(elem, op); - GPR_TIMER_END("hc_start_transport_op", 0); - grpc_call_next_op(exec_ctx, elem, op); -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - call_data *calld = elem->call_data; - calld->on_done_recv = NULL; - grpc_closure_init(&calld->hc_on_recv, hc_on_recv, elem); -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) {} - -static grpc_mdelem *scheme_from_args(const grpc_channel_args *args) { - unsigned i; - size_t j; - grpc_mdelem *valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP, - GRPC_MDELEM_SCHEME_HTTPS}; - 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) { - for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) { - if (0 == strcmp(grpc_mdstr_as_c_string(valid_schemes[j]->value), - args->args[i].value.string)) { - return valid_schemes[j]; - } - } - } - } - } - return GRPC_MDELEM_SCHEME_HTTP; -} - -static grpc_mdstr *user_agent_from_args(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(tmp); - gpr_free(tmp); - - return result; -} - -/* Constructor for channel_data */ -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = elem->channel_data; - GPR_ASSERT(!args->is_last); - chand->static_scheme = scheme_from_args(args->channel_args); - chand->user_agent = grpc_mdelem_from_metadata_strings( - GRPC_MDSTR_USER_AGENT, user_agent_from_args(args->channel_args)); -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = elem->channel_data; - GRPC_MDELEM_UNREF(chand->user_agent); -} - -const grpc_channel_filter grpc_http_client_filter = { - hc_start_transport_op, grpc_channel_next_op, sizeof(call_data), - init_call_elem, grpc_call_stack_ignore_set_pollset, destroy_call_elem, - sizeof(channel_data), init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, "http-client"}; diff --git a/third_party/grpc/src/core/channel/http_client_filter.h b/third_party/grpc/src/core/channel/http_client_filter.h deleted file mode 100644 index 21c66b9b8ee758..00000000000000 --- a/third_party/grpc/src/core/channel/http_client_filter.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/channel/http_server_filter.c b/third_party/grpc/src/core/channel/http_server_filter.c deleted file mode 100644 index bb75323933c735..00000000000000 --- a/third_party/grpc/src/core/channel/http_server_filter.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/channel/http_server_filter.h" - -#include -#include -#include -#include "src/core/profiling/timers.h" -#include "src/core/transport/static_metadata.h" - -typedef struct call_data { - uint8_t seen_path; - uint8_t seen_post; - uint8_t sent_status; - uint8_t seen_scheme; - uint8_t seen_te_trailers; - uint8_t seen_authority; - grpc_linked_mdelem status; - grpc_linked_mdelem content_type; - - grpc_metadata_batch *recv_initial_metadata; - /** Closure to call when finished with the hs_on_recv hook */ - grpc_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_closure hs_on_recv; -} call_data; - -typedef struct channel_data { uint8_t unused; } channel_data; - -typedef struct { - grpc_call_element *elem; - grpc_exec_ctx *exec_ctx; -} server_filter_args; - -static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) { - server_filter_args *a = user_data; - grpc_call_element *elem = a->elem; - call_data *calld = elem->call_data; - - /* Check if it is one of the headers we care about. */ - if (md == GRPC_MDELEM_TE_TRAILERS || md == GRPC_MDELEM_METHOD_POST || - md == GRPC_MDELEM_SCHEME_HTTP || md == GRPC_MDELEM_SCHEME_HTTPS || - md == GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC) { - /* swallow it */ - if (md == GRPC_MDELEM_METHOD_POST) { - calld->seen_post = 1; - } else if (md->key == GRPC_MDSTR_SCHEME) { - calld->seen_scheme = 1; - } else if (md == GRPC_MDELEM_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 == GRPC_MDSTR_CONTENT_TYPE) { - 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", - grpc_mdstr_as_c_string(md->value)); - } - return NULL; - } else if (md->key == GRPC_MDSTR_TE || md->key == GRPC_MDSTR_METHOD || - md->key == GRPC_MDSTR_SCHEME) { - 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(a->exec_ctx, elem); - return NULL; - } else if (md->key == GRPC_MDSTR_PATH) { - if (calld->seen_path) { - gpr_log(GPR_ERROR, "Received :path twice"); - return NULL; - } - calld->seen_path = 1; - return md; - } else if (md->key == GRPC_MDSTR_AUTHORITY) { - calld->seen_authority = 1; - return md; - } else if (md->key == GRPC_MDSTR_HOST) { - /* translate host to :authority since :authority may be - omitted */ - grpc_mdelem *authority = grpc_mdelem_from_metadata_strings( - GRPC_MDSTR_AUTHORITY, GRPC_MDSTR_REF(md->value)); - calld->seen_authority = 1; - return authority; - } else { - return md; - } -} - -static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, bool success) { - grpc_call_element *elem = user_data; - call_data *calld = elem->call_data; - if (success) { - server_filter_args a; - a.elem = elem; - a.exec_ctx = exec_ctx; - grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, &a); - /* 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(exec_ctx, elem); - } - } - calld->on_done_recv->cb(exec_ctx, 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; - - if (op->send_initial_metadata != NULL && !calld->sent_status) { - calld->sent_status = 1; - grpc_metadata_batch_add_head(op->send_initial_metadata, &calld->status, - GRPC_MDELEM_STATUS_200); - grpc_metadata_batch_add_tail( - op->send_initial_metadata, &calld->content_type, - GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC); - } - - if (op->recv_initial_metadata) { - /* substitute our callback for the higher callback */ - calld->recv_initial_metadata = op->recv_initial_metadata; - calld->on_done_recv = op->on_complete; - op->on_complete = &calld->hs_on_recv; - } -} - -static void hs_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - GPR_TIMER_BEGIN("hs_start_transport_op", 0); - hs_mutate_op(elem, op); - grpc_call_next_op(exec_ctx, elem, op); - GPR_TIMER_END("hs_start_transport_op", 0); -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - /* grab pointers to our data from the call element */ - call_data *calld = elem->call_data; - /* initialize members */ - memset(calld, 0, sizeof(*calld)); - grpc_closure_init(&calld->hs_on_recv, hs_on_recv, elem); -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) {} - -/* Constructor for channel_data */ -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - GPR_ASSERT(!args->is_last); -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) {} - -const grpc_channel_filter grpc_http_server_filter = { - hs_start_transport_op, grpc_channel_next_op, sizeof(call_data), - init_call_elem, grpc_call_stack_ignore_set_pollset, destroy_call_elem, - sizeof(channel_data), init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, "http-server"}; diff --git a/third_party/grpc/src/core/channel/http_server_filter.h b/third_party/grpc/src/core/channel/http_server_filter.h deleted file mode 100644 index f219d4e66f3872..00000000000000 --- a/third_party/grpc/src/core/channel/http_server_filter.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/channel/subchannel_call_holder.c b/third_party/grpc/src/core/channel/subchannel_call_holder.c deleted file mode 100644 index 3ad9fd9efb14c4..00000000000000 --- a/third_party/grpc/src/core/channel/subchannel_call_holder.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/channel/subchannel_call_holder.h" - -#include - -#include "src/core/profiling/timers.h" - -#define GET_CALL(holder) \ - ((grpc_subchannel_call *)(gpr_atm_acq_load(&(holder)->subchannel_call))) - -#define CANCELLED_CALL ((grpc_subchannel_call *)1) - -static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *holder, - bool success); -static void retry_ops(grpc_exec_ctx *exec_ctx, void *retry_ops_args, - bool success); - -static void add_waiting_locked(grpc_subchannel_call_holder *holder, - grpc_transport_stream_op *op); -static void fail_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder); -static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder); - -void grpc_subchannel_call_holder_init( - grpc_subchannel_call_holder *holder, - grpc_subchannel_call_holder_pick_subchannel pick_subchannel, - void *pick_subchannel_arg, grpc_call_stack *owning_call) { - gpr_atm_rel_store(&holder->subchannel_call, 0); - holder->pick_subchannel = pick_subchannel; - holder->pick_subchannel_arg = pick_subchannel_arg; - gpr_mu_init(&holder->mu); - holder->connected_subchannel = NULL; - holder->waiting_ops = NULL; - holder->waiting_ops_count = 0; - holder->waiting_ops_capacity = 0; - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; - holder->owning_call = owning_call; -} - -void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder) { - grpc_subchannel_call *call = GET_CALL(holder); - if (call != NULL && call != CANCELLED_CALL) { - GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, call, "holder"); - } - GPR_ASSERT(holder->creation_phase == - GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING); - gpr_mu_destroy(&holder->mu); - GPR_ASSERT(holder->waiting_ops_count == 0); - gpr_free(holder->waiting_ops); -} - -void grpc_subchannel_call_holder_perform_op(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder, - grpc_transport_stream_op *op) { - /* try to (atomically) get the call */ - grpc_subchannel_call *call = GET_CALL(holder); - GPR_TIMER_BEGIN("grpc_subchannel_call_holder_perform_op", 0); - if (call == CANCELLED_CALL) { - grpc_transport_stream_op_finish_with_failure(exec_ctx, op); - GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); - return; - } - if (call != NULL) { - grpc_subchannel_call_process_op(exec_ctx, call, op); - GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); - return; - } - /* we failed; lock and figure out what to do */ - gpr_mu_lock(&holder->mu); -retry: - /* need to recheck that another thread hasn't set the call */ - call = GET_CALL(holder); - if (call == CANCELLED_CALL) { - gpr_mu_unlock(&holder->mu); - grpc_transport_stream_op_finish_with_failure(exec_ctx, op); - GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); - return; - } - if (call != NULL) { - gpr_mu_unlock(&holder->mu); - grpc_subchannel_call_process_op(exec_ctx, call, op); - GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); - return; - } - /* if this is a cancellation, then we can raise our cancelled flag */ - if (op->cancel_with_status != GRPC_STATUS_OK) { - if (!gpr_atm_rel_cas(&holder->subchannel_call, 0, 1)) { - goto retry; - } else { - switch (holder->creation_phase) { - case GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING: - fail_locked(exec_ctx, holder); - break; - case GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL: - holder->pick_subchannel(exec_ctx, holder->pick_subchannel_arg, NULL, - &holder->connected_subchannel, NULL); - break; - } - gpr_mu_unlock(&holder->mu); - grpc_transport_stream_op_finish_with_failure(exec_ctx, op); - GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); - return; - } - } - /* if we don't have a subchannel, try to get one */ - if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING && - holder->connected_subchannel == NULL && - op->send_initial_metadata != NULL) { - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL; - grpc_closure_init(&holder->next_step, subchannel_ready, holder); - GRPC_CALL_STACK_REF(holder->owning_call, "pick_subchannel"); - if (holder->pick_subchannel( - exec_ctx, holder->pick_subchannel_arg, op->send_initial_metadata, - &holder->connected_subchannel, &holder->next_step)) { - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; - GRPC_CALL_STACK_UNREF(exec_ctx, holder->owning_call, "pick_subchannel"); - } - } - /* if we've got a subchannel, then let's ask it to create a call */ - if (holder->creation_phase == GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING && - holder->connected_subchannel != NULL) { - gpr_atm_rel_store( - &holder->subchannel_call, - (gpr_atm)(uintptr_t)grpc_connected_subchannel_create_call( - exec_ctx, holder->connected_subchannel, holder->pollset)); - retry_waiting_locked(exec_ctx, holder); - goto retry; - } - /* nothing to be done but wait */ - add_waiting_locked(holder, op); - gpr_mu_unlock(&holder->mu); - GPR_TIMER_END("grpc_subchannel_call_holder_perform_op", 0); -} - -static void subchannel_ready(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - grpc_subchannel_call_holder *holder = arg; - grpc_subchannel_call *call; - gpr_mu_lock(&holder->mu); - GPR_ASSERT(holder->creation_phase == - GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL); - call = GET_CALL(holder); - GPR_ASSERT(call == NULL || call == CANCELLED_CALL); - holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING; - if (holder->connected_subchannel == NULL) { - fail_locked(exec_ctx, holder); - } else { - gpr_atm_rel_store( - &holder->subchannel_call, - (gpr_atm)(uintptr_t)grpc_connected_subchannel_create_call( - exec_ctx, holder->connected_subchannel, holder->pollset)); - retry_waiting_locked(exec_ctx, holder); - } - gpr_mu_unlock(&holder->mu); - GRPC_CALL_STACK_UNREF(exec_ctx, holder->owning_call, "pick_subchannel"); -} - -typedef struct { - grpc_transport_stream_op *ops; - size_t nops; - grpc_subchannel_call *call; -} retry_ops_args; - -static void retry_waiting_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder) { - retry_ops_args *a = gpr_malloc(sizeof(*a)); - a->ops = holder->waiting_ops; - a->nops = holder->waiting_ops_count; - a->call = GET_CALL(holder); - if (a->call == CANCELLED_CALL) { - gpr_free(a); - fail_locked(exec_ctx, holder); - return; - } - holder->waiting_ops = NULL; - holder->waiting_ops_count = 0; - holder->waiting_ops_capacity = 0; - GRPC_SUBCHANNEL_CALL_REF(a->call, "retry_ops"); - grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(retry_ops, a), true, - NULL); -} - -static void retry_ops(grpc_exec_ctx *exec_ctx, void *args, bool success) { - retry_ops_args *a = args; - size_t i; - for (i = 0; i < a->nops; i++) { - grpc_subchannel_call_process_op(exec_ctx, a->call, &a->ops[i]); - } - GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, a->call, "retry_ops"); - gpr_free(a->ops); - gpr_free(a); -} - -static void add_waiting_locked(grpc_subchannel_call_holder *holder, - grpc_transport_stream_op *op) { - GPR_TIMER_BEGIN("add_waiting_locked", 0); - if (holder->waiting_ops_count == holder->waiting_ops_capacity) { - holder->waiting_ops_capacity = GPR_MAX(3, 2 * holder->waiting_ops_capacity); - holder->waiting_ops = - gpr_realloc(holder->waiting_ops, holder->waiting_ops_capacity * - sizeof(*holder->waiting_ops)); - } - holder->waiting_ops[holder->waiting_ops_count++] = *op; - GPR_TIMER_END("add_waiting_locked", 0); -} - -static void fail_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder) { - size_t i; - for (i = 0; i < holder->waiting_ops_count; i++) { - grpc_exec_ctx_enqueue(exec_ctx, holder->waiting_ops[i].on_complete, false, - NULL); - grpc_exec_ctx_enqueue(exec_ctx, holder->waiting_ops[i].recv_message_ready, - false, NULL); - } - holder->waiting_ops_count = 0; -} - -char *grpc_subchannel_call_holder_get_peer( - grpc_exec_ctx *exec_ctx, grpc_subchannel_call_holder *holder) { - grpc_subchannel_call *subchannel_call = GET_CALL(holder); - - if (subchannel_call) { - return grpc_subchannel_call_get_peer(exec_ctx, subchannel_call); - } else { - return NULL; - } -} diff --git a/third_party/grpc/src/core/channel/subchannel_call_holder.h b/third_party/grpc/src/core/channel/subchannel_call_holder.h deleted file mode 100644 index 9cf72c6cf76a1c..00000000000000 --- a/third_party/grpc/src/core/channel/subchannel_call_holder.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_SUBCHANNEL_CALL_HOLDER_H -#define GRPC_INTERNAL_CORE_CHANNEL_SUBCHANNEL_CALL_HOLDER_H - -#include "src/core/client_config/subchannel.h" - -/** Pick a subchannel for grpc_subchannel_call_holder; - Return 1 if subchannel is available immediately (in which case on_ready - should not be called), or 0 otherwise (in which case on_ready should be - called when the subchannel is available) */ -typedef int (*grpc_subchannel_call_holder_pick_subchannel)( - grpc_exec_ctx *exec_ctx, void *arg, grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **connected_subchannel, grpc_closure *on_ready); - -typedef enum { - GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING, - GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL -} grpc_subchannel_call_holder_creation_phase; - -/** Wrapper for holding a pointer to grpc_subchannel_call, and the - associated machinery to create such a pointer. - Handles queueing of stream ops until a call object is ready, waiting - for initial metadata before trying to create a call object, - and handling cancellation gracefully. - - Both the channel and uchannel filter use this as their call_data. */ -typedef struct grpc_subchannel_call_holder { - /** either 0 for no call, 1 for cancelled, or a pointer to a - grpc_subchannel_call */ - gpr_atm subchannel_call; - /** Helper function to choose the subchannel on which to create - the call object. Channel filter delegates to the load - balancing policy (once it's ready); uchannel returns - immediately */ - grpc_subchannel_call_holder_pick_subchannel pick_subchannel; - void *pick_subchannel_arg; - - gpr_mu mu; - - grpc_subchannel_call_holder_creation_phase creation_phase; - grpc_connected_subchannel *connected_subchannel; - grpc_pollset *pollset; - - grpc_transport_stream_op *waiting_ops; - size_t waiting_ops_count; - size_t waiting_ops_capacity; - - grpc_closure next_step; - - grpc_call_stack *owning_call; -} grpc_subchannel_call_holder; - -void grpc_subchannel_call_holder_init( - grpc_subchannel_call_holder *holder, - grpc_subchannel_call_holder_pick_subchannel pick_subchannel, - void *pick_subchannel_arg, grpc_call_stack *owning_call); -void grpc_subchannel_call_holder_destroy(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder); - -void grpc_subchannel_call_holder_perform_op(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder, - grpc_transport_stream_op *op); -char *grpc_subchannel_call_holder_get_peer(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call_holder *holder); - -#endif diff --git a/third_party/grpc/src/core/client_config/client_config.c b/third_party/grpc/src/core/client_config/client_config.c deleted file mode 100644 index 6ecffb3854144e..00000000000000 --- a/third_party/grpc/src/core/client_config/client_config.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, grpc_client_config *c) { - if (gpr_unref(&c->refs)) { - GRPC_LB_POLICY_UNREF(exec_ctx, c->lb_policy, "client_config"); - gpr_free(c); - } -} - -void grpc_client_config_set_lb_policy(grpc_client_config *c, - grpc_lb_policy *lb_policy) { - GPR_ASSERT(c->lb_policy == NULL); - if (lb_policy) { - GRPC_LB_POLICY_REF(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/third_party/grpc/src/core/client_config/client_config.h b/third_party/grpc/src/core/client_config/client_config.h deleted file mode 100644 index 04bf036b00b7e4..00000000000000 --- a/third_party/grpc/src/core/client_config/client_config.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, - 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/third_party/grpc/src/core/client_config/connector.c b/third_party/grpc/src/core/client_config/connector.c deleted file mode 100644 index 1603ffb8be0b34..00000000000000 --- a/third_party/grpc/src/core/client_config/connector.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx* exec_ctx, grpc_connector* connector) { - connector->vtable->unref(exec_ctx, connector); -} - -void grpc_connector_connect(grpc_exec_ctx* exec_ctx, grpc_connector* connector, - const grpc_connect_in_args* in_args, - grpc_connect_out_args* out_args, - grpc_closure* notify) { - connector->vtable->connect(exec_ctx, connector, in_args, out_args, notify); -} - -void grpc_connector_shutdown(grpc_exec_ctx* exec_ctx, - grpc_connector* connector) { - connector->vtable->shutdown(exec_ctx, connector); -} diff --git a/third_party/grpc/src/core/client_config/connector.h b/third_party/grpc/src/core/client_config/connector.h deleted file mode 100644 index b4482fa2eeb18a..00000000000000 --- a/third_party/grpc/src/core/client_config/connector.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - size_t addr_len; - /** initial connect string to send */ - gpr_slice initial_connect_string; - /** deadline for connection */ - gpr_timespec deadline; - /** channel arguments (to be passed to transport) */ - const grpc_channel_args *channel_args; -} 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; - - /** channel arguments (to be passed to the filters) */ - const grpc_channel_args *channel_args; -} grpc_connect_out_args; - -struct grpc_connector_vtable { - void (*ref)(grpc_connector *connector); - void (*unref)(grpc_exec_ctx *exec_ctx, grpc_connector *connector); - /** Implementation of grpc_connector_shutdown */ - void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_connector *connector); - /** Implementation of grpc_connector_connect */ - void (*connect)(grpc_exec_ctx *exec_ctx, grpc_connector *connector, - const grpc_connect_in_args *in_args, - grpc_connect_out_args *out_args, grpc_closure *notify); -}; - -void grpc_connector_ref(grpc_connector *connector); -void grpc_connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *connector); -/** Connect using the connector: max one outstanding call at a time */ -void grpc_connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *connector, - const grpc_connect_in_args *in_args, - grpc_connect_out_args *out_args, - grpc_closure *notify); -/** Cancel any pending connection */ -void grpc_connector_shutdown(grpc_exec_ctx *exec_ctx, - grpc_connector *connector); - -#endif diff --git a/third_party/grpc/src/core/client_config/default_initial_connect_string.c b/third_party/grpc/src/core/client_config/default_initial_connect_string.c deleted file mode 100644 index 6a4e23e6f2961a..00000000000000 --- a/third_party/grpc/src/core/client_config/default_initial_connect_string.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/iomgr/sockaddr.h" - -void grpc_set_default_initial_connect_string(struct sockaddr **addr, - size_t *addr_len, - gpr_slice *initial_str) {} diff --git a/third_party/grpc/src/core/client_config/initial_connect_string.c b/third_party/grpc/src/core/client_config/initial_connect_string.c deleted file mode 100644 index 19afa1675a7b99..00000000000000 --- a/third_party/grpc/src/core/client_config/initial_connect_string.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/initial_connect_string.h" - -#include - -extern void grpc_set_default_initial_connect_string(struct sockaddr **addr, - size_t *addr_len, - gpr_slice *initial_str); - -static grpc_set_initial_connect_string_func g_set_initial_connect_string_func = - grpc_set_default_initial_connect_string; - -void grpc_test_set_initial_connect_string_function( - grpc_set_initial_connect_string_func func) { - g_set_initial_connect_string_func = func; -} - -void grpc_set_initial_connect_string(struct sockaddr **addr, size_t *addr_len, - gpr_slice *initial_str) { - g_set_initial_connect_string_func(addr, addr_len, initial_str); -} diff --git a/third_party/grpc/src/core/client_config/initial_connect_string.h b/third_party/grpc/src/core/client_config/initial_connect_string.h deleted file mode 100644 index b6dca7134a4345..00000000000000 --- a/third_party/grpc/src/core/client_config/initial_connect_string.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_INITIAL_CONNECT_STRING_H -#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H - -#include -#include "src/core/iomgr/sockaddr.h" - -typedef void (*grpc_set_initial_connect_string_func)(struct sockaddr **addr, - size_t *addr_len, - gpr_slice *initial_str); -void grpc_test_set_initial_connect_string_function( - grpc_set_initial_connect_string_func func); - -/** Set a string to be sent once connected. Optionally reset addr. */ -void grpc_set_initial_connect_string(struct sockaddr **addr, size_t *addr_len, - gpr_slice *connect_string); - -#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H */ diff --git a/third_party/grpc/src/core/client_config/lb_policies/pick_first.c b/third_party/grpc/src/core/client_config/lb_policies/pick_first.c deleted file mode 100644 index 459bbebb68acd0..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policies/pick_first.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/client_config/lb_policy_factory.h" -#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_connected_subchannel **target; - grpc_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_closure connectivity_changed; - - /** the selected channel (a grpc_connected_subchannel) */ - gpr_atm selected; - - /** mutex protecting remaining members */ - gpr_mu mu; - /** 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; - -#define GET_SELECTED(p) \ - ((grpc_connected_subchannel *)gpr_atm_acq_load(&(p)->selected)) - -void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - grpc_connected_subchannel *selected = GET_SELECTED(p); - size_t i; - GPR_ASSERT(p->pending_picks == NULL); - for (i = 0; i < p->num_subchannels; i++) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first"); - } - if (selected != NULL) { - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, selected, "picked_first"); - } - grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); - gpr_free(p->subchannels); - gpr_mu_destroy(&p->mu); - gpr_free(p); -} - -void pf_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - pending_pick *pp; - grpc_connected_subchannel *selected; - gpr_mu_lock(&p->mu); - selected = GET_SELECTED(p); - p->shutdown = 1; - pp = p->pending_picks; - p->pending_picks = NULL; - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_FATAL_FAILURE, "shutdown"); - /* cancel subscription */ - if (selected != NULL) { - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, selected, NULL, NULL, &p->connectivity_changed); - } else { - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL, - &p->connectivity_changed); - } - gpr_mu_unlock(&p->mu); - while (pp != NULL) { - pending_pick *next = pp->next; - *pp->target = NULL; - grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, - pp->pollset); - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL); - gpr_free(pp); - pp = next; - } -} - -static void pf_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_connected_subchannel **target) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - pending_pick *pp; - gpr_mu_lock(&p->mu); - pp = p->pending_picks; - p->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if (pp->target == target) { - grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, - pp->pollset); - *target = NULL; - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL); - gpr_free(pp); - } else { - pp->next = p->pending_picks; - p->pending_picks = pp; - } - pp = next; - } - gpr_mu_unlock(&p->mu); -} - -static void start_picking(grpc_exec_ctx *exec_ctx, pick_first_lb_policy *p) { - p->started_picking = 1; - p->checking_subchannel = 0; - p->checking_connectivity = GRPC_CHANNEL_IDLE; - GRPC_LB_POLICY_WEAK_REF(&p->base, "pick_first_connectivity"); - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - &p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); -} - -void pf_exit_idle(grpc_exec_ctx *exec_ctx, 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(exec_ctx, p); - } - gpr_mu_unlock(&p->mu); -} - -int pf_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **target, grpc_closure *on_complete) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - pending_pick *pp; - - /* Check atomically for a selected channel */ - grpc_connected_subchannel *selected = GET_SELECTED(p); - if (selected != NULL) { - *target = selected; - return 1; - } - - /* No subchannel selected yet, so acquire lock and then attempt again */ - gpr_mu_lock(&p->mu); - selected = GET_SELECTED(p); - if (selected) { - gpr_mu_unlock(&p->mu); - *target = selected; - return 1; - } else { - if (!p->started_picking) { - start_picking(exec_ctx, p); - } - grpc_pollset_set_add_pollset(exec_ctx, &p->base.interested_parties, - 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); - return 0; - } -} - -static void destroy_subchannels(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - pick_first_lb_policy *p = arg; - size_t i; - size_t num_subchannels = p->num_subchannels; - grpc_subchannel **subchannels; - - gpr_mu_lock(&p->mu); - subchannels = p->subchannels; - p->num_subchannels = 0; - p->subchannels = NULL; - gpr_mu_unlock(&p->mu); - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "destroy_subchannels"); - - for (i = 0; i < num_subchannels; i++) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pick_first"); - } - - gpr_free(subchannels); -} - -static void pf_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - pick_first_lb_policy *p = arg; - grpc_subchannel *selected_subchannel; - pending_pick *pp; - grpc_connected_subchannel *selected; - - gpr_mu_lock(&p->mu); - - selected = GET_SELECTED(p); - - if (p->shutdown) { - gpr_mu_unlock(&p->mu); - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); - return; - } else if (selected != NULL) { - if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { - /* if the selected channel goes bad, we're done */ - p->checking_connectivity = GRPC_CHANNEL_FATAL_FAILURE; - } - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - p->checking_connectivity, "selected_changed"); - if (p->checking_connectivity != GRPC_CHANNEL_FATAL_FAILURE) { - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, selected, &p->base.interested_parties, - &p->checking_connectivity, &p->connectivity_changed); - } else { - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); - } - } else { - loop: - switch (p->checking_connectivity) { - case GRPC_CHANNEL_READY: - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_READY, "connecting_ready"); - selected_subchannel = p->subchannels[p->checking_subchannel]; - selected = - grpc_subchannel_get_connected_subchannel(selected_subchannel); - GPR_ASSERT(selected != NULL); - GRPC_CONNECTED_SUBCHANNEL_REF(selected, "picked_first"); - /* drop the pick list: we are connected now */ - GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels"); - gpr_atm_rel_store(&p->selected, (gpr_atm)selected); - grpc_exec_ctx_enqueue( - exec_ctx, grpc_closure_create(destroy_subchannels, p), true, NULL); - /* update any calls that were waiting for a pick */ - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = selected; - grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, - pp->pollset); - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL); - gpr_free(pp); - } - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, selected, &p->base.interested_parties, - &p->checking_connectivity, &p->connectivity_changed); - break; - case GRPC_CHANNEL_TRANSIENT_FAILURE: - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_TRANSIENT_FAILURE, - "connecting_transient_failure"); - p->checking_subchannel = - (p->checking_subchannel + 1) % p->num_subchannels; - p->checking_connectivity = grpc_subchannel_check_connectivity( - p->subchannels[p->checking_subchannel]); - if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - &p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); - } else { - goto loop; - } - break; - case GRPC_CHANNEL_CONNECTING: - case GRPC_CHANNEL_IDLE: - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_CONNECTING, - "connecting_changed"); - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - &p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); - break; - case GRPC_CHANNEL_FATAL_FAILURE: - p->num_subchannels--; - GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], - p->subchannels[p->num_subchannels]); - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], - "pick_first"); - if (p->num_subchannels == 0) { - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_FATAL_FAILURE, - "no_more_channels"); - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = NULL; - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL); - gpr_free(pp); - } - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, - "pick_first_connectivity"); - } else { - grpc_connectivity_state_set(exec_ctx, &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]); - goto loop; - } - } - } - - gpr_mu_unlock(&p->mu); -} - -static grpc_connectivity_state pf_check_connectivity(grpc_exec_ctx *exec_ctx, - 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; -} - -void pf_notify_on_state_change(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_connectivity_state *current, - grpc_closure *notify) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - gpr_mu_lock(&p->mu); - grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, - current, notify); - gpr_mu_unlock(&p->mu); -} - -void pf_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_closure *closure) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - grpc_connected_subchannel *selected = GET_SELECTED(p); - if (selected) { - grpc_connected_subchannel_ping(exec_ctx, selected, closure); - } else { - grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL); - } -} - -static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = { - pf_destroy, pf_shutdown, pf_pick, pf_cancel_pick, pf_ping_one, pf_exit_idle, - pf_check_connectivity, pf_notify_on_state_change}; - -static void pick_first_factory_ref(grpc_lb_policy_factory *factory) {} - -static void pick_first_factory_unref(grpc_lb_policy_factory *factory) {} - -static grpc_lb_policy *create_pick_first(grpc_lb_policy_factory *factory, - grpc_lb_policy_args *args) { - pick_first_lb_policy *p = gpr_malloc(sizeof(*p)); - GPR_ASSERT(args->num_subchannels > 0); - memset(p, 0, sizeof(*p)); - grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable); - p->subchannels = - gpr_malloc(sizeof(grpc_subchannel *) * args->num_subchannels); - p->num_subchannels = args->num_subchannels; - grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, - "pick_first"); - memcpy(p->subchannels, args->subchannels, - sizeof(grpc_subchannel *) * args->num_subchannels); - grpc_closure_init(&p->connectivity_changed, pf_connectivity_changed, p); - gpr_mu_init(&p->mu); - return &p->base; -} - -static const grpc_lb_policy_factory_vtable pick_first_factory_vtable = { - pick_first_factory_ref, pick_first_factory_unref, create_pick_first, - "pick_first"}; - -static grpc_lb_policy_factory pick_first_lb_policy_factory = { - &pick_first_factory_vtable}; - -grpc_lb_policy_factory *grpc_pick_first_lb_factory_create() { - return &pick_first_lb_policy_factory; -} diff --git a/third_party/grpc/src/core/client_config/lb_policies/pick_first.h b/third_party/grpc/src/core/client_config/lb_policies/pick_first.h deleted file mode 100644 index 3ca53ad42afa8c..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policies/pick_first.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_factory.h" - -/** Returns a load balancing factory for the pick first policy, which picks up - * the first subchannel from \a subchannels to succesfully connect */ -grpc_lb_policy_factory *grpc_pick_first_lb_factory_create(); - -#endif diff --git a/third_party/grpc/src/core/client_config/lb_policies/round_robin.c b/third_party/grpc/src/core/client_config/lb_policies/round_robin.c deleted file mode 100644 index b1171c45b007f2..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policies/round_robin.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/client_config/lb_policies/round_robin.h" - -#include - -#include -#include "src/core/transport/connectivity_state.h" - -typedef struct round_robin_lb_policy round_robin_lb_policy; - -int grpc_lb_round_robin_trace = 0; - -/** List of entities waiting for a pick. - * - * Once a pick is available, \a target is updated and \a on_complete called. */ -typedef struct pending_pick { - struct pending_pick *next; - grpc_pollset *pollset; - grpc_connected_subchannel **target; - grpc_closure *on_complete; -} pending_pick; - -/** List of subchannels in a connectivity READY state */ -typedef struct ready_list { - grpc_subchannel *subchannel; - struct ready_list *next; - struct ready_list *prev; -} ready_list; - -typedef struct { - /** index within policy->subchannels */ - size_t index; - /** backpointer to owning policy */ - round_robin_lb_policy *policy; - /** subchannel itself */ - grpc_subchannel *subchannel; - /** notification that connectivity has changed on subchannel */ - grpc_closure connectivity_changed_closure; - /** this subchannels current position in subchannel->ready_list */ - ready_list *ready_list_node; - /** last observed connectivity */ - grpc_connectivity_state connectivity_state; -} subchannel_data; - -struct round_robin_lb_policy { - /** base policy: must be first */ - grpc_lb_policy base; - - /** all our subchannels */ - size_t num_subchannels; - subchannel_data **subchannels; - - /** mutex protecting remaining members */ - gpr_mu mu; - /** have we started picking? */ - int started_picking; - /** are we shutting down? */ - int shutdown; - /** List of picks that are waiting on connectivity */ - pending_pick *pending_picks; - - /** our connectivity state tracker */ - grpc_connectivity_state_tracker state_tracker; - - /** (Dummy) root of the doubly linked list containing READY subchannels */ - ready_list ready_list; - /** Last pick from the ready list. */ - ready_list *ready_list_last_pick; -}; - -/** Returns the next subchannel from the connected list or NULL if the list is - * empty. - * - * Note that this function does *not* advance p->ready_list_last_pick. Use \a - * advance_last_picked_locked() for that. */ -static ready_list *peek_next_connected_locked(const round_robin_lb_policy *p) { - ready_list *selected; - selected = p->ready_list_last_pick->next; - - while (selected != NULL) { - if (selected == &p->ready_list) { - GPR_ASSERT(selected->subchannel == NULL); - /* skip dummy root */ - selected = selected->next; - } else { - GPR_ASSERT(selected->subchannel != NULL); - return selected; - } - } - return NULL; -} - -/** Advance the \a ready_list picking head. */ -static void advance_last_picked_locked(round_robin_lb_policy *p) { - if (p->ready_list_last_pick->next != NULL) { /* non-empty list */ - p->ready_list_last_pick = p->ready_list_last_pick->next; - if (p->ready_list_last_pick == &p->ready_list) { - /* skip dummy root */ - p->ready_list_last_pick = p->ready_list_last_pick->next; - } - } else { /* should be an empty list */ - GPR_ASSERT(p->ready_list_last_pick == &p->ready_list); - } - - if (grpc_lb_round_robin_trace) { - gpr_log(GPR_DEBUG, "[READYLIST] ADVANCED LAST PICK. NOW AT NODE %p (SC %p)", - p->ready_list_last_pick, p->ready_list_last_pick->subchannel); - } -} - -/** Prepends (relative to the root at p->ready_list) the connected subchannel \a - * csc to the list of ready subchannels. */ -static ready_list *add_connected_sc_locked(round_robin_lb_policy *p, - grpc_subchannel *sc) { - ready_list *new_elem = gpr_malloc(sizeof(ready_list)); - new_elem->subchannel = sc; - if (p->ready_list.prev == NULL) { - /* first element */ - new_elem->next = &p->ready_list; - new_elem->prev = &p->ready_list; - p->ready_list.next = new_elem; - p->ready_list.prev = new_elem; - } else { - new_elem->next = &p->ready_list; - new_elem->prev = p->ready_list.prev; - p->ready_list.prev->next = new_elem; - p->ready_list.prev = new_elem; - } - if (grpc_lb_round_robin_trace) { - gpr_log(GPR_DEBUG, "[READYLIST] ADDING NODE %p (SC %p)", new_elem, sc); - } - return new_elem; -} - -/** Removes \a node from the list of connected subchannels */ -static void remove_disconnected_sc_locked(round_robin_lb_policy *p, - ready_list *node) { - if (node == NULL) { - return; - } - if (node == p->ready_list_last_pick) { - /* If removing the lastly picked node, reset the last pick pointer to the - * dummy root of the list */ - p->ready_list_last_pick = &p->ready_list; - } - - /* removing last item */ - if (node->next == &p->ready_list && node->prev == &p->ready_list) { - GPR_ASSERT(p->ready_list.next == node); - GPR_ASSERT(p->ready_list.prev == node); - p->ready_list.next = NULL; - p->ready_list.prev = NULL; - } else { - node->prev->next = node->next; - node->next->prev = node->prev; - } - - if (grpc_lb_round_robin_trace) { - gpr_log(GPR_DEBUG, "[READYLIST] REMOVED NODE %p (SC %p)", node, - node->subchannel); - } - - node->next = NULL; - node->prev = NULL; - node->subchannel = NULL; - - gpr_free(node); -} - -void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - size_t i; - ready_list *elem; - for (i = 0; i < p->num_subchannels; i++) { - subchannel_data *sd = p->subchannels[i]; - GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin"); - gpr_free(sd); - } - - grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); - gpr_free(p->subchannels); - gpr_mu_destroy(&p->mu); - - elem = p->ready_list.next; - while (elem != NULL && elem != &p->ready_list) { - ready_list *tmp; - tmp = elem->next; - elem->next = NULL; - elem->prev = NULL; - elem->subchannel = NULL; - gpr_free(elem); - elem = tmp; - } - gpr_free(p); -} - -void rr_shutdown(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - pending_pick *pp; - size_t i; - - gpr_mu_lock(&p->mu); - - p->shutdown = 1; - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = NULL; - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL); - gpr_free(pp); - } - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_FATAL_FAILURE, "shutdown"); - for (i = 0; i < p->num_subchannels; i++) { - subchannel_data *sd = p->subchannels[i]; - grpc_subchannel_notify_on_state_change(exec_ctx, sd->subchannel, NULL, NULL, - &sd->connectivity_changed_closure); - } - gpr_mu_unlock(&p->mu); -} - -static void rr_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_connected_subchannel **target) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - pending_pick *pp; - gpr_mu_lock(&p->mu); - pp = p->pending_picks; - p->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if (pp->target == target) { - grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, - pp->pollset); - *target = NULL; - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, false, NULL); - gpr_free(pp); - } else { - pp->next = p->pending_picks; - p->pending_picks = pp; - } - pp = next; - } - gpr_mu_unlock(&p->mu); -} - -static void start_picking(grpc_exec_ctx *exec_ctx, round_robin_lb_policy *p) { - size_t i; - p->started_picking = 1; - - gpr_log(GPR_DEBUG, "LB_POLICY: p=%p num_subchannels=%d", p, - p->num_subchannels); - - for (i = 0; i < p->num_subchannels; i++) { - subchannel_data *sd = p->subchannels[i]; - sd->connectivity_state = GRPC_CHANNEL_IDLE; - grpc_subchannel_notify_on_state_change( - exec_ctx, sd->subchannel, &p->base.interested_parties, - &sd->connectivity_state, &sd->connectivity_changed_closure); - GRPC_LB_POLICY_WEAK_REF(&p->base, "round_robin_connectivity"); - } -} - -void rr_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - gpr_mu_lock(&p->mu); - if (!p->started_picking) { - start_picking(exec_ctx, p); - } - gpr_mu_unlock(&p->mu); -} - -int rr_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **target, grpc_closure *on_complete) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - pending_pick *pp; - ready_list *selected; - gpr_mu_lock(&p->mu); - if ((selected = peek_next_connected_locked(p))) { - gpr_mu_unlock(&p->mu); - *target = grpc_subchannel_get_connected_subchannel(selected->subchannel); - if (grpc_lb_round_robin_trace) { - gpr_log(GPR_DEBUG, - "[RR PICK] TARGET <-- CONNECTED SUBCHANNEL %p (NODE %p)", - selected->subchannel, selected); - } - /* only advance the last picked pointer if the selection was used */ - advance_last_picked_locked(p); - return 1; - } else { - if (!p->started_picking) { - start_picking(exec_ctx, p); - } - grpc_pollset_set_add_pollset(exec_ctx, &p->base.interested_parties, - 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); - return 0; - } -} - -static void rr_connectivity_changed(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - subchannel_data *sd = arg; - round_robin_lb_policy *p = sd->policy; - pending_pick *pp; - ready_list *selected; - - int unref = 0; - - gpr_mu_lock(&p->mu); - - if (p->shutdown) { - unref = 1; - } else { - switch (sd->connectivity_state) { - case GRPC_CHANNEL_READY: - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_READY, "connecting_ready"); - /* add the newly connected subchannel to the list of connected ones. - * Note that it goes to the "end of the line". */ - sd->ready_list_node = add_connected_sc_locked(p, sd->subchannel); - /* at this point we know there's at least one suitable subchannel. Go - * ahead and pick one and notify the pending suitors in - * p->pending_picks. This preemtively replicates rr_pick()'s actions. */ - selected = peek_next_connected_locked(p); - if (p->pending_picks != NULL) { - /* if the selected subchannel is going to be used for the pending - * picks, update the last picked pointer */ - advance_last_picked_locked(p); - } - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = - grpc_subchannel_get_connected_subchannel(selected->subchannel); - if (grpc_lb_round_robin_trace) { - gpr_log(GPR_DEBUG, - "[RR CONN CHANGED] TARGET <-- SUBCHANNEL %p (NODE %p)", - selected->subchannel, selected); - } - grpc_pollset_set_del_pollset(exec_ctx, &p->base.interested_parties, - pp->pollset); - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL); - gpr_free(pp); - } - grpc_subchannel_notify_on_state_change( - exec_ctx, sd->subchannel, &p->base.interested_parties, - &sd->connectivity_state, &sd->connectivity_changed_closure); - break; - case GRPC_CHANNEL_CONNECTING: - case GRPC_CHANNEL_IDLE: - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - sd->connectivity_state, - "connecting_changed"); - grpc_subchannel_notify_on_state_change( - exec_ctx, sd->subchannel, &p->base.interested_parties, - &sd->connectivity_state, &sd->connectivity_changed_closure); - break; - case GRPC_CHANNEL_TRANSIENT_FAILURE: - /* renew state notification */ - grpc_subchannel_notify_on_state_change( - exec_ctx, sd->subchannel, &p->base.interested_parties, - &sd->connectivity_state, &sd->connectivity_changed_closure); - - /* remove from ready list if still present */ - if (sd->ready_list_node != NULL) { - remove_disconnected_sc_locked(p, sd->ready_list_node); - sd->ready_list_node = NULL; - } - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_TRANSIENT_FAILURE, - "connecting_transient_failure"); - break; - case GRPC_CHANNEL_FATAL_FAILURE: - if (sd->ready_list_node != NULL) { - remove_disconnected_sc_locked(p, sd->ready_list_node); - sd->ready_list_node = NULL; - } - - p->num_subchannels--; - GPR_SWAP(subchannel_data *, p->subchannels[sd->index], - p->subchannels[p->num_subchannels]); - GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "round_robin"); - p->subchannels[sd->index]->index = sd->index; - gpr_free(sd); - - unref = 1; - if (p->num_subchannels == 0) { - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_FATAL_FAILURE, - "no_more_channels"); - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = NULL; - grpc_exec_ctx_enqueue(exec_ctx, pp->on_complete, true, NULL); - gpr_free(pp); - } - } else { - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_TRANSIENT_FAILURE, - "subchannel_failed"); - } - } /* switch */ - } /* !unref */ - - gpr_mu_unlock(&p->mu); - - if (unref) { - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "round_robin_connectivity"); - } -} - -static grpc_connectivity_state rr_check_connectivity(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *pol) { - round_robin_lb_policy *p = (round_robin_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 rr_notify_on_state_change(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *pol, - grpc_connectivity_state *current, - grpc_closure *notify) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - gpr_mu_lock(&p->mu); - grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, - current, notify); - gpr_mu_unlock(&p->mu); -} - -static void rr_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_closure *closure) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - ready_list *selected; - grpc_connected_subchannel *target; - gpr_mu_lock(&p->mu); - if ((selected = peek_next_connected_locked(p))) { - gpr_mu_unlock(&p->mu); - target = grpc_subchannel_get_connected_subchannel(selected->subchannel); - grpc_connected_subchannel_ping(exec_ctx, target, closure); - } else { - gpr_mu_unlock(&p->mu); - grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL); - } -} - -static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = { - rr_destroy, rr_shutdown, rr_pick, rr_cancel_pick, rr_ping_one, rr_exit_idle, - rr_check_connectivity, rr_notify_on_state_change}; - -static void round_robin_factory_ref(grpc_lb_policy_factory *factory) {} - -static void round_robin_factory_unref(grpc_lb_policy_factory *factory) {} - -static grpc_lb_policy *create_round_robin(grpc_lb_policy_factory *factory, - grpc_lb_policy_args *args) { - size_t i; - round_robin_lb_policy *p = gpr_malloc(sizeof(*p)); - GPR_ASSERT(args->num_subchannels > 0); - memset(p, 0, sizeof(*p)); - grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable); - p->num_subchannels = args->num_subchannels; - p->subchannels = gpr_malloc(sizeof(*p->subchannels) * p->num_subchannels); - memset(p->subchannels, 0, sizeof(*p->subchannels) * p->num_subchannels); - grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, - "round_robin"); - - gpr_mu_init(&p->mu); - for (i = 0; i < args->num_subchannels; i++) { - subchannel_data *sd = gpr_malloc(sizeof(*sd)); - memset(sd, 0, sizeof(*sd)); - p->subchannels[i] = sd; - sd->policy = p; - sd->index = i; - sd->subchannel = args->subchannels[i]; - grpc_closure_init(&sd->connectivity_changed_closure, - rr_connectivity_changed, sd); - } - - /* The (dummy node) root of the ready list */ - p->ready_list.subchannel = NULL; - p->ready_list.prev = NULL; - p->ready_list.next = NULL; - p->ready_list_last_pick = &p->ready_list; - - return &p->base; -} - -static const grpc_lb_policy_factory_vtable round_robin_factory_vtable = { - round_robin_factory_ref, round_robin_factory_unref, create_round_robin, - "round_robin"}; - -static grpc_lb_policy_factory round_robin_lb_policy_factory = { - &round_robin_factory_vtable}; - -grpc_lb_policy_factory *grpc_round_robin_lb_factory_create() { - return &round_robin_lb_policy_factory; -} diff --git a/third_party/grpc/src/core/client_config/lb_policies/round_robin.h b/third_party/grpc/src/core/client_config/lb_policies/round_robin.h deleted file mode 100644 index cf1f69c85f6dca..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policies/round_robin.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_ROUND_ROBIN_H -#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_ROUND_ROBIN_H - -#include "src/core/client_config/lb_policy.h" - -extern int grpc_lb_round_robin_trace; - -#include "src/core/client_config/lb_policy_factory.h" - -/** Returns a load balancing factory for the round robin policy */ -grpc_lb_policy_factory *grpc_round_robin_lb_factory_create(); - -#endif diff --git a/third_party/grpc/src/core/client_config/lb_policy.c b/third_party/grpc/src/core/client_config/lb_policy.c deleted file mode 100644 index d4672f6b25502a..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policy.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" - -#define WEAK_REF_BITS 16 - -void grpc_lb_policy_init(grpc_lb_policy *policy, - const grpc_lb_policy_vtable *vtable) { - policy->vtable = vtable; - gpr_atm_no_barrier_store(&policy->ref_pair, 1 << WEAK_REF_BITS); - grpc_pollset_set_init(&policy->interested_parties); -} - -#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG -#define REF_FUNC_EXTRA_ARGS , const char *file, int line, const char *reason -#define REF_MUTATE_EXTRA_ARGS REF_FUNC_EXTRA_ARGS, const char *purpose -#define REF_FUNC_PASS_ARGS(new_reason) , file, line, new_reason -#define REF_MUTATE_PASS_ARGS(purpose) , file, line, reason, purpose -#else -#define REF_FUNC_EXTRA_ARGS -#define REF_MUTATE_EXTRA_ARGS -#define REF_FUNC_PASS_ARGS(new_reason) -#define REF_MUTATE_PASS_ARGS(x) -#endif - -static gpr_atm ref_mutate(grpc_lb_policy *c, gpr_atm delta, - int barrier REF_MUTATE_EXTRA_ARGS) { - gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) - : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); -#ifdef GRPC_LB_POLICY_REFCOUNT_DEBUG - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "LB_POLICY: %p % 12s 0x%08x -> 0x%08x [%s]", c, purpose, old_val, - old_val + delta, reason); -#endif - return old_val; -} - -void grpc_lb_policy_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - ref_mutate(policy, 1 << WEAK_REF_BITS, 0 REF_MUTATE_PASS_ARGS("STRONG_REF")); -} - -void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - gpr_atm old_val = - ref_mutate(policy, (gpr_atm)1 - (gpr_atm)(1 << WEAK_REF_BITS), - 1 REF_MUTATE_PASS_ARGS("STRONG_UNREF")); - gpr_atm mask = ~(gpr_atm)((1 << WEAK_REF_BITS) - 1); - gpr_atm check = 1 << WEAK_REF_BITS; - if ((old_val & mask) == check) { - policy->vtable->shutdown(exec_ctx, policy); - } - grpc_lb_policy_weak_unref(exec_ctx, - policy REF_FUNC_PASS_ARGS("strong-unref")); -} - -void grpc_lb_policy_weak_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - ref_mutate(policy, 1, 0 REF_MUTATE_PASS_ARGS("WEAK_REF")); -} - -void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - gpr_atm old_val = - ref_mutate(policy, -(gpr_atm)1, 1 REF_MUTATE_PASS_ARGS("WEAK_UNREF")); - if (old_val == 1) { - grpc_pollset_set_destroy(&policy->interested_parties); - policy->vtable->destroy(exec_ctx, policy); - } -} - -int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **target, - grpc_closure *on_complete) { - return policy->vtable->pick(exec_ctx, policy, pollset, initial_metadata, - target, on_complete); -} - -void grpc_lb_policy_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_connected_subchannel **target) { - policy->vtable->cancel_pick(exec_ctx, policy, target); -} - -void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { - policy->vtable->exit_idle(exec_ctx, policy); -} - -void grpc_lb_policy_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_closure *closure) { - policy->vtable->ping_one(exec_ctx, policy, closure); -} - -void grpc_lb_policy_notify_on_state_change(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy, - grpc_connectivity_state *state, - grpc_closure *closure) { - policy->vtable->notify_on_state_change(exec_ctx, policy, state, closure); -} - -grpc_connectivity_state grpc_lb_policy_check_connectivity( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy) { - return policy->vtable->check_connectivity(exec_ctx, policy); -} diff --git a/third_party/grpc/src/core/client_config/lb_policy.h b/third_party/grpc/src/core/client_config/lb_policy.h deleted file mode 100644 index db5238c8ca0986..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policy.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" -#include "src/core/transport/connectivity_state.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_atm ref_pair; - grpc_pollset_set interested_parties; -}; - -struct grpc_lb_policy_vtable { - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - - void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - - /** implement grpc_lb_policy_pick */ - int (*pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_pollset *pollset, grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **target, grpc_closure *on_complete); - void (*cancel_pick)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_connected_subchannel **target); - - void (*ping_one)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_closure *closure); - - /** try to enter a READY connectivity state */ - void (*exit_idle)(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - - /** check the current connectivity of the lb_policy */ - grpc_connectivity_state (*check_connectivity)(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_lb_policy *policy, - grpc_connectivity_state *state, - grpc_closure *closure); -}; - -/*#define GRPC_LB_POLICY_REFCOUNT_DEBUG*/ -#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(exec_ctx, p, r) \ - grpc_lb_policy_unref((exec_ctx), (p), __FILE__, __LINE__, (r)) -#define GRPC_LB_POLICY_WEAK_REF(p, r) \ - grpc_lb_policy_weak_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, p, r) \ - grpc_lb_policy_weak_unref((exec_ctx), (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_exec_ctx *exec_ctx, grpc_lb_policy *policy, - const char *file, int line, const char *reason); -void grpc_lb_policy_weak_ref(grpc_lb_policy *policy, const char *file, int line, - const char *reason); -void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, 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(cl, p, r) grpc_lb_policy_unref((cl), (p)) -#define GRPC_LB_POLICY_WEAK_REF(p, r) grpc_lb_policy_weak_ref((p)) -#define GRPC_LB_POLICY_WEAK_UNREF(cl, p, r) grpc_lb_policy_weak_unref((cl), (p)) -void grpc_lb_policy_ref(grpc_lb_policy *policy); -void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); -void grpc_lb_policy_weak_ref(grpc_lb_policy *policy); -void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, 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); - -/** 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. */ -int grpc_lb_policy_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_pollset *pollset, - grpc_metadata_batch *initial_metadata, - grpc_connected_subchannel **target, - grpc_closure *on_complete); - -void grpc_lb_policy_ping_one(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_closure *closure); - -void grpc_lb_policy_cancel_pick(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_connected_subchannel **target); - -void grpc_lb_policy_exit_idle(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - -void grpc_lb_policy_notify_on_state_change(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy, - grpc_connectivity_state *state, - grpc_closure *closure); - -grpc_connectivity_state grpc_lb_policy_check_connectivity( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy); - -#endif /* GRPC_INTERNAL_CORE_CONFIG_LB_POLICY_H */ diff --git a/third_party/grpc/src/core/client_config/lb_policy_factory.c b/third_party/grpc/src/core/client_config/lb_policy_factory.c deleted file mode 100644 index e49de544e3a1d5..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policy_factory.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_factory.h" - -void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) { - factory->vtable->ref(factory); -} - -void grpc_lb_policy_factory_unref(grpc_lb_policy_factory* factory) { - factory->vtable->unref(factory); -} - -grpc_lb_policy* grpc_lb_policy_factory_create_lb_policy( - grpc_lb_policy_factory* factory, grpc_lb_policy_args* args) { - if (factory == NULL) return NULL; - return factory->vtable->create_lb_policy(factory, args); -} diff --git a/third_party/grpc/src/core/client_config/lb_policy_factory.h b/third_party/grpc/src/core/client_config/lb_policy_factory.h deleted file mode 100644 index 04610316ee4300..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policy_factory.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_FACTORY_H -#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_FACTORY_H - -#include "src/core/client_config/lb_policy.h" -#include "src/core/client_config/subchannel.h" - -typedef struct grpc_lb_policy_factory grpc_lb_policy_factory; -typedef struct grpc_lb_policy_factory_vtable grpc_lb_policy_factory_vtable; - -/** grpc_lb_policy provides grpc_client_config objects to grpc_channel - objects */ -struct grpc_lb_policy_factory { - const grpc_lb_policy_factory_vtable *vtable; -}; - -typedef struct grpc_lb_policy_args { - grpc_subchannel **subchannels; - size_t num_subchannels; -} grpc_lb_policy_args; - -struct grpc_lb_policy_factory_vtable { - void (*ref)(grpc_lb_policy_factory *factory); - void (*unref)(grpc_lb_policy_factory *factory); - - /** Implementation of grpc_lb_policy_factory_create_lb_policy */ - grpc_lb_policy *(*create_lb_policy)(grpc_lb_policy_factory *factory, - grpc_lb_policy_args *args); - - /** Name for the LB policy this factory implements */ - const char *name; -}; - -void grpc_lb_policy_factory_ref(grpc_lb_policy_factory *factory); -void grpc_lb_policy_factory_unref(grpc_lb_policy_factory *factory); - -/** Create a lb_policy instance. */ -grpc_lb_policy *grpc_lb_policy_factory_create_lb_policy( - grpc_lb_policy_factory *factory, grpc_lb_policy_args *args); - -#endif /* GRPC_INTERNAL_CORE_CONFIG_LB_POLICY_FACTORY_H */ diff --git a/third_party/grpc/src/core/client_config/lb_policy_registry.c b/third_party/grpc/src/core/client_config/lb_policy_registry.c deleted file mode 100644 index fc302e82d7e076..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policy_registry.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_registry.h" - -#include - -#define MAX_POLICIES 10 - -static grpc_lb_policy_factory *g_all_of_the_lb_policies[MAX_POLICIES]; -static int g_number_of_lb_policies = 0; - -static grpc_lb_policy_factory *g_default_lb_policy_factory; - -void grpc_lb_policy_registry_init(grpc_lb_policy_factory *default_factory) { - g_number_of_lb_policies = 0; - g_default_lb_policy_factory = default_factory; -} - -void grpc_lb_policy_registry_shutdown(void) { - int i; - for (i = 0; i < g_number_of_lb_policies; i++) { - grpc_lb_policy_factory_unref(g_all_of_the_lb_policies[i]); - } -} - -void grpc_register_lb_policy(grpc_lb_policy_factory *factory) { - int i; - for (i = 0; i < g_number_of_lb_policies; i++) { - GPR_ASSERT(0 != strcmp(factory->vtable->name, - g_all_of_the_lb_policies[i]->vtable->name)); - } - GPR_ASSERT(g_number_of_lb_policies != MAX_POLICIES); - grpc_lb_policy_factory_ref(factory); - g_all_of_the_lb_policies[g_number_of_lb_policies++] = factory; -} - -static grpc_lb_policy_factory *lookup_factory(const char *name) { - int i; - - if (name == NULL) return NULL; - - for (i = 0; i < g_number_of_lb_policies; i++) { - if (0 == strcmp(name, g_all_of_the_lb_policies[i]->vtable->name)) { - return g_all_of_the_lb_policies[i]; - } - } - - return NULL; -} - -grpc_lb_policy *grpc_lb_policy_create(const char *name, - grpc_lb_policy_args *args) { - grpc_lb_policy_factory *factory = lookup_factory(name); - grpc_lb_policy *lb_policy = - grpc_lb_policy_factory_create_lb_policy(factory, args); - return lb_policy; -} diff --git a/third_party/grpc/src/core/client_config/lb_policy_registry.h b/third_party/grpc/src/core/client_config/lb_policy_registry.h deleted file mode 100644 index 96fc2a16285137..00000000000000 --- a/third_party/grpc/src/core/client_config/lb_policy_registry.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_REGISTRY_H -#define GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_REGISTRY_H - -#include "src/core/client_config/lb_policy_factory.h" - -/** Initialize the registry and set \a default_factory as the factory to be - * returned when no name is provided in a lookup */ -void grpc_lb_policy_registry_init(grpc_lb_policy_factory *default_factory); -void grpc_lb_policy_registry_shutdown(void); - -/** Register a LB policy factory. */ -void grpc_register_lb_policy(grpc_lb_policy_factory *factory); - -/** Create a \a grpc_lb_policy instance. - * - * If \a name is NULL, the default factory from \a grpc_lb_policy_registry_init - * will be returned. */ -grpc_lb_policy *grpc_lb_policy_create(const char *name, - grpc_lb_policy_args *args); - -#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_LB_POLICY_REGISTRY_H */ diff --git a/third_party/grpc/src/core/client_config/resolver.c b/third_party/grpc/src/core/client_config/resolver.c deleted file mode 100644 index eda01e72ba7fb9..00000000000000 --- a/third_party/grpc/src/core/client_config/resolver.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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, grpc_closure_list *closure_list, - 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, - grpc_closure_list *closure_list, 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_exec_ctx *exec_ctx, grpc_resolver *resolver) { -#endif - if (gpr_unref(&resolver->refs)) { - resolver->vtable->destroy(exec_ctx, resolver); - } -} - -void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) { - resolver->vtable->shutdown(exec_ctx, resolver); -} - -void grpc_resolver_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - resolver->vtable->channel_saw_error(exec_ctx, resolver); -} - -void grpc_resolver_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_client_config **target_config, - grpc_closure *on_complete) { - resolver->vtable->next(exec_ctx, resolver, target_config, on_complete); -} diff --git a/third_party/grpc/src/core/client_config/resolver.h b/third_party/grpc/src/core/client_config/resolver.h deleted file mode 100644 index e612eaf3b3cacd..00000000000000 --- a/third_party/grpc/src/core/client_config/resolver.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/client_config/subchannel.h" -#include "src/core/iomgr/iomgr.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_exec_ctx *exec_ctx, grpc_resolver *resolver); - void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); - void (*channel_saw_error)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver); - void (*next)(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_client_config **target_config, grpc_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(cl, p, r) \ - grpc_resolver_unref((cl), (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, grpc_closure_list *closure_list, - const char *file, int line, const char *reason); -#else -#define GRPC_RESOLVER_REF(p, r) grpc_resolver_ref((p)) -#define GRPC_RESOLVER_UNREF(cl, p, r) grpc_resolver_unref((cl), (p)) -void grpc_resolver_ref(grpc_resolver *policy); -void grpc_resolver_unref(grpc_exec_ctx *exec_ctx, grpc_resolver *policy); -#endif - -void grpc_resolver_init(grpc_resolver *resolver, - const grpc_resolver_vtable *vtable); - -void grpc_resolver_shutdown(grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, - grpc_resolver *resolver); - -/** 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_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_client_config **target_config, - grpc_closure *on_complete); - -#endif /* GRPC_INTERNAL_CORE_CONFIG_RESOLVER_H */ diff --git a/third_party/grpc/src/core/client_config/resolver_factory.c b/third_party/grpc/src/core/client_config/resolver_factory.c deleted file mode 100644 index e7e9196ac4d8c9..00000000000000 --- a/third_party/grpc/src/core/client_config/resolver_factory.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_resolver_args* args) { - if (factory == NULL) return NULL; - return factory->vtable->create_resolver(factory, args); -} - -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/third_party/grpc/src/core/client_config/resolver_factory.h b/third_party/grpc/src/core/client_config/resolver_factory.h deleted file mode 100644 index 4c4df353f7cbce..00000000000000 --- a/third_party/grpc/src/core/client_config/resolver_factory.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; -}; - -typedef struct grpc_resolver_args { - grpc_uri *uri; - grpc_subchannel_factory *subchannel_factory; -} grpc_resolver_args; - -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_resolver_args *args); - - /** 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_resolver_args *args); - -/** 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/third_party/grpc/src/core/client_config/resolver_registry.c b/third_party/grpc/src/core/client_config/resolver_registry.c deleted file mode 100644 index 89a945c2d31dc6..00000000000000 --- a/third_party/grpc/src/core/client_config/resolver_registry.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_args args; - memset(&args, 0, sizeof(args)); - args.uri = uri; - args.subchannel_factory = subchannel_factory; - resolver = grpc_resolver_factory_create_resolver(factory, &args); - 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/third_party/grpc/src/core/client_config/resolver_registry.h b/third_party/grpc/src/core/client_config/resolver_registry.h deleted file mode 100644 index 5a7193b7ae258a..00000000000000 --- a/third_party/grpc/src/core/client_config/resolver_registry.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/client_config/resolvers/dns_resolver.c b/third_party/grpc/src/core/client_config/resolvers/dns_resolver.c deleted file mode 100644 index 376b6b3d7681e4..00000000000000 --- a/third_party/grpc/src/core/client_config/resolvers/dns_resolver.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/client_config/resolvers/dns_resolver.h" - -#include - -#include -#include -#include - -#include "src/core/client_config/lb_policy_registry.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 name */ - char *lb_policy_name; - - /** 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_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_exec_ctx *exec_ctx, grpc_resolver *r); - -static void dns_start_resolving_locked(dns_resolver *r); -static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - dns_resolver *r); - -static void dns_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void dns_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_client_config **target_config, - grpc_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_exec_ctx *exec_ctx, grpc_resolver *resolver) { - dns_resolver *r = (dns_resolver *)resolver; - gpr_mu_lock(&r->mu); - if (r->next_completion != NULL) { - *r->target_config = NULL; - grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL); - r->next_completion = NULL; - } - gpr_mu_unlock(&r->mu); -} - -static void dns_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - 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_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_client_config **target_config, - grpc_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(exec_ctx, r); - } - gpr_mu_unlock(&r->mu); -} - -static void dns_on_resolved(grpc_exec_ctx *exec_ctx, 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) { - grpc_lb_policy_args lb_policy_args; - 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 = (size_t)addresses->addrs[i].len; - subchannels[i] = grpc_subchannel_factory_create_subchannel( - exec_ctx, r->subchannel_factory, &args); - } - memset(&lb_policy_args, 0, sizeof(lb_policy_args)); - lb_policy_args.subchannels = subchannels; - lb_policy_args.num_subchannels = addresses->naddrs; - lb_policy = grpc_lb_policy_create(r->lb_policy_name, &lb_policy_args); - grpc_client_config_set_lb_policy(config, lb_policy); - GRPC_LB_POLICY_UNREF(exec_ctx, 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(exec_ctx, r->resolved_config); - } - r->resolved_config = config; - r->resolved_version++; - dns_maybe_finish_next_locked(exec_ctx, r); - gpr_mu_unlock(&r->mu); - - GRPC_RESOLVER_UNREF(exec_ctx, &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(grpc_exec_ctx *exec_ctx, - 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_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL); - r->next_completion = NULL; - r->published_version = r->resolved_version; - } -} - -static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { - dns_resolver *r = (dns_resolver *)gr; - gpr_mu_destroy(&r->mu); - if (r->resolved_config) { - grpc_client_config_unref(exec_ctx, r->resolved_config); - } - grpc_subchannel_factory_unref(exec_ctx, r->subchannel_factory); - gpr_free(r->name); - gpr_free(r->default_port); - gpr_free(r->lb_policy_name); - gpr_free(r); -} - -static grpc_resolver *dns_create(grpc_resolver_args *args, - const char *default_port, - const char *lb_policy_name) { - dns_resolver *r; - const char *path = args->uri->path; - - if (0 != strcmp(args->uri->authority, "")) { - gpr_log(GPR_ERROR, "authority based dns 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 = args->subchannel_factory; - grpc_subchannel_factory_ref(r->subchannel_factory); - r->lb_policy_name = gpr_strdup(lb_policy_name); - 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_resolver_args *args) { - return dns_create(args, "https", "pick_first"); -} - -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/third_party/grpc/src/core/client_config/resolvers/dns_resolver.h b/third_party/grpc/src/core/client_config/resolvers/dns_resolver.h deleted file mode 100644 index a3ef3161a63f6c..00000000000000 --- a/third_party/grpc/src/core/client_config/resolvers/dns_resolver.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.c b/third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.c deleted file mode 100644 index 68910ad975b6ab..00000000000000 --- a/third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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_policy_registry.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 name */ - char *lb_policy_name; - - /** the addresses that we've 'resolved' */ - struct sockaddr_storage *addrs; - /** the corresponding length of the addresses */ - size_t *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_closure *next_completion; - /** target config address for next completion */ - grpc_client_config **target_config; -} sockaddr_resolver; - -static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); - -static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - sockaddr_resolver *r); - -static void sockaddr_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *r); -static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_client_config **target_config, - grpc_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_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - sockaddr_resolver *r = (sockaddr_resolver *)resolver; - gpr_mu_lock(&r->mu); - if (r->next_completion != NULL) { - *r->target_config = NULL; - grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL); - r->next_completion = NULL; - } - gpr_mu_unlock(&r->mu); -} - -static void sockaddr_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - sockaddr_resolver *r = (sockaddr_resolver *)resolver; - gpr_mu_lock(&r->mu); - r->published = 0; - sockaddr_maybe_finish_next_locked(exec_ctx, r); - gpr_mu_unlock(&r->mu); -} - -static void sockaddr_next(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_client_config **target_config, - grpc_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(exec_ctx, r); - gpr_mu_unlock(&r->mu); -} - -static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - sockaddr_resolver *r) { - grpc_client_config *cfg; - grpc_lb_policy *lb_policy; - grpc_lb_policy_args lb_policy_args; - 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( - exec_ctx, r->subchannel_factory, &args); - } - memset(&lb_policy_args, 0, sizeof(lb_policy_args)); - lb_policy_args.subchannels = subchannels; - lb_policy_args.num_subchannels = r->num_addrs; - lb_policy = grpc_lb_policy_create(r->lb_policy_name, &lb_policy_args); - gpr_free(subchannels); - grpc_client_config_set_lb_policy(cfg, lb_policy); - GRPC_LB_POLICY_UNREF(exec_ctx, lb_policy, "sockaddr"); - r->published = 1; - *r->target_config = cfg; - grpc_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL); - r->next_completion = NULL; - } -} - -static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { - sockaddr_resolver *r = (sockaddr_resolver *)gr; - gpr_mu_destroy(&r->mu); - grpc_subchannel_factory_unref(exec_ctx, r->subchannel_factory); - gpr_free(r->addrs); - gpr_free(r->addrs_len); - gpr_free(r->lb_policy_name); - gpr_free(r); -} - -#ifdef GPR_POSIX_SOCKET -static int parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, - size_t *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, - size_t *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((uint16_t)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, - size_t *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((uint16_t)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_resolver_args *args, const char *default_lb_policy_name, - int parse(grpc_uri *uri, struct sockaddr_storage *dst, size_t *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(args->uri->authority, "")) { - gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme", - args->uri->scheme); - return NULL; - } - - r = gpr_malloc(sizeof(sockaddr_resolver)); - memset(r, 0, sizeof(*r)); - - r->lb_policy_name = NULL; - if (0 != strcmp(args->uri->query, "")) { - gpr_slice query_slice; - gpr_slice_buffer query_parts; - - query_slice = - gpr_slice_new(args->uri->query, strlen(args->uri->query), do_nothing); - gpr_slice_buffer_init(&query_parts); - gpr_slice_split(query_slice, "=", &query_parts); - GPR_ASSERT(query_parts.count == 2); - if (0 == gpr_slice_str_cmp(query_parts.slices[0], "lb_policy")) { - r->lb_policy_name = gpr_dump_slice(query_parts.slices[1], GPR_DUMP_ASCII); - } - gpr_slice_buffer_destroy(&query_parts); - gpr_slice_unref(query_slice); - } - if (r->lb_policy_name == NULL) { - r->lb_policy_name = gpr_strdup(default_lb_policy_name); - } - - path_slice = - gpr_slice_new(args->uri->path, strlen(args->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(*r->addrs_len) * r->num_addrs); - - for (i = 0; i < r->num_addrs; i++) { - grpc_uri ith_uri = *args->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->lb_policy_name); - gpr_free(r->addrs); - gpr_free(r->addrs_len); - 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 = args->subchannel_factory; - grpc_subchannel_factory_ref(r->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_resolver_args *args) { \ - return sockaddr_create(args, "pick_first", 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/third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.h b/third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.h deleted file mode 100644 index 1b7a18f9c2ac27..00000000000000 --- a/third_party/grpc/src/core/client_config/resolvers/sockaddr_resolver.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.c b/third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.c deleted file mode 100644 index 166738e768e6e2..00000000000000 --- a/third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/client_config/resolvers/zookeeper_resolver.h" - -#include - -#include -#include - -#include -#include - -#include "src/core/client_config/lb_policy_registry.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/surface/api_trace.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 name */ - char *lb_policy_name; - - /** 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_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_exec_ctx *exec_ctx, grpc_resolver *r); - -static void zookeeper_start_resolving_locked(zookeeper_resolver *r); -static void zookeeper_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - zookeeper_resolver *r); - -static void zookeeper_shutdown(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *r); -static void zookeeper_next(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_client_config **target_config, - grpc_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_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - zookeeper_resolver *r = (zookeeper_resolver *)resolver; - grpc_closure *call = NULL; - gpr_mu_lock(&r->mu); - if (r->next_completion != NULL) { - *r->target_config = NULL; - call = r->next_completion; - r->next_completion = NULL; - } - zookeeper_close(r->zookeeper_handle); - gpr_mu_unlock(&r->mu); - if (call != NULL) { - call->cb(exec_ctx, call->cb_arg, 1); - } -} - -static void zookeeper_channel_saw_error(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - 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_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_client_config **target_config, - grpc_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(exec_ctx, 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(grpc_exec_ctx *exec_ctx, 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) { - grpc_lb_policy_args lb_policy_args; - 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( - exec_ctx, r->subchannel_factory, &args); - } - lb_policy_args.subchannels = subchannels; - lb_policy_args.num_subchannels = addresses->naddrs; - lb_policy = grpc_lb_policy_create(r->lb_policy_name, &lb_policy_args); - grpc_client_config_set_lb_policy(config, lb_policy); - GRPC_LB_POLICY_UNREF(exec_ctx, 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(exec_ctx, r->resolved_config); - } - r->resolved_config = config; - r->resolved_version++; - zookeeper_maybe_finish_next_locked(exec_ctx, r); - gpr_mu_unlock(&r->mu); - - GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "zookeeper-resolving"); -} - -/** Callback function for each DNS resolved address */ -static void zookeeper_dns_resolved(grpc_exec_ctx *exec_ctx, 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(exec_ctx, r, r->resolved_addrs); - } -} - -/** Parses JSON format address of a zookeeper node */ -static char *zookeeper_parse_address(const char *value, size_t 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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - if (rc != 0) { - gpr_log(GPR_ERROR, "Error in getting a child node of %s", r->name); - grpc_exec_ctx_finish(&exec_ctx); - return; - } - - address = zookeeper_parse_address(value, (size_t)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(&exec_ctx, r, r->resolved_addrs); - } - } - - grpc_exec_ctx_finish(&exec_ctx); -} - -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, (size_t)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(grpc_exec_ctx *exec_ctx, - 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_exec_ctx_enqueue(exec_ctx, r->next_completion, true, NULL); - r->next_completion = NULL; - r->published_version = r->resolved_version; - } -} - -static void zookeeper_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { - zookeeper_resolver *r = (zookeeper_resolver *)gr; - gpr_mu_destroy(&r->mu); - if (r->resolved_config != NULL) { - grpc_client_config_unref(exec_ctx, r->resolved_config); - } - grpc_subchannel_factory_unref(exec_ctx, r->subchannel_factory); - gpr_free(r->name); - gpr_free(r->lb_policy_name); - gpr_free(r); -} - -static grpc_resolver *zookeeper_create(grpc_resolver_args *args, - const char *lb_policy_name) { - zookeeper_resolver *r; - size_t length; - char *path = args->uri->path; - - if (0 == strcmp(args->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 = args->subchannel_factory; - grpc_subchannel_factory_ref(r->subchannel_factory); - - r->lb_policy_name = gpr_strdup(lb_policy_name); - - /** Initializes zookeeper client */ - zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); - r->zookeeper_handle = - zookeeper_init(args->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_API_TRACE("grpc_zookeeper_register(void)", 0, ()); - 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_resolver_args *args) { - return zookeeper_create(args, "pick_first"); -} - -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/third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.h b/third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.h deleted file mode 100644 index a6f002dd6d487b..00000000000000 --- a/third_party/grpc/src/core/client_config/resolvers/zookeeper_resolver.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/client_config/subchannel.c b/third_party/grpc/src/core/client_config/subchannel.c deleted file mode 100644 index 0a996a1e8bc8b4..00000000000000 --- a/third_party/grpc/src/core/client_config/subchannel.c +++ /dev/null @@ -1,708 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/client_config/initial_connect_string.h" -#include "src/core/iomgr/timer.h" -#include "src/core/profiling/timers.h" -#include "src/core/surface/channel.h" -#include "src/core/transport/connectivity_state.h" -#include "src/core/transport/connectivity_state.h" - -#define INTERNAL_REF_BITS 16 -#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1)) - -#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 - -#define GET_CONNECTED_SUBCHANNEL(subchannel, barrier) \ - ((grpc_connected_subchannel *)(gpr_atm_##barrier##_load( \ - &(subchannel)->connected_subchannel))) - -typedef struct { - grpc_closure closure; - grpc_subchannel *subchannel; - grpc_connectivity_state connectivity_state; -} state_watcher; - -typedef struct external_state_watcher { - grpc_subchannel *subchannel; - grpc_pollset_set *pollset_set; - grpc_closure *notify; - grpc_closure closure; - struct external_state_watcher *next; - struct external_state_watcher *prev; -} external_state_watcher; - -struct grpc_subchannel { - grpc_connector *connector; - - /** refcount - - lower INTERNAL_REF_BITS bits are for internal references: - these do not keep the subchannel open. - - upper remaining bits are for public references: these do - keep the subchannel open */ - gpr_atm ref_pair; - - /** 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; - - /** initial string to send to peer */ - gpr_slice initial_connect_string; - - /** set during connection */ - grpc_connect_out_args connecting_result; - - /** callback for connection finishing */ - grpc_closure connected; - - /** pollset_set tracking who's interested in a connection - being setup */ - grpc_pollset_set pollset_set; - - /** active connection, or null; of type grpc_connected_subchannel */ - gpr_atm connected_subchannel; - - /** mutex protecting remaining elements */ - gpr_mu mu; - - /** have we seen a disconnection? */ - int disconnected; - /** are we connecting */ - int connecting; - /** connectivity state tracking */ - grpc_connectivity_state_tracker state_tracker; - - external_state_watcher root_external_state_watcher; - - /** 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_timer alarm; - /** current random value */ - uint32_t random; -}; - -struct grpc_subchannel_call { - grpc_connected_subchannel *connection; -}; - -#define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1)) -#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)(con)) -#define CALLSTACK_TO_SUBCHANNEL_CALL(callstack) \ - (((grpc_subchannel_call *)(callstack)) - 1) - -static gpr_timespec compute_connect_deadline(grpc_subchannel *c); -static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel, - bool iomgr_success); - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -#define REF_REASON reason -#define REF_LOG(name, p) \ - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "%s: %p ref %d -> %d %s", \ - (name), (p), (p)->refs.count, (p)->refs.count + 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.count, (p)->refs.count - 1, reason) -#define REF_MUTATE_EXTRA_ARGS \ - GRPC_SUBCHANNEL_REF_EXTRA_ARGS, const char *purpose -#define REF_MUTATE_PURPOSE(x) , file, line, reason, x -#else -#define REF_REASON "" -#define REF_LOG(name, p) \ - do { \ - } while (0) -#define UNREF_LOG(name, p) \ - do { \ - } while (0) -#define REF_MUTATE_EXTRA_ARGS -#define REF_MUTATE_PURPOSE(x) -#endif - -/* - * connection implementation - */ - -static void connection_destroy(grpc_exec_ctx *exec_ctx, void *arg, - bool success) { - grpc_connected_subchannel *c = arg; - grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c)); - gpr_free(c); -} - -void grpc_connected_subchannel_ref(grpc_connected_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CONNECTION(c), REF_REASON); -} - -void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx, - grpc_connected_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c), - REF_REASON); -} - -/* - * grpc_subchannel implementation - */ - -static void subchannel_destroy(grpc_exec_ctx *exec_ctx, void *arg, - bool success) { - grpc_subchannel *c = arg; - gpr_free((void *)c->filters); - grpc_channel_args_destroy(c->args); - gpr_free(c->addr); - gpr_slice_unref(c->initial_connect_string); - grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker); - grpc_connector_unref(exec_ctx, c->connector); - grpc_pollset_set_destroy(&c->pollset_set); - gpr_free(c); -} - -static gpr_atm ref_mutate(grpc_subchannel *c, gpr_atm delta, - int barrier REF_MUTATE_EXTRA_ARGS) { - gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) - : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); -#ifdef GRPC_STREAM_REFCOUNT_DEBUG - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "SUBCHANNEL: %p % 12s 0x%08x -> 0x%08x [%s]", c, purpose, old_val, - old_val + delta, reason); -#endif - return old_val; -} - -void grpc_subchannel_ref(grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - old_refs = ref_mutate(c, (1 << INTERNAL_REF_BITS), - 0 REF_MUTATE_PURPOSE("STRONG_REF")); - GPR_ASSERT((old_refs & STRONG_REF_MASK) != 0); -} - -void grpc_subchannel_weak_ref(grpc_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - old_refs = ref_mutate(c, 1, 0 REF_MUTATE_PURPOSE("WEAK_REF")); - GPR_ASSERT(old_refs != 0); -} - -static void disconnect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { - grpc_connected_subchannel *con; - gpr_mu_lock(&c->mu); - GPR_ASSERT(!c->disconnected); - c->disconnected = 1; - grpc_connector_shutdown(exec_ctx, c->connector); - con = GET_CONNECTED_SUBCHANNEL(c, no_barrier); - if (con != NULL) { - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, con, "connection"); - gpr_atm_no_barrier_store(&c->connected_subchannel, 0xdeadbeef); - } - gpr_mu_unlock(&c->mu); -} - -void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - old_refs = ref_mutate(c, (gpr_atm)1 - (gpr_atm)(1 << INTERNAL_REF_BITS), - 1 REF_MUTATE_PURPOSE("STRONG_UNREF")); - if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) { - disconnect(exec_ctx, c); - } - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "strong-unref"); -} - -void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - old_refs = ref_mutate(c, -(gpr_atm)1, 1 REF_MUTATE_PURPOSE("WEAK_UNREF")); - if (old_refs == 1) { - grpc_exec_ctx_enqueue(exec_ctx, grpc_closure_create(subchannel_destroy, c), - true, NULL); - } -} - -static uint32_t random_seed() { - return (uint32_t)(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)); - memset(c, 0, sizeof(*c)); - gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS); - c->connector = connector; - grpc_connector_ref(c->connector); - c->num_filters = args->filter_count; - if (c->num_filters > 0) { - c->filters = gpr_malloc(sizeof(grpc_channel_filter *) * c->num_filters); - memcpy((void *)c->filters, args->filters, - sizeof(grpc_channel_filter *) * c->num_filters); - } else { - c->filters = NULL; - } - c->addr = gpr_malloc(args->addr_len); - memcpy(c->addr, args->addr, args->addr_len); - grpc_pollset_set_init(&c->pollset_set); - c->addr_len = args->addr_len; - grpc_set_initial_connect_string(&c->addr, &c->addr_len, - &c->initial_connect_string); - c->args = grpc_channel_args_copy(args->args); - c->random = random_seed(); - c->root_external_state_watcher.next = c->root_external_state_watcher.prev = - &c->root_external_state_watcher; - grpc_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_exec_ctx *exec_ctx, 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.initial_connect_string = c->initial_connect_string; - - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, - GRPC_CHANNEL_CONNECTING, "state_change"); - grpc_connector_connect(exec_ctx, c->connector, &args, &c->connecting_result, - &c->connected); -} - -static void start_connect(grpc_exec_ctx *exec_ctx, 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(exec_ctx, c); -} - -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; -} - -static void on_external_state_watcher_done(grpc_exec_ctx *exec_ctx, void *arg, - bool success) { - external_state_watcher *w = arg; - grpc_closure *follow_up = w->notify; - if (w->pollset_set != NULL) { - grpc_pollset_set_del_pollset_set(exec_ctx, &w->subchannel->pollset_set, - w->pollset_set); - } - gpr_mu_lock(&w->subchannel->mu); - w->next->prev = w->prev; - w->prev->next = w->next; - gpr_mu_unlock(&w->subchannel->mu); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, w->subchannel, "external_state_watcher"); - gpr_free(w); - follow_up->cb(exec_ctx, follow_up->cb_arg, success); -} - -void grpc_subchannel_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_subchannel *c, - grpc_pollset_set *interested_parties, grpc_connectivity_state *state, - grpc_closure *notify) { - int do_connect = 0; - external_state_watcher *w; - - if (state == NULL) { - gpr_mu_lock(&c->mu); - for (w = c->root_external_state_watcher.next; - w != &c->root_external_state_watcher; w = w->next) { - if (w->notify == notify) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &c->state_tracker, NULL, &w->closure); - } - } - gpr_mu_unlock(&c->mu); - } else { - w = gpr_malloc(sizeof(*w)); - w->subchannel = c; - w->pollset_set = interested_parties; - w->notify = notify; - grpc_closure_init(&w->closure, on_external_state_watcher_done, w); - if (interested_parties != NULL) { - grpc_pollset_set_add_pollset_set(exec_ctx, &c->pollset_set, - interested_parties); - } - GRPC_SUBCHANNEL_WEAK_REF(c, "external_state_watcher"); - gpr_mu_lock(&c->mu); - w->next = &c->root_external_state_watcher; - w->prev = w->next->prev; - w->next->prev = w->prev->next = w; - if (grpc_connectivity_state_notify_on_state_change( - exec_ctx, &c->state_tracker, state, &w->closure)) { - do_connect = 1; - c->connecting = 1; - /* released by connection */ - GRPC_SUBCHANNEL_WEAK_REF(c, "connecting"); - } - gpr_mu_unlock(&c->mu); - } - - if (do_connect) { - start_connect(exec_ctx, c); - } -} - -void grpc_connected_subchannel_process_transport_op( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, - grpc_transport_op *op) { - 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(exec_ctx, top_elem, op); -} - -static void subchannel_on_child_state_changed(grpc_exec_ctx *exec_ctx, void *p, - bool iomgr_success) { - state_watcher *sw = p; - grpc_subchannel *c = sw->subchannel; - gpr_mu *mu = &c->mu; - - gpr_mu_lock(mu); - - /* if we failed just leave this closure */ - if (iomgr_success) { - if (sw->connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { - /* any errors on a subchannel ==> we're done, create a new one */ - sw->connectivity_state = GRPC_CHANNEL_FATAL_FAILURE; - } - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, - sw->connectivity_state, "reflect_child"); - if (sw->connectivity_state != GRPC_CHANNEL_FATAL_FAILURE) { - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, GET_CONNECTED_SUBCHANNEL(c, no_barrier), NULL, - &sw->connectivity_state, &sw->closure); - GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); - sw = NULL; - } - } - - gpr_mu_unlock(mu); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "state_watcher"); - gpr_free(sw); -} - -static void connected_subchannel_state_op(grpc_exec_ctx *exec_ctx, - grpc_connected_subchannel *con, - grpc_pollset_set *interested_parties, - grpc_connectivity_state *state, - grpc_closure *closure) { - grpc_transport_op op; - grpc_channel_element *elem; - memset(&op, 0, sizeof(op)); - op.connectivity_state = state; - op.on_connectivity_state_change = closure; - op.bind_pollset_set = interested_parties; - elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); - elem->filter->start_transport_op(exec_ctx, elem, &op); -} - -void grpc_connected_subchannel_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, - grpc_pollset_set *interested_parties, grpc_connectivity_state *state, - grpc_closure *closure) { - connected_subchannel_state_op(exec_ctx, con, interested_parties, state, - closure); -} - -void grpc_connected_subchannel_ping(grpc_exec_ctx *exec_ctx, - grpc_connected_subchannel *con, - grpc_closure *closure) { - grpc_transport_op op; - grpc_channel_element *elem; - memset(&op, 0, sizeof(op)); - op.send_ping = closure; - elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); - elem->filter->start_transport_op(exec_ctx, elem, &op); -} - -static void publish_transport(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { - size_t channel_stack_size; - grpc_connected_subchannel *con; - grpc_channel_stack *stk; - size_t num_filters; - const grpc_channel_filter **filters; - state_watcher *sw_subchannel; - - /* build final filter list */ - num_filters = c->num_filters + c->connecting_result.num_filters + 1; - filters = gpr_malloc(sizeof(*filters) * num_filters); - if (c->num_filters > 0) { - memcpy((void *)filters, c->filters, sizeof(*filters) * c->num_filters); - } - memcpy((void *)(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(channel_stack_size); - stk = CHANNEL_STACK_FROM_CONNECTION(con); - grpc_channel_stack_init(exec_ctx, 1, connection_destroy, con, filters, - num_filters, c->connecting_result.channel_args, - "CONNECTED_SUBCHANNEL", stk); - grpc_connected_channel_bind_transport(stk, c->connecting_result.transport); - gpr_free((void *)c->connecting_result.filters); - memset(&c->connecting_result, 0, sizeof(c->connecting_result)); - - /* initialize state watcher */ - sw_subchannel = gpr_malloc(sizeof(*sw_subchannel)); - sw_subchannel->subchannel = c; - sw_subchannel->connectivity_state = GRPC_CHANNEL_READY; - grpc_closure_init(&sw_subchannel->closure, subchannel_on_child_state_changed, - sw_subchannel); - - gpr_mu_lock(&c->mu); - - if (c->disconnected) { - gpr_mu_unlock(&c->mu); - gpr_free(sw_subchannel); - gpr_free((void *)filters); - grpc_channel_stack_destroy(exec_ctx, stk); - gpr_free(con); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - return; - } - - /* publish */ - /* TODO(ctiller): this full barrier seems to clear up a TSAN failure. - I'd have expected the rel_cas below to be enough, but - seemingly it's not. - Re-evaluate if we really need this. */ - gpr_atm_full_barrier(); - GPR_ASSERT(gpr_atm_rel_cas(&c->connected_subchannel, 0, (gpr_atm)con)); - c->connecting = 0; - - /* setup subchannel watching connected subchannel for changes; subchannel ref - for connecting is donated - to the state watcher */ - GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, con, &c->pollset_set, &sw_subchannel->connectivity_state, - &sw_subchannel->closure); - - /* signal completion */ - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, GRPC_CHANNEL_READY, - "connected"); - - gpr_mu_unlock(&c->mu); - gpr_free((void *)filters); -} - -/* Generate a random number between 0 and 1. */ -static double generate_uniform_random_number(grpc_subchannel *c) { - c->random = (1103515245 * c->random + 12345) % ((uint32_t)1 << 31); - return c->random / (double)((uint32_t)1 << 31); -} - -/* Update backoff_delta and next_attempt in subchannel */ -static void update_reconnect_parameters(grpc_subchannel *c) { - size_t i; - int32_t backoff_delta_millis, jitter; - int32_t max_backoff_millis = - GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; - double jitter_range; - - if (c->args) { - for (i = 0; i < c->args->num_args; i++) { - if (0 == strcmp(c->args->args[i].key, - "grpc.testing.fixed_reconnect_backoff")) { - GPR_ASSERT(c->args->args[i].type == GRPC_ARG_INTEGER); - c->next_attempt = gpr_time_add( - gpr_now(GPR_CLOCK_MONOTONIC), - gpr_time_from_millis(c->args->args[i].value.integer, GPR_TIMESPAN)); - return; - } - } - } - - backoff_delta_millis = - (int32_t)(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 = - (int32_t)((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(grpc_exec_ctx *exec_ctx, void *arg, bool iomgr_success) { - grpc_subchannel *c = arg; - gpr_mu_lock(&c->mu); - c->have_alarm = 0; - if (c->disconnected) { - iomgr_success = 0; - } - gpr_mu_unlock(&c->mu); - if (iomgr_success) { - update_reconnect_parameters(c); - continue_connect(exec_ctx, c); - } else { - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - } -} - -static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - grpc_subchannel *c = arg; - - if (c->connecting_result.transport != NULL) { - publish_transport(exec_ctx, c); - } else if (c->disconnected) { - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - } else { - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_mu_lock(&c->mu); - GPR_ASSERT(!c->have_alarm); - c->have_alarm = 1; - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, - GRPC_CHANNEL_TRANSIENT_FAILURE, - "connect_failed"); - grpc_timer_init(exec_ctx, &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; -} - -/* - * grpc_subchannel_call implementation - */ - -static void subchannel_call_destroy(grpc_exec_ctx *exec_ctx, void *call, - bool success) { - grpc_subchannel_call *c = call; - GPR_TIMER_BEGIN("grpc_subchannel_call_unref.destroy", 0); - grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c)); - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, c->connection, "subchannel_call"); - gpr_free(c); - GPR_TIMER_END("grpc_subchannel_call_unref.destroy", 0); -} - -void grpc_subchannel_call_ref(grpc_subchannel_call *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); -} - -void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CALL_STACK_UNREF(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); -} - -char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, top_elem); -} - -void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, top_elem, op); -} - -grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel( - grpc_subchannel *c) { - return GET_CONNECTED_SUBCHANNEL(c, acq); -} - -grpc_subchannel_call *grpc_connected_subchannel_create_call( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, - grpc_pollset *pollset) { - 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; - GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call"); - grpc_call_stack_init(exec_ctx, chanstk, 1, subchannel_call_destroy, call, - NULL, NULL, callstk); - grpc_call_stack_set_pollset(exec_ctx, callstk, pollset); - return call; -} - -grpc_call_stack *grpc_subchannel_call_get_call_stack( - grpc_subchannel_call *subchannel_call) { - return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call); -} diff --git a/third_party/grpc/src/core/client_config/subchannel.h b/third_party/grpc/src/core/client_config/subchannel.h deleted file mode 100644 index 57c7c9dc67427f..00000000000000 --- a/third_party/grpc/src/core/client_config/subchannel.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" -#include "src/core/transport/connectivity_state.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_connected_subchannel grpc_connected_subchannel; -typedef struct grpc_subchannel_call grpc_subchannel_call; -typedef struct grpc_subchannel_args grpc_subchannel_args; - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -#define GRPC_SUBCHANNEL_REF(p, r) \ - grpc_subchannel_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_SUBCHANNEL_UNREF(cl, p, r) \ - grpc_subchannel_unref((cl), (p), __FILE__, __LINE__, (r)) -#define GRPC_SUBCHANNEL_WEAK_REF(p, r) \ - grpc_subchannel_weak_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_SUBCHANNEL_WEAK_UNREF(cl, p, r) \ - grpc_subchannel_weak_unref((cl), (p), __FILE__, __LINE__, (r)) -#define GRPC_CONNECTED_SUBCHANNEL_REF(p, r) \ - grpc_connected_subchannel_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_CONNECTED_SUBCHANNEL_UNREF(cl, p, r) \ - grpc_connected_subchannel_unref((cl), (p), __FILE__, __LINE__, (r)) -#define GRPC_SUBCHANNEL_CALL_REF(p, r) \ - grpc_subchannel_call_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_SUBCHANNEL_CALL_UNREF(cl, p, r) \ - grpc_subchannel_call_unref((cl), (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(cl, p, r) grpc_subchannel_unref((cl), (p)) -#define GRPC_SUBCHANNEL_WEAK_REF(p, r) grpc_subchannel_weak_ref((p)) -#define GRPC_SUBCHANNEL_WEAK_UNREF(cl, p, r) \ - grpc_subchannel_weak_unref((cl), (p)) -#define GRPC_CONNECTED_SUBCHANNEL_REF(p, r) grpc_connected_subchannel_ref((p)) -#define GRPC_CONNECTED_SUBCHANNEL_UNREF(cl, p, r) \ - grpc_connected_subchannel_unref((cl), (p)) -#define GRPC_SUBCHANNEL_CALL_REF(p, r) grpc_subchannel_call_ref((p)) -#define GRPC_SUBCHANNEL_CALL_UNREF(cl, p, r) \ - grpc_subchannel_call_unref((cl), (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_exec_ctx *exec_ctx, - grpc_subchannel *channel - GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_subchannel_weak_ref(grpc_subchannel *channel - GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel *channel - GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_connected_subchannel_ref(grpc_connected_subchannel *channel - GRPC_SUBCHANNEL_REF_EXTRA_ARGS); -void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx, - grpc_connected_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_exec_ctx *exec_ctx, - grpc_subchannel_call *call - GRPC_SUBCHANNEL_REF_EXTRA_ARGS); - -/** construct a subchannel call */ -grpc_subchannel_call *grpc_connected_subchannel_create_call( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *connected_subchannel, - grpc_pollset *pollset); - -/** process a transport level op */ -void grpc_connected_subchannel_process_transport_op( - grpc_exec_ctx *exec_ctx, grpc_connected_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_exec_ctx *exec_ctx, grpc_subchannel *channel, - grpc_pollset_set *interested_parties, grpc_connectivity_state *state, - grpc_closure *notify); -void grpc_connected_subchannel_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *channel, - grpc_pollset_set *interested_parties, grpc_connectivity_state *state, - grpc_closure *notify); -void grpc_connected_subchannel_ping(grpc_exec_ctx *exec_ctx, - grpc_connected_subchannel *channel, - grpc_closure *notify); - -/** retrieve the grpc_connected_subchannel - or NULL if called before - the subchannel becomes connected */ -grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel( - grpc_subchannel *subchannel); - -/** continue processing a transport op */ -void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call *subchannel_call, - grpc_transport_stream_op *op); - -/** continue querying for peer */ -char *grpc_subchannel_call_get_peer(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call *subchannel_call); - -grpc_call_stack *grpc_subchannel_call_get_call_stack( - 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; -}; - -/** 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/third_party/grpc/src/core/client_config/subchannel_factory.c b/third_party/grpc/src/core/client_config/subchannel_factory.c deleted file mode 100644 index 2c64219e8b0323..00000000000000 --- a/third_party/grpc/src/core/client_config/subchannel_factory.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx* exec_ctx, - grpc_subchannel_factory* factory) { - factory->vtable->unref(exec_ctx, factory); -} - -grpc_subchannel* grpc_subchannel_factory_create_subchannel( - grpc_exec_ctx* exec_ctx, grpc_subchannel_factory* factory, - grpc_subchannel_args* args) { - return factory->vtable->create_subchannel(exec_ctx, factory, args); -} diff --git a/third_party/grpc/src/core/client_config/subchannel_factory.h b/third_party/grpc/src/core/client_config/subchannel_factory.h deleted file mode 100644 index c6d8cc90bed7f2..00000000000000 --- a/third_party/grpc/src/core/client_config/subchannel_factory.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, grpc_subchannel_factory *factory); - grpc_subchannel *(*create_subchannel)(grpc_exec_ctx *exec_ctx, - grpc_subchannel_factory *factory, - grpc_subchannel_args *args); -}; - -void grpc_subchannel_factory_ref(grpc_subchannel_factory *factory); -void grpc_subchannel_factory_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel_factory *factory); - -/** Create a new grpc_subchannel */ -grpc_subchannel *grpc_subchannel_factory_create_subchannel( - grpc_exec_ctx *exec_ctx, grpc_subchannel_factory *factory, - grpc_subchannel_args *args); - -#endif /* GRPC_INTERNAL_CORE_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H */ diff --git a/third_party/grpc/src/core/client_config/uri_parser.c b/third_party/grpc/src/core/client_config/uri_parser.c deleted file mode 100644 index cbdfffcf8e88eb..00000000000000 --- a/third_party/grpc/src/core/client_config/uri_parser.c +++ /dev/null @@ -1,242 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 -#include - -/** a size_t default value... maps to all 1's */ -#define NOT_SET (~(size_t)0) - -static grpc_uri *bad_uri(const char *uri_text, size_t pos, const char *section, - int suppress_errors) { - char *line_prefix; - size_t 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; -} - -/** Returns a copy of \a src[begin, end) */ -static char *copy_component(const char *src, size_t begin, size_t end) { - char *out = gpr_malloc(end - begin + 1); - memcpy(out, src + begin, end - begin); - out[end - begin] = 0; - return out; -} - -/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar - * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent - * sign not followed by two hex digits), NOT_SET is returned. */ -static size_t parse_pchar(const char *uri_text, size_t i) { - /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - / "*" / "+" / "," / ";" / "=" */ - char c = uri_text[i]; - if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || - ((c >= '0') && (c <= '9')) || - (c == '-' || c == '.' || c == '_' || c == '~') || /* unreserved */ - (c == '!' || c == '$' || c == '&' || c == '\'' || c == '$' || c == '&' || - c == '(' || c == ')' || c == '*' || c == '+' || c == ',' || c == ';' || - c == '=') /* sub-delims */) { - return 1; - } - if (c == '%') { /* pct-encoded */ - size_t j; - if (uri_text[i + 1] == 0 || uri_text[i + 2] == 0) { - return NOT_SET; - } - for (j = i + 1; j < 2; j++) { - c = uri_text[j]; - if (!(((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || - ((c >= 'A') && (c <= 'F')))) { - return NOT_SET; - } - } - return 2; - } - return 0; -} - -/* *( pchar / "?" / "/" ) */ -static int parse_fragment_or_query(const char *uri_text, size_t *i) { - char c; - while ((c = uri_text[*i]) != 0) { - const size_t advance = parse_pchar(uri_text, *i); /* pchar */ - switch (advance) { - case 0: /* uri_text[i] isn't in pchar */ - /* maybe it's ? or / */ - if (uri_text[*i] == '?' || uri_text[*i] == '/') { - (*i)++; - break; - } else { - return 1; - } - GPR_UNREACHABLE_CODE(return 0); - default: - (*i) += advance; - break; - case NOT_SET: /* uri_text[i] introduces an invalid URI */ - return 0; - } - } - /* *i is the first uri_text position past the \a query production, maybe \0 */ - return 1; -} - -grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) { - grpc_uri *uri; - size_t scheme_begin = 0; - size_t scheme_end = NOT_SET; - size_t authority_begin = NOT_SET; - size_t authority_end = NOT_SET; - size_t path_begin = NOT_SET; - size_t path_end = NOT_SET; - size_t query_begin = NOT_SET; - size_t query_end = NOT_SET; - size_t fragment_begin = NOT_SET; - size_t fragment_end = NOT_SET; - size_t 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 == NOT_SET) { - 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 == NOT_SET; - i++) { - if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') { - authority_end = i; - } - } - if (authority_end == NOT_SET && uri_text[i] == 0) { - authority_end = i; - } - if (authority_end == NOT_SET) { - 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] == '?' || uri_text[i] == '#') { - path_end = i; - break; - } - } - if (path_end == NOT_SET && uri_text[i] == 0) { - path_end = i; - } - if (path_end == NOT_SET) { - return bad_uri(uri_text, i, "path", suppress_errors); - } - - if (uri_text[i] == '?') { - query_begin = ++i; - if (!parse_fragment_or_query(uri_text, &i)) { - return bad_uri(uri_text, i, "query", suppress_errors); - } else if (uri_text[i] != 0 && uri_text[i] != '#') { - /* We must be at the end or at the beginning of a fragment */ - return bad_uri(uri_text, i, "query", suppress_errors); - } - query_end = i; - } - if (uri_text[i] == '#') { - fragment_begin = ++i; - if (!parse_fragment_or_query(uri_text, &i)) { - return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors); - } else if (uri_text[i] != 0) { - /* We must be at the end */ - return bad_uri(uri_text, i, "fragment", suppress_errors); - } - fragment_end = i; - } - - uri = gpr_malloc(sizeof(*uri)); - memset(uri, 0, sizeof(*uri)); - uri->scheme = copy_component(uri_text, scheme_begin, scheme_end); - uri->authority = copy_component(uri_text, authority_begin, authority_end); - uri->path = copy_component(uri_text, path_begin, path_end); - uri->query = copy_component(uri_text, query_begin, query_end); - uri->fragment = copy_component(uri_text, fragment_begin, fragment_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->query); - gpr_free(uri->fragment); - gpr_free(uri); -} diff --git a/third_party/grpc/src/core/client_config/uri_parser.h b/third_party/grpc/src/core/client_config/uri_parser.h deleted file mode 100644 index b8daa13bd4f66a..00000000000000 --- a/third_party/grpc/src/core/client_config/uri_parser.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - char *query; - char *fragment; -} 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/third_party/grpc/src/core/compression/algorithm.c b/third_party/grpc/src/core/compression/algorithm.c deleted file mode 100644 index 6f3a8eb28e63a3..00000000000000 --- a/third_party/grpc/src/core/compression/algorithm.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#include -#include - -#include "src/core/compression/algorithm_metadata.h" -#include "src/core/surface/api_trace.h" -#include "src/core/transport/static_metadata.h" - -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) */ - GRPC_API_TRACE( - "grpc_compression_algorithm_parse(" - "name=%*.*s, name_length=%lu, algorithm=%p)", - 5, ((int)name_length, (int)name_length, name, (unsigned long)name_length, - algorithm)); - 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) { - GRPC_API_TRACE("grpc_compression_algorithm_parse(algorithm=%d, name=%p)", 2, - ((int)algorithm, name)); - switch (algorithm) { - case GRPC_COMPRESS_NONE: - *name = "identity"; - return 1; - case GRPC_COMPRESS_DEFLATE: - *name = "deflate"; - return 1; - case GRPC_COMPRESS_GZIP: - *name = "gzip"; - return 1; - case GRPC_COMPRESS_ALGORITHMS_COUNT: - return 0; - } - return 0; -} - -grpc_compression_algorithm grpc_compression_algorithm_from_mdstr( - grpc_mdstr *str) { - if (str == GRPC_MDSTR_IDENTITY) return GRPC_COMPRESS_NONE; - if (str == GRPC_MDSTR_DEFLATE) return GRPC_COMPRESS_DEFLATE; - if (str == GRPC_MDSTR_GZIP) return GRPC_COMPRESS_GZIP; - return GRPC_COMPRESS_ALGORITHMS_COUNT; -} - -grpc_mdstr *grpc_compression_algorithm_mdstr( - grpc_compression_algorithm algorithm) { - switch (algorithm) { - case GRPC_COMPRESS_NONE: - return GRPC_MDSTR_IDENTITY; - case GRPC_COMPRESS_DEFLATE: - return GRPC_MDSTR_DEFLATE; - case GRPC_COMPRESS_GZIP: - return GRPC_MDSTR_GZIP; - case GRPC_COMPRESS_ALGORITHMS_COUNT: - return NULL; - } - return NULL; -} - -grpc_mdelem *grpc_compression_encoding_mdelem( - grpc_compression_algorithm algorithm) { - switch (algorithm) { - case GRPC_COMPRESS_NONE: - return GRPC_MDELEM_GRPC_ENCODING_IDENTITY; - case GRPC_COMPRESS_DEFLATE: - return GRPC_MDELEM_GRPC_ENCODING_DEFLATE; - case GRPC_COMPRESS_GZIP: - return GRPC_MDELEM_GRPC_ENCODING_GZIP; - default: - break; - } - return NULL; -} - -/* 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) { - GRPC_API_TRACE("grpc_compression_algorithm_for_level(level=%d)", 1, - ((int)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: - gpr_log(GPR_ERROR, "Unknown compression level %d.", (int)level); - abort(); - } -} - -void grpc_compression_options_init(grpc_compression_options *opts) { - opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; - opts->default_compression_algorithm = GRPC_COMPRESS_NONE; -} - -void grpc_compression_options_enable_algorithm( - grpc_compression_options *opts, grpc_compression_algorithm algorithm) { - GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm); -} - -void grpc_compression_options_disable_algorithm( - grpc_compression_options *opts, grpc_compression_algorithm algorithm) { - GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm); -} - -int grpc_compression_options_is_algorithm_enabled( - const grpc_compression_options *opts, - grpc_compression_algorithm algorithm) { - return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm); -} diff --git a/third_party/grpc/src/core/compression/algorithm_metadata.h b/third_party/grpc/src/core/compression/algorithm_metadata.h deleted file mode 100644 index 882633c3074ebd..00000000000000 --- a/third_party/grpc/src/core/compression/algorithm_metadata.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_ALGORITHM_METADATA_H -#define GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H - -#include -#include "src/core/transport/metadata.h" - -/** Return compression algorithm based metadata value */ -grpc_mdstr *grpc_compression_algorithm_mdstr( - grpc_compression_algorithm algorithm); - -/** Return compression algorithm based metadata element (grpc-encoding: xxx) */ -grpc_mdelem *grpc_compression_encoding_mdelem( - grpc_compression_algorithm algorithm); - -/** Find compression algorithm based on passed in mdstr - returns - * GRPC_COMPRESS_ALGORITHM_COUNT on failure */ -grpc_compression_algorithm grpc_compression_algorithm_from_mdstr( - grpc_mdstr *str); - -#endif /* GRPC_INTERNAL_CORE_COMPRESSION_ALGORITHM_METADATA_H */ diff --git a/third_party/grpc/src/core/compression/message_compress.c b/third_party/grpc/src/core/compression/message_compress.c deleted file mode 100644 index edc21a9eb7d63b..00000000000000 --- a/third_party/grpc/src/core/compression/message_compress.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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); - const uInt uint_max = ~(uInt)0; - - GPR_ASSERT(GPR_SLICE_LENGTH(outbuf) <= uint_max); - zs->avail_out = (uInt)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; - GPR_ASSERT(GPR_SLICE_LENGTH(input->slices[i]) <= uint_max); - zs->avail_in = (uInt)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); - GPR_ASSERT(GPR_SLICE_LENGTH(outbuf) <= uint_max); - zs->avail_out = (uInt)GPR_SLICE_LENGTH(outbuf); - zs->next_out = GPR_SLICE_START_PTR(outbuf); - } - r = flate(zs, flush); - if (r < 0 && r != Z_BUF_ERROR /* not fatal */) { - gpr_log(GPR_INFO, "zlib error (%d)", r); - 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 void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) { - return gpr_malloc(items * size); -} - -static void zfree_gpr(void* opaque, void* address) { gpr_free(address); } - -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)); - zs.zalloc = zalloc_gpr; - zs.zfree = zfree_gpr; - r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0), - 8, Z_DEFAULT_STRATEGY); - GPR_ASSERT(r == Z_OK); - 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)); - zs.zalloc = zalloc_gpr; - zs.zfree = zfree_gpr; - r = inflateInit2(&zs, 15 | (gzip ? 16 : 0)); - GPR_ASSERT(r == Z_OK); - 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; -} - -static 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/third_party/grpc/src/core/compression/message_compress.h b/third_party/grpc/src/core/compression/message_compress.h deleted file mode 100644 index 14652004b86007..00000000000000 --- a/third_party/grpc/src/core/compression/message_compress.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/debug/trace.c b/third_party/grpc/src/core/debug/trace.c deleted file mode 100644 index 3b35d81cd84885..00000000000000 --- a/third_party/grpc/src/core/debug/trace.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - size_t len; - GPR_ASSERT(end >= beg); - len = (size_t)(end - beg); - s = gpr_malloc(len + 1); - memcpy(s, beg, len); - s[len] = 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/third_party/grpc/src/core/debug/trace.h b/third_party/grpc/src/core/debug/trace.h deleted file mode 100644 index dc5875976e2595..00000000000000 --- a/third_party/grpc/src/core/debug/trace.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/ext/README.md b/third_party/grpc/src/core/ext/README.md new file mode 100644 index 00000000000000..0812b20823c55e --- /dev/null +++ b/third_party/grpc/src/core/ext/README.md @@ -0,0 +1,5 @@ +Optional plugins for gRPC Core: Modules in this directory extend gRPC Core in +useful ways. All optional code belongs here. + +NOTE: The movement of code between lib and ext is an ongoing effort, so this +directory currently contains too much of the core library. diff --git a/third_party/grpc/src/core/ext/filters/census/grpc_context.cc b/third_party/grpc/src/core/ext/filters/census/grpc_context.cc new file mode 100644 index 00000000000000..599a798ddaaeca --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/census/grpc_context.cc @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" + +void grpc_census_call_set_context(grpc_call* call, census_context* context) { + GRPC_API_TRACE("grpc_census_call_set_context(call=%p, census_context=%p)", 2, + (call, context)); + if (context != nullptr) { + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, nullptr); + } +} + +census_context* grpc_census_call_get_context(grpc_call* call) { + GRPC_API_TRACE("grpc_census_call_get_context(call=%p)", 1, (call)); + return static_cast( + grpc_call_context_get(call, GRPC_CONTEXT_TRACING)); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/OWNERS b/third_party/grpc/src/core/ext/filters/client_channel/OWNERS new file mode 100644 index 00000000000000..d38970e0fa439c --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/OWNERS @@ -0,0 +1,4 @@ +set noparent +@markdroth +@apolcyn +@AspirinSJL diff --git a/third_party/grpc/src/core/client_config/README.md b/third_party/grpc/src/core/ext/filters/client_channel/README.md similarity index 50% rename from third_party/grpc/src/core/client_config/README.md rename to third_party/grpc/src/core/ext/filters/client_channel/README.md index fff7a5af5b65e9..9676a4535b2876 100644 --- a/third_party/grpc/src/core/client_config/README.md +++ b/third_party/grpc/src/core/ext/filters/client_channel/README.md @@ -5,28 +5,27 @@ 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: +to resolve a name into a set of arguments for the channel. Such arguments +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). +The resolver provides this data as a stream of grpc_channel_args objects to +the channel. We represent arguments as a stream so that they can be changed +by the resolver during execution, by reacting to external events (such as +new service configuration data 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. +Load balancing configuration is provided by a grpc_lb_policy object. -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. +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 @@ -40,27 +39,11 @@ 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 +grpc_client_channel_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 +See [/doc/naming.md](gRPC name resolution). diff --git a/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.cc b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.cc new file mode 100644 index 00000000000000..3e2faa57bcfc84 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.cc @@ -0,0 +1,174 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/backup_poller.h" + +#include +#include +#include +#include +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/completion_queue.h" + +#define DEFAULT_POLL_INTERVAL_MS 5000 + +namespace { +struct backup_poller { + grpc_timer polling_timer; + grpc_closure run_poller_closure; + grpc_closure shutdown_closure; + gpr_mu* pollset_mu; + grpc_pollset* pollset; // guarded by pollset_mu + bool shutting_down; // guarded by pollset_mu + gpr_refcount refs; + gpr_refcount shutdown_refs; +}; +} // namespace + +static gpr_once g_once = GPR_ONCE_INIT; +static gpr_mu g_poller_mu; +static backup_poller* g_poller = nullptr; // guarded by g_poller_mu +// g_poll_interval_ms is set only once at the first time +// grpc_client_channel_start_backup_polling() is called, after that it is +// treated as const. +static int g_poll_interval_ms = DEFAULT_POLL_INTERVAL_MS; + +static void init_globals() { + gpr_mu_init(&g_poller_mu); + char* env = gpr_getenv("GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS"); + if (env != nullptr) { + int poll_interval_ms = gpr_parse_nonnegative_int(env); + if (poll_interval_ms == -1) { + gpr_log(GPR_ERROR, + "Invalid GRPC_CLIENT_CHANNEL_BACKUP_POLL_INTERVAL_MS: %s, " + "default value %d will be used.", + env, g_poll_interval_ms); + } else { + g_poll_interval_ms = poll_interval_ms; + } + } + gpr_free(env); +} + +static void backup_poller_shutdown_unref(backup_poller* p) { + if (gpr_unref(&p->shutdown_refs)) { + grpc_pollset_destroy(p->pollset); + gpr_free(p->pollset); + gpr_free(p); + } +} + +static void done_poller(void* arg, grpc_error* error) { + backup_poller_shutdown_unref(static_cast(arg)); +} + +static void g_poller_unref() { + gpr_mu_lock(&g_poller_mu); + if (gpr_unref(&g_poller->refs)) { + backup_poller* p = g_poller; + g_poller = nullptr; + gpr_mu_unlock(&g_poller_mu); + gpr_mu_lock(p->pollset_mu); + p->shutting_down = true; + grpc_pollset_shutdown( + p->pollset, GRPC_CLOSURE_INIT(&p->shutdown_closure, done_poller, p, + grpc_schedule_on_exec_ctx)); + gpr_mu_unlock(p->pollset_mu); + grpc_timer_cancel(&p->polling_timer); + } else { + gpr_mu_unlock(&g_poller_mu); + } +} + +static void run_poller(void* arg, grpc_error* error) { + backup_poller* p = static_cast(arg); + if (error != GRPC_ERROR_NONE) { + if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("run_poller", GRPC_ERROR_REF(error)); + } + backup_poller_shutdown_unref(p); + return; + } + gpr_mu_lock(p->pollset_mu); + if (p->shutting_down) { + gpr_mu_unlock(p->pollset_mu); + backup_poller_shutdown_unref(p); + return; + } + grpc_error* err = + grpc_pollset_work(p->pollset, nullptr, grpc_core::ExecCtx::Get()->Now()); + gpr_mu_unlock(p->pollset_mu); + GRPC_LOG_IF_ERROR("Run client channel backup poller", err); + grpc_timer_init(&p->polling_timer, + grpc_core::ExecCtx::Get()->Now() + g_poll_interval_ms, + &p->run_poller_closure); +} + +static void g_poller_init_locked() { + if (g_poller == nullptr) { + g_poller = static_cast(gpr_zalloc(sizeof(backup_poller))); + g_poller->pollset = + static_cast(gpr_zalloc(grpc_pollset_size())); + g_poller->shutting_down = false; + grpc_pollset_init(g_poller->pollset, &g_poller->pollset_mu); + gpr_ref_init(&g_poller->refs, 0); + // one for timer cancellation, one for pollset shutdown + gpr_ref_init(&g_poller->shutdown_refs, 2); + GRPC_CLOSURE_INIT(&g_poller->run_poller_closure, run_poller, g_poller, + grpc_schedule_on_exec_ctx); + grpc_timer_init(&g_poller->polling_timer, + grpc_core::ExecCtx::Get()->Now() + g_poll_interval_ms, + &g_poller->run_poller_closure); + } +} + +void grpc_client_channel_start_backup_polling( + grpc_pollset_set* interested_parties) { + gpr_once_init(&g_once, init_globals); + if (g_poll_interval_ms == 0) { + return; + } + gpr_mu_lock(&g_poller_mu); + g_poller_init_locked(); + gpr_ref(&g_poller->refs); + /* Get a reference to g_poller->pollset before releasing g_poller_mu to make + * TSAN happy. Otherwise, reading from g_poller (i.e g_poller->pollset) after + * releasing the lock and setting g_poller to NULL in g_poller_unref() is + * being flagged as a data-race by TSAN */ + grpc_pollset* pollset = g_poller->pollset; + gpr_mu_unlock(&g_poller_mu); + + grpc_pollset_set_add_pollset(interested_parties, pollset); +} + +void grpc_client_channel_stop_backup_polling( + grpc_pollset_set* interested_parties) { + if (g_poll_interval_ms == 0) { + return; + } + grpc_pollset_set_del_pollset(interested_parties, g_poller->pollset); + g_poller_unref(); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.h b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.h new file mode 100644 index 00000000000000..8f132f968ce4ae --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/backup_poller.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H + +#include + +#include +#include "src/core/lib/channel/channel_stack.h" + +/* Start polling \a interested_parties periodically in the timer thread */ +void grpc_client_channel_start_backup_polling( + grpc_pollset_set* interested_parties); + +/* Stop polling \a interested_parties */ +void grpc_client_channel_stop_backup_polling( + grpc_pollset_set* interested_parties); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_BACKUP_POLLER_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/channel_connectivity.cc b/third_party/grpc/src/core/ext/filters/client_channel/channel_connectivity.cc new file mode 100644 index 00000000000000..c71d10274a8658 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/channel_connectivity.cc @@ -0,0 +1,248 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/channel.h" + +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/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)); + grpc_core::ExecCtx exec_ctx; + grpc_connectivity_state state; + GRPC_API_TRACE( + "grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2, + (channel, try_to_connect)); + if (GPR_LIKELY(client_channel_elem->filter == &grpc_client_channel_filter)) { + state = grpc_client_channel_check_connectivity_state(client_channel_elem, + try_to_connect); + + return state; + } + 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_SHUTDOWN; +} + +typedef enum { + WAITING, + READY_TO_CALL_BACK, + CALLING_BACK_AND_FINISHED, +} callback_phase; + +namespace { +struct state_watcher { + gpr_mu mu; + callback_phase phase; + grpc_closure on_complete; + grpc_closure on_timeout; + grpc_closure watcher_timer_init; + grpc_timer alarm; + grpc_connectivity_state state; + grpc_completion_queue* cq; + grpc_cq_completion completion_storage; + grpc_channel* channel; + grpc_error* error; + void* tag; +}; +} // namespace + +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)); + if (client_channel_elem->filter == &grpc_client_channel_filter) { + GRPC_CHANNEL_INTERNAL_UNREF(w->channel, "watch_channel_connectivity"); + } else { + abort(); + } + gpr_mu_destroy(&w->mu); + gpr_free(w); +} + +static void finished_completion(void* pw, grpc_cq_completion* ignored) { + bool should_delete = false; + state_watcher* w = static_cast(pw); + gpr_mu_lock(&w->mu); + switch (w->phase) { + case WAITING: + case READY_TO_CALL_BACK: + GPR_UNREACHABLE_CODE(return ); + case CALLING_BACK_AND_FINISHED: + should_delete = true; + break; + } + gpr_mu_unlock(&w->mu); + + if (should_delete) { + delete_state_watcher(w); + } +} + +static void partly_done(state_watcher* w, bool due_to_completion, + grpc_error* error) { + if (due_to_completion) { + grpc_timer_cancel(&w->alarm); + } else { + grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(w->channel)); + grpc_client_channel_watch_connectivity_state( + client_channel_elem, + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(w->cq)), + nullptr, &w->on_complete, nullptr); + } + + gpr_mu_lock(&w->mu); + + if (due_to_completion) { + if (grpc_trace_operation_failures.enabled()) { + GRPC_LOG_IF_ERROR("watch_completion_error", GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); + error = GRPC_ERROR_NONE; + } else { + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Timed out waiting for connection state change"); + } else if (error == GRPC_ERROR_CANCELLED) { + error = GRPC_ERROR_NONE; + } + } + switch (w->phase) { + case WAITING: + GRPC_ERROR_REF(error); + w->error = error; + w->phase = READY_TO_CALL_BACK; + break; + case READY_TO_CALL_BACK: + if (error != GRPC_ERROR_NONE) { + GPR_ASSERT(!due_to_completion); + GRPC_ERROR_UNREF(w->error); + GRPC_ERROR_REF(error); + w->error = error; + } + w->phase = CALLING_BACK_AND_FINISHED; + grpc_cq_end_op(w->cq, w->tag, w->error, finished_completion, w, + &w->completion_storage); + break; + case CALLING_BACK_AND_FINISHED: + GPR_UNREACHABLE_CODE(return ); + break; + } + gpr_mu_unlock(&w->mu); + + GRPC_ERROR_UNREF(error); +} + +static void watch_complete(void* pw, grpc_error* error) { + partly_done(static_cast(pw), true, GRPC_ERROR_REF(error)); +} + +static void timeout_complete(void* pw, grpc_error* error) { + partly_done(static_cast(pw), false, GRPC_ERROR_REF(error)); +} + +int grpc_channel_num_external_connectivity_watchers(grpc_channel* channel) { + grpc_channel_element* client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + return grpc_client_channel_num_external_connectivity_watchers( + client_channel_elem); +} + +typedef struct watcher_timer_init_arg { + state_watcher* w; + gpr_timespec deadline; +} watcher_timer_init_arg; + +static void watcher_timer_init(void* arg, grpc_error* error_ignored) { + watcher_timer_init_arg* wa = static_cast(arg); + + grpc_timer_init(&wa->w->alarm, grpc_timespec_to_millis_round_up(wa->deadline), + &wa->w->on_timeout); + gpr_free(wa); +} + +int grpc_channel_support_connectivity_watcher(grpc_channel* channel) { + grpc_channel_element* client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + return client_channel_elem->filter != &grpc_client_channel_filter ? 0 : 1; +} + +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)); + grpc_core::ExecCtx exec_ctx; + state_watcher* w = static_cast(gpr_malloc(sizeof(*w))); + + GRPC_API_TRACE( + "grpc_channel_watch_connectivity_state(" + "channel=%p, last_observed_state=%d, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "cq=%p, tag=%p)", + 7, + (channel, (int)last_observed_state, deadline.tv_sec, deadline.tv_nsec, + (int)deadline.clock_type, cq, tag)); + + GPR_ASSERT(grpc_cq_begin_op(cq, tag)); + + gpr_mu_init(&w->mu); + GRPC_CLOSURE_INIT(&w->on_complete, watch_complete, w, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&w->on_timeout, timeout_complete, w, + grpc_schedule_on_exec_ctx); + w->phase = WAITING; + w->state = last_observed_state; + w->cq = cq; + w->tag = tag; + w->channel = channel; + w->error = nullptr; + + watcher_timer_init_arg* wa = static_cast( + gpr_malloc(sizeof(watcher_timer_init_arg))); + wa->w = w; + wa->deadline = deadline; + GRPC_CLOSURE_INIT(&w->watcher_timer_init, watcher_timer_init, wa, + grpc_schedule_on_exec_ctx); + + if (client_channel_elem->filter == &grpc_client_channel_filter) { + GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity"); + grpc_client_channel_watch_connectivity_state( + client_channel_elem, + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)), &w->state, + &w->on_complete, &w->watcher_timer_init); + } else { + abort(); + } +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.cc new file mode 100644 index 00000000000000..dd741f1e2de79c --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.cc @@ -0,0 +1,2730 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/backup_poller.h" +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/request_routing.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/resolver_result_parsing.h" +#include "src/core/ext/filters/client_channel/retry_throttle.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/filters/deadline/deadline_filter.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/channel/status_util.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/service_config.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_metadata.h" + +using grpc_core::internal::ClientChannelMethodParams; +using grpc_core::internal::ClientChannelMethodParamsTable; +using grpc_core::internal::ProcessedResolverResult; +using grpc_core::internal::ServerRetryThrottleData; + +/* Client channel implementation */ + +// By default, we buffer 256 KiB per RPC for retries. +// TODO(roth): Do we have any data to suggest a better value? +#define DEFAULT_PER_RPC_RETRY_BUFFER_SIZE (256 << 10) + +// This value was picked arbitrarily. It can be changed if there is +// any even moderately compelling reason to do so. +#define RETRY_BACKOFF_JITTER 0.2 + +grpc_core::TraceFlag grpc_client_channel_trace(false, "client_channel"); + +/************************************************************************* + * CHANNEL-WIDE FUNCTIONS + */ + +struct external_connectivity_watcher; + +typedef struct client_channel_channel_data { + grpc_core::ManualConstructor request_router; + + bool deadline_checking_enabled; + bool enable_retries; + size_t per_rpc_retry_buffer_size; + + /** combiner protecting all variables below in this data structure */ + grpc_combiner* combiner; + /** retry throttle data */ + grpc_core::RefCountedPtr retry_throttle_data; + /** maps method names to method_parameters structs */ + grpc_core::RefCountedPtr method_params_table; + /** owning stack */ + grpc_channel_stack* owning_stack; + /** interested parties (owned) */ + grpc_pollset_set* interested_parties; + + /* external_connectivity_watcher_list head is guarded by its own mutex, since + * counts need to be grabbed immediately without polling on a cq */ + gpr_mu external_connectivity_watcher_list_mu; + struct external_connectivity_watcher* external_connectivity_watcher_list_head; + + /* the following properties are guarded by a mutex since APIs require them + to be instantaneously available */ + gpr_mu info_mu; + grpc_core::UniquePtr info_lb_policy_name; + /** service config in JSON form */ + grpc_core::UniquePtr info_service_config_json; +} channel_data; + +// Synchronous callback from chand->request_router to process a resolver +// result update. +static bool process_resolver_result_locked(void* arg, + const grpc_channel_args& args, + const char** lb_policy_name, + grpc_json** lb_policy_config) { + channel_data* chand = static_cast(arg); + ProcessedResolverResult resolver_result(args, chand->enable_retries); + grpc_core::UniquePtr service_config_json = + resolver_result.service_config_json(); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p: resolver returned service config: \"%s\"", + chand, service_config_json.get()); + } + // Update channel state. + chand->retry_throttle_data = resolver_result.retry_throttle_data(); + chand->method_params_table = resolver_result.method_params_table(); + // Swap out the data used by cc_get_channel_info(). + gpr_mu_lock(&chand->info_mu); + chand->info_lb_policy_name = resolver_result.lb_policy_name(); + const bool service_config_changed = + ((service_config_json == nullptr) != + (chand->info_service_config_json == nullptr)) || + (service_config_json != nullptr && + strcmp(service_config_json.get(), + chand->info_service_config_json.get()) != 0); + chand->info_service_config_json = std::move(service_config_json); + gpr_mu_unlock(&chand->info_mu); + // Return results. + *lb_policy_name = chand->info_lb_policy_name.get(); + *lb_policy_config = resolver_result.lb_policy_config(); + return service_config_changed; +} + +static void start_transport_op_locked(void* arg, grpc_error* error_ignored) { + grpc_transport_op* op = static_cast(arg); + grpc_channel_element* elem = + static_cast(op->handler_private.extra_arg); + channel_data* chand = static_cast(elem->channel_data); + + if (op->on_connectivity_state_change != nullptr) { + chand->request_router->NotifyOnConnectivityStateChange( + op->connectivity_state, op->on_connectivity_state_change); + op->on_connectivity_state_change = nullptr; + op->connectivity_state = nullptr; + } + + if (op->send_ping.on_initiate != nullptr || op->send_ping.on_ack != nullptr) { + if (chand->request_router->lb_policy() == nullptr) { + grpc_error* error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Ping with no load balancing"); + GRPC_CLOSURE_SCHED(op->send_ping.on_initiate, GRPC_ERROR_REF(error)); + GRPC_CLOSURE_SCHED(op->send_ping.on_ack, error); + } else { + grpc_error* error = GRPC_ERROR_NONE; + grpc_core::LoadBalancingPolicy::PickState pick_state; + // Pick must return synchronously, because pick_state.on_complete is null. + GPR_ASSERT( + chand->request_router->lb_policy()->PickLocked(&pick_state, &error)); + if (pick_state.connected_subchannel != nullptr) { + pick_state.connected_subchannel->Ping(op->send_ping.on_initiate, + op->send_ping.on_ack); + } else { + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "LB policy dropped call on ping"); + } + GRPC_CLOSURE_SCHED(op->send_ping.on_initiate, GRPC_ERROR_REF(error)); + GRPC_CLOSURE_SCHED(op->send_ping.on_ack, error); + } + op->bind_pollset = nullptr; + } + op->send_ping.on_initiate = nullptr; + op->send_ping.on_ack = nullptr; + } + + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + chand->request_router->ShutdownLocked(op->disconnect_with_error); + } + + if (op->reset_connect_backoff) { + chand->request_router->ResetConnectionBackoffLocked(); + } + + GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "start_transport_op"); + GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE); +} + +static void cc_start_transport_op(grpc_channel_element* elem, + grpc_transport_op* op) { + channel_data* chand = static_cast(elem->channel_data); + + GPR_ASSERT(op->set_accept_stream == false); + if (op->bind_pollset != nullptr) { + grpc_pollset_set_add_pollset(chand->interested_parties, op->bind_pollset); + } + + op->handler_private.extra_arg = elem; + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "start_transport_op"); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&op->handler_private.closure, start_transport_op_locked, + op, grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); +} + +static void cc_get_channel_info(grpc_channel_element* elem, + const grpc_channel_info* info) { + channel_data* chand = static_cast(elem->channel_data); + gpr_mu_lock(&chand->info_mu); + if (info->lb_policy_name != nullptr) { + *info->lb_policy_name = gpr_strdup(chand->info_lb_policy_name.get()); + } + if (info->service_config_json != nullptr) { + *info->service_config_json = + gpr_strdup(chand->info_service_config_json.get()); + } + gpr_mu_unlock(&chand->info_mu); +} + +/* Constructor for channel_data */ +static grpc_error* cc_init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + GPR_ASSERT(args->is_last); + GPR_ASSERT(elem->filter == &grpc_client_channel_filter); + // Initialize data members. + chand->combiner = grpc_combiner_create(); + gpr_mu_init(&chand->info_mu); + gpr_mu_init(&chand->external_connectivity_watcher_list_mu); + + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + chand->external_connectivity_watcher_list_head = nullptr; + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + + chand->owning_stack = args->channel_stack; + chand->deadline_checking_enabled = + grpc_deadline_checking_enabled(args->channel_args); + chand->interested_parties = grpc_pollset_set_create(); + grpc_client_channel_start_backup_polling(chand->interested_parties); + // Record max per-RPC retry buffer size. + const grpc_arg* arg = grpc_channel_args_find( + args->channel_args, GRPC_ARG_PER_RPC_RETRY_BUFFER_SIZE); + chand->per_rpc_retry_buffer_size = (size_t)grpc_channel_arg_get_integer( + arg, {DEFAULT_PER_RPC_RETRY_BUFFER_SIZE, 0, INT_MAX}); + // Record enable_retries. + arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_ENABLE_RETRIES); + chand->enable_retries = grpc_channel_arg_get_bool(arg, true); + // Record client channel factory. + arg = grpc_channel_args_find(args->channel_args, + GRPC_ARG_CLIENT_CHANNEL_FACTORY); + if (arg == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Missing client channel factory in args for client channel filter"); + } + if (arg->type != GRPC_ARG_POINTER) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "client channel factory arg must be a pointer"); + } + grpc_client_channel_factory* client_channel_factory = + static_cast(arg->value.pointer.p); + // Get server name to resolve, using proxy mapper if needed. + arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI); + if (arg == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Missing server uri in args for client channel filter"); + } + if (arg->type != GRPC_ARG_STRING) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "server uri arg must be a string"); + } + char* proxy_name = nullptr; + grpc_channel_args* new_args = nullptr; + grpc_proxy_mappers_map_name(arg->value.string, args->channel_args, + &proxy_name, &new_args); + // Instantiate request router. + grpc_client_channel_factory_ref(client_channel_factory); + grpc_error* error = GRPC_ERROR_NONE; + chand->request_router.Init( + chand->owning_stack, chand->combiner, client_channel_factory, + chand->interested_parties, &grpc_client_channel_trace, + process_resolver_result_locked, chand, + proxy_name != nullptr ? proxy_name : arg->value.string /* target_uri */, + new_args != nullptr ? new_args : args->channel_args, &error); + gpr_free(proxy_name); + grpc_channel_args_destroy(new_args); + return error; +} + +/* Destructor for channel_data */ +static void cc_destroy_channel_elem(grpc_channel_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + chand->request_router.Destroy(); + // TODO(roth): Once we convert the filter API to C++, there will no + // longer be any need to explicitly reset these smart pointer data members. + chand->info_lb_policy_name.reset(); + chand->info_service_config_json.reset(); + chand->retry_throttle_data.reset(); + chand->method_params_table.reset(); + grpc_client_channel_stop_backup_polling(chand->interested_parties); + grpc_pollset_set_destroy(chand->interested_parties); + GRPC_COMBINER_UNREF(chand->combiner, "client_channel"); + gpr_mu_destroy(&chand->info_mu); + gpr_mu_destroy(&chand->external_connectivity_watcher_list_mu); +} + +/************************************************************************* + * PER-CALL FUNCTIONS + */ + +// Max number of batches that can be pending on a call at any given +// time. This includes one batch for each of the following ops: +// recv_initial_metadata +// send_initial_metadata +// recv_message +// send_message +// recv_trailing_metadata +// send_trailing_metadata +#define MAX_PENDING_BATCHES 6 + +// Retry support: +// +// In order to support retries, we act as a proxy for stream op batches. +// When we get a batch from the surface, we add it to our list of pending +// batches, and we then use those batches to construct separate "child" +// batches to be started on the subchannel call. When the child batches +// return, we then decide which pending batches have been completed and +// schedule their callbacks accordingly. If a subchannel call fails and +// we want to retry it, we do a new pick and start again, constructing +// new "child" batches for the new subchannel call. +// +// Note that retries are committed when receiving data from the server +// (except for Trailers-Only responses). However, there may be many +// send ops started before receiving any data, so we may have already +// completed some number of send ops (and returned the completions up to +// the surface) by the time we realize that we need to retry. To deal +// with this, we cache data for send ops, so that we can replay them on a +// different subchannel call even after we have completed the original +// batches. +// +// There are two sets of data to maintain: +// - In call_data (in the parent channel), we maintain a list of pending +// ops and cached data for send ops. +// - In the subchannel call, we maintain state to indicate what ops have +// already been sent down to that call. +// +// When constructing the "child" batches, we compare those two sets of +// data to see which batches need to be sent to the subchannel call. + +// TODO(roth): In subsequent PRs: +// - add support for transparent retries (including initial metadata) +// - figure out how to record stats in census for retries +// (census filter is on top of this one) +// - add census stats for retries + +namespace { + +struct call_data; + +// State used for starting a retryable batch on a subchannel call. +// This provides its own grpc_transport_stream_op_batch and other data +// structures needed to populate the ops in the batch. +// We allocate one struct on the arena for each attempt at starting a +// batch on a given subchannel call. +struct subchannel_batch_data { + subchannel_batch_data(grpc_call_element* elem, call_data* calld, int refcount, + bool set_on_complete); + // All dtor code must be added in `destroy`. This is because we may + // call closures in `subchannel_batch_data` after they are unrefed by + // `batch_data_unref`, and msan would complain about accessing this class + // after calling dtor. As a result we cannot call the `dtor` in + // `batch_data_unref`. + // TODO(soheil): We should try to call the dtor in `batch_data_unref`. + ~subchannel_batch_data() { destroy(); } + void destroy(); + + gpr_refcount refs; + grpc_call_element* elem; + grpc_subchannel_call* subchannel_call; // Holds a ref. + // The batch to use in the subchannel call. + // Its payload field points to subchannel_call_retry_state.batch_payload. + grpc_transport_stream_op_batch batch; + // For intercepting on_complete. + grpc_closure on_complete; +}; + +// Retry state associated with a subchannel call. +// Stored in the parent_data of the subchannel call object. +struct subchannel_call_retry_state { + explicit subchannel_call_retry_state(grpc_call_context_element* context) + : batch_payload(context), + started_send_initial_metadata(false), + completed_send_initial_metadata(false), + started_send_trailing_metadata(false), + completed_send_trailing_metadata(false), + started_recv_initial_metadata(false), + completed_recv_initial_metadata(false), + started_recv_trailing_metadata(false), + completed_recv_trailing_metadata(false), + retry_dispatched(false) {} + + // subchannel_batch_data.batch.payload points to this. + grpc_transport_stream_op_batch_payload batch_payload; + // For send_initial_metadata. + // Note that we need to make a copy of the initial metadata for each + // subchannel call instead of just referring to the copy in call_data, + // because filters in the subchannel stack will probably add entries, + // so we need to start in a pristine state for each attempt of the call. + grpc_linked_mdelem* send_initial_metadata_storage; + grpc_metadata_batch send_initial_metadata; + // For send_message. + grpc_core::ManualConstructor + send_message; + // For send_trailing_metadata. + grpc_linked_mdelem* send_trailing_metadata_storage; + grpc_metadata_batch send_trailing_metadata; + // For intercepting recv_initial_metadata. + grpc_metadata_batch recv_initial_metadata; + grpc_closure recv_initial_metadata_ready; + bool trailing_metadata_available = false; + // For intercepting recv_message. + grpc_closure recv_message_ready; + grpc_core::OrphanablePtr recv_message; + // For intercepting recv_trailing_metadata. + grpc_metadata_batch recv_trailing_metadata; + grpc_transport_stream_stats collect_stats; + grpc_closure recv_trailing_metadata_ready; + // These fields indicate which ops have been started and completed on + // this subchannel call. + size_t started_send_message_count = 0; + size_t completed_send_message_count = 0; + size_t started_recv_message_count = 0; + size_t completed_recv_message_count = 0; + bool started_send_initial_metadata : 1; + bool completed_send_initial_metadata : 1; + bool started_send_trailing_metadata : 1; + bool completed_send_trailing_metadata : 1; + bool started_recv_initial_metadata : 1; + bool completed_recv_initial_metadata : 1; + bool started_recv_trailing_metadata : 1; + bool completed_recv_trailing_metadata : 1; + // State for callback processing. + subchannel_batch_data* recv_initial_metadata_ready_deferred_batch = nullptr; + grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE; + subchannel_batch_data* recv_message_ready_deferred_batch = nullptr; + grpc_error* recv_message_error = GRPC_ERROR_NONE; + subchannel_batch_data* recv_trailing_metadata_internal_batch = nullptr; + // NOTE: Do not move this next to the metadata bitfields above. That would + // save space but will also result in a data race because compiler will + // generate a 2 byte store which overwrites the meta-data fields upon + // setting this field. + bool retry_dispatched : 1; +}; + +// Pending batches stored in call data. +struct pending_batch { + // The pending batch. If nullptr, this slot is empty. + grpc_transport_stream_op_batch* batch; + // Indicates whether payload for send ops has been cached in call data. + bool send_ops_cached; +}; + +/** Call data. Holds a pointer to grpc_subchannel_call and the + associated machinery to create such a pointer. + Handles queueing of stream ops until a call object is ready, waiting + for initial metadata before trying to create a call object, + and handling cancellation gracefully. */ +struct call_data { + call_data(grpc_call_element* elem, const channel_data& chand, + const grpc_call_element_args& args) + : deadline_state(elem, args.call_stack, args.call_combiner, + GPR_LIKELY(chand.deadline_checking_enabled) + ? args.deadline + : GRPC_MILLIS_INF_FUTURE), + path(grpc_slice_ref_internal(args.path)), + call_start_time(args.start_time), + deadline(args.deadline), + arena(args.arena), + owning_call(args.call_stack), + call_combiner(args.call_combiner), + pending_send_initial_metadata(false), + pending_send_message(false), + pending_send_trailing_metadata(false), + enable_retries(chand.enable_retries), + retry_committed(false), + last_attempt_got_server_pushback(false) {} + + ~call_data() { + if (GPR_LIKELY(subchannel_call != nullptr)) { + GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, + "client_channel_destroy_call"); + } + grpc_slice_unref_internal(path); + GRPC_ERROR_UNREF(cancel_error); + for (size_t i = 0; i < GPR_ARRAY_SIZE(pending_batches); ++i) { + GPR_ASSERT(pending_batches[i].batch == nullptr); + } + if (have_request) { + request.Destroy(); + } + } + + // State for handling deadlines. + // The code in deadline_filter.c requires this to be the first field. + // TODO(roth): This is slightly sub-optimal in that grpc_deadline_state + // and this struct both independently store pointers to the call stack + // and call combiner. If/when we have time, find a way to avoid this + // without breaking the grpc_deadline_state abstraction. + grpc_deadline_state deadline_state; + + grpc_slice path; // Request path. + gpr_timespec call_start_time; + grpc_millis deadline; + gpr_arena* arena; + grpc_call_stack* owning_call; + grpc_call_combiner* call_combiner; + + grpc_core::RefCountedPtr retry_throttle_data; + grpc_core::RefCountedPtr method_params; + + grpc_subchannel_call* subchannel_call = nullptr; + + // Set when we get a cancel_stream op. + grpc_error* cancel_error = GRPC_ERROR_NONE; + + grpc_core::ManualConstructor request; + bool have_request = false; + grpc_closure pick_closure; + + grpc_polling_entity* pollent = nullptr; + + // Batches are added to this list when received from above. + // They are removed when we are done handling the batch (i.e., when + // either we have invoked all of the batch's callbacks or we have + // passed the batch down to the subchannel call and are not + // intercepting any of its callbacks). + pending_batch pending_batches[MAX_PENDING_BATCHES] = {}; + bool pending_send_initial_metadata : 1; + bool pending_send_message : 1; + bool pending_send_trailing_metadata : 1; + + // Retry state. + bool enable_retries : 1; + bool retry_committed : 1; + bool last_attempt_got_server_pushback : 1; + int num_attempts_completed = 0; + size_t bytes_buffered_for_retry = 0; + grpc_core::ManualConstructor retry_backoff; + grpc_timer retry_timer; + + // The number of pending retriable subchannel batches containing send ops. + // We hold a ref to the call stack while this is non-zero, since replay + // batches may not complete until after all callbacks have been returned + // to the surface, and we need to make sure that the call is not destroyed + // until all of these batches have completed. + // Note that we actually only need to track replay batches, but it's + // easier to track all batches with send ops. + int num_pending_retriable_subchannel_send_batches = 0; + + // Cached data for retrying send ops. + // send_initial_metadata + bool seen_send_initial_metadata = false; + grpc_linked_mdelem* send_initial_metadata_storage = nullptr; + grpc_metadata_batch send_initial_metadata; + uint32_t send_initial_metadata_flags; + gpr_atm* peer_string; + // send_message + // When we get a send_message op, we replace the original byte stream + // with a CachingByteStream that caches the slices to a local buffer for + // use in retries. + // Note: We inline the cache for the first 3 send_message ops and use + // dynamic allocation after that. This number was essentially picked + // at random; it could be changed in the future to tune performance. + grpc_core::InlinedVector send_messages; + // send_trailing_metadata + bool seen_send_trailing_metadata = false; + grpc_linked_mdelem* send_trailing_metadata_storage = nullptr; + grpc_metadata_batch send_trailing_metadata; +}; + +} // namespace + +// Forward declarations. +static void retry_commit(grpc_call_element* elem, + subchannel_call_retry_state* retry_state); +static void start_internal_recv_trailing_metadata(grpc_call_element* elem); +static void on_complete(void* arg, grpc_error* error); +static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored); +static void start_pick_locked(void* arg, grpc_error* ignored); + +// +// send op data caching +// + +// Caches data for send ops so that it can be retried later, if not +// already cached. +static void maybe_cache_send_ops_for_batch(call_data* calld, + pending_batch* pending) { + if (pending->send_ops_cached) return; + pending->send_ops_cached = true; + grpc_transport_stream_op_batch* batch = pending->batch; + // Save a copy of metadata for send_initial_metadata ops. + if (batch->send_initial_metadata) { + calld->seen_send_initial_metadata = true; + GPR_ASSERT(calld->send_initial_metadata_storage == nullptr); + grpc_metadata_batch* send_initial_metadata = + batch->payload->send_initial_metadata.send_initial_metadata; + calld->send_initial_metadata_storage = (grpc_linked_mdelem*)gpr_arena_alloc( + calld->arena, + sizeof(grpc_linked_mdelem) * send_initial_metadata->list.count); + grpc_metadata_batch_copy(send_initial_metadata, + &calld->send_initial_metadata, + calld->send_initial_metadata_storage); + calld->send_initial_metadata_flags = + batch->payload->send_initial_metadata.send_initial_metadata_flags; + calld->peer_string = batch->payload->send_initial_metadata.peer_string; + } + // Set up cache for send_message ops. + if (batch->send_message) { + grpc_core::ByteStreamCache* cache = + static_cast( + gpr_arena_alloc(calld->arena, sizeof(grpc_core::ByteStreamCache))); + new (cache) grpc_core::ByteStreamCache( + std::move(batch->payload->send_message.send_message)); + calld->send_messages.push_back(cache); + } + // Save metadata batch for send_trailing_metadata ops. + if (batch->send_trailing_metadata) { + calld->seen_send_trailing_metadata = true; + GPR_ASSERT(calld->send_trailing_metadata_storage == nullptr); + grpc_metadata_batch* send_trailing_metadata = + batch->payload->send_trailing_metadata.send_trailing_metadata; + calld->send_trailing_metadata_storage = + (grpc_linked_mdelem*)gpr_arena_alloc( + calld->arena, + sizeof(grpc_linked_mdelem) * send_trailing_metadata->list.count); + grpc_metadata_batch_copy(send_trailing_metadata, + &calld->send_trailing_metadata, + calld->send_trailing_metadata_storage); + } +} + +// Frees cached send_initial_metadata. +static void free_cached_send_initial_metadata(channel_data* chand, + call_data* calld) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: destroying calld->send_initial_metadata", chand, + calld); + } + grpc_metadata_batch_destroy(&calld->send_initial_metadata); +} + +// Frees cached send_message at index idx. +static void free_cached_send_message(channel_data* chand, call_data* calld, + size_t idx) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: destroying calld->send_messages[%" PRIuPTR "]", + chand, calld, idx); + } + calld->send_messages[idx]->Destroy(); +} + +// Frees cached send_trailing_metadata. +static void free_cached_send_trailing_metadata(channel_data* chand, + call_data* calld) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: destroying calld->send_trailing_metadata", + chand, calld); + } + grpc_metadata_batch_destroy(&calld->send_trailing_metadata); +} + +// Frees cached send ops that have already been completed after +// committing the call. +static void free_cached_send_op_data_after_commit( + grpc_call_element* elem, subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (retry_state->completed_send_initial_metadata) { + free_cached_send_initial_metadata(chand, calld); + } + for (size_t i = 0; i < retry_state->completed_send_message_count; ++i) { + free_cached_send_message(chand, calld, i); + } + if (retry_state->completed_send_trailing_metadata) { + free_cached_send_trailing_metadata(chand, calld); + } +} + +// Frees cached send ops that were completed by the completed batch in +// batch_data. Used when batches are completed after the call is committed. +static void free_cached_send_op_data_for_completed_batch( + grpc_call_element* elem, subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (batch_data->batch.send_initial_metadata) { + free_cached_send_initial_metadata(chand, calld); + } + if (batch_data->batch.send_message) { + free_cached_send_message(chand, calld, + retry_state->completed_send_message_count - 1); + } + if (batch_data->batch.send_trailing_metadata) { + free_cached_send_trailing_metadata(chand, calld); + } +} + +// +// pending_batches management +// + +// Returns the index into calld->pending_batches to be used for batch. +static size_t get_batch_index(grpc_transport_stream_op_batch* batch) { + // Note: It is important the send_initial_metadata be the first entry + // here, since the code in pick_subchannel_locked() assumes it will be. + if (batch->send_initial_metadata) return 0; + if (batch->send_message) return 1; + if (batch->send_trailing_metadata) return 2; + if (batch->recv_initial_metadata) return 3; + if (batch->recv_message) return 4; + if (batch->recv_trailing_metadata) return 5; + GPR_UNREACHABLE_CODE(return (size_t)-1); +} + +// This is called via the call combiner, so access to calld is synchronized. +static void pending_batches_add(grpc_call_element* elem, + grpc_transport_stream_op_batch* batch) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + const size_t idx = get_batch_index(batch); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: adding pending batch at index %" PRIuPTR, chand, + calld, idx); + } + pending_batch* pending = &calld->pending_batches[idx]; + GPR_ASSERT(pending->batch == nullptr); + pending->batch = batch; + pending->send_ops_cached = false; + if (calld->enable_retries) { + // Update state in calld about pending batches. + // Also check if the batch takes us over the retry buffer limit. + // Note: We don't check the size of trailing metadata here, because + // gRPC clients do not send trailing metadata. + if (batch->send_initial_metadata) { + calld->pending_send_initial_metadata = true; + calld->bytes_buffered_for_retry += grpc_metadata_batch_size( + batch->payload->send_initial_metadata.send_initial_metadata); + } + if (batch->send_message) { + calld->pending_send_message = true; + calld->bytes_buffered_for_retry += + batch->payload->send_message.send_message->length(); + } + if (batch->send_trailing_metadata) { + calld->pending_send_trailing_metadata = true; + } + if (GPR_UNLIKELY(calld->bytes_buffered_for_retry > + chand->per_rpc_retry_buffer_size)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: exceeded retry buffer size, committing", + chand, calld); + } + subchannel_call_retry_state* retry_state = + calld->subchannel_call == nullptr + ? nullptr + : static_cast( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + retry_commit(elem, retry_state); + // If we are not going to retry and have not yet started, pretend + // retries are disabled so that we don't bother with retry overhead. + if (calld->num_attempts_completed == 0) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: disabling retries before first attempt", + chand, calld); + } + calld->enable_retries = false; + } + } + } +} + +static void pending_batch_clear(call_data* calld, pending_batch* pending) { + if (calld->enable_retries) { + if (pending->batch->send_initial_metadata) { + calld->pending_send_initial_metadata = false; + } + if (pending->batch->send_message) { + calld->pending_send_message = false; + } + if (pending->batch->send_trailing_metadata) { + calld->pending_send_trailing_metadata = false; + } + } + pending->batch = nullptr; +} + +// This is called via the call combiner, so access to calld is synchronized. +static void fail_pending_batch_in_call_combiner(void* arg, grpc_error* error) { + grpc_transport_stream_op_batch* batch = + static_cast(arg); + call_data* calld = static_cast(batch->handler_private.extra_arg); + // Note: This will release the call combiner. + grpc_transport_stream_op_batch_finish_with_failure( + batch, GRPC_ERROR_REF(error), calld->call_combiner); +} + +// This is called via the call combiner, so access to calld is synchronized. +// If yield_call_combiner is true, assumes responsibility for yielding +// the call combiner. +static void pending_batches_fail(grpc_call_element* elem, grpc_error* error, + bool yield_call_combiner) { + GPR_ASSERT(error != GRPC_ERROR_NONE); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + size_t num_batches = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + if (calld->pending_batches[i].batch != nullptr) ++num_batches; + } + gpr_log(GPR_INFO, + "chand=%p calld=%p: failing %" PRIuPTR " pending batches: %s", + elem->channel_data, calld, num_batches, grpc_error_string(error)); + } + grpc_core::CallCombinerClosureList closures; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch != nullptr) { + batch->handler_private.extra_arg = calld; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, + fail_pending_batch_in_call_combiner, batch, + grpc_schedule_on_exec_ctx); + closures.Add(&batch->handler_private.closure, GRPC_ERROR_REF(error), + "pending_batches_fail"); + pending_batch_clear(calld, pending); + } + } + if (yield_call_combiner) { + closures.RunClosures(calld->call_combiner); + } else { + closures.RunClosuresWithoutYielding(calld->call_combiner); + } + GRPC_ERROR_UNREF(error); +} + +// This is called via the call combiner, so access to calld is synchronized. +static void resume_pending_batch_in_call_combiner(void* arg, + grpc_error* ignored) { + grpc_transport_stream_op_batch* batch = + static_cast(arg); + grpc_subchannel_call* subchannel_call = + static_cast(batch->handler_private.extra_arg); + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(subchannel_call, batch); +} + +// This is called via the call combiner, so access to calld is synchronized. +static void pending_batches_resume(grpc_call_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (calld->enable_retries) { + start_retriable_subchannel_batches(elem, GRPC_ERROR_NONE); + return; + } + // Retries not enabled; send down batches as-is. + if (grpc_client_channel_trace.enabled()) { + size_t num_batches = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + if (calld->pending_batches[i].batch != nullptr) ++num_batches; + } + gpr_log(GPR_INFO, + "chand=%p calld=%p: starting %" PRIuPTR + " pending batches on subchannel_call=%p", + chand, calld, num_batches, calld->subchannel_call); + } + grpc_core::CallCombinerClosureList closures; + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch != nullptr) { + batch->handler_private.extra_arg = calld->subchannel_call; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, + resume_pending_batch_in_call_combiner, batch, + grpc_schedule_on_exec_ctx); + closures.Add(&batch->handler_private.closure, GRPC_ERROR_NONE, + "pending_batches_resume"); + pending_batch_clear(calld, pending); + } + } + // Note: This will release the call combiner. + closures.RunClosures(calld->call_combiner); +} + +static void maybe_clear_pending_batch(grpc_call_element* elem, + pending_batch* pending) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + grpc_transport_stream_op_batch* batch = pending->batch; + // We clear the pending batch if all of its callbacks have been + // scheduled and reset to nullptr. + if (batch->on_complete == nullptr && + (!batch->recv_initial_metadata || + batch->payload->recv_initial_metadata.recv_initial_metadata_ready == + nullptr) && + (!batch->recv_message || + batch->payload->recv_message.recv_message_ready == nullptr) && + (!batch->recv_trailing_metadata || + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready == + nullptr)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: clearing pending batch", chand, + calld); + } + pending_batch_clear(calld, pending); + } +} + +// Returns a pointer to the first pending batch for which predicate(batch) +// returns true, or null if not found. +template +static pending_batch* pending_batch_find(grpc_call_element* elem, + const char* log_message, + Predicate predicate) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch != nullptr && predicate(batch)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: %s pending batch at index %" PRIuPTR, chand, + calld, log_message, i); + } + return pending; + } + } + return nullptr; +} + +// +// retry code +// + +// Commits the call so that no further retry attempts will be performed. +static void retry_commit(grpc_call_element* elem, + subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (calld->retry_committed) return; + calld->retry_committed = true; + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: committing retries", chand, calld); + } + if (retry_state != nullptr) { + free_cached_send_op_data_after_commit(elem, retry_state); + } +} + +// Starts a retry after appropriate back-off. +static void do_retry(grpc_call_element* elem, + subchannel_call_retry_state* retry_state, + grpc_millis server_pushback_ms) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + GPR_ASSERT(calld->method_params != nullptr); + const ClientChannelMethodParams::RetryPolicy* retry_policy = + calld->method_params->retry_policy(); + GPR_ASSERT(retry_policy != nullptr); + // Reset subchannel call and connected subchannel. + if (calld->subchannel_call != nullptr) { + GRPC_SUBCHANNEL_CALL_UNREF(calld->subchannel_call, + "client_channel_call_retry"); + calld->subchannel_call = nullptr; + } + if (calld->have_request) { + calld->have_request = false; + calld->request.Destroy(); + } + // Compute backoff delay. + grpc_millis next_attempt_time; + if (server_pushback_ms >= 0) { + next_attempt_time = grpc_core::ExecCtx::Get()->Now() + server_pushback_ms; + calld->last_attempt_got_server_pushback = true; + } else { + if (calld->num_attempts_completed == 1 || + calld->last_attempt_got_server_pushback) { + calld->retry_backoff.Init( + grpc_core::BackOff::Options() + .set_initial_backoff(retry_policy->initial_backoff) + .set_multiplier(retry_policy->backoff_multiplier) + .set_jitter(RETRY_BACKOFF_JITTER) + .set_max_backoff(retry_policy->max_backoff)); + calld->last_attempt_got_server_pushback = false; + } + next_attempt_time = calld->retry_backoff->NextAttemptTime(); + } + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: retrying failed call in %" PRId64 " ms", chand, + calld, next_attempt_time - grpc_core::ExecCtx::Get()->Now()); + } + // Schedule retry after computed delay. + GRPC_CLOSURE_INIT(&calld->pick_closure, start_pick_locked, elem, + grpc_combiner_scheduler(chand->combiner)); + grpc_timer_init(&calld->retry_timer, next_attempt_time, &calld->pick_closure); + // Update bookkeeping. + if (retry_state != nullptr) retry_state->retry_dispatched = true; +} + +// Returns true if the call is being retried. +static bool maybe_retry(grpc_call_element* elem, + subchannel_batch_data* batch_data, + grpc_status_code status, + grpc_mdelem* server_pushback_md) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + // Get retry policy. + if (calld->method_params == nullptr) return false; + const ClientChannelMethodParams::RetryPolicy* retry_policy = + calld->method_params->retry_policy(); + if (retry_policy == nullptr) return false; + // If we've already dispatched a retry from this call, return true. + // This catches the case where the batch has multiple callbacks + // (i.e., it includes either recv_message or recv_initial_metadata). + subchannel_call_retry_state* retry_state = nullptr; + if (batch_data != nullptr) { + retry_state = static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + if (retry_state->retry_dispatched) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: retry already dispatched", chand, + calld); + } + return true; + } + } + // Check status. + if (GPR_LIKELY(status == GRPC_STATUS_OK)) { + if (calld->retry_throttle_data != nullptr) { + calld->retry_throttle_data->RecordSuccess(); + } + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: call succeeded", chand, calld); + } + return false; + } + // Status is not OK. Check whether the status is retryable. + if (!retry_policy->retryable_status_codes.Contains(status)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: status %s not configured as retryable", chand, + calld, grpc_status_code_to_string(status)); + } + return false; + } + // Record the failure and check whether retries are throttled. + // Note that it's important for this check to come after the status + // code check above, since we should only record failures whose statuses + // match the configured retryable status codes, so that we don't count + // things like failures due to malformed requests (INVALID_ARGUMENT). + // Conversely, it's important for this to come before the remaining + // checks, so that we don't fail to record failures due to other factors. + if (calld->retry_throttle_data != nullptr && + !calld->retry_throttle_data->RecordFailure()) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: retries throttled", chand, calld); + } + return false; + } + // Check whether the call is committed. + if (calld->retry_committed) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: retries already committed", chand, + calld); + } + return false; + } + // Check whether we have retries remaining. + ++calld->num_attempts_completed; + if (calld->num_attempts_completed >= retry_policy->max_attempts) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: exceeded %d retry attempts", chand, + calld, retry_policy->max_attempts); + } + return false; + } + // If the call was cancelled from the surface, don't retry. + if (calld->cancel_error != GRPC_ERROR_NONE) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: call cancelled from surface, not retrying", + chand, calld); + } + return false; + } + // Check server push-back. + grpc_millis server_pushback_ms = -1; + if (server_pushback_md != nullptr) { + // If the value is "-1" or any other unparseable string, we do not retry. + uint32_t ms; + if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(*server_pushback_md), &ms)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: not retrying due to server push-back", + chand, calld); + } + return false; + } else { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: server push-back: retry in %u ms", + chand, calld, ms); + } + server_pushback_ms = (grpc_millis)ms; + } + } + do_retry(elem, retry_state, server_pushback_ms); + return true; +} + +// +// subchannel_batch_data +// + +namespace { + +subchannel_batch_data::subchannel_batch_data(grpc_call_element* elem, + call_data* calld, int refcount, + bool set_on_complete) + : elem(elem), + subchannel_call(GRPC_SUBCHANNEL_CALL_REF(calld->subchannel_call, + "batch_data_create")) { + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + batch.payload = &retry_state->batch_payload; + gpr_ref_init(&refs, refcount); + if (set_on_complete) { + GRPC_CLOSURE_INIT(&on_complete, ::on_complete, this, + grpc_schedule_on_exec_ctx); + batch.on_complete = &on_complete; + } + GRPC_CALL_STACK_REF(calld->owning_call, "batch_data"); +} + +void subchannel_batch_data::destroy() { + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data(subchannel_call)); + if (batch.send_initial_metadata) { + grpc_metadata_batch_destroy(&retry_state->send_initial_metadata); + } + if (batch.send_trailing_metadata) { + grpc_metadata_batch_destroy(&retry_state->send_trailing_metadata); + } + if (batch.recv_initial_metadata) { + grpc_metadata_batch_destroy(&retry_state->recv_initial_metadata); + } + if (batch.recv_trailing_metadata) { + grpc_metadata_batch_destroy(&retry_state->recv_trailing_metadata); + } + GRPC_SUBCHANNEL_CALL_UNREF(subchannel_call, "batch_data_unref"); + call_data* calld = static_cast(elem->call_data); + GRPC_CALL_STACK_UNREF(calld->owning_call, "batch_data"); +} + +} // namespace + +// Creates a subchannel_batch_data object on the call's arena with the +// specified refcount. If set_on_complete is true, the batch's +// on_complete callback will be set to point to on_complete(); +// otherwise, the batch's on_complete callback will be null. +static subchannel_batch_data* batch_data_create(grpc_call_element* elem, + int refcount, + bool set_on_complete) { + call_data* calld = static_cast(elem->call_data); + subchannel_batch_data* batch_data = + new (gpr_arena_alloc(calld->arena, sizeof(*batch_data))) + subchannel_batch_data(elem, calld, refcount, set_on_complete); + return batch_data; +} + +static void batch_data_unref(subchannel_batch_data* batch_data) { + if (gpr_unref(&batch_data->refs)) { + batch_data->destroy(); + } +} + +// +// recv_initial_metadata callback handling +// + +// Invokes recv_initial_metadata_ready for a subchannel batch. +static void invoke_recv_initial_metadata_callback(void* arg, + grpc_error* error) { + subchannel_batch_data* batch_data = static_cast(arg); + // Find pending batch. + pending_batch* pending = pending_batch_find( + batch_data->elem, "invoking recv_initial_metadata_ready for", + [](grpc_transport_stream_op_batch* batch) { + return batch->recv_initial_metadata && + batch->payload->recv_initial_metadata + .recv_initial_metadata_ready != nullptr; + }); + GPR_ASSERT(pending != nullptr); + // Return metadata. + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + grpc_metadata_batch_move( + &retry_state->recv_initial_metadata, + pending->batch->payload->recv_initial_metadata.recv_initial_metadata); + // Update bookkeeping. + // Note: Need to do this before invoking the callback, since invoking + // the callback will result in yielding the call combiner. + grpc_closure* recv_initial_metadata_ready = + pending->batch->payload->recv_initial_metadata + .recv_initial_metadata_ready; + pending->batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + nullptr; + maybe_clear_pending_batch(batch_data->elem, pending); + batch_data_unref(batch_data); + // Invoke callback. + GRPC_CLOSURE_RUN(recv_initial_metadata_ready, GRPC_ERROR_REF(error)); +} + +// Intercepts recv_initial_metadata_ready callback for retries. +// Commits the call and returns the initial metadata up the stack. +static void recv_initial_metadata_ready(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: got recv_initial_metadata_ready, error=%s", + chand, calld, grpc_error_string(error)); + } + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + retry_state->completed_recv_initial_metadata = true; + // If a retry was already dispatched, then we're not going to use the + // result of this recv_initial_metadata op, so do nothing. + if (retry_state->retry_dispatched) { + GRPC_CALL_COMBINER_STOP( + calld->call_combiner, + "recv_initial_metadata_ready after retry dispatched"); + return; + } + // If we got an error or a Trailers-Only response and have not yet gotten + // the recv_trailing_metadata_ready callback, then defer propagating this + // callback back to the surface. We can evaluate whether to retry when + // recv_trailing_metadata comes back. + if (GPR_UNLIKELY((retry_state->trailing_metadata_available || + error != GRPC_ERROR_NONE) && + !retry_state->completed_recv_trailing_metadata)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: deferring recv_initial_metadata_ready " + "(Trailers-Only)", + chand, calld); + } + retry_state->recv_initial_metadata_ready_deferred_batch = batch_data; + retry_state->recv_initial_metadata_error = GRPC_ERROR_REF(error); + if (!retry_state->started_recv_trailing_metadata) { + // recv_trailing_metadata not yet started by application; start it + // ourselves to get status. + start_internal_recv_trailing_metadata(elem); + } else { + GRPC_CALL_COMBINER_STOP( + calld->call_combiner, + "recv_initial_metadata_ready trailers-only or error"); + } + return; + } + // Received valid initial metadata, so commit the call. + retry_commit(elem, retry_state); + // Invoke the callback to return the result to the surface. + // Manually invoking a callback function; it does not take ownership of error. + invoke_recv_initial_metadata_callback(batch_data, error); +} + +// +// recv_message callback handling +// + +// Invokes recv_message_ready for a subchannel batch. +static void invoke_recv_message_callback(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast(arg); + // Find pending op. + pending_batch* pending = pending_batch_find( + batch_data->elem, "invoking recv_message_ready for", + [](grpc_transport_stream_op_batch* batch) { + return batch->recv_message && + batch->payload->recv_message.recv_message_ready != nullptr; + }); + GPR_ASSERT(pending != nullptr); + // Return payload. + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + *pending->batch->payload->recv_message.recv_message = + std::move(retry_state->recv_message); + // Update bookkeeping. + // Note: Need to do this before invoking the callback, since invoking + // the callback will result in yielding the call combiner. + grpc_closure* recv_message_ready = + pending->batch->payload->recv_message.recv_message_ready; + pending->batch->payload->recv_message.recv_message_ready = nullptr; + maybe_clear_pending_batch(batch_data->elem, pending); + batch_data_unref(batch_data); + // Invoke callback. + GRPC_CLOSURE_RUN(recv_message_ready, GRPC_ERROR_REF(error)); +} + +// Intercepts recv_message_ready callback for retries. +// Commits the call and returns the message up the stack. +static void recv_message_ready(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: got recv_message_ready, error=%s", + chand, calld, grpc_error_string(error)); + } + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + ++retry_state->completed_recv_message_count; + // If a retry was already dispatched, then we're not going to use the + // result of this recv_message op, so do nothing. + if (retry_state->retry_dispatched) { + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "recv_message_ready after retry dispatched"); + return; + } + // If we got an error or the payload was nullptr and we have not yet gotten + // the recv_trailing_metadata_ready callback, then defer propagating this + // callback back to the surface. We can evaluate whether to retry when + // recv_trailing_metadata comes back. + if (GPR_UNLIKELY( + (retry_state->recv_message == nullptr || error != GRPC_ERROR_NONE) && + !retry_state->completed_recv_trailing_metadata)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: deferring recv_message_ready (nullptr " + "message and recv_trailing_metadata pending)", + chand, calld); + } + retry_state->recv_message_ready_deferred_batch = batch_data; + retry_state->recv_message_error = GRPC_ERROR_REF(error); + if (!retry_state->started_recv_trailing_metadata) { + // recv_trailing_metadata not yet started by application; start it + // ourselves to get status. + start_internal_recv_trailing_metadata(elem); + } else { + GRPC_CALL_COMBINER_STOP(calld->call_combiner, "recv_message_ready null"); + } + return; + } + // Received a valid message, so commit the call. + retry_commit(elem, retry_state); + // Invoke the callback to return the result to the surface. + // Manually invoking a callback function; it does not take ownership of error. + invoke_recv_message_callback(batch_data, error); +} + +// +// recv_trailing_metadata handling +// + +// Sets *status and *server_pushback_md based on md_batch and error. +// Only sets *server_pushback_md if server_pushback_md != nullptr. +static void get_call_status(grpc_call_element* elem, + grpc_metadata_batch* md_batch, grpc_error* error, + grpc_status_code* status, + grpc_mdelem** server_pushback_md) { + call_data* calld = static_cast(elem->call_data); + if (error != GRPC_ERROR_NONE) { + grpc_error_get_status(error, calld->deadline, status, nullptr, nullptr, + nullptr); + } else { + GPR_ASSERT(md_batch->idx.named.grpc_status != nullptr); + *status = + grpc_get_status_code_from_metadata(md_batch->idx.named.grpc_status->md); + if (server_pushback_md != nullptr && + md_batch->idx.named.grpc_retry_pushback_ms != nullptr) { + *server_pushback_md = &md_batch->idx.named.grpc_retry_pushback_ms->md; + } + } + GRPC_ERROR_UNREF(error); +} + +// Adds recv_trailing_metadata_ready closure to closures. +static void add_closure_for_recv_trailing_metadata_ready( + grpc_call_element* elem, subchannel_batch_data* batch_data, + grpc_error* error, grpc_core::CallCombinerClosureList* closures) { + // Find pending batch. + pending_batch* pending = pending_batch_find( + elem, "invoking recv_trailing_metadata for", + [](grpc_transport_stream_op_batch* batch) { + return batch->recv_trailing_metadata && + batch->payload->recv_trailing_metadata + .recv_trailing_metadata_ready != nullptr; + }); + // If we generated the recv_trailing_metadata op internally via + // start_internal_recv_trailing_metadata(), then there will be no + // pending batch. + if (pending == nullptr) { + GRPC_ERROR_UNREF(error); + return; + } + // Return metadata. + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + grpc_metadata_batch_move( + &retry_state->recv_trailing_metadata, + pending->batch->payload->recv_trailing_metadata.recv_trailing_metadata); + // Add closure. + closures->Add(pending->batch->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + error, "recv_trailing_metadata_ready for pending batch"); + // Update bookkeeping. + pending->batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + nullptr; + maybe_clear_pending_batch(elem, pending); +} + +// Adds any necessary closures for deferred recv_initial_metadata and +// recv_message callbacks to closures. +static void add_closures_for_deferred_recv_callbacks( + subchannel_batch_data* batch_data, subchannel_call_retry_state* retry_state, + grpc_core::CallCombinerClosureList* closures) { + if (batch_data->batch.recv_trailing_metadata) { + // Add closure for deferred recv_initial_metadata_ready. + if (GPR_UNLIKELY(retry_state->recv_initial_metadata_ready_deferred_batch != + nullptr)) { + GRPC_CLOSURE_INIT(&retry_state->recv_initial_metadata_ready, + invoke_recv_initial_metadata_callback, + retry_state->recv_initial_metadata_ready_deferred_batch, + grpc_schedule_on_exec_ctx); + closures->Add(&retry_state->recv_initial_metadata_ready, + retry_state->recv_initial_metadata_error, + "resuming recv_initial_metadata_ready"); + retry_state->recv_initial_metadata_ready_deferred_batch = nullptr; + } + // Add closure for deferred recv_message_ready. + if (GPR_UNLIKELY(retry_state->recv_message_ready_deferred_batch != + nullptr)) { + GRPC_CLOSURE_INIT(&retry_state->recv_message_ready, + invoke_recv_message_callback, + retry_state->recv_message_ready_deferred_batch, + grpc_schedule_on_exec_ctx); + closures->Add(&retry_state->recv_message_ready, + retry_state->recv_message_error, + "resuming recv_message_ready"); + retry_state->recv_message_ready_deferred_batch = nullptr; + } + } +} + +// Returns true if any op in the batch was not yet started. +// Only looks at send ops, since recv ops are always started immediately. +static bool pending_batch_is_unstarted( + pending_batch* pending, call_data* calld, + subchannel_call_retry_state* retry_state) { + if (pending->batch == nullptr || pending->batch->on_complete == nullptr) { + return false; + } + if (pending->batch->send_initial_metadata && + !retry_state->started_send_initial_metadata) { + return true; + } + if (pending->batch->send_message && + retry_state->started_send_message_count < calld->send_messages.size()) { + return true; + } + if (pending->batch->send_trailing_metadata && + !retry_state->started_send_trailing_metadata) { + return true; + } + return false; +} + +// For any pending batch containing an op that has not yet been started, +// adds the pending batch's completion closures to closures. +static void add_closures_to_fail_unstarted_pending_batches( + grpc_call_element* elem, subchannel_call_retry_state* retry_state, + grpc_error* error, grpc_core::CallCombinerClosureList* closures) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + if (pending_batch_is_unstarted(pending, calld, retry_state)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: failing unstarted pending batch at index " + "%" PRIuPTR, + chand, calld, i); + } + closures->Add(pending->batch->on_complete, GRPC_ERROR_REF(error), + "failing on_complete for pending batch"); + pending->batch->on_complete = nullptr; + maybe_clear_pending_batch(elem, pending); + } + } + GRPC_ERROR_UNREF(error); +} + +// Runs necessary closures upon completion of a call attempt. +static void run_closures_for_completed_call(subchannel_batch_data* batch_data, + grpc_error* error) { + grpc_call_element* elem = batch_data->elem; + call_data* calld = static_cast(elem->call_data); + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + // Construct list of closures to execute. + grpc_core::CallCombinerClosureList closures; + // First, add closure for recv_trailing_metadata_ready. + add_closure_for_recv_trailing_metadata_ready( + elem, batch_data, GRPC_ERROR_REF(error), &closures); + // If there are deferred recv_initial_metadata_ready or recv_message_ready + // callbacks, add them to closures. + add_closures_for_deferred_recv_callbacks(batch_data, retry_state, &closures); + // Add closures to fail any pending batches that have not yet been started. + add_closures_to_fail_unstarted_pending_batches( + elem, retry_state, GRPC_ERROR_REF(error), &closures); + // Don't need batch_data anymore. + batch_data_unref(batch_data); + // Schedule all of the closures identified above. + // Note: This will release the call combiner. + closures.RunClosures(calld->call_combiner); + GRPC_ERROR_UNREF(error); +} + +// Intercepts recv_trailing_metadata_ready callback for retries. +// Commits the call and returns the trailing metadata up the stack. +static void recv_trailing_metadata_ready(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: got recv_trailing_metadata_ready, error=%s", + chand, calld, grpc_error_string(error)); + } + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + retry_state->completed_recv_trailing_metadata = true; + // Get the call's status and check for server pushback metadata. + grpc_status_code status = GRPC_STATUS_OK; + grpc_mdelem* server_pushback_md = nullptr; + grpc_metadata_batch* md_batch = + batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata; + get_call_status(elem, md_batch, GRPC_ERROR_REF(error), &status, + &server_pushback_md); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: call finished, status=%s", chand, + calld, grpc_status_code_to_string(status)); + } + // Check if we should retry. + if (maybe_retry(elem, batch_data, status, server_pushback_md)) { + // Unref batch_data for deferred recv_initial_metadata_ready or + // recv_message_ready callbacks, if any. + if (retry_state->recv_initial_metadata_ready_deferred_batch != nullptr) { + batch_data_unref(batch_data); + GRPC_ERROR_UNREF(retry_state->recv_initial_metadata_error); + } + if (retry_state->recv_message_ready_deferred_batch != nullptr) { + batch_data_unref(batch_data); + GRPC_ERROR_UNREF(retry_state->recv_message_error); + } + batch_data_unref(batch_data); + return; + } + // Not retrying, so commit the call. + retry_commit(elem, retry_state); + // Run any necessary closures. + run_closures_for_completed_call(batch_data, GRPC_ERROR_REF(error)); +} + +// +// on_complete callback handling +// + +// Adds the on_complete closure for the pending batch completed in +// batch_data to closures. +static void add_closure_for_completed_pending_batch( + grpc_call_element* elem, subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state, grpc_error* error, + grpc_core::CallCombinerClosureList* closures) { + pending_batch* pending = pending_batch_find( + elem, "completed", [batch_data](grpc_transport_stream_op_batch* batch) { + // Match the pending batch with the same set of send ops as the + // subchannel batch we've just completed. + return batch->on_complete != nullptr && + batch_data->batch.send_initial_metadata == + batch->send_initial_metadata && + batch_data->batch.send_message == batch->send_message && + batch_data->batch.send_trailing_metadata == + batch->send_trailing_metadata; + }); + // If batch_data is a replay batch, then there will be no pending + // batch to complete. + if (pending == nullptr) { + GRPC_ERROR_UNREF(error); + return; + } + // Add closure. + closures->Add(pending->batch->on_complete, error, + "on_complete for pending batch"); + pending->batch->on_complete = nullptr; + maybe_clear_pending_batch(elem, pending); +} + +// If there are any cached ops to replay or pending ops to start on the +// subchannel call, adds a closure to closures to invoke +// start_retriable_subchannel_batches(). +static void add_closures_for_replay_or_pending_send_ops( + grpc_call_element* elem, subchannel_batch_data* batch_data, + subchannel_call_retry_state* retry_state, + grpc_core::CallCombinerClosureList* closures) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + bool have_pending_send_message_ops = + retry_state->started_send_message_count < calld->send_messages.size(); + bool have_pending_send_trailing_metadata_op = + calld->seen_send_trailing_metadata && + !retry_state->started_send_trailing_metadata; + if (!have_pending_send_message_ops && + !have_pending_send_trailing_metadata_op) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch == nullptr || pending->send_ops_cached) continue; + if (batch->send_message) have_pending_send_message_ops = true; + if (batch->send_trailing_metadata) { + have_pending_send_trailing_metadata_op = true; + } + } + } + if (have_pending_send_message_ops || have_pending_send_trailing_metadata_op) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: starting next batch for pending send op(s)", + chand, calld); + } + GRPC_CLOSURE_INIT(&batch_data->batch.handler_private.closure, + start_retriable_subchannel_batches, elem, + grpc_schedule_on_exec_ctx); + closures->Add(&batch_data->batch.handler_private.closure, GRPC_ERROR_NONE, + "starting next batch for send_* op(s)"); + } +} + +// Callback used to intercept on_complete from subchannel calls. +// Called only when retries are enabled. +static void on_complete(void* arg, grpc_error* error) { + subchannel_batch_data* batch_data = static_cast(arg); + grpc_call_element* elem = batch_data->elem; + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + char* batch_str = grpc_transport_stream_op_batch_string(&batch_data->batch); + gpr_log(GPR_INFO, "chand=%p calld=%p: got on_complete, error=%s, batch=%s", + chand, calld, grpc_error_string(error), batch_str); + gpr_free(batch_str); + } + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + batch_data->subchannel_call)); + // Update bookkeeping in retry_state. + if (batch_data->batch.send_initial_metadata) { + retry_state->completed_send_initial_metadata = true; + } + if (batch_data->batch.send_message) { + ++retry_state->completed_send_message_count; + } + if (batch_data->batch.send_trailing_metadata) { + retry_state->completed_send_trailing_metadata = true; + } + // If the call is committed, free cached data for send ops that we've just + // completed. + if (calld->retry_committed) { + free_cached_send_op_data_for_completed_batch(elem, batch_data, retry_state); + } + // Construct list of closures to execute. + grpc_core::CallCombinerClosureList closures; + // If a retry was already dispatched, that means we saw + // recv_trailing_metadata before this, so we do nothing here. + // Otherwise, invoke the callback to return the result to the surface. + if (!retry_state->retry_dispatched) { + // Add closure for the completed pending batch, if any. + add_closure_for_completed_pending_batch(elem, batch_data, retry_state, + GRPC_ERROR_REF(error), &closures); + // If needed, add a callback to start any replay or pending send ops on + // the subchannel call. + if (!retry_state->completed_recv_trailing_metadata) { + add_closures_for_replay_or_pending_send_ops(elem, batch_data, retry_state, + &closures); + } + } + // Track number of pending subchannel send batches and determine if this + // was the last one. + --calld->num_pending_retriable_subchannel_send_batches; + const bool last_send_batch_complete = + calld->num_pending_retriable_subchannel_send_batches == 0; + // Don't need batch_data anymore. + batch_data_unref(batch_data); + // Schedule all of the closures identified above. + // Note: This yeilds the call combiner. + closures.RunClosures(calld->call_combiner); + // If this was the last subchannel send batch, unref the call stack. + if (last_send_batch_complete) { + GRPC_CALL_STACK_UNREF(calld->owning_call, "subchannel_send_batches"); + } +} + +// +// subchannel batch construction +// + +// Helper function used to start a subchannel batch in the call combiner. +static void start_batch_in_call_combiner(void* arg, grpc_error* ignored) { + grpc_transport_stream_op_batch* batch = + static_cast(arg); + grpc_subchannel_call* subchannel_call = + static_cast(batch->handler_private.extra_arg); + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(subchannel_call, batch); +} + +// Adds a closure to closures that will execute batch in the call combiner. +static void add_closure_for_subchannel_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch, + grpc_core::CallCombinerClosureList* closures) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + batch->handler_private.extra_arg = calld->subchannel_call; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, + start_batch_in_call_combiner, batch, + grpc_schedule_on_exec_ctx); + if (grpc_client_channel_trace.enabled()) { + char* batch_str = grpc_transport_stream_op_batch_string(batch); + gpr_log(GPR_INFO, "chand=%p calld=%p: starting subchannel batch: %s", chand, + calld, batch_str); + gpr_free(batch_str); + } + closures->Add(&batch->handler_private.closure, GRPC_ERROR_NONE, + "start_subchannel_batch"); +} + +// Adds retriable send_initial_metadata op to batch_data. +static void add_retriable_send_initial_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + // Maps the number of retries to the corresponding metadata value slice. + static const grpc_slice* retry_count_strings[] = { + &GRPC_MDSTR_1, &GRPC_MDSTR_2, &GRPC_MDSTR_3, &GRPC_MDSTR_4}; + // We need to make a copy of the metadata batch for each attempt, since + // the filters in the subchannel stack may modify this batch, and we don't + // want those modifications to be passed forward to subsequent attempts. + // + // If we've already completed one or more attempts, add the + // grpc-retry-attempts header. + retry_state->send_initial_metadata_storage = + static_cast(gpr_arena_alloc( + calld->arena, sizeof(grpc_linked_mdelem) * + (calld->send_initial_metadata.list.count + + (calld->num_attempts_completed > 0)))); + grpc_metadata_batch_copy(&calld->send_initial_metadata, + &retry_state->send_initial_metadata, + retry_state->send_initial_metadata_storage); + if (GPR_UNLIKELY(retry_state->send_initial_metadata.idx.named + .grpc_previous_rpc_attempts != nullptr)) { + grpc_metadata_batch_remove(&retry_state->send_initial_metadata, + retry_state->send_initial_metadata.idx.named + .grpc_previous_rpc_attempts); + } + if (GPR_UNLIKELY(calld->num_attempts_completed > 0)) { + grpc_mdelem retry_md = grpc_mdelem_create( + GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS, + *retry_count_strings[calld->num_attempts_completed - 1], nullptr); + grpc_error* error = grpc_metadata_batch_add_tail( + &retry_state->send_initial_metadata, + &retry_state->send_initial_metadata_storage[calld->send_initial_metadata + .list.count], + retry_md); + if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) { + gpr_log(GPR_ERROR, "error adding retry metadata: %s", + grpc_error_string(error)); + GPR_ASSERT(false); + } + } + retry_state->started_send_initial_metadata = true; + batch_data->batch.send_initial_metadata = true; + batch_data->batch.payload->send_initial_metadata.send_initial_metadata = + &retry_state->send_initial_metadata; + batch_data->batch.payload->send_initial_metadata.send_initial_metadata_flags = + calld->send_initial_metadata_flags; + batch_data->batch.payload->send_initial_metadata.peer_string = + calld->peer_string; +} + +// Adds retriable send_message op to batch_data. +static void add_retriable_send_message_op( + grpc_call_element* elem, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: starting calld->send_messages[%" PRIuPTR "]", + chand, calld, retry_state->started_send_message_count); + } + grpc_core::ByteStreamCache* cache = + calld->send_messages[retry_state->started_send_message_count]; + ++retry_state->started_send_message_count; + retry_state->send_message.Init(cache); + batch_data->batch.send_message = true; + batch_data->batch.payload->send_message.send_message.reset( + retry_state->send_message.get()); +} + +// Adds retriable send_trailing_metadata op to batch_data. +static void add_retriable_send_trailing_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + // We need to make a copy of the metadata batch for each attempt, since + // the filters in the subchannel stack may modify this batch, and we don't + // want those modifications to be passed forward to subsequent attempts. + retry_state->send_trailing_metadata_storage = + static_cast(gpr_arena_alloc( + calld->arena, sizeof(grpc_linked_mdelem) * + calld->send_trailing_metadata.list.count)); + grpc_metadata_batch_copy(&calld->send_trailing_metadata, + &retry_state->send_trailing_metadata, + retry_state->send_trailing_metadata_storage); + retry_state->started_send_trailing_metadata = true; + batch_data->batch.send_trailing_metadata = true; + batch_data->batch.payload->send_trailing_metadata.send_trailing_metadata = + &retry_state->send_trailing_metadata; +} + +// Adds retriable recv_initial_metadata op to batch_data. +static void add_retriable_recv_initial_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + retry_state->started_recv_initial_metadata = true; + batch_data->batch.recv_initial_metadata = true; + grpc_metadata_batch_init(&retry_state->recv_initial_metadata); + batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata = + &retry_state->recv_initial_metadata; + batch_data->batch.payload->recv_initial_metadata.trailing_metadata_available = + &retry_state->trailing_metadata_available; + GRPC_CLOSURE_INIT(&retry_state->recv_initial_metadata_ready, + recv_initial_metadata_ready, batch_data, + grpc_schedule_on_exec_ctx); + batch_data->batch.payload->recv_initial_metadata.recv_initial_metadata_ready = + &retry_state->recv_initial_metadata_ready; +} + +// Adds retriable recv_message op to batch_data. +static void add_retriable_recv_message_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + ++retry_state->started_recv_message_count; + batch_data->batch.recv_message = true; + batch_data->batch.payload->recv_message.recv_message = + &retry_state->recv_message; + GRPC_CLOSURE_INIT(&retry_state->recv_message_ready, recv_message_ready, + batch_data, grpc_schedule_on_exec_ctx); + batch_data->batch.payload->recv_message.recv_message_ready = + &retry_state->recv_message_ready; +} + +// Adds retriable recv_trailing_metadata op to batch_data. +static void add_retriable_recv_trailing_metadata_op( + call_data* calld, subchannel_call_retry_state* retry_state, + subchannel_batch_data* batch_data) { + retry_state->started_recv_trailing_metadata = true; + batch_data->batch.recv_trailing_metadata = true; + grpc_metadata_batch_init(&retry_state->recv_trailing_metadata); + batch_data->batch.payload->recv_trailing_metadata.recv_trailing_metadata = + &retry_state->recv_trailing_metadata; + batch_data->batch.payload->recv_trailing_metadata.collect_stats = + &retry_state->collect_stats; + GRPC_CLOSURE_INIT(&retry_state->recv_trailing_metadata_ready, + recv_trailing_metadata_ready, batch_data, + grpc_schedule_on_exec_ctx); + batch_data->batch.payload->recv_trailing_metadata + .recv_trailing_metadata_ready = + &retry_state->recv_trailing_metadata_ready; +} + +// Helper function used to start a recv_trailing_metadata batch. This +// is used in the case where a recv_initial_metadata or recv_message +// op fails in a way that we know the call is over but when the application +// has not yet started its own recv_trailing_metadata op. +static void start_internal_recv_trailing_metadata(grpc_call_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: call failed but recv_trailing_metadata not " + "started; starting it internally", + chand, calld); + } + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + // Create batch_data with 2 refs, since this batch will be unreffed twice: + // once for the recv_trailing_metadata_ready callback when the subchannel + // batch returns, and again when we actually get a recv_trailing_metadata + // op from the surface. + subchannel_batch_data* batch_data = + batch_data_create(elem, 2, false /* set_on_complete */); + add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data); + retry_state->recv_trailing_metadata_internal_batch = batch_data; + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(calld->subchannel_call, &batch_data->batch); +} + +// If there are any cached send ops that need to be replayed on the +// current subchannel call, creates and returns a new subchannel batch +// to replay those ops. Otherwise, returns nullptr. +static subchannel_batch_data* maybe_create_subchannel_batch_for_replay( + grpc_call_element* elem, subchannel_call_retry_state* retry_state) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + subchannel_batch_data* replay_batch_data = nullptr; + // send_initial_metadata. + if (calld->seen_send_initial_metadata && + !retry_state->started_send_initial_metadata && + !calld->pending_send_initial_metadata) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: replaying previously completed " + "send_initial_metadata op", + chand, calld); + } + replay_batch_data = batch_data_create(elem, 1, true /* set_on_complete */); + add_retriable_send_initial_metadata_op(calld, retry_state, + replay_batch_data); + } + // send_message. + // Note that we can only have one send_message op in flight at a time. + if (retry_state->started_send_message_count < calld->send_messages.size() && + retry_state->started_send_message_count == + retry_state->completed_send_message_count && + !calld->pending_send_message) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: replaying previously completed " + "send_message op", + chand, calld); + } + if (replay_batch_data == nullptr) { + replay_batch_data = + batch_data_create(elem, 1, true /* set_on_complete */); + } + add_retriable_send_message_op(elem, retry_state, replay_batch_data); + } + // send_trailing_metadata. + // Note that we only add this op if we have no more send_message ops + // to start, since we can't send down any more send_message ops after + // send_trailing_metadata. + if (calld->seen_send_trailing_metadata && + retry_state->started_send_message_count == calld->send_messages.size() && + !retry_state->started_send_trailing_metadata && + !calld->pending_send_trailing_metadata) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: replaying previously completed " + "send_trailing_metadata op", + chand, calld); + } + if (replay_batch_data == nullptr) { + replay_batch_data = + batch_data_create(elem, 1, true /* set_on_complete */); + } + add_retriable_send_trailing_metadata_op(calld, retry_state, + replay_batch_data); + } + return replay_batch_data; +} + +// Adds subchannel batches for pending batches to batches, updating +// *num_batches as needed. +static void add_subchannel_batches_for_pending_batches( + grpc_call_element* elem, subchannel_call_retry_state* retry_state, + grpc_core::CallCombinerClosureList* closures) { + call_data* calld = static_cast(elem->call_data); + for (size_t i = 0; i < GPR_ARRAY_SIZE(calld->pending_batches); ++i) { + pending_batch* pending = &calld->pending_batches[i]; + grpc_transport_stream_op_batch* batch = pending->batch; + if (batch == nullptr) continue; + // Skip any batch that either (a) has already been started on this + // subchannel call or (b) we can't start yet because we're still + // replaying send ops that need to be completed first. + // TODO(roth): Note that if any one op in the batch can't be sent + // yet due to ops that we're replaying, we don't start any of the ops + // in the batch. This is probably okay, but it could conceivably + // lead to increased latency in some cases -- e.g., we could delay + // starting a recv op due to it being in the same batch with a send + // op. If/when we revamp the callback protocol in + // transport_stream_op_batch, we may be able to fix this. + if (batch->send_initial_metadata && + retry_state->started_send_initial_metadata) { + continue; + } + if (batch->send_message && retry_state->completed_send_message_count < + retry_state->started_send_message_count) { + continue; + } + // Note that we only start send_trailing_metadata if we have no more + // send_message ops to start, since we can't send down any more + // send_message ops after send_trailing_metadata. + if (batch->send_trailing_metadata && + (retry_state->started_send_message_count + batch->send_message < + calld->send_messages.size() || + retry_state->started_send_trailing_metadata)) { + continue; + } + if (batch->recv_initial_metadata && + retry_state->started_recv_initial_metadata) { + continue; + } + if (batch->recv_message && retry_state->completed_recv_message_count < + retry_state->started_recv_message_count) { + continue; + } + if (batch->recv_trailing_metadata && + retry_state->started_recv_trailing_metadata) { + // If we previously completed a recv_trailing_metadata op + // initiated by start_internal_recv_trailing_metadata(), use the + // result of that instead of trying to re-start this op. + if (GPR_UNLIKELY((retry_state->recv_trailing_metadata_internal_batch != + nullptr))) { + // If the batch completed, then trigger the completion callback + // directly, so that we return the previously returned results to + // the application. Otherwise, just unref the internally + // started subchannel batch, since we'll propagate the + // completion when it completes. + if (retry_state->completed_recv_trailing_metadata) { + // Batches containing recv_trailing_metadata always succeed. + closures->Add( + &retry_state->recv_trailing_metadata_ready, GRPC_ERROR_NONE, + "re-executing recv_trailing_metadata_ready to propagate " + "internally triggered result"); + } else { + batch_data_unref(retry_state->recv_trailing_metadata_internal_batch); + } + retry_state->recv_trailing_metadata_internal_batch = nullptr; + } + continue; + } + // If we're not retrying, just send the batch as-is. + if (calld->method_params == nullptr || + calld->method_params->retry_policy() == nullptr || + calld->retry_committed) { + add_closure_for_subchannel_batch(elem, batch, closures); + pending_batch_clear(calld, pending); + continue; + } + // Create batch with the right number of callbacks. + const bool has_send_ops = batch->send_initial_metadata || + batch->send_message || + batch->send_trailing_metadata; + const int num_callbacks = has_send_ops + batch->recv_initial_metadata + + batch->recv_message + + batch->recv_trailing_metadata; + subchannel_batch_data* batch_data = batch_data_create( + elem, num_callbacks, has_send_ops /* set_on_complete */); + // Cache send ops if needed. + maybe_cache_send_ops_for_batch(calld, pending); + // send_initial_metadata. + if (batch->send_initial_metadata) { + add_retriable_send_initial_metadata_op(calld, retry_state, batch_data); + } + // send_message. + if (batch->send_message) { + add_retriable_send_message_op(elem, retry_state, batch_data); + } + // send_trailing_metadata. + if (batch->send_trailing_metadata) { + add_retriable_send_trailing_metadata_op(calld, retry_state, batch_data); + } + // recv_initial_metadata. + if (batch->recv_initial_metadata) { + // recv_flags is only used on the server side. + GPR_ASSERT(batch->payload->recv_initial_metadata.recv_flags == nullptr); + add_retriable_recv_initial_metadata_op(calld, retry_state, batch_data); + } + // recv_message. + if (batch->recv_message) { + add_retriable_recv_message_op(calld, retry_state, batch_data); + } + // recv_trailing_metadata. + if (batch->recv_trailing_metadata) { + add_retriable_recv_trailing_metadata_op(calld, retry_state, batch_data); + } + add_closure_for_subchannel_batch(elem, &batch_data->batch, closures); + // Track number of pending subchannel send batches. + // If this is the first one, take a ref to the call stack. + if (batch->send_initial_metadata || batch->send_message || + batch->send_trailing_metadata) { + if (calld->num_pending_retriable_subchannel_send_batches == 0) { + GRPC_CALL_STACK_REF(calld->owning_call, "subchannel_send_batches"); + } + ++calld->num_pending_retriable_subchannel_send_batches; + } + } +} + +// Constructs and starts whatever subchannel batches are needed on the +// subchannel call. +static void start_retriable_subchannel_batches(void* arg, grpc_error* ignored) { + grpc_call_element* elem = static_cast(arg); + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: constructing retriable batches", + chand, calld); + } + subchannel_call_retry_state* retry_state = + static_cast( + grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)); + // Construct list of closures to execute, one for each pending batch. + grpc_core::CallCombinerClosureList closures; + // Replay previously-returned send_* ops if needed. + subchannel_batch_data* replay_batch_data = + maybe_create_subchannel_batch_for_replay(elem, retry_state); + if (replay_batch_data != nullptr) { + add_closure_for_subchannel_batch(elem, &replay_batch_data->batch, + &closures); + // Track number of pending subchannel send batches. + // If this is the first one, take a ref to the call stack. + if (calld->num_pending_retriable_subchannel_send_batches == 0) { + GRPC_CALL_STACK_REF(calld->owning_call, "subchannel_send_batches"); + } + ++calld->num_pending_retriable_subchannel_send_batches; + } + // Now add pending batches. + add_subchannel_batches_for_pending_batches(elem, retry_state, &closures); + // Start batches on subchannel call. + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: starting %" PRIuPTR + " retriable batches on subchannel_call=%p", + chand, calld, closures.size(), calld->subchannel_call); + } + // Note: This will yield the call combiner. + closures.RunClosures(calld->call_combiner); +} + +// +// LB pick +// + +static void create_subchannel_call(grpc_call_element* elem, grpc_error* error) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + const size_t parent_data_size = + calld->enable_retries ? sizeof(subchannel_call_retry_state) : 0; + const grpc_core::ConnectedSubchannel::CallArgs call_args = { + calld->pollent, // pollent + calld->path, // path + calld->call_start_time, // start_time + calld->deadline, // deadline + calld->arena, // arena + calld->request->pick()->subchannel_call_context, // context + calld->call_combiner, // call_combiner + parent_data_size // parent_data_size + }; + grpc_error* new_error = + calld->request->pick()->connected_subchannel->CreateCall( + call_args, &calld->subchannel_call); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: create subchannel_call=%p: error=%s", + chand, calld, calld->subchannel_call, grpc_error_string(new_error)); + } + if (GPR_UNLIKELY(new_error != GRPC_ERROR_NONE)) { + new_error = grpc_error_add_child(new_error, error); + pending_batches_fail(elem, new_error, true /* yield_call_combiner */); + } else { + if (parent_data_size > 0) { + new (grpc_connected_subchannel_call_get_parent_data( + calld->subchannel_call)) + subchannel_call_retry_state( + calld->request->pick()->subchannel_call_context); + } + pending_batches_resume(elem); + } + GRPC_ERROR_UNREF(error); +} + +// Invoked when a pick is completed, on both success or failure. +static void pick_done(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (GPR_UNLIKELY(calld->request->pick()->connected_subchannel == nullptr)) { + // Failed to create subchannel. + // If there was no error, this is an LB policy drop, in which case + // we return an error; otherwise, we may retry. + grpc_status_code status = GRPC_STATUS_OK; + grpc_error_get_status(error, calld->deadline, &status, nullptr, nullptr, + nullptr); + if (error == GRPC_ERROR_NONE || !calld->enable_retries || + !maybe_retry(elem, nullptr /* batch_data */, status, + nullptr /* server_pushback_md */)) { + grpc_error* new_error = + error == GRPC_ERROR_NONE + ? GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Call dropped by load balancing policy") + : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to create subchannel", &error, 1); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: failed to create subchannel: error=%s", + chand, calld, grpc_error_string(new_error)); + } + pending_batches_fail(elem, new_error, true /* yield_call_combiner */); + } + } else { + /* Create call on subchannel. */ + create_subchannel_call(elem, GRPC_ERROR_REF(error)); + } +} + +// If the channel is in TRANSIENT_FAILURE and the call is not +// wait_for_ready=true, fails the call and returns true. +static bool fail_call_if_in_transient_failure(grpc_call_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + grpc_transport_stream_op_batch* batch = calld->pending_batches[0].batch; + if (chand->request_router->GetConnectivityState() == + GRPC_CHANNEL_TRANSIENT_FAILURE && + (batch->payload->send_initial_metadata.send_initial_metadata_flags & + GRPC_INITIAL_METADATA_WAIT_FOR_READY) == 0) { + pending_batches_fail( + elem, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "channel is in state TRANSIENT_FAILURE"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), + true /* yield_call_combiner */); + return true; + } + return false; +} + +// Applies service config to the call. Must be invoked once we know +// that the resolver has returned results to the channel. +static void apply_service_config_to_call_locked(grpc_call_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: applying service config to call", + chand, calld); + } + if (chand->retry_throttle_data != nullptr) { + calld->retry_throttle_data = chand->retry_throttle_data->Ref(); + } + if (chand->method_params_table != nullptr) { + calld->method_params = grpc_core::ServiceConfig::MethodConfigTableLookup( + *chand->method_params_table, calld->path); + if (calld->method_params != nullptr) { + // If the deadline from the service config is shorter than the one + // from the client API, reset the deadline timer. + if (chand->deadline_checking_enabled && + calld->method_params->timeout() != 0) { + const grpc_millis per_method_deadline = + grpc_timespec_to_millis_round_up(calld->call_start_time) + + calld->method_params->timeout(); + if (per_method_deadline < calld->deadline) { + calld->deadline = per_method_deadline; + grpc_deadline_state_reset(elem, calld->deadline); + } + } + // If the service config set wait_for_ready and the application + // did not explicitly set it, use the value from the service config. + uint32_t* send_initial_metadata_flags = + &calld->pending_batches[0] + .batch->payload->send_initial_metadata + .send_initial_metadata_flags; + if (GPR_UNLIKELY( + calld->method_params->wait_for_ready() != + ClientChannelMethodParams::WAIT_FOR_READY_UNSET && + !(*send_initial_metadata_flags & + GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET))) { + if (calld->method_params->wait_for_ready() == + ClientChannelMethodParams::WAIT_FOR_READY_TRUE) { + *send_initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY; + } else { + *send_initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY; + } + } + } + } + // If no retry policy, disable retries. + // TODO(roth): Remove this when adding support for transparent retries. + if (calld->method_params == nullptr || + calld->method_params->retry_policy() == nullptr) { + calld->enable_retries = false; + } +} + +// Invoked once resolver results are available. +static bool maybe_apply_service_config_to_call_locked(void* arg) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + // Only get service config data on the first attempt. + if (GPR_LIKELY(calld->num_attempts_completed == 0)) { + apply_service_config_to_call_locked(elem); + // Check this after applying service config, since it may have + // affected the call's wait_for_ready value. + if (fail_call_if_in_transient_failure(elem)) return false; + } + return true; +} + +static void start_pick_locked(void* arg, grpc_error* ignored) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + GPR_ASSERT(!calld->have_request); + GPR_ASSERT(calld->subchannel_call == nullptr); + // Normally, we want to do this check until after we've processed the + // service config, so that we can honor the wait_for_ready setting in + // the service config. However, if the channel is in TRANSIENT_FAILURE + // and we don't have an LB policy at this point, that means that the + // resolver has returned a failure, so we're not going to get a service + // config right away. In that case, we fail the call now based on the + // wait_for_ready value passed in from the application. + if (chand->request_router->lb_policy() == nullptr && + fail_call_if_in_transient_failure(elem)) { + return; + } + // If this is a retry, use the send_initial_metadata payload that + // we've cached; otherwise, use the pending batch. The + // send_initial_metadata batch will be the first pending batch in the + // list, as set by get_batch_index() above. + // TODO(roth): What if the LB policy needs to add something to the + // call's initial metadata, and then there's a retry? We don't want + // the new metadata to be added twice. We might need to somehow + // allocate the subchannel batch earlier so that we can give the + // subchannel's copy of the metadata batch (which is copied for each + // attempt) to the LB policy instead the one from the parent channel. + grpc_metadata_batch* initial_metadata = + calld->seen_send_initial_metadata + ? &calld->send_initial_metadata + : calld->pending_batches[0] + .batch->payload->send_initial_metadata.send_initial_metadata; + uint32_t* initial_metadata_flags = + calld->seen_send_initial_metadata + ? &calld->send_initial_metadata_flags + : &calld->pending_batches[0] + .batch->payload->send_initial_metadata + .send_initial_metadata_flags; + GRPC_CLOSURE_INIT(&calld->pick_closure, pick_done, elem, + grpc_schedule_on_exec_ctx); + calld->request.Init(calld->owning_call, calld->call_combiner, calld->pollent, + initial_metadata, initial_metadata_flags, + maybe_apply_service_config_to_call_locked, elem, + &calld->pick_closure); + calld->have_request = true; + chand->request_router->RouteCallLocked(calld->request.get()); +} + +// +// filter call vtable functions +// + +static void cc_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + GPR_TIMER_SCOPE("cc_start_transport_stream_op_batch", 0); + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + if (GPR_LIKELY(chand->deadline_checking_enabled)) { + grpc_deadline_state_client_start_transport_stream_op_batch(elem, batch); + } + // If we've previously been cancelled, immediately fail any new batches. + if (GPR_UNLIKELY(calld->cancel_error != GRPC_ERROR_NONE)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: failing batch with error: %s", + chand, calld, grpc_error_string(calld->cancel_error)); + } + // Note: This will release the call combiner. + grpc_transport_stream_op_batch_finish_with_failure( + batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner); + return; + } + // Handle cancellation. + if (GPR_UNLIKELY(batch->cancel_stream)) { + // Stash a copy of cancel_error in our call data, so that we can use + // it for subsequent operations. This ensures that if the call is + // cancelled before any batches are passed down (e.g., if the deadline + // is in the past when the call starts), we can return the right + // error to the caller when the first batch does get passed down. + GRPC_ERROR_UNREF(calld->cancel_error); + calld->cancel_error = + GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: recording cancel_error=%s", chand, + calld, grpc_error_string(calld->cancel_error)); + } + // If we do not have a subchannel call (i.e., a pick has not yet + // been started), fail all pending batches. Otherwise, send the + // cancellation down to the subchannel call. + if (calld->subchannel_call == nullptr) { + pending_batches_fail(elem, GRPC_ERROR_REF(calld->cancel_error), + false /* yield_call_combiner */); + // Note: This will release the call combiner. + grpc_transport_stream_op_batch_finish_with_failure( + batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner); + } else { + // Note: This will release the call combiner. + grpc_subchannel_call_process_op(calld->subchannel_call, batch); + } + return; + } + // Add the batch to the pending list. + pending_batches_add(elem, batch); + // Check if we've already gotten a subchannel call. + // Note that once we have completed the pick, we do not need to enter + // the channel combiner, which is more efficient (especially for + // streaming calls). + if (calld->subchannel_call != nullptr) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: starting batch on subchannel_call=%p", chand, + calld, calld->subchannel_call); + } + pending_batches_resume(elem); + return; + } + // We do not yet have a subchannel call. + // For batches containing a send_initial_metadata op, enter the channel + // combiner to start a pick. + if (GPR_LIKELY(batch->send_initial_metadata)) { + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, "chand=%p calld=%p: entering client_channel combiner", + chand, calld); + } + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&batch->handler_private.closure, start_pick_locked, + elem, grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); + } else { + // For all other batches, release the call combiner. + if (grpc_client_channel_trace.enabled()) { + gpr_log(GPR_INFO, + "chand=%p calld=%p: saved batch, yielding call combiner", chand, + calld); + } + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "batch does not include send_initial_metadata"); + } +} + +/* Constructor for call_data */ +static grpc_error* cc_init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + new (elem->call_data) call_data(elem, *chand, *args); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void cc_destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_schedule_closure) { + call_data* calld = static_cast(elem->call_data); + if (GPR_LIKELY(calld->subchannel_call != nullptr)) { + grpc_subchannel_call_set_cleanup_closure(calld->subchannel_call, + then_schedule_closure); + then_schedule_closure = nullptr; + } + calld->~call_data(); + GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE); +} + +static void cc_set_pollset_or_pollset_set(grpc_call_element* elem, + grpc_polling_entity* pollent) { + call_data* calld = static_cast(elem->call_data); + calld->pollent = pollent; +} + +/************************************************************************* + * EXPORTED SYMBOLS + */ + +const grpc_channel_filter grpc_client_channel_filter = { + cc_start_transport_stream_op_batch, + cc_start_transport_op, + sizeof(call_data), + cc_init_call_elem, + cc_set_pollset_or_pollset_set, + cc_destroy_call_elem, + sizeof(channel_data), + cc_init_channel_elem, + cc_destroy_channel_elem, + cc_get_channel_info, + "client-channel", +}; + +void grpc_client_channel_set_channelz_node( + grpc_channel_element* elem, grpc_core::channelz::ClientChannelNode* node) { + channel_data* chand = static_cast(elem->channel_data); + chand->request_router->set_channelz_node(node); +} + +void grpc_client_channel_populate_child_refs( + grpc_channel_element* elem, + grpc_core::channelz::ChildRefsList* child_subchannels, + grpc_core::channelz::ChildRefsList* child_channels) { + channel_data* chand = static_cast(elem->channel_data); + if (chand->request_router->lb_policy() != nullptr) { + chand->request_router->lb_policy()->FillChildRefsForChannelz( + child_subchannels, child_channels); + } +} + +static void try_to_connect_locked(void* arg, grpc_error* error_ignored) { + channel_data* chand = static_cast(arg); + chand->request_router->ExitIdleLocked(); + GRPC_CHANNEL_STACK_UNREF(chand->owning_stack, "try_to_connect"); +} + +grpc_connectivity_state grpc_client_channel_check_connectivity_state( + grpc_channel_element* elem, int try_to_connect) { + channel_data* chand = static_cast(elem->channel_data); + grpc_connectivity_state out = chand->request_router->GetConnectivityState(); + if (out == GRPC_CHANNEL_IDLE && try_to_connect) { + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "try_to_connect"); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(try_to_connect_locked, chand, + grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); + } + return out; +} + +typedef struct external_connectivity_watcher { + channel_data* chand; + grpc_polling_entity pollent; + grpc_closure* on_complete; + grpc_closure* watcher_timer_init; + grpc_connectivity_state* state; + grpc_closure my_closure; + struct external_connectivity_watcher* next; +} external_connectivity_watcher; + +static external_connectivity_watcher* lookup_external_connectivity_watcher( + channel_data* chand, grpc_closure* on_complete) { + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + external_connectivity_watcher* w = + chand->external_connectivity_watcher_list_head; + while (w != nullptr && w->on_complete != on_complete) { + w = w->next; + } + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + return w; +} + +static void external_connectivity_watcher_list_append( + channel_data* chand, external_connectivity_watcher* w) { + GPR_ASSERT(!lookup_external_connectivity_watcher(chand, w->on_complete)); + + gpr_mu_lock(&w->chand->external_connectivity_watcher_list_mu); + GPR_ASSERT(!w->next); + w->next = chand->external_connectivity_watcher_list_head; + chand->external_connectivity_watcher_list_head = w; + gpr_mu_unlock(&w->chand->external_connectivity_watcher_list_mu); +} + +static void external_connectivity_watcher_list_remove( + channel_data* chand, external_connectivity_watcher* to_remove) { + GPR_ASSERT( + lookup_external_connectivity_watcher(chand, to_remove->on_complete)); + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + if (to_remove == chand->external_connectivity_watcher_list_head) { + chand->external_connectivity_watcher_list_head = to_remove->next; + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + return; + } + external_connectivity_watcher* w = + chand->external_connectivity_watcher_list_head; + while (w != nullptr) { + if (w->next == to_remove) { + w->next = w->next->next; + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + return; + } + w = w->next; + } + GPR_UNREACHABLE_CODE(return ); +} + +int grpc_client_channel_num_external_connectivity_watchers( + grpc_channel_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + int count = 0; + + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + external_connectivity_watcher* w = + chand->external_connectivity_watcher_list_head; + while (w != nullptr) { + count++; + w = w->next; + } + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + + return count; +} + +static void on_external_watch_complete_locked(void* arg, grpc_error* error) { + external_connectivity_watcher* w = + static_cast(arg); + grpc_closure* follow_up = w->on_complete; + grpc_polling_entity_del_from_pollset_set(&w->pollent, + w->chand->interested_parties); + GRPC_CHANNEL_STACK_UNREF(w->chand->owning_stack, + "external_connectivity_watcher"); + external_connectivity_watcher_list_remove(w->chand, w); + gpr_free(w); + GRPC_CLOSURE_SCHED(follow_up, GRPC_ERROR_REF(error)); +} + +static void watch_connectivity_state_locked(void* arg, + grpc_error* error_ignored) { + external_connectivity_watcher* w = + static_cast(arg); + external_connectivity_watcher* found = nullptr; + if (w->state != nullptr) { + external_connectivity_watcher_list_append(w->chand, w); + // An assumption is being made that the closure is scheduled on the exec ctx + // scheduler and that GRPC_CLOSURE_RUN would run the closure immediately. + GRPC_CLOSURE_RUN(w->watcher_timer_init, GRPC_ERROR_NONE); + GRPC_CLOSURE_INIT(&w->my_closure, on_external_watch_complete_locked, w, + grpc_combiner_scheduler(w->chand->combiner)); + w->chand->request_router->NotifyOnConnectivityStateChange(w->state, + &w->my_closure); + } else { + GPR_ASSERT(w->watcher_timer_init == nullptr); + found = lookup_external_connectivity_watcher(w->chand, w->on_complete); + if (found) { + GPR_ASSERT(found->on_complete == w->on_complete); + found->chand->request_router->NotifyOnConnectivityStateChange( + nullptr, &found->my_closure); + } + grpc_polling_entity_del_from_pollset_set(&w->pollent, + w->chand->interested_parties); + GRPC_CHANNEL_STACK_UNREF(w->chand->owning_stack, + "external_connectivity_watcher"); + gpr_free(w); + } +} + +void grpc_client_channel_watch_connectivity_state( + grpc_channel_element* elem, grpc_polling_entity pollent, + grpc_connectivity_state* state, grpc_closure* closure, + grpc_closure* watcher_timer_init) { + channel_data* chand = static_cast(elem->channel_data); + external_connectivity_watcher* w = + static_cast(gpr_zalloc(sizeof(*w))); + w->chand = chand; + w->pollent = pollent; + w->on_complete = closure; + w->state = state; + w->watcher_timer_init = watcher_timer_init; + grpc_polling_entity_add_to_pollset_set(&w->pollent, + chand->interested_parties); + GRPC_CHANNEL_STACK_REF(w->chand->owning_stack, + "external_connectivity_watcher"); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&w->my_closure, watch_connectivity_state_locked, w, + grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); +} + +grpc_subchannel_call* grpc_client_channel_get_subchannel_call( + grpc_call_element* elem) { + call_data* calld = static_cast(elem->call_data); + return calld->subchannel_call; +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel.h b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.h new file mode 100644 index 00000000000000..4935fd24d87934 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel.h @@ -0,0 +1,66 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H + +#include + +#include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/ext/filters/client_channel/resolver.h" +#include "src/core/lib/channel/channel_stack.h" + +extern grpc_core::TraceFlag grpc_client_channel_trace; + +// Channel arg key for server URI string. +#define GRPC_ARG_SERVER_URI "grpc.server_uri" + +/* 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; + +void grpc_client_channel_set_channelz_node( + grpc_channel_element* elem, grpc_core::channelz::ClientChannelNode* node); + +void grpc_client_channel_populate_child_refs( + grpc_channel_element* elem, + grpc_core::channelz::ChildRefsList* child_subchannels, + grpc_core::channelz::ChildRefsList* child_channels); + +grpc_connectivity_state grpc_client_channel_check_connectivity_state( + grpc_channel_element* elem, int try_to_connect); + +int grpc_client_channel_num_external_connectivity_watchers( + grpc_channel_element* elem); + +void grpc_client_channel_watch_connectivity_state( + grpc_channel_element* elem, grpc_polling_entity pollent, + grpc_connectivity_state* state, grpc_closure* on_complete, + grpc_closure* watcher_timer_init); + +/* Debug helper: pull the subchannel call from a call stack element */ +grpc_subchannel_call* grpc_client_channel_get_subchannel_call( + grpc_call_element* elem); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.cc new file mode 100644 index 00000000000000..8e5426081c4c25 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.cc @@ -0,0 +1,186 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/lib/channel/channelz_registry.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/connectivity_state.h" + +#include + +namespace grpc_core { +namespace channelz { +namespace { + +void* client_channel_channelz_copy(void* p) { return p; } + +void client_channel_channelz_destroy(void* p) {} + +int client_channel_channelz_cmp(void* a, void* b) { return GPR_ICMP(a, b); } + +} // namespace + +static const grpc_arg_pointer_vtable client_channel_channelz_vtable = { + client_channel_channelz_copy, client_channel_channelz_destroy, + client_channel_channelz_cmp}; + +ClientChannelNode::ClientChannelNode(grpc_channel* channel, + size_t channel_tracer_max_nodes, + bool is_top_level_channel) + : ChannelNode(channel, channel_tracer_max_nodes, is_top_level_channel) { + client_channel_ = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + grpc_client_channel_set_channelz_node(client_channel_, this); + GPR_ASSERT(client_channel_->filter == &grpc_client_channel_filter); +} + +void ClientChannelNode::PopulateConnectivityState(grpc_json* json) { + grpc_connectivity_state state; + if (ChannelIsDestroyed()) { + state = GRPC_CHANNEL_SHUTDOWN; + } else { + state = + grpc_client_channel_check_connectivity_state(client_channel_, false); + } + json = grpc_json_create_child(nullptr, json, "state", nullptr, + GRPC_JSON_OBJECT, false); + grpc_json_create_child(nullptr, json, "state", + grpc_connectivity_state_name(state), GRPC_JSON_STRING, + false); +} + +void ClientChannelNode::PopulateChildRefs(grpc_json* json) { + ChildRefsList child_subchannels; + ChildRefsList child_channels; + grpc_json* json_iterator = nullptr; + grpc_client_channel_populate_child_refs(client_channel_, &child_subchannels, + &child_channels); + if (!child_subchannels.empty()) { + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "subchannelRef", nullptr, GRPC_JSON_ARRAY, false); + for (size_t i = 0; i < child_subchannels.size(); ++i) { + json_iterator = + grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr, + GRPC_JSON_OBJECT, false); + grpc_json_add_number_string_child(json_iterator, nullptr, "subchannelId", + child_subchannels[i]); + } + } + if (!child_channels.empty()) { + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "channelRef", nullptr, GRPC_JSON_ARRAY, false); + json_iterator = nullptr; + for (size_t i = 0; i < child_channels.size(); ++i) { + json_iterator = + grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr, + GRPC_JSON_OBJECT, false); + grpc_json_add_number_string_child(json_iterator, nullptr, "channelId", + child_channels[i]); + } + } +} + +grpc_arg ClientChannelNode::CreateChannelArg() { + return grpc_channel_arg_pointer_create( + const_cast(GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC), + reinterpret_cast(MakeClientChannelNode), + &client_channel_channelz_vtable); +} + +RefCountedPtr ClientChannelNode::MakeClientChannelNode( + grpc_channel* channel, size_t channel_tracer_max_nodes, + bool is_top_level_channel) { + return MakeRefCounted(channel, channel_tracer_max_nodes, + is_top_level_channel); +} + +SubchannelNode::SubchannelNode(grpc_subchannel* subchannel, + size_t channel_tracer_max_nodes) + : BaseNode(EntityType::kSubchannel), + subchannel_(subchannel), + target_( + UniquePtr(gpr_strdup(grpc_subchannel_get_target(subchannel_)))), + trace_(channel_tracer_max_nodes) {} + +SubchannelNode::~SubchannelNode() {} + +void SubchannelNode::PopulateConnectivityState(grpc_json* json) { + grpc_connectivity_state state; + if (subchannel_ == nullptr) { + state = GRPC_CHANNEL_SHUTDOWN; + } else { + state = grpc_subchannel_check_connectivity( + subchannel_, nullptr, true /* inhibit_health_checking */); + } + json = grpc_json_create_child(nullptr, json, "state", nullptr, + GRPC_JSON_OBJECT, false); + grpc_json_create_child(nullptr, json, "state", + grpc_connectivity_state_name(state), GRPC_JSON_STRING, + false); +} + +grpc_json* SubchannelNode::RenderJson() { + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_add_number_string_child(json, json_iterator, + "subchannelId", uuid()); + // reset json iterators to top level object + json = top_level_json; + json_iterator = nullptr; + // create and fill the data child. + grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, + GRPC_JSON_OBJECT, false); + json = data; + json_iterator = nullptr; + PopulateConnectivityState(json); + GPR_ASSERT(target_.get() != nullptr); + grpc_json_create_child(nullptr, json, "target", target_.get(), + GRPC_JSON_STRING, false); + // fill in the channel trace if applicable + grpc_json* trace_json = trace_.RenderJson(); + if (trace_json != nullptr) { + trace_json->key = "trace"; // this object is named trace in channelz.proto + grpc_json_link_child(json, trace_json, nullptr); + } + // ask CallCountingHelper to populate trace and call count data. + call_counter_.PopulateCallCounts(json); + json = top_level_json; + // populate the child socket. + intptr_t socket_uuid = grpc_subchannel_get_child_socket_uuid(subchannel_); + if (socket_uuid != 0) { + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false); + json_iterator = grpc_json_create_child(json_iterator, array_parent, nullptr, + nullptr, GRPC_JSON_OBJECT, false); + grpc_json_add_number_string_child(json_iterator, nullptr, "socketId", + socket_uuid); + } + return top_level_json; +} + +} // namespace channelz +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.h b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.h new file mode 100644 index 00000000000000..8a5c3e7e5e5780 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_channelz.h @@ -0,0 +1,98 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_CHANNELZ_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_CHANNELZ_H + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/channel/channelz.h" + +typedef struct grpc_subchannel grpc_subchannel; + +namespace grpc_core { +namespace channelz { + +// Subtype of ChannelNode that overrides and provides client_channel specific +// functionality like querying for connectivity_state and subchannel data. +class ClientChannelNode : public ChannelNode { + public: + static RefCountedPtr MakeClientChannelNode( + grpc_channel* channel, size_t channel_tracer_max_nodes, + bool is_top_level_channel); + + ClientChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes, + bool is_top_level_channel); + virtual ~ClientChannelNode() {} + + // Overriding template methods from ChannelNode to render information that + // only ClientChannelNode knows about. + void PopulateConnectivityState(grpc_json* json) override; + void PopulateChildRefs(grpc_json* json) override; + + // Helper to create a channel arg to ensure this type of ChannelNode is + // created. + static grpc_arg CreateChannelArg(); + + private: + grpc_channel_element* client_channel_; +}; + +// Handles channelz bookkeeping for sockets +class SubchannelNode : public BaseNode { + public: + SubchannelNode(grpc_subchannel* subchannel, size_t channel_tracer_max_nodes); + ~SubchannelNode() override; + + void MarkSubchannelDestroyed() { + GPR_ASSERT(subchannel_ != nullptr); + subchannel_ = nullptr; + } + + grpc_json* RenderJson() override; + + // proxy methods to composed classes. + void AddTraceEvent(ChannelTrace::Severity severity, grpc_slice data) { + trace_.AddTraceEvent(severity, data); + } + void AddTraceEventWithReference(ChannelTrace::Severity severity, + grpc_slice data, + RefCountedPtr referenced_channel) { + trace_.AddTraceEventWithReference(severity, data, + std::move(referenced_channel)); + } + void RecordCallStarted() { call_counter_.RecordCallStarted(); } + void RecordCallFailed() { call_counter_.RecordCallFailed(); } + void RecordCallSucceeded() { call_counter_.RecordCallSucceeded(); } + + private: + grpc_subchannel* subchannel_; + UniquePtr target_; + CallCountingHelper call_counter_; + ChannelTrace trace_; + + void PopulateConnectivityState(grpc_json* json); +}; + +} // namespace channelz +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_CHANNELZ_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.cc new file mode 100644 index 00000000000000..172e9f03c735dd --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.cc @@ -0,0 +1,67 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/lib/channel/channel_args.h" + +void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory) { + factory->vtable->ref(factory); +} + +void grpc_client_channel_factory_unref(grpc_client_channel_factory* factory) { + factory->vtable->unref(factory); +} + +grpc_subchannel* grpc_client_channel_factory_create_subchannel( + grpc_client_channel_factory* factory, const grpc_subchannel_args* args) { + return factory->vtable->create_subchannel(factory, args); +} + +grpc_channel* grpc_client_channel_factory_create_channel( + grpc_client_channel_factory* factory, const char* target, + grpc_client_channel_type type, const grpc_channel_args* args) { + return factory->vtable->create_client_channel(factory, target, type, args); +} + +static void* factory_arg_copy(void* factory) { + grpc_client_channel_factory_ref( + static_cast(factory)); + return factory; +} + +static void factory_arg_destroy(void* factory) { + grpc_client_channel_factory_unref( + static_cast(factory)); +} + +static int factory_arg_cmp(void* factory1, void* factory2) { + if (factory1 < factory2) return -1; + if (factory1 > factory2) return 1; + return 0; +} + +static const grpc_arg_pointer_vtable factory_arg_vtable = { + factory_arg_copy, factory_arg_destroy, factory_arg_cmp}; + +grpc_arg grpc_client_channel_factory_create_channel_arg( + grpc_client_channel_factory* factory) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_CLIENT_CHANNEL_FACTORY, + factory, &factory_arg_vtable); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.h b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.h new file mode 100644 index 00000000000000..601ec46b2a5f07 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_factory.h @@ -0,0 +1,74 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H + +#include + +#include + +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/lib/channel/channel_stack.h" + +// Channel arg key for client channel factory. +#define GRPC_ARG_CLIENT_CHANNEL_FACTORY "grpc.client_channel_factory" + +typedef struct grpc_client_channel_factory grpc_client_channel_factory; +typedef struct grpc_client_channel_factory_vtable + grpc_client_channel_factory_vtable; + +typedef enum { + GRPC_CLIENT_CHANNEL_TYPE_REGULAR, /** for the user-level regular calls */ + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, /** for communication with a load + balancing service */ +} grpc_client_channel_type; + +/** Constructor for new configured channels. + Creating decorators around this type is encouraged to adapt behavior. */ +struct grpc_client_channel_factory { + const grpc_client_channel_factory_vtable* vtable; +}; + +struct grpc_client_channel_factory_vtable { + void (*ref)(grpc_client_channel_factory* factory); + void (*unref)(grpc_client_channel_factory* factory); + grpc_subchannel* (*create_subchannel)(grpc_client_channel_factory* factory, + const grpc_subchannel_args* args); + grpc_channel* (*create_client_channel)(grpc_client_channel_factory* factory, + const char* target, + grpc_client_channel_type type, + const grpc_channel_args* args); +}; + +void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory); +void grpc_client_channel_factory_unref(grpc_client_channel_factory* factory); + +/** Create a new grpc_subchannel */ +grpc_subchannel* grpc_client_channel_factory_create_subchannel( + grpc_client_channel_factory* factory, const grpc_subchannel_args* args); + +/** Create a new grpc_channel */ +grpc_channel* grpc_client_channel_factory_create_channel( + grpc_client_channel_factory* factory, const char* target, + grpc_client_channel_type type, const grpc_channel_args* args); + +grpc_arg grpc_client_channel_factory_create_channel_arg( + grpc_client_channel_factory* factory); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/client_channel_plugin.cc b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_plugin.cc new file mode 100644 index 00000000000000..e0784b7e5c1f19 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/client_channel_plugin.cc @@ -0,0 +1,71 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/http_proxy.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/retry_throttle.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/surface/channel_init.h" + +static bool append_filter(grpc_channel_stack_builder* builder, void* arg) { + const grpc_channel_args* args = + grpc_channel_stack_builder_get_channel_arguments(builder); + grpc_arg args_to_add[] = { + grpc_core::channelz::ClientChannelNode::CreateChannelArg()}; + grpc_channel_args* new_args = grpc_channel_args_copy_and_add( + args, args_to_add, GPR_ARRAY_SIZE(args_to_add)); + grpc_channel_stack_builder_set_channel_arguments(builder, new_args); + grpc_channel_args_destroy(new_args); + return grpc_channel_stack_builder_append_filter( + builder, static_cast(arg), nullptr, nullptr); +} + +void grpc_client_channel_init(void) { + grpc_core::LoadBalancingPolicyRegistry::Builder::InitRegistry(); + grpc_core::ResolverRegistry::Builder::InitRegistry(); + grpc_core::internal::ServerRetryThrottleMap::Init(); + grpc_proxy_mapper_registry_init(); + grpc_register_http_proxy_mapper(); + grpc_subchannel_index_init(); + grpc_channel_init_register_stage( + GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, append_filter, + (void*)&grpc_client_channel_filter); + grpc_http_connect_register_handshaker_factory(); +} + +void grpc_client_channel_shutdown(void) { + grpc_subchannel_index_shutdown(); + grpc_channel_init_shutdown(); + grpc_proxy_mapper_registry_shutdown(); + grpc_core::internal::ServerRetryThrottleMap::Shutdown(); + grpc_core::ResolverRegistry::Builder::ShutdownRegistry(); + grpc_core::LoadBalancingPolicyRegistry::Builder::ShutdownRegistry(); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/connector.cc b/third_party/grpc/src/core/ext/filters/client_channel/connector.cc new file mode 100644 index 00000000000000..5e04b3b4539c86 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/connector.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/connector.h" + +grpc_connector* grpc_connector_ref(grpc_connector* connector) { + connector->vtable->ref(connector); + return 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_closure* notify) { + connector->vtable->connect(connector, in_args, out_args, notify); +} + +void grpc_connector_shutdown(grpc_connector* connector, grpc_error* why) { + connector->vtable->shutdown(connector, why); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/connector.h b/third_party/grpc/src/core/ext/filters/client_channel/connector.h new file mode 100644 index 00000000000000..ea34dcdab57822 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/connector.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H + +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/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; + /** deadline for connection */ + grpc_millis deadline; + /** channel arguments (to be passed to transport) */ + const grpc_channel_args* channel_args; +} grpc_connect_in_args; + +typedef struct { + /** the connected transport */ + grpc_transport* transport; + + /** channel arguments (to be passed to the filters) */ + grpc_channel_args* channel_args; + + /** socket uuid of the connected transport. 0 if not available */ + intptr_t socket_uuid; +} grpc_connect_out_args; + +struct grpc_connector_vtable { + void (*ref)(grpc_connector* connector); + void (*unref)(grpc_connector* connector); + /** Implementation of grpc_connector_shutdown */ + void (*shutdown)(grpc_connector* connector, grpc_error* why); + /** Implementation of grpc_connector_connect */ + void (*connect)(grpc_connector* connector, + const grpc_connect_in_args* in_args, + grpc_connect_out_args* out_args, grpc_closure* notify); +}; + +grpc_connector* grpc_connector_ref(grpc_connector* connector); +void grpc_connector_unref(grpc_connector* connector); +/** Connect using the connector: max one outstanding call at a time */ +void grpc_connector_connect(grpc_connector* connector, + const grpc_connect_in_args* in_args, + grpc_connect_out_args* out_args, + grpc_closure* notify); +/** Cancel any pending connection */ +void grpc_connector_shutdown(grpc_connector* connector, grpc_error* why); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.c new file mode 100644 index 00000000000000..5499c549cc6a81 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.c @@ -0,0 +1,23 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/ext/filters/client_channel/health/health.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_health_v1_HealthCheckRequest_fields[2] = { + PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_health_v1_HealthCheckRequest, service, service, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_health_v1_HealthCheckResponse_fields[2] = { + PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, grpc_health_v1_HealthCheckResponse, status, status, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.h new file mode 100644 index 00000000000000..9d54ccd618297c --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health.pb.h @@ -0,0 +1,73 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_HEALTH_V1_HEALTH_PB_H_INCLUDED +#define PB_GRPC_HEALTH_V1_HEALTH_PB_H_INCLUDED +#include "pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _grpc_health_v1_HealthCheckResponse_ServingStatus { + grpc_health_v1_HealthCheckResponse_ServingStatus_UNKNOWN = 0, + grpc_health_v1_HealthCheckResponse_ServingStatus_SERVING = 1, + grpc_health_v1_HealthCheckResponse_ServingStatus_NOT_SERVING = 2, + grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN = 3 +} grpc_health_v1_HealthCheckResponse_ServingStatus; +#define _grpc_health_v1_HealthCheckResponse_ServingStatus_MIN grpc_health_v1_HealthCheckResponse_ServingStatus_UNKNOWN +#define _grpc_health_v1_HealthCheckResponse_ServingStatus_MAX grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN +#define _grpc_health_v1_HealthCheckResponse_ServingStatus_ARRAYSIZE ((grpc_health_v1_HealthCheckResponse_ServingStatus)(grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN+1)) + +/* Struct definitions */ +typedef struct _grpc_health_v1_HealthCheckRequest { + bool has_service; + char service[200]; +/* @@protoc_insertion_point(struct:grpc_health_v1_HealthCheckRequest) */ +} grpc_health_v1_HealthCheckRequest; + +typedef struct _grpc_health_v1_HealthCheckResponse { + bool has_status; + grpc_health_v1_HealthCheckResponse_ServingStatus status; +/* @@protoc_insertion_point(struct:grpc_health_v1_HealthCheckResponse) */ +} grpc_health_v1_HealthCheckResponse; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_health_v1_HealthCheckRequest_init_default {false, ""} +#define grpc_health_v1_HealthCheckResponse_init_default {false, (grpc_health_v1_HealthCheckResponse_ServingStatus)0} +#define grpc_health_v1_HealthCheckRequest_init_zero {false, ""} +#define grpc_health_v1_HealthCheckResponse_init_zero {false, (grpc_health_v1_HealthCheckResponse_ServingStatus)0} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_health_v1_HealthCheckRequest_service_tag 1 +#define grpc_health_v1_HealthCheckResponse_status_tag 1 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_health_v1_HealthCheckRequest_fields[2]; +extern const pb_field_t grpc_health_v1_HealthCheckResponse_fields[2]; + +/* Maximum encoded size of messages (where known) */ +#define grpc_health_v1_HealthCheckRequest_size 203 +#define grpc_health_v1_HealthCheckResponse_size 2 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define HEALTH_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.cc b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.cc new file mode 100644 index 00000000000000..2232c57120e5e8 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.cc @@ -0,0 +1,650 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/health/health_check_client.h" + +#include "pb_decode.h" +#include "pb_encode.h" +#include "src/core/ext/filters/client_channel/health/health.pb.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/status_metadata.h" + +#define HEALTH_CHECK_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define HEALTH_CHECK_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define HEALTH_CHECK_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define HEALTH_CHECK_RECONNECT_JITTER 0.2 + +grpc_core::TraceFlag grpc_health_check_client_trace(false, + "health_check_client"); + +namespace grpc_core { + +// +// HealthCheckClient +// + +HealthCheckClient::HealthCheckClient( + const char* service_name, + RefCountedPtr connected_subchannel, + grpc_pollset_set* interested_parties, + grpc_core::RefCountedPtr channelz_node) + : InternallyRefCounted(&grpc_health_check_client_trace), + service_name_(service_name), + connected_subchannel_(std::move(connected_subchannel)), + interested_parties_(interested_parties), + channelz_node_(std::move(channelz_node)), + retry_backoff_( + BackOff::Options() + .set_initial_backoff( + HEALTH_CHECK_INITIAL_CONNECT_BACKOFF_SECONDS * 1000) + .set_multiplier(HEALTH_CHECK_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(HEALTH_CHECK_RECONNECT_JITTER) + .set_max_backoff(HEALTH_CHECK_RECONNECT_MAX_BACKOFF_SECONDS * + 1000)) { + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "created HealthCheckClient %p", this); + } + GRPC_CLOSURE_INIT(&retry_timer_callback_, OnRetryTimer, this, + grpc_schedule_on_exec_ctx); + gpr_mu_init(&mu_); + StartCall(); +} + +HealthCheckClient::~HealthCheckClient() { + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "destroying HealthCheckClient %p", this); + } + GRPC_ERROR_UNREF(error_); + gpr_mu_destroy(&mu_); +} + +void HealthCheckClient::NotifyOnHealthChange(grpc_connectivity_state* state, + grpc_closure* closure) { + MutexLock lock(&mu_); + GPR_ASSERT(notify_state_ == nullptr); + if (*state != state_) { + *state = state_; + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_REF(error_)); + return; + } + notify_state_ = state; + on_health_changed_ = closure; +} + +void HealthCheckClient::SetHealthStatus(grpc_connectivity_state state, + grpc_error* error) { + MutexLock lock(&mu_); + SetHealthStatusLocked(state, error); +} + +void HealthCheckClient::SetHealthStatusLocked(grpc_connectivity_state state, + grpc_error* error) { + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "HealthCheckClient %p: setting state=%d error=%s", this, + state, grpc_error_string(error)); + } + if (notify_state_ != nullptr && *notify_state_ != state) { + *notify_state_ = state; + notify_state_ = nullptr; + GRPC_CLOSURE_SCHED(on_health_changed_, GRPC_ERROR_REF(error)); + on_health_changed_ = nullptr; + } + state_ = state; + GRPC_ERROR_UNREF(error_); + error_ = error; +} + +void HealthCheckClient::Orphan() { + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "HealthCheckClient %p: shutting down", this); + } + { + MutexLock lock(&mu_); + if (on_health_changed_ != nullptr) { + *notify_state_ = GRPC_CHANNEL_SHUTDOWN; + notify_state_ = nullptr; + GRPC_CLOSURE_SCHED(on_health_changed_, GRPC_ERROR_NONE); + on_health_changed_ = nullptr; + } + shutting_down_ = true; + call_state_.reset(); + if (retry_timer_callback_pending_) { + grpc_timer_cancel(&retry_timer_); + } + } + Unref(DEBUG_LOCATION, "orphan"); +} + +void HealthCheckClient::StartCall() { + MutexLock lock(&mu_); + StartCallLocked(); +} + +void HealthCheckClient::StartCallLocked() { + if (shutting_down_) return; + GPR_ASSERT(call_state_ == nullptr); + SetHealthStatusLocked(GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE); + call_state_ = MakeOrphanable(Ref(), interested_parties_); + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "HealthCheckClient %p: created CallState %p", this, + call_state_.get()); + } + call_state_->StartCall(); +} + +void HealthCheckClient::StartRetryTimer() { + MutexLock lock(&mu_); + SetHealthStatusLocked( + GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "health check call failed; will retry after backoff")); + grpc_millis next_try = retry_backoff_.NextAttemptTime(); + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "HealthCheckClient %p: health check call lost...", this); + grpc_millis timeout = next_try - ExecCtx::Get()->Now(); + if (timeout > 0) { + gpr_log(GPR_INFO, + "HealthCheckClient %p: ... will retry in %" PRId64 "ms.", this, + timeout); + } else { + gpr_log(GPR_INFO, "HealthCheckClient %p: ... retrying immediately.", + this); + } + } + // Ref for callback, tracked manually. + Ref(DEBUG_LOCATION, "health_retry_timer").release(); + retry_timer_callback_pending_ = true; + grpc_timer_init(&retry_timer_, next_try, &retry_timer_callback_); +} + +void HealthCheckClient::OnRetryTimer(void* arg, grpc_error* error) { + HealthCheckClient* self = static_cast(arg); + { + MutexLock lock(&self->mu_); + self->retry_timer_callback_pending_ = false; + if (!self->shutting_down_ && error == GRPC_ERROR_NONE && + self->call_state_ == nullptr) { + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "HealthCheckClient %p: restarting health check call", + self); + } + self->StartCallLocked(); + } + } + self->Unref(DEBUG_LOCATION, "health_retry_timer"); +} + +// +// protobuf helpers +// + +namespace { + +void EncodeRequest(const char* service_name, + ManualConstructor* send_message) { + grpc_health_v1_HealthCheckRequest request_struct; + request_struct.has_service = true; + snprintf(request_struct.service, sizeof(request_struct.service), "%s", + service_name); + pb_ostream_t ostream; + memset(&ostream, 0, sizeof(ostream)); + pb_encode(&ostream, grpc_health_v1_HealthCheckRequest_fields, + &request_struct); + grpc_slice request_slice = GRPC_SLICE_MALLOC(ostream.bytes_written); + ostream = pb_ostream_from_buffer(GRPC_SLICE_START_PTR(request_slice), + GRPC_SLICE_LENGTH(request_slice)); + GPR_ASSERT(pb_encode(&ostream, grpc_health_v1_HealthCheckRequest_fields, + &request_struct) != 0); + grpc_slice_buffer slice_buffer; + grpc_slice_buffer_init(&slice_buffer); + grpc_slice_buffer_add(&slice_buffer, request_slice); + send_message->Init(&slice_buffer, 0); + grpc_slice_buffer_destroy_internal(&slice_buffer); +} + +// Returns true if healthy. +// If there was an error parsing the response, sets *error and returns false. +bool DecodeResponse(grpc_slice_buffer* slice_buffer, grpc_error** error) { + // If message is empty, assume unhealthy. + if (slice_buffer->length == 0) { + *error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("health check response was empty"); + return false; + } + // Concatenate the slices to form a single string. + UniquePtr recv_message_deleter; + uint8_t* recv_message; + if (slice_buffer->count == 1) { + recv_message = GRPC_SLICE_START_PTR(slice_buffer->slices[0]); + } else { + recv_message = static_cast(gpr_malloc(slice_buffer->length)); + recv_message_deleter.reset(recv_message); + size_t offset = 0; + for (size_t i = 0; i < slice_buffer->count; ++i) { + memcpy(recv_message + offset, + GRPC_SLICE_START_PTR(slice_buffer->slices[i]), + GRPC_SLICE_LENGTH(slice_buffer->slices[i])); + offset += GRPC_SLICE_LENGTH(slice_buffer->slices[i]); + } + } + // Deserialize message. + grpc_health_v1_HealthCheckResponse response_struct; + pb_istream_t istream = + pb_istream_from_buffer(recv_message, slice_buffer->length); + if (!pb_decode(&istream, grpc_health_v1_HealthCheckResponse_fields, + &response_struct)) { + // Can't parse message; assume unhealthy. + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "cannot parse health check response"); + return false; + } + if (!response_struct.has_status) { + // Field not present; assume unhealthy. + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "status field not present in health check response"); + return false; + } + return response_struct.status == + grpc_health_v1_HealthCheckResponse_ServingStatus_SERVING; +} + +} // namespace + +// +// HealthCheckClient::CallState +// + +HealthCheckClient::CallState::CallState( + RefCountedPtr health_check_client, + grpc_pollset_set* interested_parties) + : InternallyRefCounted(&grpc_health_check_client_trace), + health_check_client_(std::move(health_check_client)), + pollent_(grpc_polling_entity_create_from_pollset_set(interested_parties)), + arena_(gpr_arena_create(health_check_client_->connected_subchannel_ + ->GetInitialCallSizeEstimate(0))), + payload_(context_) { + grpc_call_combiner_init(&call_combiner_); + gpr_atm_rel_store(&seen_response_, static_cast(0)); +} + +HealthCheckClient::CallState::~CallState() { + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, "HealthCheckClient %p: destroying CallState %p", + health_check_client_.get(), this); + } + if (call_ != nullptr) GRPC_SUBCHANNEL_CALL_UNREF(call_, "call_ended"); + for (size_t i = 0; i < GRPC_CONTEXT_COUNT; i++) { + if (context_[i].destroy != nullptr) { + context_[i].destroy(context_[i].value); + } + } + // Unset the call combiner cancellation closure. This has the + // effect of scheduling the previously set cancellation closure, if + // any, so that it can release any internal references it may be + // holding to the call stack. Also flush the closures on exec_ctx so that + // filters that schedule cancel notification closures on exec_ctx do not + // need to take a ref of the call stack to guarantee closure liveness. + grpc_call_combiner_set_notify_on_cancel(&call_combiner_, nullptr); + grpc_core::ExecCtx::Get()->Flush(); + grpc_call_combiner_destroy(&call_combiner_); + gpr_arena_destroy(arena_); +} + +void HealthCheckClient::CallState::Orphan() { + grpc_call_combiner_cancel(&call_combiner_, GRPC_ERROR_CANCELLED); + Cancel(); +} + +void HealthCheckClient::CallState::StartCall() { + ConnectedSubchannel::CallArgs args = { + &pollent_, + GRPC_MDSTR_SLASH_GRPC_DOT_HEALTH_DOT_V1_DOT_HEALTH_SLASH_WATCH, + gpr_now(GPR_CLOCK_MONOTONIC), // start_time + GRPC_MILLIS_INF_FUTURE, // deadline + arena_, + context_, + &call_combiner_, + 0, // parent_data_size + }; + grpc_error* error = + health_check_client_->connected_subchannel_->CreateCall(args, &call_); + if (error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, + "HealthCheckClient %p CallState %p: error creating health " + "checking call on subchannel (%s); will retry", + health_check_client_.get(), this, grpc_error_string(error)); + GRPC_ERROR_UNREF(error); + // Schedule instead of running directly, since we must not be + // holding health_check_client_->mu_ when CallEnded() is called. + Ref(DEBUG_LOCATION, "call_end_closure").release(); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&batch_.handler_private.closure, CallEndedRetry, this, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + return; + } + // Initialize payload and batch. + memset(&batch_, 0, sizeof(batch_)); + payload_.context = context_; + batch_.payload = &payload_; + // on_complete callback takes ref, handled manually. + Ref(DEBUG_LOCATION, "on_complete").release(); + batch_.on_complete = GRPC_CLOSURE_INIT(&on_complete_, OnComplete, this, + grpc_schedule_on_exec_ctx); + // Add send_initial_metadata op. + grpc_metadata_batch_init(&send_initial_metadata_); + error = grpc_metadata_batch_add_head( + &send_initial_metadata_, &path_metadata_storage_, + grpc_mdelem_from_slices( + GRPC_MDSTR_PATH, + GRPC_MDSTR_SLASH_GRPC_DOT_HEALTH_DOT_V1_DOT_HEALTH_SLASH_WATCH)); + GPR_ASSERT(error == GRPC_ERROR_NONE); + payload_.send_initial_metadata.send_initial_metadata = + &send_initial_metadata_; + payload_.send_initial_metadata.send_initial_metadata_flags = 0; + payload_.send_initial_metadata.peer_string = nullptr; + batch_.send_initial_metadata = true; + // Add send_message op. + EncodeRequest(health_check_client_->service_name_, &send_message_); + payload_.send_message.send_message.reset(send_message_.get()); + batch_.send_message = true; + // Add send_trailing_metadata op. + grpc_metadata_batch_init(&send_trailing_metadata_); + payload_.send_trailing_metadata.send_trailing_metadata = + &send_trailing_metadata_; + batch_.send_trailing_metadata = true; + // Add recv_initial_metadata op. + grpc_metadata_batch_init(&recv_initial_metadata_); + payload_.recv_initial_metadata.recv_initial_metadata = + &recv_initial_metadata_; + payload_.recv_initial_metadata.recv_flags = nullptr; + payload_.recv_initial_metadata.trailing_metadata_available = nullptr; + payload_.recv_initial_metadata.peer_string = nullptr; + // recv_initial_metadata_ready callback takes ref, handled manually. + Ref(DEBUG_LOCATION, "recv_initial_metadata_ready").release(); + payload_.recv_initial_metadata.recv_initial_metadata_ready = + GRPC_CLOSURE_INIT(&recv_initial_metadata_ready_, RecvInitialMetadataReady, + this, grpc_schedule_on_exec_ctx); + batch_.recv_initial_metadata = true; + // Add recv_message op. + payload_.recv_message.recv_message = &recv_message_; + // recv_message callback takes ref, handled manually. + Ref(DEBUG_LOCATION, "recv_message_ready").release(); + payload_.recv_message.recv_message_ready = GRPC_CLOSURE_INIT( + &recv_message_ready_, RecvMessageReady, this, grpc_schedule_on_exec_ctx); + batch_.recv_message = true; + // Start batch. + StartBatch(&batch_); + // Initialize recv_trailing_metadata batch. + memset(&recv_trailing_metadata_batch_, 0, + sizeof(recv_trailing_metadata_batch_)); + recv_trailing_metadata_batch_.payload = &payload_; + // Add recv_trailing_metadata op. + grpc_metadata_batch_init(&recv_trailing_metadata_); + payload_.recv_trailing_metadata.recv_trailing_metadata = + &recv_trailing_metadata_; + payload_.recv_trailing_metadata.collect_stats = &collect_stats_; + // This callback signals the end of the call, so it relies on the + // initial ref instead of taking a new ref. When it's invoked, the + // initial ref is released. + payload_.recv_trailing_metadata.recv_trailing_metadata_ready = + GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready_, + RecvTrailingMetadataReady, this, + grpc_schedule_on_exec_ctx); + recv_trailing_metadata_batch_.recv_trailing_metadata = true; + // Start recv_trailing_metadata batch. + StartBatch(&recv_trailing_metadata_batch_); +} + +void HealthCheckClient::CallState::StartBatchInCallCombiner(void* arg, + grpc_error* error) { + grpc_transport_stream_op_batch* batch = + static_cast(arg); + grpc_subchannel_call* call = + static_cast(batch->handler_private.extra_arg); + grpc_subchannel_call_process_op(call, batch); +} + +void HealthCheckClient::CallState::StartBatch( + grpc_transport_stream_op_batch* batch) { + batch->handler_private.extra_arg = call_; + GRPC_CLOSURE_INIT(&batch->handler_private.closure, StartBatchInCallCombiner, + batch, grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(&call_combiner_, &batch->handler_private.closure, + GRPC_ERROR_NONE, "start_subchannel_batch"); +} + +void HealthCheckClient::CallState::OnCancelComplete(void* arg, + grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "health_cancel"); + self->Unref(DEBUG_LOCATION, "cancel"); +} + +void HealthCheckClient::CallState::StartCancel(void* arg, grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + auto* batch = grpc_make_transport_stream_op( + GRPC_CLOSURE_CREATE(OnCancelComplete, self, grpc_schedule_on_exec_ctx)); + batch->cancel_stream = true; + batch->payload->cancel_stream.cancel_error = GRPC_ERROR_CANCELLED; + grpc_subchannel_call_process_op(self->call_, batch); +} + +void HealthCheckClient::CallState::Cancel() { + if (call_ != nullptr) { + Ref(DEBUG_LOCATION, "cancel").release(); + GRPC_CALL_COMBINER_START( + &call_combiner_, + GRPC_CLOSURE_CREATE(StartCancel, this, grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE, "health_cancel"); + } +} + +void HealthCheckClient::CallState::OnComplete(void* arg, grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "on_complete"); + grpc_metadata_batch_destroy(&self->send_initial_metadata_); + grpc_metadata_batch_destroy(&self->send_trailing_metadata_); + self->Unref(DEBUG_LOCATION, "on_complete"); +} + +void HealthCheckClient::CallState::RecvInitialMetadataReady(void* arg, + grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "recv_initial_metadata_ready"); + grpc_metadata_batch_destroy(&self->recv_initial_metadata_); + self->Unref(DEBUG_LOCATION, "recv_initial_metadata_ready"); +} + +void HealthCheckClient::CallState::DoneReadingRecvMessage(grpc_error* error) { + recv_message_.reset(); + if (error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(error); + Cancel(); + grpc_slice_buffer_destroy_internal(&recv_message_buffer_); + Unref(DEBUG_LOCATION, "recv_message_ready"); + return; + } + const bool healthy = DecodeResponse(&recv_message_buffer_, &error); + const grpc_connectivity_state state = + healthy ? GRPC_CHANNEL_READY : GRPC_CHANNEL_TRANSIENT_FAILURE; + if (error == GRPC_ERROR_NONE && !healthy) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("backend unhealthy"); + } + health_check_client_->SetHealthStatus(state, error); + gpr_atm_rel_store(&seen_response_, static_cast(1)); + grpc_slice_buffer_destroy_internal(&recv_message_buffer_); + // Start another recv_message batch. + // This re-uses the ref we're holding. + // Note: Can't just reuse batch_ here, since we don't know that all + // callbacks from the original batch have completed yet. + memset(&recv_message_batch_, 0, sizeof(recv_message_batch_)); + recv_message_batch_.payload = &payload_; + payload_.recv_message.recv_message = &recv_message_; + payload_.recv_message.recv_message_ready = GRPC_CLOSURE_INIT( + &recv_message_ready_, RecvMessageReady, this, grpc_schedule_on_exec_ctx); + recv_message_batch_.recv_message = true; + StartBatch(&recv_message_batch_); +} + +grpc_error* HealthCheckClient::CallState::PullSliceFromRecvMessage() { + grpc_slice slice; + grpc_error* error = recv_message_->Pull(&slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&recv_message_buffer_, slice); + } + return error; +} + +void HealthCheckClient::CallState::ContinueReadingRecvMessage() { + while (recv_message_->Next(SIZE_MAX, &recv_message_ready_)) { + grpc_error* error = PullSliceFromRecvMessage(); + if (error != GRPC_ERROR_NONE) { + DoneReadingRecvMessage(error); + return; + } + if (recv_message_buffer_.length == recv_message_->length()) { + DoneReadingRecvMessage(GRPC_ERROR_NONE); + break; + } + } +} + +void HealthCheckClient::CallState::OnByteStreamNext(void* arg, + grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + if (error != GRPC_ERROR_NONE) { + self->DoneReadingRecvMessage(GRPC_ERROR_REF(error)); + return; + } + error = self->PullSliceFromRecvMessage(); + if (error != GRPC_ERROR_NONE) { + self->DoneReadingRecvMessage(error); + return; + } + if (self->recv_message_buffer_.length == self->recv_message_->length()) { + self->DoneReadingRecvMessage(GRPC_ERROR_NONE); + } else { + self->ContinueReadingRecvMessage(); + } +} + +void HealthCheckClient::CallState::RecvMessageReady(void* arg, + grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + GRPC_CALL_COMBINER_STOP(&self->call_combiner_, "recv_message_ready"); + if (self->recv_message_ == nullptr) { + self->Unref(DEBUG_LOCATION, "recv_message_ready"); + return; + } + grpc_slice_buffer_init(&self->recv_message_buffer_); + GRPC_CLOSURE_INIT(&self->recv_message_ready_, OnByteStreamNext, self, + grpc_schedule_on_exec_ctx); + self->ContinueReadingRecvMessage(); + // Ref will continue to be held until we finish draining the byte stream. +} + +void HealthCheckClient::CallState::RecvTrailingMetadataReady( + void* arg, grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + GRPC_CALL_COMBINER_STOP(&self->call_combiner_, + "recv_trailing_metadata_ready"); + // Get call status. + grpc_status_code status = GRPC_STATUS_UNKNOWN; + if (error != GRPC_ERROR_NONE) { + grpc_error_get_status(error, GRPC_MILLIS_INF_FUTURE, &status, + nullptr /* slice */, nullptr /* http_error */, + nullptr /* error_string */); + } else if (self->recv_trailing_metadata_.idx.named.grpc_status != nullptr) { + status = grpc_get_status_code_from_metadata( + self->recv_trailing_metadata_.idx.named.grpc_status->md); + } + if (grpc_health_check_client_trace.enabled()) { + gpr_log(GPR_INFO, + "HealthCheckClient %p CallState %p: health watch failed with " + "status %d", + self->health_check_client_.get(), self, status); + } + // Clean up. + grpc_metadata_batch_destroy(&self->recv_trailing_metadata_); + // For status UNIMPLEMENTED, give up and assume always healthy. + bool retry = true; + if (status == GRPC_STATUS_UNIMPLEMENTED) { + static const char kErrorMessage[] = + "health checking Watch method returned UNIMPLEMENTED; " + "disabling health checks but assuming server is healthy"; + gpr_log(GPR_ERROR, kErrorMessage); + if (self->health_check_client_->channelz_node_ != nullptr) { + self->health_check_client_->channelz_node_->AddTraceEvent( + channelz::ChannelTrace::Error, + grpc_slice_from_static_string(kErrorMessage)); + } + self->health_check_client_->SetHealthStatus(GRPC_CHANNEL_READY, + GRPC_ERROR_NONE); + retry = false; + } + self->CallEnded(retry); +} + +void HealthCheckClient::CallState::CallEndedRetry(void* arg, + grpc_error* error) { + HealthCheckClient::CallState* self = + static_cast(arg); + self->CallEnded(true /* retry */); + self->Unref(DEBUG_LOCATION, "call_end_closure"); +} + +void HealthCheckClient::CallState::CallEnded(bool retry) { + // If this CallState is still in use, this call ended because of a failure, + // so we need to stop using it and optionally create a new one. + // Otherwise, we have deliberately ended this call, and no further action + // is required. + if (this == health_check_client_->call_state_.get()) { + health_check_client_->call_state_.reset(); + if (retry) { + GPR_ASSERT(!health_check_client_->shutting_down_); + if (static_cast(gpr_atm_acq_load(&seen_response_))) { + // If the call fails after we've gotten a successful response, reset + // the backoff and restart the call immediately. + health_check_client_->retry_backoff_.Reset(); + health_check_client_->StartCall(); + } else { + // If the call failed without receiving any messages, retry later. + health_check_client_->StartRetryTimer(); + } + } + } + Unref(DEBUG_LOCATION, "call_ended"); +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.h b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.h new file mode 100644 index 00000000000000..2369b73feac773 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/health/health_check_client.h @@ -0,0 +1,172 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HEALTH_HEALTH_CHECK_CLIENT_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HEALTH_HEALTH_CHECK_CLIENT_H + +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/gpr/arena.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/call_combiner.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/transport/byte_stream.h" +#include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/transport.h" + +namespace grpc_core { + +class HealthCheckClient : public InternallyRefCounted { + public: + HealthCheckClient(const char* service_name, + RefCountedPtr connected_subchannel, + grpc_pollset_set* interested_parties, + RefCountedPtr channelz_node); + + ~HealthCheckClient(); + + // When the health state changes from *state, sets *state to the new + // value and schedules closure. + // Only one closure can be outstanding at a time. + void NotifyOnHealthChange(grpc_connectivity_state* state, + grpc_closure* closure); + + void Orphan() override; + + private: + // Contains a call to the backend and all the data related to the call. + class CallState : public InternallyRefCounted { + public: + CallState(RefCountedPtr health_check_client, + grpc_pollset_set* interested_parties_); + ~CallState(); + + void Orphan() override; + + void StartCall(); + + private: + void Cancel(); + + void StartBatch(grpc_transport_stream_op_batch* batch); + static void StartBatchInCallCombiner(void* arg, grpc_error* error); + + static void CallEndedRetry(void* arg, grpc_error* error); + void CallEnded(bool retry); + + static void OnComplete(void* arg, grpc_error* error); + static void RecvInitialMetadataReady(void* arg, grpc_error* error); + static void RecvMessageReady(void* arg, grpc_error* error); + static void RecvTrailingMetadataReady(void* arg, grpc_error* error); + static void StartCancel(void* arg, grpc_error* error); + static void OnCancelComplete(void* arg, grpc_error* error); + + static void OnByteStreamNext(void* arg, grpc_error* error); + void ContinueReadingRecvMessage(); + grpc_error* PullSliceFromRecvMessage(); + void DoneReadingRecvMessage(grpc_error* error); + + RefCountedPtr health_check_client_; + grpc_polling_entity pollent_; + + gpr_arena* arena_; + grpc_call_combiner call_combiner_; + grpc_call_context_element context_[GRPC_CONTEXT_COUNT] = {}; + + // The streaming call to the backend. Always non-NULL. + grpc_subchannel_call* call_; + + grpc_transport_stream_op_batch_payload payload_; + grpc_transport_stream_op_batch batch_; + grpc_transport_stream_op_batch recv_message_batch_; + grpc_transport_stream_op_batch recv_trailing_metadata_batch_; + + grpc_closure on_complete_; + + // send_initial_metadata + grpc_metadata_batch send_initial_metadata_; + grpc_linked_mdelem path_metadata_storage_; + + // send_message + ManualConstructor send_message_; + + // send_trailing_metadata + grpc_metadata_batch send_trailing_metadata_; + + // recv_initial_metadata + grpc_metadata_batch recv_initial_metadata_; + grpc_closure recv_initial_metadata_ready_; + + // recv_message + OrphanablePtr recv_message_; + grpc_closure recv_message_ready_; + grpc_slice_buffer recv_message_buffer_; + gpr_atm seen_response_; + + // recv_trailing_metadata + grpc_metadata_batch recv_trailing_metadata_; + grpc_transport_stream_stats collect_stats_; + grpc_closure recv_trailing_metadata_ready_; + }; + + void StartCall(); + void StartCallLocked(); // Requires holding mu_. + + void StartRetryTimer(); + static void OnRetryTimer(void* arg, grpc_error* error); + + void SetHealthStatus(grpc_connectivity_state state, grpc_error* error); + void SetHealthStatusLocked(grpc_connectivity_state state, + grpc_error* error); // Requires holding mu_. + + const char* service_name_; // Do not own. + RefCountedPtr connected_subchannel_; + grpc_pollset_set* interested_parties_; // Do not own. + RefCountedPtr channelz_node_; + + gpr_mu mu_; + grpc_connectivity_state state_ = GRPC_CHANNEL_CONNECTING; + grpc_error* error_ = GRPC_ERROR_NONE; + grpc_connectivity_state* notify_state_ = nullptr; + grpc_closure* on_health_changed_ = nullptr; + bool shutting_down_ = false; + + // The data associated with the current health check call. It holds a ref + // to this HealthCheckClient object. + OrphanablePtr call_state_; + + // Call retry state. + BackOff retry_backoff_; + grpc_timer retry_timer_; + grpc_closure retry_timer_callback_; + bool retry_timer_callback_pending_ = false; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HEALTH_HEALTH_CHECK_CLIENT_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.cc b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.cc new file mode 100644 index 00000000000000..0716e4681816c2 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.cc @@ -0,0 +1,371 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" + +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/http/format_request.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/uri/uri_parser.h" + +typedef struct http_connect_handshaker { + // Base class. Must be first. + grpc_handshaker base; + + gpr_refcount refcount; + gpr_mu mu; + + bool shutdown; + // Endpoint and read buffer to destroy after a shutdown. + grpc_endpoint* endpoint_to_destroy; + grpc_slice_buffer* read_buffer_to_destroy; + + // State saved while performing the handshake. + grpc_handshaker_args* args; + grpc_closure* on_handshake_done; + + // Objects for processing the HTTP CONNECT request and response. + grpc_slice_buffer write_buffer; + grpc_closure request_done_closure; + grpc_closure response_read_closure; + grpc_http_parser http_parser; + grpc_http_response http_response; +} http_connect_handshaker; + +// Unref and clean up handshaker. +static void http_connect_handshaker_unref(http_connect_handshaker* handshaker) { + if (gpr_unref(&handshaker->refcount)) { + gpr_mu_destroy(&handshaker->mu); + if (handshaker->endpoint_to_destroy != nullptr) { + grpc_endpoint_destroy(handshaker->endpoint_to_destroy); + } + if (handshaker->read_buffer_to_destroy != nullptr) { + grpc_slice_buffer_destroy_internal(handshaker->read_buffer_to_destroy); + gpr_free(handshaker->read_buffer_to_destroy); + } + grpc_slice_buffer_destroy_internal(&handshaker->write_buffer); + grpc_http_parser_destroy(&handshaker->http_parser); + grpc_http_response_destroy(&handshaker->http_response); + gpr_free(handshaker); + } +} + +// Set args fields to nullptr, saving the endpoint and read buffer for +// later destruction. +static void cleanup_args_for_failure_locked( + http_connect_handshaker* handshaker) { + handshaker->endpoint_to_destroy = handshaker->args->endpoint; + handshaker->args->endpoint = nullptr; + handshaker->read_buffer_to_destroy = handshaker->args->read_buffer; + handshaker->args->read_buffer = nullptr; + grpc_channel_args_destroy(handshaker->args->args); + handshaker->args->args = nullptr; +} + +// If the handshake failed or we're shutting down, clean up and invoke the +// callback with the error. +static void handshake_failed_locked(http_connect_handshaker* handshaker, + grpc_error* error) { + if (error == GRPC_ERROR_NONE) { + // If we were shut down after an endpoint operation succeeded but + // before the endpoint callback was invoked, we need to generate our + // own error. + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown"); + } + if (!handshaker->shutdown) { + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(handshaker->args->endpoint, GRPC_ERROR_REF(error)); + // Not shutting down, so the handshake failed. Clean up before + // invoking the callback. + cleanup_args_for_failure_locked(handshaker); + // Set shutdown to true so that subsequent calls to + // http_connect_handshaker_shutdown() do nothing. + handshaker->shutdown = true; + } + // Invoke callback. + GRPC_CLOSURE_SCHED(handshaker->on_handshake_done, error); +} + +// Callback invoked when finished writing HTTP CONNECT request. +static void on_write_done(void* arg, grpc_error* error) { + http_connect_handshaker* handshaker = + static_cast(arg); + gpr_mu_lock(&handshaker->mu); + if (error != GRPC_ERROR_NONE || handshaker->shutdown) { + // If the write failed or we're shutting down, clean up and invoke the + // callback with the error. + handshake_failed_locked(handshaker, GRPC_ERROR_REF(error)); + gpr_mu_unlock(&handshaker->mu); + http_connect_handshaker_unref(handshaker); + } else { + // Otherwise, read the response. + // The read callback inherits our ref to the handshaker. + grpc_endpoint_read(handshaker->args->endpoint, + handshaker->args->read_buffer, + &handshaker->response_read_closure); + gpr_mu_unlock(&handshaker->mu); + } +} + +// Callback invoked for reading HTTP CONNECT response. +static void on_read_done(void* arg, grpc_error* error) { + http_connect_handshaker* handshaker = + static_cast(arg); + gpr_mu_lock(&handshaker->mu); + if (error != GRPC_ERROR_NONE || handshaker->shutdown) { + // If the read failed or we're shutting down, clean up and invoke the + // callback with the error. + handshake_failed_locked(handshaker, GRPC_ERROR_REF(error)); + goto done; + } + // Add buffer to parser. + for (size_t i = 0; i < handshaker->args->read_buffer->count; ++i) { + if (GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i]) > 0) { + size_t body_start_offset = 0; + error = grpc_http_parser_parse(&handshaker->http_parser, + handshaker->args->read_buffer->slices[i], + &body_start_offset); + if (error != GRPC_ERROR_NONE) { + handshake_failed_locked(handshaker, error); + goto done; + } + if (handshaker->http_parser.state == GRPC_HTTP_BODY) { + // Remove the data we've already read from the read buffer, + // leaving only the leftover bytes (if any). + grpc_slice_buffer tmp_buffer; + grpc_slice_buffer_init(&tmp_buffer); + if (body_start_offset < + GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i])) { + grpc_slice_buffer_add( + &tmp_buffer, + grpc_slice_split_tail(&handshaker->args->read_buffer->slices[i], + body_start_offset)); + } + grpc_slice_buffer_addn(&tmp_buffer, + &handshaker->args->read_buffer->slices[i + 1], + handshaker->args->read_buffer->count - i - 1); + grpc_slice_buffer_swap(handshaker->args->read_buffer, &tmp_buffer); + grpc_slice_buffer_destroy_internal(&tmp_buffer); + break; + } + } + } + // If we're not done reading the response, read more data. + // TODO(roth): In practice, I suspect that the response to a CONNECT + // request will never include a body, in which case this check is + // sufficient. However, the language of RFC-2817 doesn't explicitly + // forbid the response from including a body. If there is a body, + // it's possible that we might have parsed part but not all of the + // body, in which case this check will cause us to fail to parse the + // remainder of the body. If that ever becomes an issue, we may + // need to fix the HTTP parser to understand when the body is + // complete (e.g., handling chunked transfer encoding or looking + // at the Content-Length: header). + if (handshaker->http_parser.state != GRPC_HTTP_BODY) { + grpc_slice_buffer_reset_and_unref_internal(handshaker->args->read_buffer); + grpc_endpoint_read(handshaker->args->endpoint, + handshaker->args->read_buffer, + &handshaker->response_read_closure); + gpr_mu_unlock(&handshaker->mu); + return; + } + // Make sure we got a 2xx response. + if (handshaker->http_response.status < 200 || + handshaker->http_response.status >= 300) { + char* msg; + gpr_asprintf(&msg, "HTTP proxy returned response code %d", + handshaker->http_response.status); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + handshake_failed_locked(handshaker, error); + goto done; + } + // Success. Invoke handshake-done callback. + GRPC_CLOSURE_SCHED(handshaker->on_handshake_done, error); +done: + // Set shutdown to true so that subsequent calls to + // http_connect_handshaker_shutdown() do nothing. + handshaker->shutdown = true; + gpr_mu_unlock(&handshaker->mu); + http_connect_handshaker_unref(handshaker); +} + +// +// Public handshaker methods +// + +static void http_connect_handshaker_destroy(grpc_handshaker* handshaker_in) { + http_connect_handshaker* handshaker = + reinterpret_cast(handshaker_in); + http_connect_handshaker_unref(handshaker); +} + +static void http_connect_handshaker_shutdown(grpc_handshaker* handshaker_in, + grpc_error* why) { + http_connect_handshaker* handshaker = + reinterpret_cast(handshaker_in); + gpr_mu_lock(&handshaker->mu); + if (!handshaker->shutdown) { + handshaker->shutdown = true; + grpc_endpoint_shutdown(handshaker->args->endpoint, GRPC_ERROR_REF(why)); + cleanup_args_for_failure_locked(handshaker); + } + gpr_mu_unlock(&handshaker->mu); + GRPC_ERROR_UNREF(why); +} + +static void http_connect_handshaker_do_handshake( + grpc_handshaker* handshaker_in, grpc_tcp_server_acceptor* acceptor, + grpc_closure* on_handshake_done, grpc_handshaker_args* args) { + http_connect_handshaker* handshaker = + reinterpret_cast(handshaker_in); + // Check for HTTP CONNECT channel arg. + // If not found, invoke on_handshake_done without doing anything. + const grpc_arg* arg = + grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_SERVER); + char* server_name = grpc_channel_arg_get_string(arg); + if (server_name == nullptr) { + // Set shutdown to true so that subsequent calls to + // http_connect_handshaker_shutdown() do nothing. + gpr_mu_lock(&handshaker->mu); + handshaker->shutdown = true; + gpr_mu_unlock(&handshaker->mu); + GRPC_CLOSURE_SCHED(on_handshake_done, GRPC_ERROR_NONE); + return; + } + // Get headers from channel args. + arg = grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_HEADERS); + char* arg_header_string = grpc_channel_arg_get_string(arg); + grpc_http_header* headers = nullptr; + size_t num_headers = 0; + char** header_strings = nullptr; + size_t num_header_strings = 0; + if (arg_header_string != nullptr) { + gpr_string_split(arg_header_string, "\n", &header_strings, + &num_header_strings); + headers = static_cast( + gpr_malloc(sizeof(grpc_http_header) * num_header_strings)); + for (size_t i = 0; i < num_header_strings; ++i) { + char* sep = strchr(header_strings[i], ':'); + if (sep == nullptr) { + gpr_log(GPR_ERROR, "skipping unparseable HTTP CONNECT header: %s", + header_strings[i]); + continue; + } + *sep = '\0'; + headers[num_headers].key = header_strings[i]; + headers[num_headers].value = sep + 1; + ++num_headers; + } + } + // Save state in the handshaker object. + gpr_mu_lock(&handshaker->mu); + handshaker->args = args; + handshaker->on_handshake_done = on_handshake_done; + // Log connection via proxy. + char* proxy_name = grpc_endpoint_get_peer(args->endpoint); + gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name, + proxy_name); + gpr_free(proxy_name); + // Construct HTTP CONNECT request. + grpc_httpcli_request request; + memset(&request, 0, sizeof(request)); + request.host = server_name; + request.http.method = (char*)"CONNECT"; + request.http.path = server_name; + request.http.hdrs = headers; + request.http.hdr_count = num_headers; + request.handshaker = &grpc_httpcli_plaintext; + grpc_slice request_slice = grpc_httpcli_format_connect_request(&request); + grpc_slice_buffer_add(&handshaker->write_buffer, request_slice); + // Clean up. + gpr_free(headers); + for (size_t i = 0; i < num_header_strings; ++i) { + gpr_free(header_strings[i]); + } + gpr_free(header_strings); + // Take a new ref to be held by the write callback. + gpr_ref(&handshaker->refcount); + grpc_endpoint_write(args->endpoint, &handshaker->write_buffer, + &handshaker->request_done_closure, nullptr); + gpr_mu_unlock(&handshaker->mu); +} + +static const grpc_handshaker_vtable http_connect_handshaker_vtable = { + http_connect_handshaker_destroy, http_connect_handshaker_shutdown, + http_connect_handshaker_do_handshake, "http_connect"}; + +static grpc_handshaker* grpc_http_connect_handshaker_create() { + http_connect_handshaker* handshaker = + static_cast(gpr_malloc(sizeof(*handshaker))); + memset(handshaker, 0, sizeof(*handshaker)); + grpc_handshaker_init(&http_connect_handshaker_vtable, &handshaker->base); + gpr_mu_init(&handshaker->mu); + gpr_ref_init(&handshaker->refcount, 1); + grpc_slice_buffer_init(&handshaker->write_buffer); + GRPC_CLOSURE_INIT(&handshaker->request_done_closure, on_write_done, + handshaker, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&handshaker->response_read_closure, on_read_done, + handshaker, grpc_schedule_on_exec_ctx); + grpc_http_parser_init(&handshaker->http_parser, GRPC_HTTP_RESPONSE, + &handshaker->http_response); + return &handshaker->base; +} + +// +// handshaker factory +// + +static void handshaker_factory_add_handshakers( + grpc_handshaker_factory* factory, const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) { + grpc_handshake_manager_add(handshake_mgr, + grpc_http_connect_handshaker_create()); +} + +static void handshaker_factory_destroy(grpc_handshaker_factory* factory) {} + +static const grpc_handshaker_factory_vtable handshaker_factory_vtable = { + handshaker_factory_add_handshakers, handshaker_factory_destroy}; + +static grpc_handshaker_factory handshaker_factory = { + &handshaker_factory_vtable}; + +void grpc_http_connect_register_handshaker_factory() { + grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_CLIENT, + &handshaker_factory); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.h b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.h new file mode 100644 index 00000000000000..928a23dc93665a --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/http_connect_handshaker.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H + +/// Channel arg indicating the server in HTTP CONNECT request (string). +/// The presence of this arg triggers the use of HTTP CONNECT. +#define GRPC_ARG_HTTP_CONNECT_SERVER "grpc.http_connect_server" + +/// Channel arg indicating HTTP CONNECT headers (string). +/// Multiple headers are separated by newlines. Key/value pairs are +/// seperated by colons. +#define GRPC_ARG_HTTP_CONNECT_HEADERS "grpc.http_connect_headers" + +/// Registers handshaker factory. +void grpc_http_connect_register_handshaker_factory(); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.cc b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.cc new file mode 100644 index 00000000000000..8951a2920c41e2 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.cc @@ -0,0 +1,212 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/http_proxy.h" + +#include +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/uri/uri_parser.h" + +/** + * Parses the 'https_proxy' env var (fallback on 'http_proxy') and returns the + * proxy hostname to resolve or nullptr on error. Also sets 'user_cred' to user + * credentials if present in the 'http_proxy' env var, otherwise leaves it + * unchanged. It is caller's responsibility to gpr_free user_cred. + */ +static char* get_http_proxy_server(char** user_cred) { + GPR_ASSERT(user_cred != nullptr); + char* proxy_name = nullptr; + char** authority_strs = nullptr; + size_t authority_nstrs; + /* Prefer using 'https_proxy'. Fallback on 'http_proxy' if it is not set. The + * fallback behavior can be removed if there's a demand for it. + */ + char* uri_str = gpr_getenv("https_proxy"); + if (uri_str == nullptr) uri_str = gpr_getenv("http_proxy"); + if (uri_str == nullptr) return nullptr; + grpc_uri* uri = grpc_uri_parse(uri_str, false /* suppress_errors */); + if (uri == nullptr || uri->authority == nullptr) { + gpr_log(GPR_ERROR, "cannot parse value of 'http_proxy' env var"); + goto done; + } + if (strcmp(uri->scheme, "http") != 0) { + gpr_log(GPR_ERROR, "'%s' scheme not supported in proxy URI", uri->scheme); + goto done; + } + /* Split on '@' to separate user credentials from host */ + gpr_string_split(uri->authority, "@", &authority_strs, &authority_nstrs); + GPR_ASSERT(authority_nstrs != 0); /* should have at least 1 string */ + if (authority_nstrs == 1) { + /* User cred not present in authority */ + proxy_name = authority_strs[0]; + } else if (authority_nstrs == 2) { + /* User cred found */ + *user_cred = authority_strs[0]; + proxy_name = authority_strs[1]; + gpr_log(GPR_DEBUG, "userinfo found in proxy URI"); + } else { + /* Bad authority */ + for (size_t i = 0; i < authority_nstrs; i++) { + gpr_free(authority_strs[i]); + } + proxy_name = nullptr; + } + gpr_free(authority_strs); +done: + gpr_free(uri_str); + grpc_uri_destroy(uri); + return proxy_name; +} + +/** + * Checks the value of GRPC_ARG_ENABLE_HTTP_PROXY to determine if http_proxy + * should be used. + */ +bool http_proxy_enabled(const grpc_channel_args* args) { + const grpc_arg* arg = + grpc_channel_args_find(args, GRPC_ARG_ENABLE_HTTP_PROXY); + return grpc_channel_arg_get_bool(arg, true); +} + +static bool proxy_mapper_map_name(grpc_proxy_mapper* mapper, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + if (!http_proxy_enabled(args)) { + return false; + } + char* user_cred = nullptr; + *name_to_resolve = get_http_proxy_server(&user_cred); + if (*name_to_resolve == nullptr) return false; + char* no_proxy_str = nullptr; + grpc_uri* uri = grpc_uri_parse(server_uri, false /* suppress_errors */); + if (uri == nullptr || uri->path[0] == '\0') { + gpr_log(GPR_ERROR, + "'http_proxy' environment variable set, but cannot " + "parse server URI '%s' -- not using proxy", + server_uri); + goto no_use_proxy; + } + if (strcmp(uri->scheme, "unix") == 0) { + gpr_log(GPR_INFO, "not using proxy for Unix domain socket '%s'", + server_uri); + goto no_use_proxy; + } + no_proxy_str = gpr_getenv("no_proxy"); + if (no_proxy_str != nullptr) { + static const char* NO_PROXY_SEPARATOR = ","; + bool use_proxy = true; + char* server_host; + char* server_port; + if (!gpr_split_host_port(uri->path[0] == '/' ? uri->path + 1 : uri->path, + &server_host, &server_port)) { + gpr_log(GPR_INFO, + "unable to split host and port, not checking no_proxy list for " + "host '%s'", + server_uri); + gpr_free(no_proxy_str); + } else { + size_t uri_len = strlen(server_host); + char** no_proxy_hosts; + size_t num_no_proxy_hosts; + gpr_string_split(no_proxy_str, NO_PROXY_SEPARATOR, &no_proxy_hosts, + &num_no_proxy_hosts); + for (size_t i = 0; i < num_no_proxy_hosts; i++) { + char* no_proxy_entry = no_proxy_hosts[i]; + size_t no_proxy_len = strlen(no_proxy_entry); + if (no_proxy_len <= uri_len && + gpr_stricmp(no_proxy_entry, &server_host[uri_len - no_proxy_len]) == + 0) { + gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'", + server_uri); + use_proxy = false; + break; + } + } + for (size_t i = 0; i < num_no_proxy_hosts; i++) { + gpr_free(no_proxy_hosts[i]); + } + gpr_free(no_proxy_hosts); + gpr_free(server_host); + gpr_free(server_port); + gpr_free(no_proxy_str); + if (!use_proxy) goto no_use_proxy; + } + } + grpc_arg args_to_add[2]; + args_to_add[0] = grpc_channel_arg_string_create( + (char*)GRPC_ARG_HTTP_CONNECT_SERVER, + uri->path[0] == '/' ? uri->path + 1 : uri->path); + if (user_cred != nullptr) { + /* Use base64 encoding for user credentials as stated in RFC 7617 */ + char* encoded_user_cred = + grpc_base64_encode(user_cred, strlen(user_cred), 0, 0); + char* header; + gpr_asprintf(&header, "Proxy-Authorization:Basic %s", encoded_user_cred); + gpr_free(encoded_user_cred); + args_to_add[1] = grpc_channel_arg_string_create( + (char*)GRPC_ARG_HTTP_CONNECT_HEADERS, header); + *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 2); + gpr_free(header); + } else { + *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 1); + } + grpc_uri_destroy(uri); + gpr_free(user_cred); + return true; +no_use_proxy: + if (uri != nullptr) grpc_uri_destroy(uri); + gpr_free(*name_to_resolve); + *name_to_resolve = nullptr; + gpr_free(user_cred); + return false; +} + +static bool proxy_mapper_map_address(grpc_proxy_mapper* mapper, + const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args) { + return false; +} + +static void proxy_mapper_destroy(grpc_proxy_mapper* mapper) {} + +static const grpc_proxy_mapper_vtable proxy_mapper_vtable = { + proxy_mapper_map_name, proxy_mapper_map_address, proxy_mapper_destroy}; + +static grpc_proxy_mapper proxy_mapper = {&proxy_mapper_vtable}; + +void grpc_register_http_proxy_mapper() { + grpc_proxy_mapper_register(true /* at_start */, &proxy_mapper); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.h b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.h new file mode 100644 index 00000000000000..34694931d04e1d --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/http_proxy.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H + +void grpc_register_http_proxy_mapper(); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.cc new file mode 100644 index 00000000000000..b4e803689e9e2e --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.cc @@ -0,0 +1,59 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy.h" +#include "src/core/lib/iomgr/combiner.h" + +grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount( + false, "lb_policy_refcount"); + +namespace grpc_core { + +LoadBalancingPolicy::LoadBalancingPolicy(const Args& args) + : InternallyRefCounted(&grpc_trace_lb_policy_refcount), + combiner_(GRPC_COMBINER_REF(args.combiner, "lb_policy")), + client_channel_factory_(args.client_channel_factory), + interested_parties_(grpc_pollset_set_create()), + request_reresolution_(nullptr) {} + +LoadBalancingPolicy::~LoadBalancingPolicy() { + grpc_pollset_set_destroy(interested_parties_); + GRPC_COMBINER_UNREF(combiner_, "lb_policy"); +} + +void LoadBalancingPolicy::TryReresolutionLocked( + grpc_core::TraceFlag* grpc_lb_trace, grpc_error* error) { + if (request_reresolution_ != nullptr) { + GRPC_CLOSURE_SCHED(request_reresolution_, error); + request_reresolution_ = nullptr; + if (grpc_lb_trace->enabled()) { + gpr_log(GPR_INFO, + "%s %p: scheduling re-resolution closure with error=%s.", + grpc_lb_trace->name(), this, grpc_error_string(error)); + } + } else { + if (grpc_lb_trace->enabled()) { + gpr_log(GPR_INFO, "%s %p: no available re-resolution closure.", + grpc_lb_trace->name(), this); + } + } +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.h new file mode 100644 index 00000000000000..293d8e960cf65b --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy.h @@ -0,0 +1,215 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H + +#include + +#include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/transport/connectivity_state.h" + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_lb_policy_refcount; + +namespace grpc_core { + +/// Interface for load balancing policies. +/// +/// Note: All methods with a "Locked" suffix must be called from the +/// combiner passed to the constructor. +/// +/// Any I/O done by the LB policy should be done under the pollset_set +/// returned by \a interested_parties(). +class LoadBalancingPolicy : public InternallyRefCounted { + public: + struct Args { + /// The combiner under which all LB policy calls will be run. + /// Policy does NOT take ownership of the reference to the combiner. + // TODO(roth): Once we have a C++-like interface for combiners, this + // API should change to take a smart pointer that does pass ownership + // of a reference. + grpc_combiner* combiner = nullptr; + /// Used to create channels and subchannels. + grpc_client_channel_factory* client_channel_factory = nullptr; + /// Channel args from the resolver. + /// Note that the LB policy gets the set of addresses from the + /// GRPC_ARG_SERVER_ADDRESS_LIST channel arg. + grpc_channel_args* args = nullptr; + /// Load balancing config from the resolver. + grpc_json* lb_config = nullptr; + }; + + /// State used for an LB pick. + struct PickState { + /// Initial metadata associated with the picking call. + grpc_metadata_batch* initial_metadata = nullptr; + /// Pointer to bitmask used for selective cancelling. See + /// \a CancelMatchingPicksLocked() and \a GRPC_INITIAL_METADATA_* in + /// grpc_types.h. + uint32_t* initial_metadata_flags = nullptr; + /// Storage for LB token in \a initial_metadata, or nullptr if not used. + grpc_linked_mdelem lb_token_mdelem_storage; + /// Closure to run when pick is complete, if not completed synchronously. + /// If null, pick will fail if a result is not available synchronously. + grpc_closure* on_complete = nullptr; + /// Will be set to the selected subchannel, or nullptr on failure or when + /// the LB policy decides to drop the call. + RefCountedPtr connected_subchannel; + /// Will be populated with context to pass to the subchannel call, if + /// needed. + grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT] = {}; + /// Next pointer. For internal use by LB policy. + PickState* next = nullptr; + }; + + // Not copyable nor movable. + LoadBalancingPolicy(const LoadBalancingPolicy&) = delete; + LoadBalancingPolicy& operator=(const LoadBalancingPolicy&) = delete; + + /// Returns the name of the LB policy. + virtual const char* name() const GRPC_ABSTRACT; + + /// Updates the policy with a new set of \a args and a new \a lb_config from + /// the resolver. Note that the LB policy gets the set of addresses from the + /// GRPC_ARG_SERVER_ADDRESS_LIST channel arg. + virtual void UpdateLocked(const grpc_channel_args& args, + grpc_json* lb_config) GRPC_ABSTRACT; + + /// Finds an appropriate subchannel for a call, based on data in \a pick. + /// \a pick must remain alive until the pick is complete. + /// + /// If a result is known immediately, returns true, setting \a *error + /// upon failure. Otherwise, \a pick->on_complete will be invoked once + /// the pick is complete with its error argument set to indicate success + /// or failure. + /// + /// If \a pick->on_complete is null and no result is known immediately, + /// a synchronous failure will be returned (i.e., \a *error will be + /// set and true will be returned). + virtual bool PickLocked(PickState* pick, grpc_error** error) GRPC_ABSTRACT; + + /// Cancels \a pick. + /// The \a on_complete callback of the pending pick will be invoked with + /// \a pick->connected_subchannel set to null. + virtual void CancelPickLocked(PickState* pick, + grpc_error* error) GRPC_ABSTRACT; + + /// Cancels all pending picks for which their \a initial_metadata_flags (as + /// given in the call to \a PickLocked()) matches + /// \a initial_metadata_flags_eq when ANDed with + /// \a initial_metadata_flags_mask. + virtual void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) GRPC_ABSTRACT; + + /// Requests a notification when the connectivity state of the policy + /// changes from \a *state. When that happens, sets \a *state to the + /// new state and schedules \a closure. + virtual void NotifyOnStateChangeLocked(grpc_connectivity_state* state, + grpc_closure* closure) GRPC_ABSTRACT; + + /// Returns the policy's current connectivity state. Sets \a error to + /// the associated error, if any. + virtual grpc_connectivity_state CheckConnectivityLocked( + grpc_error** connectivity_error) GRPC_ABSTRACT; + + /// Hands off pending picks to \a new_policy. + virtual void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) + GRPC_ABSTRACT; + + /// Tries to enter a READY connectivity state. + /// TODO(roth): As part of restructuring how we handle IDLE state, + /// consider whether this method is still needed. + virtual void ExitIdleLocked() GRPC_ABSTRACT; + + /// Resets connection backoff. + virtual void ResetBackoffLocked() GRPC_ABSTRACT; + + /// Populates child_subchannels and child_channels with the uuids of this + /// LB policy's referenced children. This is not invoked from the + /// client_channel's combiner. The implementation is responsible for + /// providing its own synchronization. + virtual void FillChildRefsForChannelz( + channelz::ChildRefsList* child_subchannels, + channelz::ChildRefsList* child_channels) GRPC_ABSTRACT; + + void Orphan() override { + // Invoke ShutdownAndUnrefLocked() inside of the combiner. + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(&LoadBalancingPolicy::ShutdownAndUnrefLocked, this, + grpc_combiner_scheduler(combiner_)), + GRPC_ERROR_NONE); + } + + /// Sets the re-resolution closure to \a request_reresolution. + void SetReresolutionClosureLocked(grpc_closure* request_reresolution) { + GPR_ASSERT(request_reresolution_ == nullptr); + request_reresolution_ = request_reresolution; + } + + grpc_pollset_set* interested_parties() const { return interested_parties_; } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE + + explicit LoadBalancingPolicy(const Args& args); + virtual ~LoadBalancingPolicy(); + + grpc_combiner* combiner() const { return combiner_; } + grpc_client_channel_factory* client_channel_factory() const { + return client_channel_factory_; + } + + /// Shuts down the policy. Any pending picks that have not been + /// handed off to a new policy via HandOffPendingPicksLocked() will be + /// failed. + virtual void ShutdownLocked() GRPC_ABSTRACT; + + /// Tries to request a re-resolution. + void TryReresolutionLocked(grpc_core::TraceFlag* grpc_lb_trace, + grpc_error* error); + + private: + static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored) { + LoadBalancingPolicy* policy = static_cast(arg); + policy->ShutdownLocked(); + policy->Unref(); + } + + /// Combiner under which LB policy actions take place. + grpc_combiner* combiner_; + /// Client channel factory, used to create channels and subchannels. + grpc_client_channel_factory* client_channel_factory_; + /// Owned pointer to interested parties in load balancing decisions. + grpc_pollset_set* interested_parties_; + /// Callback to force a re-resolution. + grpc_closure* request_reresolution_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc new file mode 100644 index 00000000000000..399bb452f45f08 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc @@ -0,0 +1,144 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" + +#include +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/profiling/timers.h" + +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + return GRPC_ERROR_NONE; +} + +static void destroy_channel_elem(grpc_channel_element* elem) {} + +namespace { + +struct call_data { + call_data(const grpc_call_element_args& args) { + if (args.context[GRPC_GRPCLB_CLIENT_STATS].value != nullptr) { + // Get stats object from context and take a ref. + client_stats = static_cast( + args.context[GRPC_GRPCLB_CLIENT_STATS].value) + ->Ref(); + // Record call started. + client_stats->AddCallStarted(); + } + } + + // Stats object to update. + grpc_core::RefCountedPtr client_stats; + // State for intercepting send_initial_metadata. + grpc_closure on_complete_for_send; + grpc_closure* original_on_complete_for_send; + bool send_initial_metadata_succeeded = false; + // State for intercepting recv_initial_metadata. + grpc_closure recv_initial_metadata_ready; + grpc_closure* original_recv_initial_metadata_ready; + bool recv_initial_metadata_succeeded = false; +}; + +} // namespace + +static void on_complete_for_send(void* arg, grpc_error* error) { + call_data* calld = static_cast(arg); + if (error == GRPC_ERROR_NONE) { + calld->send_initial_metadata_succeeded = true; + } + GRPC_CLOSURE_RUN(calld->original_on_complete_for_send, GRPC_ERROR_REF(error)); +} + +static void recv_initial_metadata_ready(void* arg, grpc_error* error) { + call_data* calld = static_cast(arg); + if (error == GRPC_ERROR_NONE) { + calld->recv_initial_metadata_succeeded = true; + } + GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); +} + +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + GPR_ASSERT(args->context != nullptr); + new (elem->call_data) call_data(*args); + return GRPC_ERROR_NONE; +} + +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = static_cast(elem->call_data); + if (calld->client_stats != nullptr) { + // Record call finished, optionally setting client_failed_to_send and + // received. + calld->client_stats->AddCallFinished( + !calld->send_initial_metadata_succeeded /* client_failed_to_send */, + calld->recv_initial_metadata_succeeded /* known_received */); + // All done, so unref the stats object. + // TODO(roth): Eliminate this once filter stack is converted to C++. + calld->client_stats.reset(); + } + calld->~call_data(); +} + +static void start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + call_data* calld = static_cast(elem->call_data); + GPR_TIMER_SCOPE("clr_start_transport_stream_op_batch", 0); + if (calld->client_stats != nullptr) { + // Intercept send_initial_metadata. + if (batch->send_initial_metadata) { + calld->original_on_complete_for_send = batch->on_complete; + GRPC_CLOSURE_INIT(&calld->on_complete_for_send, on_complete_for_send, + calld, grpc_schedule_on_exec_ctx); + batch->on_complete = &calld->on_complete_for_send; + } + // Intercept recv_initial_metadata. + if (batch->recv_initial_metadata) { + calld->original_recv_initial_metadata_ready = + batch->payload->recv_initial_metadata.recv_initial_metadata_ready; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, calld, + grpc_schedule_on_exec_ctx); + batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + } + // Chain to next filter. + grpc_call_next_op(elem, batch); +} + +const grpc_channel_filter grpc_client_load_reporting_filter = { + start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, // sizeof(channel_data) + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "client_load_reporting"}; diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h new file mode 100644 index 00000000000000..838e2ef1cae0d5 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_client_load_reporting_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_CLIENT_LOAD_REPORTING_FILTER_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc new file mode 100644 index 00000000000000..ba40febd534445 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -0,0 +1,1867 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// Implementation of the gRPC LB policy. +/// +/// This policy takes as input a list of resolved addresses, which must +/// include at least one balancer address. +/// +/// An internal channel (\a lb_channel_) is created for the addresses +/// from that are balancers. This channel behaves just like a regular +/// channel that uses pick_first to select from the list of balancer +/// addresses. +/// +/// The first time the policy gets a request for a pick, a ping, or to exit +/// the idle state, \a StartPickingLocked() is called. This method is +/// responsible for instantiating the internal *streaming* call to the LB +/// server (whichever address pick_first chose). The call will be complete +/// when either the balancer sends status or when we cancel the call (e.g., +/// because we are shutting down). In needed, we retry the call. If we +/// received at least one valid message from the server, a new call attempt +/// will be made immediately; otherwise, we apply back-off delays between +/// attempts. +/// +/// We maintain an internal round_robin policy instance for distributing +/// requests across backends. Whenever we receive a new serverlist from +/// the balancer, we update the round_robin policy with the new list of +/// addresses. If we cannot communicate with the balancer on startup, +/// however, we may enter fallback mode, in which case we will populate +/// the RR policy's addresses from the backend addresses returned by the +/// resolver. +/// +/// Once an RR policy instance is in place (and getting updated as described), +/// calls for a pick, a ping, or a cancellation will be serviced right +/// away by forwarding them to the RR instance. Any time there's no RR +/// policy available (i.e., right after the creation of the gRPCLB policy), +/// pick and ping requests are added to a list of pending picks and pings +/// to be flushed and serviced when the RR policy instance becomes available. +/// +/// \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the +/// high level design and details. + +// With the addition of a libuv endpoint, sockaddr.h now includes uv.h when +// using that endpoint. Because of various transitive includes in uv.h, +// including windows.h on Windows, uv.h must be included before other system +// headers. Therefore, sockaddr.h must always be included first. +#include + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/static_metadata.h" + +#define GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_GRPCLB_RECONNECT_JITTER 0.2 +#define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000 + +#define GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN "grpc.grpclb_address_lb_token" + +namespace grpc_core { + +TraceFlag grpc_lb_glb_trace(false, "glb"); + +namespace { + +constexpr char kGrpclb[] = "grpclb"; + +class GrpcLb : public LoadBalancingPolicy { + public: + explicit GrpcLb(const Args& args); + + const char* name() const override { return kGrpclb; } + + void UpdateLocked(const grpc_channel_args& args, + grpc_json* lb_config) override; + bool PickLocked(PickState* pick, grpc_error** error) override; + void CancelPickLocked(PickState* pick, grpc_error* error) override; + void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) override; + void NotifyOnStateChangeLocked(grpc_connectivity_state* state, + grpc_closure* closure) override; + grpc_connectivity_state CheckConnectivityLocked( + grpc_error** connectivity_error) override; + void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override; + void ExitIdleLocked() override; + void ResetBackoffLocked() override; + void FillChildRefsForChannelz( + channelz::ChildRefsList* child_subchannels, + channelz::ChildRefsList* child_channels) override; + + private: + /// Linked list of pending pick requests. It stores all information needed to + /// eventually call (Round Robin's) pick() on them. They mainly stay pending + /// waiting for the RR policy to be created. + /// + /// Note that when a pick is sent to the RR policy, we inject our own + /// on_complete callback, so that we can intercept the result before + /// invoking the original on_complete callback. This allows us to set the + /// LB token metadata and add client_stats to the call context. + /// See \a pending_pick_complete() for details. + struct PendingPick { + // The grpclb instance that created the wrapping. This instance is not + // owned; reference counts are untouched. It's used only for logging + // purposes. + GrpcLb* grpclb_policy; + // The original pick. + PickState* pick; + // Our on_complete closure and the original one. + grpc_closure on_complete; + grpc_closure* original_on_complete; + // Stats for client-side load reporting. + RefCountedPtr client_stats; + // Next pending pick. + PendingPick* next = nullptr; + }; + + /// Contains a call to the LB server and all the data related to the call. + class BalancerCallState : public InternallyRefCounted { + public: + explicit BalancerCallState( + RefCountedPtr parent_grpclb_policy); + + // It's the caller's responsibility to ensure that Orphan() is called from + // inside the combiner. + void Orphan() override; + + void StartQuery(); + + GrpcLbClientStats* client_stats() const { return client_stats_.get(); } + + bool seen_initial_response() const { return seen_initial_response_; } + + private: + // So Delete() can access our private dtor. + template + friend void grpc_core::Delete(T*); + + ~BalancerCallState(); + + GrpcLb* grpclb_policy() const { + return static_cast(grpclb_policy_.get()); + } + + void ScheduleNextClientLoadReportLocked(); + void SendClientLoadReportLocked(); + + static bool LoadReportCountersAreZero(grpc_grpclb_request* request); + + static void MaybeSendClientLoadReportLocked(void* arg, grpc_error* error); + static void ClientLoadReportDoneLocked(void* arg, grpc_error* error); + static void OnInitialRequestSentLocked(void* arg, grpc_error* error); + static void OnBalancerMessageReceivedLocked(void* arg, grpc_error* error); + static void OnBalancerStatusReceivedLocked(void* arg, grpc_error* error); + + // The owning LB policy. + RefCountedPtr grpclb_policy_; + + // The streaming call to the LB server. Always non-NULL. + grpc_call* lb_call_ = nullptr; + + // recv_initial_metadata + grpc_metadata_array lb_initial_metadata_recv_; + + // send_message + grpc_byte_buffer* send_message_payload_ = nullptr; + grpc_closure lb_on_initial_request_sent_; + + // recv_message + grpc_byte_buffer* recv_message_payload_ = nullptr; + grpc_closure lb_on_balancer_message_received_; + bool seen_initial_response_ = false; + + // recv_trailing_metadata + grpc_closure lb_on_balancer_status_received_; + grpc_metadata_array lb_trailing_metadata_recv_; + grpc_status_code lb_call_status_; + grpc_slice lb_call_status_details_; + + // The stats for client-side load reporting associated with this LB call. + // Created after the first serverlist is received. + RefCountedPtr client_stats_; + grpc_millis client_stats_report_interval_ = 0; + grpc_timer client_load_report_timer_; + bool client_load_report_timer_callback_pending_ = false; + bool last_client_load_report_counters_were_zero_ = false; + bool client_load_report_is_due_ = false; + // The closure used for either the load report timer or the callback for + // completion of sending the load report. + grpc_closure client_load_report_closure_; + }; + + ~GrpcLb(); + + void ShutdownLocked() override; + + // Helper function used in ctor and UpdateLocked(). + void ProcessChannelArgsLocked(const grpc_channel_args& args); + + // Methods for dealing with the balancer channel and call. + void StartPickingLocked(); + void StartBalancerCallLocked(); + static void OnFallbackTimerLocked(void* arg, grpc_error* error); + void StartBalancerCallRetryTimerLocked(); + static void OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error); + static void OnBalancerChannelConnectivityChangedLocked(void* arg, + grpc_error* error); + + // Pending pick methods. + static void PendingPickSetMetadataAndContext(PendingPick* pp); + PendingPick* PendingPickCreate(PickState* pick); + void AddPendingPick(PendingPick* pp); + static void OnPendingPickComplete(void* arg, grpc_error* error); + + // Methods for dealing with the RR policy. + void CreateOrUpdateRoundRobinPolicyLocked(); + grpc_channel_args* CreateRoundRobinPolicyArgsLocked(); + void CreateRoundRobinPolicyLocked(const Args& args); + bool PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp, + grpc_error** error); + void UpdateConnectivityStateFromRoundRobinPolicyLocked( + grpc_error* rr_state_error); + static void OnRoundRobinConnectivityChangedLocked(void* arg, + grpc_error* error); + static void OnRoundRobinRequestReresolutionLocked(void* arg, + grpc_error* error); + + // Who the client is trying to communicate with. + const char* server_name_ = nullptr; + + // Current channel args from the resolver. + grpc_channel_args* args_ = nullptr; + + // Internal state. + bool started_picking_ = false; + bool shutting_down_ = false; + grpc_connectivity_state_tracker state_tracker_; + + // The channel for communicating with the LB server. + grpc_channel* lb_channel_ = nullptr; + // Mutex to protect the channel to the LB server. This is used when + // processing a channelz request. + gpr_mu lb_channel_mu_; + grpc_connectivity_state lb_channel_connectivity_; + grpc_closure lb_channel_on_connectivity_changed_; + // Are we already watching the LB channel's connectivity? + bool watching_lb_channel_ = false; + // Response generator to inject address updates into lb_channel_. + RefCountedPtr response_generator_; + + // The data associated with the current LB call. It holds a ref to this LB + // policy. It's initialized every time we query for backends. It's reset to + // NULL whenever the current LB call is no longer needed (e.g., the LB policy + // is shutting down, or the LB call has ended). A non-NULL lb_calld_ always + // contains a non-NULL lb_call_. + OrphanablePtr lb_calld_; + // Timeout in milliseconds for the LB call. 0 means no deadline. + int lb_call_timeout_ms_ = 0; + // Balancer call retry state. + BackOff lb_call_backoff_; + bool retry_timer_callback_pending_ = false; + grpc_timer lb_call_retry_timer_; + grpc_closure lb_on_call_retry_; + + // The deserialized response from the balancer. May be nullptr until one + // such response has arrived. + grpc_grpclb_serverlist* serverlist_ = nullptr; + // Index into serverlist for next pick. + // If the server at this index is a drop, we return a drop. + // Otherwise, we delegate to the RR policy. + size_t serverlist_index_ = 0; + + // Timeout in milliseconds for before using fallback backend addresses. + // 0 means not using fallback. + int lb_fallback_timeout_ms_ = 0; + // The backend addresses from the resolver. + UniquePtr fallback_backend_addresses_; + // Fallback timer. + bool fallback_timer_callback_pending_ = false; + grpc_timer lb_fallback_timer_; + grpc_closure lb_on_fallback_; + + // Pending picks that are waiting on the RR policy's connectivity. + PendingPick* pending_picks_ = nullptr; + + // The RR policy to use for the backends. + OrphanablePtr rr_policy_; + grpc_connectivity_state rr_connectivity_state_; + grpc_closure on_rr_connectivity_changed_; + grpc_closure on_rr_request_reresolution_; +}; + +// +// serverlist parsing code +// + +// vtable for LB token channel arg. +void* lb_token_copy(void* token) { + return token == nullptr + ? nullptr + : (void*)GRPC_MDELEM_REF(grpc_mdelem{(uintptr_t)token}).payload; +} +void lb_token_destroy(void* token) { + if (token != nullptr) { + GRPC_MDELEM_UNREF(grpc_mdelem{(uintptr_t)token}); + } +} +int lb_token_cmp(void* token1, void* token2) { + // Always indicate a match, since we don't want this channel arg to + // affect the subchannel's key in the index. + return 0; +} +const grpc_arg_pointer_vtable lb_token_arg_vtable = { + lb_token_copy, lb_token_destroy, lb_token_cmp}; + +bool IsServerValid(const grpc_grpclb_server* server, size_t idx, bool log) { + if (server->drop) return false; + const grpc_grpclb_ip_address* ip = &server->ip_address; + if (GPR_UNLIKELY(server->port >> 16 != 0)) { + if (log) { + gpr_log(GPR_ERROR, + "Invalid port '%d' at index %lu of serverlist. Ignoring.", + server->port, (unsigned long)idx); + } + return false; + } + if (GPR_UNLIKELY(ip->size != 4 && ip->size != 16)) { + if (log) { + gpr_log(GPR_ERROR, + "Expected IP to be 4 or 16 bytes, got %d at index %lu of " + "serverlist. Ignoring", + ip->size, (unsigned long)idx); + } + return false; + } + return true; +} + +void ParseServer(const grpc_grpclb_server* server, + grpc_resolved_address* addr) { + memset(addr, 0, sizeof(*addr)); + if (server->drop) return; + const uint16_t netorder_port = grpc_htons((uint16_t)server->port); + /* the addresses are given in binary format (a in(6)_addr struct) in + * server->ip_address.bytes. */ + const grpc_grpclb_ip_address* ip = &server->ip_address; + if (ip->size == 4) { + addr->len = static_cast(sizeof(grpc_sockaddr_in)); + grpc_sockaddr_in* addr4 = reinterpret_cast(&addr->addr); + addr4->sin_family = GRPC_AF_INET; + memcpy(&addr4->sin_addr, ip->bytes, ip->size); + addr4->sin_port = netorder_port; + } else if (ip->size == 16) { + addr->len = static_cast(sizeof(grpc_sockaddr_in6)); + grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)&addr->addr; + addr6->sin6_family = GRPC_AF_INET6; + memcpy(&addr6->sin6_addr, ip->bytes, ip->size); + addr6->sin6_port = netorder_port; + } +} + +// Returns addresses extracted from \a serverlist. +ServerAddressList ProcessServerlist(const grpc_grpclb_serverlist* serverlist) { + ServerAddressList addresses; + for (size_t i = 0; i < serverlist->num_servers; ++i) { + const grpc_grpclb_server* server = serverlist->servers[i]; + if (!IsServerValid(serverlist->servers[i], i, false)) continue; + // Address processing. + grpc_resolved_address addr; + ParseServer(server, &addr); + // LB token processing. + grpc_mdelem lb_token; + if (server->has_load_balance_token) { + const size_t lb_token_max_length = + GPR_ARRAY_SIZE(server->load_balance_token); + const size_t lb_token_length = + strnlen(server->load_balance_token, lb_token_max_length); + grpc_slice lb_token_mdstr = grpc_slice_from_copied_buffer( + server->load_balance_token, lb_token_length); + lb_token = grpc_mdelem_from_slices(GRPC_MDSTR_LB_TOKEN, lb_token_mdstr); + } else { + char* uri = grpc_sockaddr_to_uri(&addr); + gpr_log(GPR_INFO, + "Missing LB token for backend address '%s'. The empty token will " + "be used instead", + uri); + gpr_free(uri); + lb_token = GRPC_MDELEM_LB_TOKEN_EMPTY; + } + // Add address. + grpc_arg arg = grpc_channel_arg_pointer_create( + const_cast(GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN), + (void*)lb_token.payload, &lb_token_arg_vtable); + grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1); + addresses.emplace_back(addr, args); + // Clean up. + GRPC_MDELEM_UNREF(lb_token); + } + return addresses; +} + +// +// GrpcLb::BalancerCallState +// + +GrpcLb::BalancerCallState::BalancerCallState( + RefCountedPtr parent_grpclb_policy) + : InternallyRefCounted(&grpc_lb_glb_trace), + grpclb_policy_(std::move(parent_grpclb_policy)) { + GPR_ASSERT(grpclb_policy_ != nullptr); + GPR_ASSERT(!grpclb_policy()->shutting_down_); + // Init the LB call. Note that the LB call will progress every time there's + // activity in grpclb_policy_->interested_parties(), which is comprised of + // the polling entities from client_channel. + GPR_ASSERT(grpclb_policy()->server_name_ != nullptr); + GPR_ASSERT(grpclb_policy()->server_name_[0] != '\0'); + const grpc_millis deadline = + grpclb_policy()->lb_call_timeout_ms_ == 0 + ? GRPC_MILLIS_INF_FUTURE + : ExecCtx::Get()->Now() + grpclb_policy()->lb_call_timeout_ms_; + lb_call_ = grpc_channel_create_pollset_set_call( + grpclb_policy()->lb_channel_, nullptr, GRPC_PROPAGATE_DEFAULTS, + grpclb_policy_->interested_parties(), + GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD, + nullptr, deadline, nullptr); + // Init the LB call request payload. + grpc_grpclb_request* request = + grpc_grpclb_request_create(grpclb_policy()->server_name_); + grpc_slice request_payload_slice = grpc_grpclb_request_encode(request); + send_message_payload_ = + grpc_raw_byte_buffer_create(&request_payload_slice, 1); + grpc_slice_unref_internal(request_payload_slice); + grpc_grpclb_request_destroy(request); + // Init other data associated with the LB call. + grpc_metadata_array_init(&lb_initial_metadata_recv_); + grpc_metadata_array_init(&lb_trailing_metadata_recv_); + GRPC_CLOSURE_INIT(&lb_on_initial_request_sent_, OnInitialRequestSentLocked, + this, grpc_combiner_scheduler(grpclb_policy()->combiner())); + GRPC_CLOSURE_INIT(&lb_on_balancer_message_received_, + OnBalancerMessageReceivedLocked, this, + grpc_combiner_scheduler(grpclb_policy()->combiner())); + GRPC_CLOSURE_INIT(&lb_on_balancer_status_received_, + OnBalancerStatusReceivedLocked, this, + grpc_combiner_scheduler(grpclb_policy()->combiner())); +} + +GrpcLb::BalancerCallState::~BalancerCallState() { + GPR_ASSERT(lb_call_ != nullptr); + grpc_call_unref(lb_call_); + grpc_metadata_array_destroy(&lb_initial_metadata_recv_); + grpc_metadata_array_destroy(&lb_trailing_metadata_recv_); + grpc_byte_buffer_destroy(send_message_payload_); + grpc_byte_buffer_destroy(recv_message_payload_); + grpc_slice_unref_internal(lb_call_status_details_); +} + +void GrpcLb::BalancerCallState::Orphan() { + GPR_ASSERT(lb_call_ != nullptr); + // If we are here because grpclb_policy wants to cancel the call, + // lb_on_balancer_status_received_ will complete the cancellation and clean + // up. Otherwise, we are here because grpclb_policy has to orphan a failed + // call, then the following cancellation will be a no-op. + grpc_call_cancel(lb_call_, nullptr); + if (client_load_report_timer_callback_pending_) { + grpc_timer_cancel(&client_load_report_timer_); + } + // Note that the initial ref is hold by lb_on_balancer_status_received_ + // instead of the caller of this function. So the corresponding unref happens + // in lb_on_balancer_status_received_ instead of here. +} + +void GrpcLb::BalancerCallState::StartQuery() { + GPR_ASSERT(lb_call_ != nullptr); + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, "[grpclb %p] lb_calld=%p: Starting LB call %p", + grpclb_policy_.get(), this, lb_call_); + } + // Create the ops. + grpc_call_error call_error; + grpc_op ops[3]; + memset(ops, 0, sizeof(ops)); + // Op: send initial metadata. + grpc_op* op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op->flags = 0; + op->reserved = nullptr; + op++; + // Op: send request message. + GPR_ASSERT(send_message_payload_ != nullptr); + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = send_message_payload_; + op->flags = 0; + op->reserved = nullptr; + op++; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "on_initial_request_sent"); + self.release(); + call_error = grpc_call_start_batch_and_execute( + lb_call_, ops, (size_t)(op - ops), &lb_on_initial_request_sent_); + GPR_ASSERT(GRPC_CALL_OK == call_error); + // Op: recv initial metadata. + op = ops; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &lb_initial_metadata_recv_; + op->flags = 0; + op->reserved = nullptr; + op++; + // Op: recv response. + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &recv_message_payload_; + op->flags = 0; + op->reserved = nullptr; + op++; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + self = Ref(DEBUG_LOCATION, "on_message_received"); + self.release(); + call_error = grpc_call_start_batch_and_execute( + lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_message_received_); + GPR_ASSERT(GRPC_CALL_OK == call_error); + // Op: recv server status. + op = ops; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = + &lb_trailing_metadata_recv_; + op->data.recv_status_on_client.status = &lb_call_status_; + op->data.recv_status_on_client.status_details = &lb_call_status_details_; + op->flags = 0; + op->reserved = nullptr; + op++; + // This callback signals the end of the LB call, so it relies on the initial + // ref instead of a new ref. When it's invoked, it's the initial ref that is + // unreffed. + call_error = grpc_call_start_batch_and_execute( + lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_status_received_); + GPR_ASSERT(GRPC_CALL_OK == call_error); +}; + +void GrpcLb::BalancerCallState::ScheduleNextClientLoadReportLocked() { + const grpc_millis next_client_load_report_time = + ExecCtx::Get()->Now() + client_stats_report_interval_; + GRPC_CLOSURE_INIT(&client_load_report_closure_, + MaybeSendClientLoadReportLocked, this, + grpc_combiner_scheduler(grpclb_policy()->combiner())); + grpc_timer_init(&client_load_report_timer_, next_client_load_report_time, + &client_load_report_closure_); + client_load_report_timer_callback_pending_ = true; +} + +void GrpcLb::BalancerCallState::MaybeSendClientLoadReportLocked( + void* arg, grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); + lb_calld->client_load_report_timer_callback_pending_ = false; + if (error != GRPC_ERROR_NONE || lb_calld != grpclb_policy->lb_calld_.get()) { + lb_calld->Unref(DEBUG_LOCATION, "client_load_report"); + return; + } + // If we've already sent the initial request, then we can go ahead and send + // the load report. Otherwise, we need to wait until the initial request has + // been sent to send this (see OnInitialRequestSentLocked()). + if (lb_calld->send_message_payload_ == nullptr) { + lb_calld->SendClientLoadReportLocked(); + } else { + lb_calld->client_load_report_is_due_ = true; + } +} + +bool GrpcLb::BalancerCallState::LoadReportCountersAreZero( + grpc_grpclb_request* request) { + GrpcLbClientStats::DroppedCallCounts* drop_entries = + static_cast( + request->client_stats.calls_finished_with_drop.arg); + return request->client_stats.num_calls_started == 0 && + request->client_stats.num_calls_finished == 0 && + request->client_stats.num_calls_finished_with_client_failed_to_send == + 0 && + request->client_stats.num_calls_finished_known_received == 0 && + (drop_entries == nullptr || drop_entries->size() == 0); +} + +void GrpcLb::BalancerCallState::SendClientLoadReportLocked() { + // Construct message payload. + GPR_ASSERT(send_message_payload_ == nullptr); + grpc_grpclb_request* request = + grpc_grpclb_load_report_request_create_locked(client_stats_.get()); + // Skip client load report if the counters were all zero in the last + // report and they are still zero in this one. + if (LoadReportCountersAreZero(request)) { + if (last_client_load_report_counters_were_zero_) { + grpc_grpclb_request_destroy(request); + ScheduleNextClientLoadReportLocked(); + return; + } + last_client_load_report_counters_were_zero_ = true; + } else { + last_client_load_report_counters_were_zero_ = false; + } + grpc_slice request_payload_slice = grpc_grpclb_request_encode(request); + send_message_payload_ = + grpc_raw_byte_buffer_create(&request_payload_slice, 1); + grpc_slice_unref_internal(request_payload_slice); + grpc_grpclb_request_destroy(request); + // Send the report. + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_SEND_MESSAGE; + op.data.send_message.send_message = send_message_payload_; + GRPC_CLOSURE_INIT(&client_load_report_closure_, ClientLoadReportDoneLocked, + this, grpc_combiner_scheduler(grpclb_policy()->combiner())); + grpc_call_error call_error = grpc_call_start_batch_and_execute( + lb_call_, &op, 1, &client_load_report_closure_); + if (GPR_UNLIKELY(call_error != GRPC_CALL_OK)) { + gpr_log(GPR_ERROR, + "[grpclb %p] lb_calld=%p call_error=%d sending client load report", + grpclb_policy_.get(), this, call_error); + GPR_ASSERT(GRPC_CALL_OK == call_error); + } +} + +void GrpcLb::BalancerCallState::ClientLoadReportDoneLocked(void* arg, + grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); + grpc_byte_buffer_destroy(lb_calld->send_message_payload_); + lb_calld->send_message_payload_ = nullptr; + if (error != GRPC_ERROR_NONE || lb_calld != grpclb_policy->lb_calld_.get()) { + lb_calld->Unref(DEBUG_LOCATION, "client_load_report"); + return; + } + lb_calld->ScheduleNextClientLoadReportLocked(); +} + +void GrpcLb::BalancerCallState::OnInitialRequestSentLocked(void* arg, + grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + grpc_byte_buffer_destroy(lb_calld->send_message_payload_); + lb_calld->send_message_payload_ = nullptr; + // If we attempted to send a client load report before the initial request was + // sent (and this lb_calld is still in use), send the load report now. + if (lb_calld->client_load_report_is_due_ && + lb_calld == lb_calld->grpclb_policy()->lb_calld_.get()) { + lb_calld->SendClientLoadReportLocked(); + lb_calld->client_load_report_is_due_ = false; + } + lb_calld->Unref(DEBUG_LOCATION, "on_initial_request_sent"); +} + +void GrpcLb::BalancerCallState::OnBalancerMessageReceivedLocked( + void* arg, grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); + // Null payload means the LB call was cancelled. + if (lb_calld != grpclb_policy->lb_calld_.get() || + lb_calld->recv_message_payload_ == nullptr) { + lb_calld->Unref(DEBUG_LOCATION, "on_message_received"); + return; + } + grpc_byte_buffer_reader bbr; + grpc_byte_buffer_reader_init(&bbr, lb_calld->recv_message_payload_); + grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr); + grpc_byte_buffer_reader_destroy(&bbr); + grpc_byte_buffer_destroy(lb_calld->recv_message_payload_); + lb_calld->recv_message_payload_ = nullptr; + grpc_grpclb_initial_response* initial_response; + grpc_grpclb_serverlist* serverlist; + if (!lb_calld->seen_initial_response_ && + (initial_response = grpc_grpclb_initial_response_parse(response_slice)) != + nullptr) { + // Have NOT seen initial response, look for initial response. + if (initial_response->has_client_stats_report_interval) { + lb_calld->client_stats_report_interval_ = GPR_MAX( + GPR_MS_PER_SEC, grpc_grpclb_duration_to_millis( + &initial_response->client_stats_report_interval)); + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] lb_calld=%p: Received initial LB response " + "message; client load reporting interval = %" PRId64 + " milliseconds", + grpclb_policy, lb_calld, + lb_calld->client_stats_report_interval_); + } + } else if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] lb_calld=%p: Received initial LB response message; " + "client load reporting NOT enabled", + grpclb_policy, lb_calld); + } + grpc_grpclb_initial_response_destroy(initial_response); + lb_calld->seen_initial_response_ = true; + } else if ((serverlist = grpc_grpclb_response_parse_serverlist( + response_slice)) != nullptr) { + // Have seen initial response, look for serverlist. + GPR_ASSERT(lb_calld->lb_call_ != nullptr); + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] lb_calld=%p: Serverlist with %" PRIuPTR + " servers received", + grpclb_policy, lb_calld, serverlist->num_servers); + for (size_t i = 0; i < serverlist->num_servers; ++i) { + grpc_resolved_address addr; + ParseServer(serverlist->servers[i], &addr); + char* ipport; + grpc_sockaddr_to_string(&ipport, &addr, false); + gpr_log(GPR_INFO, + "[grpclb %p] lb_calld=%p: Serverlist[%" PRIuPTR "]: %s", + grpclb_policy, lb_calld, i, ipport); + gpr_free(ipport); + } + } + // Start sending client load report only after we start using the + // serverlist returned from the current LB call. + if (lb_calld->client_stats_report_interval_ > 0 && + lb_calld->client_stats_ == nullptr) { + lb_calld->client_stats_.reset(New()); + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report"); + self.release(); + lb_calld->ScheduleNextClientLoadReportLocked(); + } + // Check if the serverlist differs from the previous one. + if (grpc_grpclb_serverlist_equals(grpclb_policy->serverlist_, serverlist)) { + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] lb_calld=%p: Incoming server list identical to " + "current, ignoring.", + grpclb_policy, lb_calld); + } + grpc_grpclb_destroy_serverlist(serverlist); + } else { // New serverlist. + if (grpclb_policy->serverlist_ != nullptr) { + // Dispose of the old serverlist. + grpc_grpclb_destroy_serverlist(grpclb_policy->serverlist_); + } else { + // Dispose of the fallback. + grpclb_policy->fallback_backend_addresses_.reset(); + if (grpclb_policy->fallback_timer_callback_pending_) { + grpc_timer_cancel(&grpclb_policy->lb_fallback_timer_); + } + } + // Update the serverlist in the GrpcLb instance. This serverlist + // instance will be destroyed either upon the next update or when the + // GrpcLb instance is destroyed. + grpclb_policy->serverlist_ = serverlist; + grpclb_policy->serverlist_index_ = 0; + grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked(); + } + } else { + // No valid initial response or serverlist found. + char* response_slice_str = + grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX); + gpr_log(GPR_ERROR, + "[grpclb %p] lb_calld=%p: Invalid LB response received: '%s'. " + "Ignoring.", + grpclb_policy, lb_calld, response_slice_str); + gpr_free(response_slice_str); + } + grpc_slice_unref_internal(response_slice); + if (!grpclb_policy->shutting_down_) { + // Keep listening for serverlist updates. + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_MESSAGE; + op.data.recv_message.recv_message = &lb_calld->recv_message_payload_; + op.flags = 0; + op.reserved = nullptr; + // Reuse the "OnBalancerMessageReceivedLocked" ref taken in StartQuery(). + const grpc_call_error call_error = grpc_call_start_batch_and_execute( + lb_calld->lb_call_, &op, 1, + &lb_calld->lb_on_balancer_message_received_); + GPR_ASSERT(GRPC_CALL_OK == call_error); + } else { + lb_calld->Unref(DEBUG_LOCATION, "on_message_received+grpclb_shutdown"); + } +} + +void GrpcLb::BalancerCallState::OnBalancerStatusReceivedLocked( + void* arg, grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + GrpcLb* grpclb_policy = lb_calld->grpclb_policy(); + GPR_ASSERT(lb_calld->lb_call_ != nullptr); + if (grpc_lb_glb_trace.enabled()) { + char* status_details = + grpc_slice_to_c_string(lb_calld->lb_call_status_details_); + gpr_log(GPR_INFO, + "[grpclb %p] lb_calld=%p: Status from LB server received. " + "Status = %d, details = '%s', (lb_call: %p), error '%s'", + grpclb_policy, lb_calld, lb_calld->lb_call_status_, status_details, + lb_calld->lb_call_, grpc_error_string(error)); + gpr_free(status_details); + } + grpclb_policy->TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_NONE); + // If this lb_calld is still in use, this call ended because of a failure so + // we want to retry connecting. Otherwise, we have deliberately ended this + // call and no further action is required. + if (lb_calld == grpclb_policy->lb_calld_.get()) { + grpclb_policy->lb_calld_.reset(); + GPR_ASSERT(!grpclb_policy->shutting_down_); + if (lb_calld->seen_initial_response_) { + // If we lose connection to the LB server, reset the backoff and restart + // the LB call immediately. + grpclb_policy->lb_call_backoff_.Reset(); + grpclb_policy->StartBalancerCallLocked(); + } else { + // If this LB call fails establishing any connection to the LB server, + // retry later. + grpclb_policy->StartBalancerCallRetryTimerLocked(); + } + } + lb_calld->Unref(DEBUG_LOCATION, "lb_call_ended"); +} + +// +// helper code for creating balancer channel +// + +ServerAddressList ExtractBalancerAddresses(const ServerAddressList& addresses) { + ServerAddressList balancer_addresses; + for (size_t i = 0; i < addresses.size(); ++i) { + if (addresses[i].IsBalancer()) { + // Strip out the is_balancer channel arg, since we don't want to + // recursively use the grpclb policy in the channel used to talk to + // the balancers. Note that we do NOT strip out the balancer_name + // channel arg, since we need that to set the authority correctly + // to talk to the balancers. + static const char* args_to_remove[] = { + GRPC_ARG_ADDRESS_IS_BALANCER, + }; + balancer_addresses.emplace_back( + addresses[i].address(), + grpc_channel_args_copy_and_remove(addresses[i].args(), args_to_remove, + GPR_ARRAY_SIZE(args_to_remove))); + } + } + return balancer_addresses; +} + +/* Returns the channel args for the LB channel, used to create a bidirectional + * stream for the reception of load balancing updates. + * + * Inputs: + * - \a addresses: corresponding to the balancers. + * - \a response_generator: in order to propagate updates from the resolver + * above the grpclb policy. + * - \a args: other args inherited from the grpclb policy. */ +grpc_channel_args* BuildBalancerChannelArgs( + const ServerAddressList& addresses, + FakeResolverResponseGenerator* response_generator, + const grpc_channel_args* args) { + ServerAddressList balancer_addresses = ExtractBalancerAddresses(addresses); + // Channel args to remove. + static const char* args_to_remove[] = { + // LB policy name, since we want to use the default (pick_first) in + // the LB channel. + GRPC_ARG_LB_POLICY_NAME, + // The channel arg for the server URI, since that will be different for + // the LB channel than for the parent channel. The client channel + // factory will re-add this arg with the right value. + GRPC_ARG_SERVER_URI, + // The resolved addresses, which will be generated by the name resolver + // used in the LB channel. Note that the LB channel will use the fake + // resolver, so this won't actually generate a query to DNS (or some + // other name service). However, the addresses returned by the fake + // resolver will have is_balancer=false, whereas our own addresses have + // is_balancer=true. We need the LB channel to return addresses with + // is_balancer=false so that it does not wind up recursively using the + // grpclb LB policy, as per the special case logic in client_channel.c. + GRPC_ARG_SERVER_ADDRESS_LIST, + // The fake resolver response generator, because we are replacing it + // with the one from the grpclb policy, used to propagate updates to + // the LB channel. + GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR, + // The LB channel should use the authority indicated by the target + // authority table (see \a grpc_lb_policy_grpclb_modify_lb_channel_args), + // as opposed to the authority from the parent channel. + GRPC_ARG_DEFAULT_AUTHORITY, + // Just as for \a GRPC_ARG_DEFAULT_AUTHORITY, the LB channel should be + // treated as a stand-alone channel and not inherit this argument from the + // args of the parent channel. + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, + }; + // Channel args to add. + const grpc_arg args_to_add[] = { + // New address list. + // Note that we pass these in both when creating the LB channel + // and via the fake resolver. The latter is what actually gets used. + CreateServerAddressListChannelArg(&balancer_addresses), + // The fake resolver response generator, which we use to inject + // address updates into the LB channel. + grpc_core::FakeResolverResponseGenerator::MakeChannelArg( + response_generator), + // A channel arg indicating the target is a grpclb load balancer. + grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER), 1), + // A channel arg indicating this is an internal channels, aka it is + // owned by components in Core, not by the user application. + grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), 1), + }; + // Construct channel args. + grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove( + args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), args_to_add, + GPR_ARRAY_SIZE(args_to_add)); + // Make any necessary modifications for security. + return grpc_lb_policy_grpclb_modify_lb_channel_args(new_args); +} + +// +// ctor and dtor +// + +GrpcLb::GrpcLb(const LoadBalancingPolicy::Args& args) + : LoadBalancingPolicy(args), + response_generator_(MakeRefCounted()), + lb_call_backoff_( + BackOff::Options() + .set_initial_backoff(GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS * + 1000) + .set_multiplier(GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(GRPC_GRPCLB_RECONNECT_JITTER) + .set_max_backoff(GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * + 1000)) { + // Initialization. + gpr_mu_init(&lb_channel_mu_); + grpc_subchannel_index_ref(); + GRPC_CLOSURE_INIT(&lb_channel_on_connectivity_changed_, + &GrpcLb::OnBalancerChannelConnectivityChangedLocked, this, + grpc_combiner_scheduler(args.combiner)); + GRPC_CLOSURE_INIT(&on_rr_connectivity_changed_, + &GrpcLb::OnRoundRobinConnectivityChangedLocked, this, + grpc_combiner_scheduler(args.combiner)); + GRPC_CLOSURE_INIT(&on_rr_request_reresolution_, + &GrpcLb::OnRoundRobinRequestReresolutionLocked, this, + grpc_combiner_scheduler(args.combiner)); + grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, "grpclb"); + // Record server name. + const grpc_arg* arg = grpc_channel_args_find(args.args, GRPC_ARG_SERVER_URI); + const char* server_uri = grpc_channel_arg_get_string(arg); + GPR_ASSERT(server_uri != nullptr); + grpc_uri* uri = grpc_uri_parse(server_uri, true); + GPR_ASSERT(uri->path[0] != '\0'); + server_name_ = gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path); + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] Will use '%s' as the server name for LB request.", + this, server_name_); + } + grpc_uri_destroy(uri); + // Record LB call timeout. + arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS); + lb_call_timeout_ms_ = grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX}); + // Record fallback timeout. + arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS); + lb_fallback_timeout_ms_ = grpc_channel_arg_get_integer( + arg, {GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS, 0, INT_MAX}); + // Process channel args. + ProcessChannelArgsLocked(*args.args); +} + +GrpcLb::~GrpcLb() { + GPR_ASSERT(pending_picks_ == nullptr); + gpr_mu_destroy(&lb_channel_mu_); + gpr_free((void*)server_name_); + grpc_channel_args_destroy(args_); + grpc_connectivity_state_destroy(&state_tracker_); + if (serverlist_ != nullptr) { + grpc_grpclb_destroy_serverlist(serverlist_); + } + grpc_subchannel_index_unref(); +} + +void GrpcLb::ShutdownLocked() { + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown"); + shutting_down_ = true; + lb_calld_.reset(); + if (retry_timer_callback_pending_) { + grpc_timer_cancel(&lb_call_retry_timer_); + } + if (fallback_timer_callback_pending_) { + grpc_timer_cancel(&lb_fallback_timer_); + } + rr_policy_.reset(); + TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_CANCELLED); + // We destroy the LB channel here instead of in our destructor because + // destroying the channel triggers a last callback to + // OnBalancerChannelConnectivityChangedLocked(), and we need to be + // alive when that callback is invoked. + if (lb_channel_ != nullptr) { + gpr_mu_lock(&lb_channel_mu_); + grpc_channel_destroy(lb_channel_); + lb_channel_ = nullptr; + gpr_mu_unlock(&lb_channel_mu_); + } + grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_REF(error), "grpclb_shutdown"); + // Clear pending picks. + PendingPick* pp; + while ((pp = pending_picks_) != nullptr) { + pending_picks_ = pp->next; + pp->pick->connected_subchannel.reset(); + // Note: pp is deleted in this callback. + GRPC_CLOSURE_SCHED(&pp->on_complete, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +// +// public methods +// + +void GrpcLb::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) { + PendingPick* pp; + while ((pp = pending_picks_) != nullptr) { + pending_picks_ = pp->next; + pp->pick->on_complete = pp->original_on_complete; + grpc_error* error = GRPC_ERROR_NONE; + if (new_policy->PickLocked(pp->pick, &error)) { + // Synchronous return; schedule closure. + GRPC_CLOSURE_SCHED(pp->pick->on_complete, error); + } + Delete(pp); + } +} + +// Cancel a specific pending pick. +// +// A grpclb pick progresses as follows: +// - If there's a Round Robin policy (rr_policy_) available, it'll be +// handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From +// that point onwards, it'll be RR's responsibility. For cancellations, that +// implies the pick needs also be cancelled by the RR instance. +// - Otherwise, without an RR instance, picks stay pending at this policy's +// level (grpclb), inside the pending_picks_ list. To cancel these, +// we invoke the completion closure and set the pick's connected +// subchannel to nullptr right here. +void GrpcLb::CancelPickLocked(PickState* pick, grpc_error* error) { + PendingPick* pp = pending_picks_; + pending_picks_ = nullptr; + while (pp != nullptr) { + PendingPick* next = pp->next; + if (pp->pick == pick) { + pick->connected_subchannel.reset(); + // Note: pp is deleted in this callback. + GRPC_CLOSURE_SCHED(&pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = pending_picks_; + pending_picks_ = pp; + } + pp = next; + } + if (rr_policy_ != nullptr) { + rr_policy_->CancelPickLocked(pick, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +// Cancel all pending picks. +// +// A grpclb pick progresses as follows: +// - If there's a Round Robin policy (rr_policy_) available, it'll be +// handed over to the RR policy (in CreateRoundRobinPolicyLocked()). From +// that point onwards, it'll be RR's responsibility. For cancellations, that +// implies the pick needs also be cancelled by the RR instance. +// - Otherwise, without an RR instance, picks stay pending at this policy's +// level (grpclb), inside the pending_picks_ list. To cancel these, +// we invoke the completion closure and set the pick's connected +// subchannel to nullptr right here. +void GrpcLb::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) { + PendingPick* pp = pending_picks_; + pending_picks_ = nullptr; + while (pp != nullptr) { + PendingPick* next = pp->next; + if ((*pp->pick->initial_metadata_flags & initial_metadata_flags_mask) == + initial_metadata_flags_eq) { + // Note: pp is deleted in this callback. + GRPC_CLOSURE_SCHED(&pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = pending_picks_; + pending_picks_ = pp; + } + pp = next; + } + if (rr_policy_ != nullptr) { + rr_policy_->CancelMatchingPicksLocked(initial_metadata_flags_mask, + initial_metadata_flags_eq, + GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +void GrpcLb::ExitIdleLocked() { + if (!started_picking_) { + StartPickingLocked(); + } +} + +void GrpcLb::ResetBackoffLocked() { + if (lb_channel_ != nullptr) { + grpc_channel_reset_connect_backoff(lb_channel_); + } + if (rr_policy_ != nullptr) { + rr_policy_->ResetBackoffLocked(); + } +} + +bool GrpcLb::PickLocked(PickState* pick, grpc_error** error) { + PendingPick* pp = PendingPickCreate(pick); + bool pick_done = false; + if (rr_policy_ != nullptr) { + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, "[grpclb %p] about to PICK from RR %p", this, + rr_policy_.get()); + } + pick_done = + PickFromRoundRobinPolicyLocked(false /* force_async */, pp, error); + } else { // rr_policy_ == NULL + if (pick->on_complete == nullptr) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No pick result available but synchronous result required."); + pick_done = true; + } else { + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] No RR policy. Adding to grpclb's pending picks", + this); + } + AddPendingPick(pp); + if (!started_picking_) { + StartPickingLocked(); + } + pick_done = false; + } + } + return pick_done; +} + +void GrpcLb::FillChildRefsForChannelz( + channelz::ChildRefsList* child_subchannels, + channelz::ChildRefsList* child_channels) { + // delegate to the RoundRobin to fill the children subchannels. + rr_policy_->FillChildRefsForChannelz(child_subchannels, child_channels); + MutexLock lock(&lb_channel_mu_); + if (lb_channel_ != nullptr) { + grpc_core::channelz::ChannelNode* channel_node = + grpc_channel_get_channelz_node(lb_channel_); + if (channel_node != nullptr) { + child_channels->push_back(channel_node->uuid()); + } + } +} + +grpc_connectivity_state GrpcLb::CheckConnectivityLocked( + grpc_error** connectivity_error) { + return grpc_connectivity_state_get(&state_tracker_, connectivity_error); +} + +void GrpcLb::NotifyOnStateChangeLocked(grpc_connectivity_state* current, + grpc_closure* notify) { + grpc_connectivity_state_notify_on_state_change(&state_tracker_, current, + notify); +} + +// Returns the backend addresses extracted from the given addresses. +UniquePtr ExtractBackendAddresses( + const ServerAddressList& addresses) { + void* lb_token = (void*)GRPC_MDELEM_LB_TOKEN_EMPTY.payload; + grpc_arg arg = grpc_channel_arg_pointer_create( + const_cast(GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN), lb_token, + &lb_token_arg_vtable); + auto backend_addresses = MakeUnique(); + for (size_t i = 0; i < addresses.size(); ++i) { + if (!addresses[i].IsBalancer()) { + backend_addresses->emplace_back( + addresses[i].address(), + grpc_channel_args_copy_and_add(addresses[i].args(), &arg, 1)); + } + } + return backend_addresses; +} + +void GrpcLb::ProcessChannelArgsLocked(const grpc_channel_args& args) { + const ServerAddressList* addresses = FindServerAddressListChannelArg(&args); + if (addresses == nullptr) { + // Ignore this update. + gpr_log( + GPR_ERROR, + "[grpclb %p] No valid LB addresses channel arg in update, ignoring.", + this); + return; + } + // Update fallback address list. + fallback_backend_addresses_ = ExtractBackendAddresses(*addresses); + // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args, + // since we use this to trigger the client_load_reporting filter. + static const char* args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME}; + grpc_arg new_arg = grpc_channel_arg_string_create( + (char*)GRPC_ARG_LB_POLICY_NAME, (char*)"grpclb"); + grpc_channel_args_destroy(args_); + args_ = grpc_channel_args_copy_and_add_and_remove( + &args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1); + // Construct args for balancer channel. + grpc_channel_args* lb_channel_args = + BuildBalancerChannelArgs(*addresses, response_generator_.get(), &args); + // Create balancer channel if needed. + if (lb_channel_ == nullptr) { + char* uri_str; + gpr_asprintf(&uri_str, "fake:///%s", server_name_); + gpr_mu_lock(&lb_channel_mu_); + lb_channel_ = grpc_client_channel_factory_create_channel( + client_channel_factory(), uri_str, + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, lb_channel_args); + gpr_mu_unlock(&lb_channel_mu_); + GPR_ASSERT(lb_channel_ != nullptr); + gpr_free(uri_str); + } + // Propagate updates to the LB channel (pick_first) through the fake + // resolver. + response_generator_->SetResponse(lb_channel_args); + grpc_channel_args_destroy(lb_channel_args); +} + +void GrpcLb::UpdateLocked(const grpc_channel_args& args, grpc_json* lb_config) { + ProcessChannelArgsLocked(args); + // Update the existing RR policy. + if (rr_policy_ != nullptr) CreateOrUpdateRoundRobinPolicyLocked(); + // Start watching the LB channel connectivity for connection, if not + // already doing so. + if (!watching_lb_channel_) { + lb_channel_connectivity_ = grpc_channel_check_connectivity_state( + lb_channel_, true /* try to connect */); + grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(lb_channel_)); + GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); + watching_lb_channel_ = true; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "watch_lb_channel_connectivity"); + self.release(); + grpc_client_channel_watch_connectivity_state( + client_channel_elem, + grpc_polling_entity_create_from_pollset_set(interested_parties()), + &lb_channel_connectivity_, &lb_channel_on_connectivity_changed_, + nullptr); + } +} + +// +// code for balancer channel and call +// + +void GrpcLb::StartPickingLocked() { + // Start a timer to fall back. + if (lb_fallback_timeout_ms_ > 0 && serverlist_ == nullptr && + !fallback_timer_callback_pending_) { + grpc_millis deadline = ExecCtx::Get()->Now() + lb_fallback_timeout_ms_; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "on_fallback_timer"); + self.release(); + GRPC_CLOSURE_INIT(&lb_on_fallback_, &GrpcLb::OnFallbackTimerLocked, this, + grpc_combiner_scheduler(combiner())); + fallback_timer_callback_pending_ = true; + grpc_timer_init(&lb_fallback_timer_, deadline, &lb_on_fallback_); + } + started_picking_ = true; + StartBalancerCallLocked(); +} + +void GrpcLb::StartBalancerCallLocked() { + GPR_ASSERT(lb_channel_ != nullptr); + if (shutting_down_) return; + // Init the LB call data. + GPR_ASSERT(lb_calld_ == nullptr); + lb_calld_ = MakeOrphanable(Ref()); + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] Query for backends (lb_channel: %p, lb_calld: %p)", + this, lb_channel_, lb_calld_.get()); + } + lb_calld_->StartQuery(); +} + +void GrpcLb::OnFallbackTimerLocked(void* arg, grpc_error* error) { + GrpcLb* grpclb_policy = static_cast(arg); + grpclb_policy->fallback_timer_callback_pending_ = false; + // If we receive a serverlist after the timer fires but before this callback + // actually runs, don't fall back. + if (grpclb_policy->serverlist_ == nullptr && !grpclb_policy->shutting_down_ && + error == GRPC_ERROR_NONE) { + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] Falling back to use backends from resolver", + grpclb_policy); + } + GPR_ASSERT(grpclb_policy->fallback_backend_addresses_ != nullptr); + grpclb_policy->CreateOrUpdateRoundRobinPolicyLocked(); + } + grpclb_policy->Unref(DEBUG_LOCATION, "on_fallback_timer"); +} + +void GrpcLb::StartBalancerCallRetryTimerLocked() { + grpc_millis next_try = lb_call_backoff_.NextAttemptTime(); + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, "[grpclb %p] Connection to LB server lost...", this); + grpc_millis timeout = next_try - ExecCtx::Get()->Now(); + if (timeout > 0) { + gpr_log(GPR_INFO, "[grpclb %p] ... retry_timer_active in %" PRId64 "ms.", + this, timeout); + } else { + gpr_log(GPR_INFO, "[grpclb %p] ... retry_timer_active immediately.", + this); + } + } + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "on_balancer_call_retry_timer"); + self.release(); + GRPC_CLOSURE_INIT(&lb_on_call_retry_, &GrpcLb::OnBalancerCallRetryTimerLocked, + this, grpc_combiner_scheduler(combiner())); + retry_timer_callback_pending_ = true; + grpc_timer_init(&lb_call_retry_timer_, next_try, &lb_on_call_retry_); +} + +void GrpcLb::OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error) { + GrpcLb* grpclb_policy = static_cast(arg); + grpclb_policy->retry_timer_callback_pending_ = false; + if (!grpclb_policy->shutting_down_ && error == GRPC_ERROR_NONE && + grpclb_policy->lb_calld_ == nullptr) { + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, "[grpclb %p] Restarting call to LB server", + grpclb_policy); + } + grpclb_policy->StartBalancerCallLocked(); + } + grpclb_policy->Unref(DEBUG_LOCATION, "on_balancer_call_retry_timer"); +} + +// Invoked as part of the update process. It continues watching the LB channel +// until it shuts down or becomes READY. It's invoked even if the LB channel +// stayed READY throughout the update (for example if the update is identical). +void GrpcLb::OnBalancerChannelConnectivityChangedLocked(void* arg, + grpc_error* error) { + GrpcLb* grpclb_policy = static_cast(arg); + if (grpclb_policy->shutting_down_) goto done; + // Re-initialize the lb_call. This should also take care of updating the + // embedded RR policy. Note that the current RR policy, if any, will stay in + // effect until an update from the new lb_call is received. + switch (grpclb_policy->lb_channel_connectivity_) { + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_TRANSIENT_FAILURE: { + // Keep watching the LB channel. + grpc_channel_element* client_channel_elem = + grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(grpclb_policy->lb_channel_)); + GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); + grpc_client_channel_watch_connectivity_state( + client_channel_elem, + grpc_polling_entity_create_from_pollset_set( + grpclb_policy->interested_parties()), + &grpclb_policy->lb_channel_connectivity_, + &grpclb_policy->lb_channel_on_connectivity_changed_, nullptr); + break; + } + // The LB channel may be IDLE because it's shut down before the update. + // Restart the LB call to kick the LB channel into gear. + case GRPC_CHANNEL_IDLE: + case GRPC_CHANNEL_READY: + grpclb_policy->lb_calld_.reset(); + if (grpclb_policy->started_picking_) { + if (grpclb_policy->retry_timer_callback_pending_) { + grpc_timer_cancel(&grpclb_policy->lb_call_retry_timer_); + } + grpclb_policy->lb_call_backoff_.Reset(); + grpclb_policy->StartBalancerCallLocked(); + } + // fallthrough + case GRPC_CHANNEL_SHUTDOWN: + done: + grpclb_policy->watching_lb_channel_ = false; + grpclb_policy->Unref(DEBUG_LOCATION, + "watch_lb_channel_connectivity_cb_shutdown"); + } +} + +// +// PendingPick +// + +// Adds lb_token of selected subchannel (address) to the call's initial +// metadata. +grpc_error* AddLbTokenToInitialMetadata( + grpc_mdelem lb_token, grpc_linked_mdelem* lb_token_mdelem_storage, + grpc_metadata_batch* initial_metadata) { + GPR_ASSERT(lb_token_mdelem_storage != nullptr); + GPR_ASSERT(!GRPC_MDISNULL(lb_token)); + return grpc_metadata_batch_add_tail(initial_metadata, lb_token_mdelem_storage, + lb_token); +} + +// Destroy function used when embedding client stats in call context. +void DestroyClientStats(void* arg) { + static_cast(arg)->Unref(); +} + +void GrpcLb::PendingPickSetMetadataAndContext(PendingPick* pp) { + // If connected_subchannel is nullptr, no pick has been made by the RR + // policy (e.g., all addresses failed to connect). There won't be any + // LB token available. + if (pp->pick->connected_subchannel != nullptr) { + const grpc_arg* arg = + grpc_channel_args_find(pp->pick->connected_subchannel->args(), + GRPC_ARG_GRPCLB_ADDRESS_LB_TOKEN); + if (arg != nullptr) { + grpc_mdelem lb_token = { + reinterpret_cast(arg->value.pointer.p)}; + AddLbTokenToInitialMetadata(GRPC_MDELEM_REF(lb_token), + &pp->pick->lb_token_mdelem_storage, + pp->pick->initial_metadata); + } else { + gpr_log(GPR_ERROR, + "[grpclb %p] No LB token for connected subchannel pick %p", + pp->grpclb_policy, pp->pick); + abort(); + } + // Pass on client stats via context. Passes ownership of the reference. + if (pp->client_stats != nullptr) { + pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].value = + pp->client_stats.release(); + pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].destroy = + DestroyClientStats; + } + } else { + pp->client_stats.reset(); + } +} + +/* The \a on_complete closure passed as part of the pick requires keeping a + * reference to its associated round robin instance. We wrap this closure in + * order to unref the round robin instance upon its invocation */ +void GrpcLb::OnPendingPickComplete(void* arg, grpc_error* error) { + PendingPick* pp = static_cast(arg); + PendingPickSetMetadataAndContext(pp); + GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_REF(error)); + Delete(pp); +} + +GrpcLb::PendingPick* GrpcLb::PendingPickCreate(PickState* pick) { + PendingPick* pp = New(); + pp->grpclb_policy = this; + pp->pick = pick; + GRPC_CLOSURE_INIT(&pp->on_complete, &GrpcLb::OnPendingPickComplete, pp, + grpc_schedule_on_exec_ctx); + pp->original_on_complete = pick->on_complete; + pick->on_complete = &pp->on_complete; + return pp; +} + +void GrpcLb::AddPendingPick(PendingPick* pp) { + pp->next = pending_picks_; + pending_picks_ = pp; +} + +// +// code for interacting with the RR policy +// + +// Performs a pick over \a rr_policy_. Given that a pick can return +// immediately (ignoring its completion callback), we need to perform the +// cleanups this callback would otherwise be responsible for. +// If \a force_async is true, then we will manually schedule the +// completion callback even if the pick is available immediately. +bool GrpcLb::PickFromRoundRobinPolicyLocked(bool force_async, PendingPick* pp, + grpc_error** error) { + // Check for drops if we are not using fallback backend addresses. + if (serverlist_ != nullptr && serverlist_->num_servers > 0) { + // Look at the index into the serverlist to see if we should drop this call. + grpc_grpclb_server* server = serverlist_->servers[serverlist_index_++]; + if (serverlist_index_ == serverlist_->num_servers) { + serverlist_index_ = 0; // Wrap-around. + } + if (server->drop) { + // Update client load reporting stats to indicate the number of + // dropped calls. Note that we have to do this here instead of in + // the client_load_reporting filter, because we do not create a + // subchannel call (and therefore no client_load_reporting filter) + // for dropped calls. + if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) { + lb_calld_->client_stats()->AddCallDroppedLocked( + server->load_balance_token); + } + if (force_async) { + GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_NONE); + Delete(pp); + return false; + } + Delete(pp); + return true; + } + } + // Set client_stats. + if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) { + pp->client_stats = lb_calld_->client_stats()->Ref(); + } + // Pick via the RR policy. + bool pick_done = rr_policy_->PickLocked(pp->pick, error); + if (pick_done) { + PendingPickSetMetadataAndContext(pp); + if (force_async) { + GRPC_CLOSURE_SCHED(pp->original_on_complete, *error); + *error = GRPC_ERROR_NONE; + pick_done = false; + } + Delete(pp); + } + // else, the pending pick will be registered and taken care of by the + // pending pick list inside the RR policy. Eventually, + // OnPendingPickComplete() will be called, which will (among other + // things) add the LB token to the call's initial metadata. + return pick_done; +} + +void GrpcLb::CreateRoundRobinPolicyLocked(const Args& args) { + GPR_ASSERT(rr_policy_ == nullptr); + rr_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy( + "round_robin", args); + if (GPR_UNLIKELY(rr_policy_ == nullptr)) { + gpr_log(GPR_ERROR, "[grpclb %p] Failure creating a RoundRobin policy", + this); + return; + } + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, "[grpclb %p] Created new RR policy %p", this, + rr_policy_.get()); + } + // TODO(roth): We currently track this ref manually. Once the new + // ClosureRef API is done, pass the RefCountedPtr<> along with the closure. + auto self = Ref(DEBUG_LOCATION, "on_rr_reresolution_requested"); + self.release(); + rr_policy_->SetReresolutionClosureLocked(&on_rr_request_reresolution_); + grpc_error* rr_state_error = nullptr; + rr_connectivity_state_ = rr_policy_->CheckConnectivityLocked(&rr_state_error); + // Connectivity state is a function of the RR policy updated/created. + UpdateConnectivityStateFromRoundRobinPolicyLocked(rr_state_error); + // Add the gRPC LB's interested_parties pollset_set to that of the newly + // created RR policy. This will make the RR policy progress upon activity on + // gRPC LB, which in turn is tied to the application's call. + grpc_pollset_set_add_pollset_set(rr_policy_->interested_parties(), + interested_parties()); + // Subscribe to changes to the connectivity of the new RR. + // TODO(roth): We currently track this ref manually. Once the new + // ClosureRef API is done, pass the RefCountedPtr<> along with the closure. + self = Ref(DEBUG_LOCATION, "on_rr_connectivity_changed"); + self.release(); + rr_policy_->NotifyOnStateChangeLocked(&rr_connectivity_state_, + &on_rr_connectivity_changed_); + rr_policy_->ExitIdleLocked(); + // Send pending picks to RR policy. + PendingPick* pp; + while ((pp = pending_picks_)) { + pending_picks_ = pp->next; + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, + "[grpclb %p] Pending pick about to (async) PICK from RR %p", this, + rr_policy_.get()); + } + grpc_error* error = GRPC_ERROR_NONE; + PickFromRoundRobinPolicyLocked(true /* force_async */, pp, &error); + } +} + +grpc_channel_args* GrpcLb::CreateRoundRobinPolicyArgsLocked() { + ServerAddressList tmp_addresses; + ServerAddressList* addresses = &tmp_addresses; + bool is_backend_from_grpclb_load_balancer = false; + if (serverlist_ != nullptr) { + tmp_addresses = ProcessServerlist(serverlist_); + is_backend_from_grpclb_load_balancer = true; + } else { + // If CreateOrUpdateRoundRobinPolicyLocked() is invoked when we haven't + // received any serverlist from the balancer, we use the fallback backends + // returned by the resolver. Note that the fallback backend list may be + // empty, in which case the new round_robin policy will keep the requested + // picks pending. + GPR_ASSERT(fallback_backend_addresses_ != nullptr); + addresses = fallback_backend_addresses_.get(); + } + GPR_ASSERT(addresses != nullptr); + // Replace the server address list in the channel args that we pass down to + // the subchannel. + static const char* keys_to_remove[] = {GRPC_ARG_SERVER_ADDRESS_LIST}; + grpc_arg args_to_add[3] = { + CreateServerAddressListChannelArg(addresses), + // A channel arg indicating if the target is a backend inferred from a + // grpclb load balancer. + grpc_channel_arg_integer_create( + const_cast( + GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER), + is_backend_from_grpclb_load_balancer), + }; + size_t num_args_to_add = 2; + if (is_backend_from_grpclb_load_balancer) { + args_to_add[2] = grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1); + ++num_args_to_add; + } + grpc_channel_args* args = grpc_channel_args_copy_and_add_and_remove( + args_, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), args_to_add, + num_args_to_add); + return args; +} + +void GrpcLb::CreateOrUpdateRoundRobinPolicyLocked() { + if (shutting_down_) return; + grpc_channel_args* args = CreateRoundRobinPolicyArgsLocked(); + GPR_ASSERT(args != nullptr); + if (rr_policy_ != nullptr) { + if (grpc_lb_glb_trace.enabled()) { + gpr_log(GPR_INFO, "[grpclb %p] Updating RR policy %p", this, + rr_policy_.get()); + } + rr_policy_->UpdateLocked(*args, nullptr); + } else { + LoadBalancingPolicy::Args lb_policy_args; + lb_policy_args.combiner = combiner(); + lb_policy_args.client_channel_factory = client_channel_factory(); + lb_policy_args.args = args; + CreateRoundRobinPolicyLocked(lb_policy_args); + } + grpc_channel_args_destroy(args); +} + +void GrpcLb::OnRoundRobinRequestReresolutionLocked(void* arg, + grpc_error* error) { + GrpcLb* grpclb_policy = static_cast(arg); + if (grpclb_policy->shutting_down_ || error != GRPC_ERROR_NONE) { + grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_reresolution_requested"); + return; + } + if (grpc_lb_glb_trace.enabled()) { + gpr_log( + GPR_INFO, + "[grpclb %p] Re-resolution requested from the internal RR policy (%p).", + grpclb_policy, grpclb_policy->rr_policy_.get()); + } + // If we are talking to a balancer, we expect to get updated addresses form + // the balancer, so we can ignore the re-resolution request from the RR + // policy. Otherwise, handle the re-resolution request using the + // grpclb policy's original re-resolution closure. + if (grpclb_policy->lb_calld_ == nullptr || + !grpclb_policy->lb_calld_->seen_initial_response()) { + grpclb_policy->TryReresolutionLocked(&grpc_lb_glb_trace, GRPC_ERROR_NONE); + } + // Give back the wrapper closure to the RR policy. + grpclb_policy->rr_policy_->SetReresolutionClosureLocked( + &grpclb_policy->on_rr_request_reresolution_); +} + +void GrpcLb::UpdateConnectivityStateFromRoundRobinPolicyLocked( + grpc_error* rr_state_error) { + const grpc_connectivity_state curr_glb_state = + grpc_connectivity_state_check(&state_tracker_); + /* The new connectivity status is a function of the previous one and the new + * input coming from the status of the RR policy. + * + * current state (grpclb's) + * | + * v || I | C | R | TF | SD | <- new state (RR's) + * ===++====+=====+=====+======+======+ + * I || I | C | R | [I] | [I] | + * ---++----+-----+-----+------+------+ + * C || I | C | R | [C] | [C] | + * ---++----+-----+-----+------+------+ + * R || I | C | R | [R] | [R] | + * ---++----+-----+-----+------+------+ + * TF || I | C | R | [TF] | [TF] | + * ---++----+-----+-----+------+------+ + * SD || NA | NA | NA | NA | NA | (*) + * ---++----+-----+-----+------+------+ + * + * A [STATE] indicates that the old RR policy is kept. In those cases, STATE + * is the current state of grpclb, which is left untouched. + * + * In summary, if the new state is TRANSIENT_FAILURE or SHUTDOWN, stick to + * the previous RR instance. + * + * Note that the status is never updated to SHUTDOWN as a result of calling + * this function. Only glb_shutdown() has the power to set that state. + * + * (*) This function mustn't be called during shutting down. */ + GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN); + switch (rr_connectivity_state_) { + case GRPC_CHANNEL_TRANSIENT_FAILURE: + case GRPC_CHANNEL_SHUTDOWN: + GPR_ASSERT(rr_state_error != GRPC_ERROR_NONE); + break; + case GRPC_CHANNEL_IDLE: + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_READY: + GPR_ASSERT(rr_state_error == GRPC_ERROR_NONE); + } + if (grpc_lb_glb_trace.enabled()) { + gpr_log( + GPR_INFO, + "[grpclb %p] Setting grpclb's state to %s from new RR policy %p state.", + this, grpc_connectivity_state_name(rr_connectivity_state_), + rr_policy_.get()); + } + grpc_connectivity_state_set(&state_tracker_, rr_connectivity_state_, + rr_state_error, + "update_lb_connectivity_status_locked"); +} + +void GrpcLb::OnRoundRobinConnectivityChangedLocked(void* arg, + grpc_error* error) { + GrpcLb* grpclb_policy = static_cast(arg); + if (grpclb_policy->shutting_down_) { + grpclb_policy->Unref(DEBUG_LOCATION, "on_rr_connectivity_changed"); + return; + } + grpclb_policy->UpdateConnectivityStateFromRoundRobinPolicyLocked( + GRPC_ERROR_REF(error)); + // Resubscribe. Reuse the "on_rr_connectivity_changed" ref. + grpclb_policy->rr_policy_->NotifyOnStateChangeLocked( + &grpclb_policy->rr_connectivity_state_, + &grpclb_policy->on_rr_connectivity_changed_); +} + +// +// factory +// + +class GrpcLbFactory : public LoadBalancingPolicyFactory { + public: + OrphanablePtr CreateLoadBalancingPolicy( + const LoadBalancingPolicy::Args& args) const override { + /* Count the number of gRPC-LB addresses. There must be at least one. */ + const ServerAddressList* addresses = + FindServerAddressListChannelArg(args.args); + if (addresses == nullptr) return nullptr; + bool found_balancer = false; + for (size_t i = 0; i < addresses->size(); ++i) { + if ((*addresses)[i].IsBalancer()) { + found_balancer = true; + break; + } + } + if (!found_balancer) return nullptr; + return OrphanablePtr(New(args)); + } + + const char* name() const override { return kGrpclb; } +}; + +} // namespace + +} // namespace grpc_core + +// +// Plugin registration +// + +namespace { + +// Only add client_load_reporting filter if the grpclb LB policy is used. +bool maybe_add_client_load_reporting_filter(grpc_channel_stack_builder* builder, + void* arg) { + const grpc_channel_args* args = + grpc_channel_stack_builder_get_channel_arguments(builder); + const grpc_arg* channel_arg = + grpc_channel_args_find(args, GRPC_ARG_LB_POLICY_NAME); + if (channel_arg != nullptr && channel_arg->type == GRPC_ARG_STRING && + strcmp(channel_arg->value.string, "grpclb") == 0) { + return grpc_channel_stack_builder_append_filter( + builder, (const grpc_channel_filter*)arg, nullptr, nullptr); + } + return true; +} + +} // namespace + +void grpc_lb_policy_grpclb_init() { + grpc_core::LoadBalancingPolicyRegistry::Builder:: + RegisterLoadBalancingPolicyFactory( + grpc_core::UniquePtr( + grpc_core::New())); + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_client_load_reporting_filter, + (void*)&grpc_client_load_reporting_filter); +} + +void grpc_lb_policy_grpclb_shutdown() {} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h new file mode 100644 index 00000000000000..4d39c4d504b8f5 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H + +#include + +/** Channel arg indicating if a target corresponding to the address is grpclb + * loadbalancer. The type of this arg is an integer and the value is treated as + * a bool. */ +#define GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER \ + "grpc.address_is_grpclb_load_balancer" +/** Channel arg indicating if a target corresponding to the address is a backend + * received from a balancer. The type of this arg is an integer and the value is + * treated as a bool. */ +#define GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER \ + "grpc.address_is_backend_from_grpclb_load_balancer" + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc new file mode 100644 index 00000000000000..fd873f096d8292 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc @@ -0,0 +1,26 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" + +grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args( + grpc_channel_args* args) { + return args; +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h new file mode 100644 index 00000000000000..3b2dc370eb3b36 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H + +#include + +#include + +/// Makes any necessary modifications to \a args for use in the grpclb +/// balancer channel. +/// +/// Takes ownership of \a args. +/// +/// Caller takes ownership of the returned args. +grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args( + grpc_channel_args* args); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc new file mode 100644 index 00000000000000..657ff693126d0a --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc @@ -0,0 +1,105 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" + +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/transport/target_authority_table.h" +#include "src/core/lib/slice/slice_internal.h" + +namespace grpc_core { +namespace { + +int BalancerNameCmp(const grpc_core::UniquePtr& a, + const grpc_core::UniquePtr& b) { + return strcmp(a.get(), b.get()); +} + +RefCountedPtr CreateTargetAuthorityTable( + const ServerAddressList& addresses) { + TargetAuthorityTable::Entry* target_authority_entries = + static_cast( + gpr_zalloc(sizeof(*target_authority_entries) * addresses.size())); + for (size_t i = 0; i < addresses.size(); ++i) { + char* addr_str; + GPR_ASSERT( + grpc_sockaddr_to_string(&addr_str, &addresses[i].address(), true) > 0); + target_authority_entries[i].key = grpc_slice_from_copied_string(addr_str); + gpr_free(addr_str); + char* balancer_name = grpc_channel_arg_get_string(grpc_channel_args_find( + addresses[i].args(), GRPC_ARG_ADDRESS_BALANCER_NAME)); + target_authority_entries[i].value.reset(gpr_strdup(balancer_name)); + } + RefCountedPtr target_authority_table = + TargetAuthorityTable::Create(addresses.size(), target_authority_entries, + BalancerNameCmp); + gpr_free(target_authority_entries); + return target_authority_table; +} + +} // namespace +} // namespace grpc_core + +grpc_channel_args* grpc_lb_policy_grpclb_modify_lb_channel_args( + grpc_channel_args* args) { + const char* args_to_remove[1]; + size_t num_args_to_remove = 0; + grpc_arg args_to_add[2]; + size_t num_args_to_add = 0; + // Add arg for targets info table. + grpc_core::ServerAddressList* addresses = + grpc_core::FindServerAddressListChannelArg(args); + GPR_ASSERT(addresses != nullptr); + grpc_core::RefCountedPtr + target_authority_table = + grpc_core::CreateTargetAuthorityTable(*addresses); + args_to_add[num_args_to_add++] = + grpc_core::CreateTargetAuthorityTableChannelArg( + target_authority_table.get()); + // Substitute the channel credentials with a version without call + // credentials: the load balancer is not necessarily trusted to handle + // bearer token credentials. + grpc_channel_credentials* channel_credentials = + grpc_channel_credentials_find_in_args(args); + grpc_core::RefCountedPtr creds_sans_call_creds; + if (channel_credentials != nullptr) { + creds_sans_call_creds = + channel_credentials->duplicate_without_call_credentials(); + GPR_ASSERT(creds_sans_call_creds != nullptr); + args_to_remove[num_args_to_remove++] = GRPC_ARG_CHANNEL_CREDENTIALS; + args_to_add[num_args_to_add++] = + grpc_channel_credentials_to_arg(creds_sans_call_creds.get()); + } + grpc_channel_args* result = grpc_channel_args_copy_and_add_and_remove( + args, args_to_remove, num_args_to_remove, args_to_add, num_args_to_add); + // Clean up. + grpc_channel_args_destroy(args); + return result; +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc new file mode 100644 index 00000000000000..087cd8f276e47a --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc @@ -0,0 +1,86 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" + +#include + +#include +#include + +namespace grpc_core { + +void GrpcLbClientStats::AddCallStarted() { + gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1); +} + +void GrpcLbClientStats::AddCallFinished( + bool finished_with_client_failed_to_send, bool finished_known_received) { + gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1); + if (finished_with_client_failed_to_send) { + gpr_atm_full_fetch_add(&num_calls_finished_with_client_failed_to_send_, + (gpr_atm)1); + } + if (finished_known_received) { + gpr_atm_full_fetch_add(&num_calls_finished_known_received_, (gpr_atm)1); + } +} + +void GrpcLbClientStats::AddCallDroppedLocked(char* token) { + // Increment num_calls_started and num_calls_finished. + gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1); + gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1); + // Record the drop. + if (drop_token_counts_ == nullptr) { + drop_token_counts_.reset(New()); + } + for (size_t i = 0; i < drop_token_counts_->size(); ++i) { + if (strcmp((*drop_token_counts_)[i].token.get(), token) == 0) { + ++(*drop_token_counts_)[i].count; + return; + } + } + // Not found, so add a new entry. + drop_token_counts_->emplace_back(UniquePtr(gpr_strdup(token)), 1); +} + +namespace { + +void AtomicGetAndResetCounter(int64_t* value, gpr_atm* counter) { + *value = static_cast(gpr_atm_full_xchg(counter, (gpr_atm)0)); +} + +} // namespace + +void GrpcLbClientStats::GetLocked( + int64_t* num_calls_started, int64_t* num_calls_finished, + int64_t* num_calls_finished_with_client_failed_to_send, + int64_t* num_calls_finished_known_received, + UniquePtr* drop_token_counts) { + AtomicGetAndResetCounter(num_calls_started, &num_calls_started_); + AtomicGetAndResetCounter(num_calls_finished, &num_calls_finished_); + AtomicGetAndResetCounter(num_calls_finished_with_client_failed_to_send, + &num_calls_finished_with_client_failed_to_send_); + AtomicGetAndResetCounter(num_calls_finished_known_received, + &num_calls_finished_known_received_); + *drop_token_counts = std::move(drop_token_counts_); +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h new file mode 100644 index 00000000000000..18ab2c945298c3 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h @@ -0,0 +1,72 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H + +#include + +#include + +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" + +namespace grpc_core { + +class GrpcLbClientStats : public RefCounted { + public: + struct DropTokenCount { + UniquePtr token; + int64_t count; + + DropTokenCount(UniquePtr token, int64_t count) + : token(std::move(token)), count(count) {} + }; + + typedef InlinedVector DroppedCallCounts; + + GrpcLbClientStats() {} + + void AddCallStarted(); + void AddCallFinished(bool finished_with_client_failed_to_send, + bool finished_known_received); + + // This method is not thread-safe; caller must synchronize. + void AddCallDroppedLocked(char* token); + + // This method is not thread-safe; caller must synchronize. + void GetLocked(int64_t* num_calls_started, int64_t* num_calls_finished, + int64_t* num_calls_finished_with_client_failed_to_send, + int64_t* num_calls_finished_known_received, + UniquePtr* drop_token_counts); + + private: + // This field must only be accessed via *_locked() methods. + UniquePtr drop_token_counts_; + // These fields may be accessed from multiple threads at a time. + gpr_atm num_calls_started_ = 0; + gpr_atm num_calls_finished_ = 0; + gpr_atm num_calls_finished_with_client_failed_to_send_ = 0; + gpr_atm num_calls_finished_known_received_ = 0; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc new file mode 100644 index 00000000000000..f24281a5bfb682 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc @@ -0,0 +1,307 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "pb_decode.h" +#include "pb_encode.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" + +#include + +/* invoked once for every Server in ServerList */ +static bool count_serverlist(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + grpc_grpclb_serverlist* sl = static_cast(*arg); + grpc_grpclb_server server; + if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, &server))) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; + } + ++sl->num_servers; + return true; +} + +typedef struct decode_serverlist_arg { + /* The decoding callback is invoked once per server in serverlist. Remember + * which index of the serverlist are we currently decoding */ + size_t decoding_idx; + /* The decoded serverlist */ + grpc_grpclb_serverlist* serverlist; +} decode_serverlist_arg; + +/* invoked once for every Server in ServerList */ +static bool decode_serverlist(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + decode_serverlist_arg* dec_arg = static_cast(*arg); + GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx); + grpc_grpclb_server* server = + static_cast(gpr_zalloc(sizeof(grpc_grpclb_server))); + if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, server))) { + gpr_free(server); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; + } + dec_arg->serverlist->servers[dec_arg->decoding_idx++] = server; + return true; +} + +grpc_grpclb_request* grpc_grpclb_request_create(const char* lb_service_name) { + grpc_grpclb_request* req = static_cast( + gpr_malloc(sizeof(grpc_grpclb_request))); + req->has_client_stats = false; + req->has_initial_request = true; + req->initial_request.has_name = true; + strncpy(req->initial_request.name, lb_service_name, + GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH); + return req; +} + +static void populate_timestamp(gpr_timespec timestamp, + grpc_grpclb_timestamp* timestamp_pb) { + timestamp_pb->has_seconds = true; + timestamp_pb->seconds = timestamp.tv_sec; + timestamp_pb->has_nanos = true; + timestamp_pb->nanos = timestamp.tv_nsec; +} + +static bool encode_string(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + char* str = static_cast(*arg); + if (!pb_encode_tag_for_field(stream, field)) return false; + return pb_encode_string(stream, reinterpret_cast(str), strlen(str)); +} + +static bool encode_drops(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + grpc_core::GrpcLbClientStats::DroppedCallCounts* drop_entries = + static_cast(*arg); + if (drop_entries == nullptr) return true; + for (size_t i = 0; i < drop_entries->size(); ++i) { + if (!pb_encode_tag_for_field(stream, field)) return false; + grpc_lb_v1_ClientStatsPerToken drop_message; + drop_message.load_balance_token.funcs.encode = encode_string; + drop_message.load_balance_token.arg = (*drop_entries)[i].token.get(); + drop_message.has_num_calls = true; + drop_message.num_calls = (*drop_entries)[i].count; + if (!pb_encode_submessage(stream, grpc_lb_v1_ClientStatsPerToken_fields, + &drop_message)) { + return false; + } + } + return true; +} + +grpc_grpclb_request* grpc_grpclb_load_report_request_create_locked( + grpc_core::GrpcLbClientStats* client_stats) { + grpc_grpclb_request* req = static_cast( + gpr_zalloc(sizeof(grpc_grpclb_request))); + req->has_client_stats = true; + req->client_stats.has_timestamp = true; + populate_timestamp(gpr_now(GPR_CLOCK_REALTIME), &req->client_stats.timestamp); + req->client_stats.has_num_calls_started = true; + req->client_stats.has_num_calls_finished = true; + req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; + req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; + req->client_stats.has_num_calls_finished_known_received = true; + req->client_stats.calls_finished_with_drop.funcs.encode = encode_drops; + grpc_core::UniquePtr + drop_counts; + client_stats->GetLocked( + &req->client_stats.num_calls_started, + &req->client_stats.num_calls_finished, + &req->client_stats.num_calls_finished_with_client_failed_to_send, + &req->client_stats.num_calls_finished_known_received, &drop_counts); + // Will be deleted in grpc_grpclb_request_destroy(). + req->client_stats.calls_finished_with_drop.arg = drop_counts.release(); + return req; +} + +grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request* request) { + size_t encoded_length; + pb_ostream_t sizestream; + pb_ostream_t outputstream; + grpc_slice slice; + memset(&sizestream, 0, sizeof(pb_ostream_t)); + pb_encode(&sizestream, grpc_lb_v1_LoadBalanceRequest_fields, request); + encoded_length = sizestream.bytes_written; + + slice = GRPC_SLICE_MALLOC(encoded_length); + outputstream = + pb_ostream_from_buffer(GRPC_SLICE_START_PTR(slice), encoded_length); + GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v1_LoadBalanceRequest_fields, + request) != 0); + return slice; +} + +void grpc_grpclb_request_destroy(grpc_grpclb_request* request) { + if (request->has_client_stats) { + grpc_core::GrpcLbClientStats::DroppedCallCounts* drop_entries = + static_cast( + request->client_stats.calls_finished_with_drop.arg); + grpc_core::Delete(drop_entries); + } + gpr_free(request); +} + +typedef grpc_lb_v1_LoadBalanceResponse grpc_grpclb_response; +grpc_grpclb_initial_response* grpc_grpclb_initial_response_parse( + grpc_slice encoded_grpc_grpclb_response) { + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response), + GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response)); + grpc_grpclb_response res; + memset(&res, 0, sizeof(grpc_grpclb_response)); + if (GPR_UNLIKELY( + !pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res))) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return nullptr; + } + + if (!res.has_initial_response) return nullptr; + + grpc_grpclb_initial_response* initial_res = + static_cast( + gpr_malloc(sizeof(grpc_grpclb_initial_response))); + memcpy(initial_res, &res.initial_response, + sizeof(grpc_grpclb_initial_response)); + + return initial_res; +} + +grpc_grpclb_serverlist* grpc_grpclb_response_parse_serverlist( + grpc_slice encoded_grpc_grpclb_response) { + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response), + GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response)); + pb_istream_t stream_at_start = stream; + grpc_grpclb_serverlist* sl = static_cast( + gpr_zalloc(sizeof(grpc_grpclb_serverlist))); + grpc_grpclb_response res; + memset(&res, 0, sizeof(grpc_grpclb_response)); + // First pass: count number of servers. + res.server_list.servers.funcs.decode = count_serverlist; + res.server_list.servers.arg = sl; + bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res); + if (GPR_UNLIKELY(!status)) { + gpr_free(sl); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return nullptr; + } + // Second pass: populate servers. + if (sl->num_servers > 0) { + sl->servers = static_cast( + gpr_zalloc(sizeof(grpc_grpclb_server*) * sl->num_servers)); + decode_serverlist_arg decode_arg; + memset(&decode_arg, 0, sizeof(decode_arg)); + decode_arg.serverlist = sl; + res.server_list.servers.funcs.decode = decode_serverlist; + res.server_list.servers.arg = &decode_arg; + status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, + &res); + if (GPR_UNLIKELY(!status)) { + grpc_grpclb_destroy_serverlist(sl); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return nullptr; + } + } + return sl; +} + +void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist* serverlist) { + if (serverlist == nullptr) { + return; + } + for (size_t i = 0; i < serverlist->num_servers; i++) { + gpr_free(serverlist->servers[i]); + } + gpr_free(serverlist->servers); + gpr_free(serverlist); +} + +grpc_grpclb_serverlist* grpc_grpclb_serverlist_copy( + const grpc_grpclb_serverlist* sl) { + grpc_grpclb_serverlist* copy = static_cast( + gpr_zalloc(sizeof(grpc_grpclb_serverlist))); + copy->num_servers = sl->num_servers; + copy->servers = static_cast( + gpr_malloc(sizeof(grpc_grpclb_server*) * sl->num_servers)); + for (size_t i = 0; i < sl->num_servers; i++) { + copy->servers[i] = static_cast( + gpr_malloc(sizeof(grpc_grpclb_server))); + memcpy(copy->servers[i], sl->servers[i], sizeof(grpc_grpclb_server)); + } + return copy; +} + +bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist* lhs, + const grpc_grpclb_serverlist* rhs) { + if (lhs == nullptr || rhs == nullptr) { + return false; + } + if (lhs->num_servers != rhs->num_servers) { + return false; + } + for (size_t i = 0; i < lhs->num_servers; i++) { + if (!grpc_grpclb_server_equals(lhs->servers[i], rhs->servers[i])) { + return false; + } + } + return true; +} + +bool grpc_grpclb_server_equals(const grpc_grpclb_server* lhs, + const grpc_grpclb_server* rhs) { + return memcmp(lhs, rhs, sizeof(grpc_grpclb_server)) == 0; +} + +int grpc_grpclb_duration_compare(const grpc_grpclb_duration* lhs, + const grpc_grpclb_duration* rhs) { + GPR_ASSERT(lhs && rhs); + if (lhs->has_seconds && rhs->has_seconds) { + if (lhs->seconds < rhs->seconds) return -1; + if (lhs->seconds > rhs->seconds) return 1; + } else if (lhs->has_seconds) { + return 1; + } else if (rhs->has_seconds) { + return -1; + } + + GPR_ASSERT(lhs->seconds == rhs->seconds); + if (lhs->has_nanos && rhs->has_nanos) { + if (lhs->nanos < rhs->nanos) return -1; + if (lhs->nanos > rhs->nanos) return 1; + } else if (lhs->has_nanos) { + return 1; + } else if (rhs->has_nanos) { + return -1; + } + + return 0; +} + +grpc_millis grpc_grpclb_duration_to_millis(grpc_grpclb_duration* duration_pb) { + return static_cast( + (duration_pb->has_seconds ? duration_pb->seconds : 0) * GPR_MS_PER_SEC + + (duration_pb->has_nanos ? duration_pb->nanos : 0) / GPR_NS_PER_MS); +} + +void grpc_grpclb_initial_response_destroy( + grpc_grpclb_initial_response* response) { + gpr_free(response); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h new file mode 100644 index 00000000000000..71d371c880adad --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h @@ -0,0 +1,90 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H + +#include + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +#define GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH 128 + +typedef grpc_lb_v1_Server_ip_address_t grpc_grpclb_ip_address; +typedef grpc_lb_v1_LoadBalanceRequest grpc_grpclb_request; +typedef grpc_lb_v1_InitialLoadBalanceResponse grpc_grpclb_initial_response; +typedef grpc_lb_v1_Server grpc_grpclb_server; +typedef google_protobuf_Duration grpc_grpclb_duration; +typedef google_protobuf_Timestamp grpc_grpclb_timestamp; + +typedef struct { + grpc_grpclb_server** servers; + size_t num_servers; +} grpc_grpclb_serverlist; + +/** Create a request for a gRPC LB service under \a lb_service_name */ +grpc_grpclb_request* grpc_grpclb_request_create(const char* lb_service_name); +grpc_grpclb_request* grpc_grpclb_load_report_request_create_locked( + grpc_core::GrpcLbClientStats* client_stats); + +/** Protocol Buffers v3-encode \a request */ +grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request* request); + +/** Destroy \a request */ +void grpc_grpclb_request_destroy(grpc_grpclb_request* request); + +/** Parse (ie, decode) the bytes in \a encoded_grpc_grpclb_response as a \a + * grpc_grpclb_initial_response */ +grpc_grpclb_initial_response* grpc_grpclb_initial_response_parse( + grpc_slice encoded_grpc_grpclb_response); + +/** Parse the list of servers from an encoded \a grpc_grpclb_response */ +grpc_grpclb_serverlist* grpc_grpclb_response_parse_serverlist( + grpc_slice encoded_grpc_grpclb_response); + +/** Return a copy of \a sl. The caller is responsible for calling \a + * grpc_grpclb_destroy_serverlist on the returned copy. */ +grpc_grpclb_serverlist* grpc_grpclb_serverlist_copy( + const grpc_grpclb_serverlist* sl); + +bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist* lhs, + const grpc_grpclb_serverlist* rhs); + +bool grpc_grpclb_server_equals(const grpc_grpclb_server* lhs, + const grpc_grpclb_server* rhs); + +/** Destroy \a serverlist */ +void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist* serverlist); + +/** Compare \a lhs against \a rhs and return 0 if \a lhs and \a rhs are equal, + * < 0 if \a lhs represents a duration shorter than \a rhs and > 0 otherwise */ +int grpc_grpclb_duration_compare(const grpc_grpclb_duration* lhs, + const grpc_grpclb_duration* rhs); + +grpc_millis grpc_grpclb_duration_to_millis(grpc_grpclb_duration* duration_pb); + +/** Destroy \a initial_response */ +void grpc_grpclb_initial_response_destroy( + grpc_grpclb_initial_response* response); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_LOAD_BALANCER_API_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c new file mode 100644 index 00000000000000..131d9b7cae076f --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.c @@ -0,0 +1,19 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Duration_fields[3] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_protobuf_Duration, seconds, seconds, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, google_protobuf_Duration, nanos, seconds, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h new file mode 100644 index 00000000000000..93070c65b830bf --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h @@ -0,0 +1,54 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GOOGLE_PROTOBUF_DURATION_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_DURATION_PB_H_INCLUDED +#include "pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_Duration { + bool has_seconds; + int64_t seconds; + bool has_nanos; + int32_t nanos; +/* @@protoc_insertion_point(struct:google_protobuf_Duration) */ +} google_protobuf_Duration; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Duration_init_default {false, 0, false, 0} +#define google_protobuf_Duration_init_zero {false, 0, false, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_Duration_seconds_tag 1 +#define google_protobuf_Duration_nanos_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Duration_fields[3]; + +/* Maximum encoded size of messages (where known) */ +#define google_protobuf_Duration_size 22 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define DURATION_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c new file mode 100644 index 00000000000000..b6f8ffc5592b1e --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.c @@ -0,0 +1,19 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_protobuf_Timestamp_fields[3] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_protobuf_Timestamp, seconds, seconds, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, google_protobuf_Timestamp, nanos, seconds, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h new file mode 100644 index 00000000000000..7f484815244e6b --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h @@ -0,0 +1,54 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED +#define PB_GOOGLE_PROTOBUF_TIMESTAMP_PB_H_INCLUDED +#include "pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _google_protobuf_Timestamp { + bool has_seconds; + int64_t seconds; + bool has_nanos; + int32_t nanos; +/* @@protoc_insertion_point(struct:google_protobuf_Timestamp) */ +} google_protobuf_Timestamp; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define google_protobuf_Timestamp_init_default {false, 0, false, 0} +#define google_protobuf_Timestamp_init_zero {false, 0, false, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define google_protobuf_Timestamp_seconds_tag 1 +#define google_protobuf_Timestamp_nanos_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t google_protobuf_Timestamp_fields[3]; + +/* Maximum encoded size of messages (where known) */ +#define google_protobuf_Timestamp_size 22 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define TIMESTAMP_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c new file mode 100644 index 00000000000000..f6538e1349fb00 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c @@ -0,0 +1,89 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_lb_v1_LoadBalanceRequest_fields[3] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_LoadBalanceRequest, initial_request, initial_request, &grpc_lb_v1_InitialLoadBalanceRequest_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_LoadBalanceRequest, client_stats, initial_request, &grpc_lb_v1_ClientStats_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_InitialLoadBalanceRequest_fields[2] = { + PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v1_InitialLoadBalanceRequest, name, name, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_ClientStatsPerToken_fields[3] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_lb_v1_ClientStatsPerToken, load_balance_token, load_balance_token, 0), + PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStatsPerToken, num_calls, load_balance_token, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_ClientStats_fields[7] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_ClientStats, timestamp, timestamp, &google_protobuf_Timestamp_fields), + PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_started, timestamp, 0), + PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished, num_calls_started, 0), + PB_FIELD( 6, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_with_client_failed_to_send, num_calls_finished, 0), + PB_FIELD( 7, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_known_received, num_calls_finished_with_client_failed_to_send, 0), + PB_FIELD( 8, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_lb_v1_ClientStats, calls_finished_with_drop, num_calls_finished_known_received, &grpc_lb_v1_ClientStatsPerToken_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_LoadBalanceResponse_fields[3] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_LoadBalanceResponse, initial_response, initial_response, &grpc_lb_v1_InitialLoadBalanceResponse_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_LoadBalanceResponse, server_list, initial_response, &grpc_lb_v1_ServerList_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_InitialLoadBalanceResponse_fields[3] = { + PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v1_InitialLoadBalanceResponse, load_balancer_delegate, load_balancer_delegate, 0), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval, load_balancer_delegate, &google_protobuf_Duration_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_ServerList_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, grpc_lb_v1_ServerList, servers, servers, &grpc_lb_v1_Server_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_Server_fields[5] = { + PB_FIELD( 1, BYTES , OPTIONAL, STATIC , FIRST, grpc_lb_v1_Server, ip_address, ip_address, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, port, ip_address, 0), + PB_FIELD( 3, STRING , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, load_balance_token, port, 0), + PB_FIELD( 4, BOOL , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, drop, load_balance_token, 0), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_lb_v1_LoadBalanceRequest, initial_request) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 65536 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 65536 && pb_membersize(grpc_lb_v1_ServerList, servers) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_lb_v1_LoadBalanceRequest, initial_request) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 256 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 256 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 256 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 256 && pb_membersize(grpc_lb_v1_ServerList, servers) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h new file mode 100644 index 00000000000000..a4ff516d8e10e2 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h @@ -0,0 +1,164 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_LB_V1_LOAD_BALANCER_PB_H_INCLUDED +#define PB_GRPC_LB_V1_LOAD_BALANCER_PB_H_INCLUDED +#include "pb.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/duration.pb.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/google/protobuf/timestamp.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _grpc_lb_v1_ServerList { + pb_callback_t servers; +/* @@protoc_insertion_point(struct:grpc_lb_v1_ServerList) */ +} grpc_lb_v1_ServerList; + +typedef struct _grpc_lb_v1_ClientStats { + bool has_timestamp; + google_protobuf_Timestamp timestamp; + bool has_num_calls_started; + int64_t num_calls_started; + bool has_num_calls_finished; + int64_t num_calls_finished; + bool has_num_calls_finished_with_client_failed_to_send; + int64_t num_calls_finished_with_client_failed_to_send; + bool has_num_calls_finished_known_received; + int64_t num_calls_finished_known_received; + pb_callback_t calls_finished_with_drop; +/* @@protoc_insertion_point(struct:grpc_lb_v1_ClientStats) */ +} grpc_lb_v1_ClientStats; + +typedef struct _grpc_lb_v1_ClientStatsPerToken { + pb_callback_t load_balance_token; + bool has_num_calls; + int64_t num_calls; +/* @@protoc_insertion_point(struct:grpc_lb_v1_ClientStatsPerToken) */ +} grpc_lb_v1_ClientStatsPerToken; + +typedef struct _grpc_lb_v1_InitialLoadBalanceRequest { + bool has_name; + char name[128]; +/* @@protoc_insertion_point(struct:grpc_lb_v1_InitialLoadBalanceRequest) */ +} grpc_lb_v1_InitialLoadBalanceRequest; + +typedef struct _grpc_lb_v1_InitialLoadBalanceResponse { + bool has_load_balancer_delegate; + char load_balancer_delegate[64]; + bool has_client_stats_report_interval; + google_protobuf_Duration client_stats_report_interval; +/* @@protoc_insertion_point(struct:grpc_lb_v1_InitialLoadBalanceResponse) */ +} grpc_lb_v1_InitialLoadBalanceResponse; + +typedef PB_BYTES_ARRAY_T(16) grpc_lb_v1_Server_ip_address_t; +typedef struct _grpc_lb_v1_Server { + bool has_ip_address; + grpc_lb_v1_Server_ip_address_t ip_address; + bool has_port; + int32_t port; + bool has_load_balance_token; + char load_balance_token[50]; + bool has_drop; + bool drop; +/* @@protoc_insertion_point(struct:grpc_lb_v1_Server) */ +} grpc_lb_v1_Server; + +typedef struct _grpc_lb_v1_LoadBalanceRequest { + bool has_initial_request; + grpc_lb_v1_InitialLoadBalanceRequest initial_request; + bool has_client_stats; + grpc_lb_v1_ClientStats client_stats; +/* @@protoc_insertion_point(struct:grpc_lb_v1_LoadBalanceRequest) */ +} grpc_lb_v1_LoadBalanceRequest; + +typedef struct _grpc_lb_v1_LoadBalanceResponse { + bool has_initial_response; + grpc_lb_v1_InitialLoadBalanceResponse initial_response; + bool has_server_list; + grpc_lb_v1_ServerList server_list; +/* @@protoc_insertion_point(struct:grpc_lb_v1_LoadBalanceResponse) */ +} grpc_lb_v1_LoadBalanceResponse; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_lb_v1_LoadBalanceRequest_init_default {false, grpc_lb_v1_InitialLoadBalanceRequest_init_default, false, grpc_lb_v1_ClientStats_init_default} +#define grpc_lb_v1_InitialLoadBalanceRequest_init_default {false, ""} +#define grpc_lb_v1_ClientStatsPerToken_init_default {{{NULL}, NULL}, false, 0} +#define grpc_lb_v1_ClientStats_init_default {false, google_protobuf_Timestamp_init_default, false, 0, false, 0, false, 0, false, 0, {{NULL}, NULL}} +#define grpc_lb_v1_LoadBalanceResponse_init_default {false, grpc_lb_v1_InitialLoadBalanceResponse_init_default, false, grpc_lb_v1_ServerList_init_default} +#define grpc_lb_v1_InitialLoadBalanceResponse_init_default {false, "", false, google_protobuf_Duration_init_default} +#define grpc_lb_v1_ServerList_init_default {{{NULL}, NULL}} +#define grpc_lb_v1_Server_init_default {false, {0, {0}}, false, 0, false, "", false, 0} +#define grpc_lb_v1_LoadBalanceRequest_init_zero {false, grpc_lb_v1_InitialLoadBalanceRequest_init_zero, false, grpc_lb_v1_ClientStats_init_zero} +#define grpc_lb_v1_InitialLoadBalanceRequest_init_zero {false, ""} +#define grpc_lb_v1_ClientStatsPerToken_init_zero {{{NULL}, NULL}, false, 0} +#define grpc_lb_v1_ClientStats_init_zero {false, google_protobuf_Timestamp_init_zero, false, 0, false, 0, false, 0, false, 0, {{NULL}, NULL}} +#define grpc_lb_v1_LoadBalanceResponse_init_zero {false, grpc_lb_v1_InitialLoadBalanceResponse_init_zero, false, grpc_lb_v1_ServerList_init_zero} +#define grpc_lb_v1_InitialLoadBalanceResponse_init_zero {false, "", false, google_protobuf_Duration_init_zero} +#define grpc_lb_v1_ServerList_init_zero {{{NULL}, NULL}} +#define grpc_lb_v1_Server_init_zero {false, {0, {0}}, false, 0, false, "", false, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_lb_v1_ServerList_servers_tag 1 +#define grpc_lb_v1_ClientStats_timestamp_tag 1 +#define grpc_lb_v1_ClientStats_num_calls_started_tag 2 +#define grpc_lb_v1_ClientStats_num_calls_finished_tag 3 +#define grpc_lb_v1_ClientStats_num_calls_finished_with_client_failed_to_send_tag 6 +#define grpc_lb_v1_ClientStats_num_calls_finished_known_received_tag 7 +#define grpc_lb_v1_ClientStats_calls_finished_with_drop_tag 8 +#define grpc_lb_v1_ClientStatsPerToken_load_balance_token_tag 1 +#define grpc_lb_v1_ClientStatsPerToken_num_calls_tag 2 +#define grpc_lb_v1_InitialLoadBalanceRequest_name_tag 1 +#define grpc_lb_v1_InitialLoadBalanceResponse_load_balancer_delegate_tag 1 +#define grpc_lb_v1_InitialLoadBalanceResponse_client_stats_report_interval_tag 2 +#define grpc_lb_v1_Server_ip_address_tag 1 +#define grpc_lb_v1_Server_port_tag 2 +#define grpc_lb_v1_Server_load_balance_token_tag 3 +#define grpc_lb_v1_Server_drop_tag 4 +#define grpc_lb_v1_LoadBalanceRequest_initial_request_tag 1 +#define grpc_lb_v1_LoadBalanceRequest_client_stats_tag 2 +#define grpc_lb_v1_LoadBalanceResponse_initial_response_tag 1 +#define grpc_lb_v1_LoadBalanceResponse_server_list_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_lb_v1_LoadBalanceRequest_fields[3]; +extern const pb_field_t grpc_lb_v1_InitialLoadBalanceRequest_fields[2]; +extern const pb_field_t grpc_lb_v1_ClientStatsPerToken_fields[3]; +extern const pb_field_t grpc_lb_v1_ClientStats_fields[7]; +extern const pb_field_t grpc_lb_v1_LoadBalanceResponse_fields[3]; +extern const pb_field_t grpc_lb_v1_InitialLoadBalanceResponse_fields[3]; +extern const pb_field_t grpc_lb_v1_ServerList_fields[2]; +extern const pb_field_t grpc_lb_v1_Server_fields[5]; + +/* Maximum encoded size of messages (where known) */ +#define grpc_lb_v1_LoadBalanceRequest_size (140 + grpc_lb_v1_ClientStats_size) +#define grpc_lb_v1_InitialLoadBalanceRequest_size 131 +/* grpc_lb_v1_ClientStatsPerToken_size depends on runtime parameters */ +/* grpc_lb_v1_ClientStats_size depends on runtime parameters */ +#define grpc_lb_v1_LoadBalanceResponse_size (98 + grpc_lb_v1_ServerList_size) +#define grpc_lb_v1_InitialLoadBalanceResponse_size 90 +/* grpc_lb_v1_ServerList_size depends on runtime parameters */ +#define grpc_lb_v1_Server_size 83 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define LOAD_BALANCER_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc new file mode 100644 index 00000000000000..d6ff74ec7f7745 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc @@ -0,0 +1,643 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/subchannel_list.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/transport/connectivity_state.h" + +namespace grpc_core { + +TraceFlag grpc_lb_pick_first_trace(false, "pick_first"); + +namespace { + +// +// pick_first LB policy +// + +constexpr char kPickFirst[] = "pick_first"; + +class PickFirst : public LoadBalancingPolicy { + public: + explicit PickFirst(const Args& args); + + const char* name() const override { return kPickFirst; } + + void UpdateLocked(const grpc_channel_args& args, + grpc_json* lb_config) override; + bool PickLocked(PickState* pick, grpc_error** error) override; + void CancelPickLocked(PickState* pick, grpc_error* error) override; + void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) override; + void NotifyOnStateChangeLocked(grpc_connectivity_state* state, + grpc_closure* closure) override; + grpc_connectivity_state CheckConnectivityLocked( + grpc_error** connectivity_error) override; + void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override; + void ExitIdleLocked() override; + void ResetBackoffLocked() override; + void FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels, + channelz::ChildRefsList* ignored) override; + + private: + ~PickFirst(); + + class PickFirstSubchannelList; + + class PickFirstSubchannelData + : public SubchannelData { + public: + PickFirstSubchannelData( + SubchannelList* + subchannel_list, + const ServerAddress& address, grpc_subchannel* subchannel, + grpc_combiner* combiner) + : SubchannelData(subchannel_list, address, subchannel, combiner) {} + + void ProcessConnectivityChangeLocked( + grpc_connectivity_state connectivity_state, grpc_error* error) override; + + // Processes the connectivity change to READY for an unselected subchannel. + void ProcessUnselectedReadyLocked(); + + void CheckConnectivityStateAndStartWatchingLocked(); + }; + + class PickFirstSubchannelList + : public SubchannelList { + public: + PickFirstSubchannelList(PickFirst* policy, TraceFlag* tracer, + const ServerAddressList& addresses, + grpc_combiner* combiner, + grpc_client_channel_factory* client_channel_factory, + const grpc_channel_args& args) + : SubchannelList(policy, tracer, addresses, combiner, + client_channel_factory, args) { + // Need to maintain a ref to the LB policy as long as we maintain + // any references to subchannels, since the subchannels' + // pollset_sets will include the LB policy's pollset_set. + policy->Ref(DEBUG_LOCATION, "subchannel_list").release(); + } + + ~PickFirstSubchannelList() { + PickFirst* p = static_cast(policy()); + p->Unref(DEBUG_LOCATION, "subchannel_list"); + } + }; + + // Helper class to ensure that any function that modifies the child refs + // data structures will update the channelz snapshot data structures before + // returning. + class AutoChildRefsUpdater { + public: + explicit AutoChildRefsUpdater(PickFirst* pf) : pf_(pf) {} + ~AutoChildRefsUpdater() { pf_->UpdateChildRefsLocked(); } + + private: + PickFirst* pf_; + }; + + void ShutdownLocked() override; + + void StartPickingLocked(); + void UpdateChildRefsLocked(); + + // All our subchannels. + OrphanablePtr subchannel_list_; + // Latest pending subchannel list. + OrphanablePtr latest_pending_subchannel_list_; + // Selected subchannel in \a subchannel_list_. + PickFirstSubchannelData* selected_ = nullptr; + // Have we started picking? + bool started_picking_ = false; + // Are we shut down? + bool shutdown_ = false; + // List of picks that are waiting on connectivity. + PickState* pending_picks_ = nullptr; + // Our connectivity state tracker. + grpc_connectivity_state_tracker state_tracker_; + + /// Lock and data used to capture snapshots of this channels child + /// channels and subchannels. This data is consumed by channelz. + gpr_mu child_refs_mu_; + channelz::ChildRefsList child_subchannels_; + channelz::ChildRefsList child_channels_; +}; + +PickFirst::PickFirst(const Args& args) : LoadBalancingPolicy(args) { + GPR_ASSERT(args.client_channel_factory != nullptr); + gpr_mu_init(&child_refs_mu_); + grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, + "pick_first"); + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, "Pick First %p created.", this); + } + UpdateLocked(*args.args, args.lb_config); + grpc_subchannel_index_ref(); +} + +PickFirst::~PickFirst() { + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, "Destroying Pick First %p", this); + } + gpr_mu_destroy(&child_refs_mu_); + GPR_ASSERT(subchannel_list_ == nullptr); + GPR_ASSERT(latest_pending_subchannel_list_ == nullptr); + GPR_ASSERT(pending_picks_ == nullptr); + grpc_connectivity_state_destroy(&state_tracker_); + grpc_subchannel_index_unref(); +} + +void PickFirst::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) { + PickState* pick; + while ((pick = pending_picks_) != nullptr) { + pending_picks_ = pick->next; + grpc_error* error = GRPC_ERROR_NONE; + if (new_policy->PickLocked(pick, &error)) { + // Synchronous return, schedule closure. + GRPC_CLOSURE_SCHED(pick->on_complete, error); + } + } +} + +void PickFirst::ShutdownLocked() { + AutoChildRefsUpdater guard(this); + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown"); + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, "Pick First %p Shutting down", this); + } + shutdown_ = true; + PickState* pick; + while ((pick = pending_picks_) != nullptr) { + pending_picks_ = pick->next; + pick->connected_subchannel.reset(); + GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error)); + } + grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_REF(error), "shutdown"); + subchannel_list_.reset(); + latest_pending_subchannel_list_.reset(); + TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_CANCELLED); + GRPC_ERROR_UNREF(error); +} + +void PickFirst::CancelPickLocked(PickState* pick, grpc_error* error) { + PickState* pp = pending_picks_; + pending_picks_ = nullptr; + while (pp != nullptr) { + PickState* next = pp->next; + if (pp == pick) { + pick->connected_subchannel.reset(); + GRPC_CLOSURE_SCHED(pick->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = pending_picks_; + pending_picks_ = pp; + } + pp = next; + } + GRPC_ERROR_UNREF(error); +} + +void PickFirst::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) { + PickState* pick = pending_picks_; + pending_picks_ = nullptr; + while (pick != nullptr) { + PickState* next = pick->next; + if ((*pick->initial_metadata_flags & initial_metadata_flags_mask) == + initial_metadata_flags_eq) { + GRPC_CLOSURE_SCHED(pick->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pick->next = pending_picks_; + pending_picks_ = pick; + } + pick = next; + } + GRPC_ERROR_UNREF(error); +} + +void PickFirst::StartPickingLocked() { + started_picking_ = true; + if (subchannel_list_ != nullptr && subchannel_list_->num_subchannels() > 0) { + subchannel_list_->subchannel(0) + ->CheckConnectivityStateAndStartWatchingLocked(); + } +} + +void PickFirst::ExitIdleLocked() { + if (!started_picking_) { + StartPickingLocked(); + } +} + +void PickFirst::ResetBackoffLocked() { + subchannel_list_->ResetBackoffLocked(); + if (latest_pending_subchannel_list_ != nullptr) { + latest_pending_subchannel_list_->ResetBackoffLocked(); + } +} + +bool PickFirst::PickLocked(PickState* pick, grpc_error** error) { + // If we have a selected subchannel already, return synchronously. + if (selected_ != nullptr) { + pick->connected_subchannel = selected_->connected_subchannel()->Ref(); + return true; + } + // No subchannel selected yet, so handle asynchronously. + if (pick->on_complete == nullptr) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No pick result available but synchronous result required."); + return true; + } + pick->next = pending_picks_; + pending_picks_ = pick; + if (!started_picking_) { + StartPickingLocked(); + } + return false; +} + +grpc_connectivity_state PickFirst::CheckConnectivityLocked(grpc_error** error) { + return grpc_connectivity_state_get(&state_tracker_, error); +} + +void PickFirst::NotifyOnStateChangeLocked(grpc_connectivity_state* current, + grpc_closure* notify) { + grpc_connectivity_state_notify_on_state_change(&state_tracker_, current, + notify); +} + +void PickFirst::FillChildRefsForChannelz( + channelz::ChildRefsList* child_subchannels_to_fill, + channelz::ChildRefsList* ignored) { + MutexLock lock(&child_refs_mu_); + for (size_t i = 0; i < child_subchannels_.size(); ++i) { + // TODO(ncteisen): implement a de dup loop that is not O(n^2). Might + // have to implement lightweight set. For now, we don't care about + // performance when channelz requests are made. + bool found = false; + for (size_t j = 0; j < child_subchannels_to_fill->size(); ++j) { + if ((*child_subchannels_to_fill)[j] == child_subchannels_[i]) { + found = true; + break; + } + } + if (!found) { + child_subchannels_to_fill->push_back(child_subchannels_[i]); + } + } +} + +void PickFirst::UpdateChildRefsLocked() { + channelz::ChildRefsList cs; + if (subchannel_list_ != nullptr) { + subchannel_list_->PopulateChildRefsList(&cs); + } + if (latest_pending_subchannel_list_ != nullptr) { + latest_pending_subchannel_list_->PopulateChildRefsList(&cs); + } + // atomically update the data that channelz will actually be looking at. + MutexLock lock(&child_refs_mu_); + child_subchannels_ = std::move(cs); +} + +void PickFirst::UpdateLocked(const grpc_channel_args& args, + grpc_json* lb_config) { + AutoChildRefsUpdater guard(this); + const ServerAddressList* addresses = FindServerAddressListChannelArg(&args); + if (addresses == nullptr) { + if (subchannel_list_ == nullptr) { + // If we don't have a current subchannel list, go into TRANSIENT FAILURE. + grpc_connectivity_state_set( + &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), + "pf_update_missing"); + } else { + // otherwise, keep using the current subchannel list (ignore this update). + gpr_log(GPR_ERROR, + "No valid LB addresses channel arg for Pick First %p update, " + "ignoring.", + this); + } + return; + } + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, + "Pick First %p received update with %" PRIuPTR " addresses", this, + addresses->size()); + } + grpc_arg new_arg = grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1); + grpc_channel_args* new_args = + grpc_channel_args_copy_and_add(&args, &new_arg, 1); + auto subchannel_list = MakeOrphanable( + this, &grpc_lb_pick_first_trace, *addresses, combiner(), + client_channel_factory(), *new_args); + grpc_channel_args_destroy(new_args); + if (subchannel_list->num_subchannels() == 0) { + // Empty update or no valid subchannels. Unsubscribe from all current + // subchannels and put the channel in TRANSIENT_FAILURE. + grpc_connectivity_state_set( + &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), + "pf_update_empty"); + subchannel_list_ = std::move(subchannel_list); // Empty list. + selected_ = nullptr; + return; + } + // If one of the subchannels in the new list is already in state + // READY, then select it immediately. This can happen when the + // currently selected subchannel is also present in the update. It + // can also happen if one of the subchannels in the update is already + // in the subchannel index because it's in use by another channel. + for (size_t i = 0; i < subchannel_list->num_subchannels(); ++i) { + PickFirstSubchannelData* sd = subchannel_list->subchannel(i); + grpc_error* error = GRPC_ERROR_NONE; + grpc_connectivity_state state = sd->CheckConnectivityStateLocked(&error); + GRPC_ERROR_UNREF(error); + if (state == GRPC_CHANNEL_READY) { + subchannel_list_ = std::move(subchannel_list); + sd->ProcessUnselectedReadyLocked(); + sd->StartConnectivityWatchLocked(); + // If there was a previously pending update (which may or may + // not have contained the currently selected subchannel), drop + // it, so that it doesn't override what we've done here. + latest_pending_subchannel_list_.reset(); + // Make sure that subsequent calls to ExitIdleLocked() don't cause + // us to start watching a subchannel other than the one we've + // selected. + started_picking_ = true; + return; + } + } + if (selected_ == nullptr) { + // We don't yet have a selected subchannel, so replace the current + // subchannel list immediately. + subchannel_list_ = std::move(subchannel_list); + // If we've started picking, start trying to connect to the first + // subchannel in the new list. + if (started_picking_) { + // Note: No need to use CheckConnectivityStateAndStartWatchingLocked() + // here, since we've already checked the initial connectivity + // state of all subchannels above. + subchannel_list_->subchannel(0)->StartConnectivityWatchLocked(); + } + } else { + // We do have a selected subchannel, so keep using it until one of + // the subchannels in the new list reports READY. + if (latest_pending_subchannel_list_ != nullptr) { + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, + "Pick First %p Shutting down latest pending subchannel list " + "%p, about to be replaced by newer latest %p", + this, latest_pending_subchannel_list_.get(), + subchannel_list.get()); + } + } + latest_pending_subchannel_list_ = std::move(subchannel_list); + // If we've started picking, start trying to connect to the first + // subchannel in the new list. + if (started_picking_) { + // Note: No need to use CheckConnectivityStateAndStartWatchingLocked() + // here, since we've already checked the initial connectivity + // state of all subchannels above. + latest_pending_subchannel_list_->subchannel(0) + ->StartConnectivityWatchLocked(); + } + } +} + +void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked( + grpc_connectivity_state connectivity_state, grpc_error* error) { + PickFirst* p = static_cast(subchannel_list()->policy()); + AutoChildRefsUpdater guard(p); + // The notification must be for a subchannel in either the current or + // latest pending subchannel lists. + GPR_ASSERT(subchannel_list() == p->subchannel_list_.get() || + subchannel_list() == p->latest_pending_subchannel_list_.get()); + GPR_ASSERT(connectivity_state != GRPC_CHANNEL_SHUTDOWN); + // Handle updates for the currently selected subchannel. + if (p->selected_ == this) { + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, + "Pick First %p connectivity changed for selected subchannel", p); + } + // If the new state is anything other than READY and there is a + // pending update, switch to the pending update. + if (connectivity_state != GRPC_CHANNEL_READY && + p->latest_pending_subchannel_list_ != nullptr) { + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, + "Pick First %p promoting pending subchannel list %p to " + "replace %p", + p, p->latest_pending_subchannel_list_.get(), + p->subchannel_list_.get()); + } + p->selected_ = nullptr; + StopConnectivityWatchLocked(); + p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_); + grpc_connectivity_state_set( + &p->state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE, + error != GRPC_ERROR_NONE + ? GRPC_ERROR_REF(error) + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "selected subchannel not ready; switching to pending " + "update"), + "selected_not_ready+switch_to_update"); + } else { + if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + // If the selected subchannel goes bad, request a re-resolution. We also + // set the channel state to IDLE and reset started_picking_. The reason + // is that if the new state is TRANSIENT_FAILURE due to a GOAWAY + // reception we don't want to connect to the re-resolved backends until + // we leave the IDLE state. + grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_IDLE, + GRPC_ERROR_NONE, + "selected_changed+reresolve"); + p->started_picking_ = false; + p->TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_NONE); + // In transient failure. Rely on re-resolution to recover. + p->selected_ = nullptr; + StopConnectivityWatchLocked(); + } else { + grpc_connectivity_state_set(&p->state_tracker_, connectivity_state, + GRPC_ERROR_REF(error), "selected_changed"); + // Renew notification. + RenewConnectivityWatchLocked(); + } + } + GRPC_ERROR_UNREF(error); + return; + } + // If we get here, there are two possible cases: + // 1. We do not currently have a selected subchannel, and the update is + // for a subchannel in p->subchannel_list_ that we're trying to + // connect to. The goal here is to find a subchannel that we can + // select. + // 2. We do currently have a selected subchannel, and the update is + // for a subchannel in p->latest_pending_subchannel_list_. The + // goal here is to find a subchannel from the update that we can + // select in place of the current one. + switch (connectivity_state) { + case GRPC_CHANNEL_READY: { + ProcessUnselectedReadyLocked(); + // Renew notification. + RenewConnectivityWatchLocked(); + break; + } + case GRPC_CHANNEL_TRANSIENT_FAILURE: { + StopConnectivityWatchLocked(); + PickFirstSubchannelData* sd = this; + size_t next_index = + (sd->Index() + 1) % subchannel_list()->num_subchannels(); + sd = subchannel_list()->subchannel(next_index); + // Case 1: Only set state to TRANSIENT_FAILURE if we've tried + // all subchannels. + if (sd->Index() == 0 && subchannel_list() == p->subchannel_list_.get()) { + p->TryReresolutionLocked(&grpc_lb_pick_first_trace, GRPC_ERROR_NONE); + grpc_connectivity_state_set( + &p->state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_REF(error), "exhausted_subchannels"); + } + sd->CheckConnectivityStateAndStartWatchingLocked(); + break; + } + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_IDLE: { + // Only update connectivity state in case 1. + if (subchannel_list() == p->subchannel_list_.get()) { + grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_CONNECTING, + GRPC_ERROR_REF(error), + "connecting_changed"); + } + // Renew notification. + RenewConnectivityWatchLocked(); + break; + } + case GRPC_CHANNEL_SHUTDOWN: + GPR_UNREACHABLE_CODE(break); + } + GRPC_ERROR_UNREF(error); +} + +void PickFirst::PickFirstSubchannelData::ProcessUnselectedReadyLocked() { + PickFirst* p = static_cast(subchannel_list()->policy()); + // If we get here, there are two possible cases: + // 1. We do not currently have a selected subchannel, and the update is + // for a subchannel in p->subchannel_list_ that we're trying to + // connect to. The goal here is to find a subchannel that we can + // select. + // 2. We do currently have a selected subchannel, and the update is + // for a subchannel in p->latest_pending_subchannel_list_. The + // goal here is to find a subchannel from the update that we can + // select in place of the current one. + GPR_ASSERT(subchannel_list() == p->subchannel_list_.get() || + subchannel_list() == p->latest_pending_subchannel_list_.get()); + // Case 2. Promote p->latest_pending_subchannel_list_ to p->subchannel_list_. + if (subchannel_list() == p->latest_pending_subchannel_list_.get()) { + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, + "Pick First %p promoting pending subchannel list %p to " + "replace %p", + p, p->latest_pending_subchannel_list_.get(), + p->subchannel_list_.get()); + } + p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_); + } + // Cases 1 and 2. + grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_READY, + GRPC_ERROR_NONE, "subchannel_ready"); + p->selected_ = this; + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", p, subchannel()); + } + // Update any calls that were waiting for a pick. + PickState* pick; + while ((pick = p->pending_picks_)) { + p->pending_picks_ = pick->next; + pick->connected_subchannel = p->selected_->connected_subchannel()->Ref(); + if (grpc_lb_pick_first_trace.enabled()) { + gpr_log(GPR_INFO, "Servicing pending pick with selected subchannel %p", + p->selected_->subchannel()); + } + GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE); + } +} + +void PickFirst::PickFirstSubchannelData:: + CheckConnectivityStateAndStartWatchingLocked() { + PickFirst* p = static_cast(subchannel_list()->policy()); + grpc_error* error = GRPC_ERROR_NONE; + if (p->selected_ != this && + CheckConnectivityStateLocked(&error) == GRPC_CHANNEL_READY) { + // We must process the READY subchannel before we start watching it. + // Otherwise, we won't know it's READY because we will be waiting for its + // connectivity state to change from READY. + ProcessUnselectedReadyLocked(); + } + GRPC_ERROR_UNREF(error); + StartConnectivityWatchLocked(); +} + +// +// factory +// + +class PickFirstFactory : public LoadBalancingPolicyFactory { + public: + OrphanablePtr CreateLoadBalancingPolicy( + const LoadBalancingPolicy::Args& args) const override { + return OrphanablePtr(New(args)); + } + + const char* name() const override { return kPickFirst; } +}; + +} // namespace + +} // namespace grpc_core + +void grpc_lb_policy_pick_first_init() { + grpc_core::LoadBalancingPolicyRegistry::Builder:: + RegisterLoadBalancingPolicyFactory( + grpc_core::UniquePtr( + grpc_core::New())); +} + +void grpc_lb_policy_pick_first_shutdown() {} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc new file mode 100644 index 00000000000000..3bcb33ef11c5f3 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc @@ -0,0 +1,721 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** Round Robin Policy. + * + * Before every pick, the \a get_next_ready_subchannel_index_locked function + * returns the p->subchannel_list->subchannels index for next subchannel, + * respecting the relative order of the addresses provided upon creation or + * updates. Note however that updates will start picking from the beginning of + * the updated list. */ + +#include + +#include + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/subchannel_list.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/static_metadata.h" + +namespace grpc_core { + +TraceFlag grpc_lb_round_robin_trace(false, "round_robin"); + +namespace { + +// +// round_robin LB policy +// + +constexpr char kRoundRobin[] = "round_robin"; + +class RoundRobin : public LoadBalancingPolicy { + public: + explicit RoundRobin(const Args& args); + + const char* name() const override { return kRoundRobin; } + + void UpdateLocked(const grpc_channel_args& args, + grpc_json* lb_config) override; + bool PickLocked(PickState* pick, grpc_error** error) override; + void CancelPickLocked(PickState* pick, grpc_error* error) override; + void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) override; + void NotifyOnStateChangeLocked(grpc_connectivity_state* state, + grpc_closure* closure) override; + grpc_connectivity_state CheckConnectivityLocked( + grpc_error** connectivity_error) override; + void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override; + void ExitIdleLocked() override; + void ResetBackoffLocked() override; + void FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels, + channelz::ChildRefsList* ignored) override; + + private: + ~RoundRobin(); + + // Forward declaration. + class RoundRobinSubchannelList; + + // Data for a particular subchannel in a subchannel list. + // This subclass adds the following functionality: + // - Tracks the previous connectivity state of the subchannel, so that + // we know how many subchannels are in each state. + class RoundRobinSubchannelData + : public SubchannelData { + public: + RoundRobinSubchannelData( + SubchannelList* + subchannel_list, + const ServerAddress& address, grpc_subchannel* subchannel, + grpc_combiner* combiner) + : SubchannelData(subchannel_list, address, subchannel, combiner) {} + + grpc_connectivity_state connectivity_state() const { + return last_connectivity_state_; + } + + void UpdateConnectivityStateLocked( + grpc_connectivity_state connectivity_state, grpc_error* error); + + private: + void ProcessConnectivityChangeLocked( + grpc_connectivity_state connectivity_state, grpc_error* error) override; + + grpc_connectivity_state last_connectivity_state_ = GRPC_CHANNEL_IDLE; + }; + + // A list of subchannels. + class RoundRobinSubchannelList + : public SubchannelList { + public: + RoundRobinSubchannelList( + RoundRobin* policy, TraceFlag* tracer, + const ServerAddressList& addresses, grpc_combiner* combiner, + grpc_client_channel_factory* client_channel_factory, + const grpc_channel_args& args) + : SubchannelList(policy, tracer, addresses, combiner, + client_channel_factory, args), + last_ready_index_(num_subchannels() - 1) { + // Need to maintain a ref to the LB policy as long as we maintain + // any references to subchannels, since the subchannels' + // pollset_sets will include the LB policy's pollset_set. + policy->Ref(DEBUG_LOCATION, "subchannel_list").release(); + } + + ~RoundRobinSubchannelList() { + GRPC_ERROR_UNREF(last_transient_failure_error_); + RoundRobin* p = static_cast(policy()); + p->Unref(DEBUG_LOCATION, "subchannel_list"); + } + + // Starts watching the subchannels in this list. + void StartWatchingLocked(); + + // Updates the counters of subchannels in each state when a + // subchannel transitions from old_state to new_state. + // transient_failure_error is the error that is reported when + // new_state is TRANSIENT_FAILURE. + void UpdateStateCountersLocked(grpc_connectivity_state old_state, + grpc_connectivity_state new_state, + grpc_error* transient_failure_error); + + // If this subchannel list is the RR policy's current subchannel + // list, updates the RR policy's connectivity state based on the + // subchannel list's state counters. + void MaybeUpdateRoundRobinConnectivityStateLocked(); + + // Updates the RR policy's overall state based on the counters of + // subchannels in each state. + void UpdateRoundRobinStateFromSubchannelStateCountsLocked(); + + size_t GetNextReadySubchannelIndexLocked(); + void UpdateLastReadySubchannelIndexLocked(size_t last_ready_index); + + private: + size_t num_ready_ = 0; + size_t num_connecting_ = 0; + size_t num_transient_failure_ = 0; + grpc_error* last_transient_failure_error_ = GRPC_ERROR_NONE; + size_t last_ready_index_; // Index into list of last pick. + }; + + // Helper class to ensure that any function that modifies the child refs + // data structures will update the channelz snapshot data structures before + // returning. + class AutoChildRefsUpdater { + public: + explicit AutoChildRefsUpdater(RoundRobin* rr) : rr_(rr) {} + ~AutoChildRefsUpdater() { rr_->UpdateChildRefsLocked(); } + + private: + RoundRobin* rr_; + }; + + void ShutdownLocked() override; + + void StartPickingLocked(); + bool DoPickLocked(PickState* pick); + void DrainPendingPicksLocked(); + void UpdateChildRefsLocked(); + + /** list of subchannels */ + OrphanablePtr subchannel_list_; + /** Latest version of the subchannel list. + * Subchannel connectivity callbacks will only promote updated subchannel + * lists if they equal \a latest_pending_subchannel_list. In other words, + * racing callbacks that reference outdated subchannel lists won't perform any + * update. */ + OrphanablePtr latest_pending_subchannel_list_; + /** have we started picking? */ + bool started_picking_ = false; + /** are we shutting down? */ + bool shutdown_ = false; + /** List of picks that are waiting on connectivity */ + PickState* pending_picks_ = nullptr; + /** our connectivity state tracker */ + grpc_connectivity_state_tracker state_tracker_; + /// Lock and data used to capture snapshots of this channel's child + /// channels and subchannels. This data is consumed by channelz. + gpr_mu child_refs_mu_; + channelz::ChildRefsList child_subchannels_; + channelz::ChildRefsList child_channels_; +}; + +RoundRobin::RoundRobin(const Args& args) : LoadBalancingPolicy(args) { + GPR_ASSERT(args.client_channel_factory != nullptr); + gpr_mu_init(&child_refs_mu_); + grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, + "round_robin"); + UpdateLocked(*args.args, args.lb_config); + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, "[RR %p] Created with %" PRIuPTR " subchannels", this, + subchannel_list_->num_subchannels()); + } + grpc_subchannel_index_ref(); +} + +RoundRobin::~RoundRobin() { + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, "[RR %p] Destroying Round Robin policy", this); + } + gpr_mu_destroy(&child_refs_mu_); + GPR_ASSERT(subchannel_list_ == nullptr); + GPR_ASSERT(latest_pending_subchannel_list_ == nullptr); + GPR_ASSERT(pending_picks_ == nullptr); + grpc_connectivity_state_destroy(&state_tracker_); + grpc_subchannel_index_unref(); +} + +void RoundRobin::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) { + PickState* pick; + while ((pick = pending_picks_) != nullptr) { + pending_picks_ = pick->next; + grpc_error* error = GRPC_ERROR_NONE; + if (new_policy->PickLocked(pick, &error)) { + // Synchronous return, schedule closure. + GRPC_CLOSURE_SCHED(pick->on_complete, error); + } + } +} + +void RoundRobin::ShutdownLocked() { + AutoChildRefsUpdater guard(this); + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown"); + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, "[RR %p] Shutting down", this); + } + shutdown_ = true; + PickState* pick; + while ((pick = pending_picks_) != nullptr) { + pending_picks_ = pick->next; + pick->connected_subchannel.reset(); + GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_REF(error)); + } + grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_REF(error), "rr_shutdown"); + subchannel_list_.reset(); + latest_pending_subchannel_list_.reset(); + TryReresolutionLocked(&grpc_lb_round_robin_trace, GRPC_ERROR_CANCELLED); + GRPC_ERROR_UNREF(error); +} + +void RoundRobin::CancelPickLocked(PickState* pick, grpc_error* error) { + PickState* pp = pending_picks_; + pending_picks_ = nullptr; + while (pp != nullptr) { + PickState* next = pp->next; + if (pp == pick) { + pick->connected_subchannel.reset(); + GRPC_CLOSURE_SCHED(pick->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = pending_picks_; + pending_picks_ = pp; + } + pp = next; + } + GRPC_ERROR_UNREF(error); +} + +void RoundRobin::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) { + PickState* pick = pending_picks_; + pending_picks_ = nullptr; + while (pick != nullptr) { + PickState* next = pick->next; + if ((*pick->initial_metadata_flags & initial_metadata_flags_mask) == + initial_metadata_flags_eq) { + pick->connected_subchannel.reset(); + GRPC_CLOSURE_SCHED(pick->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pick->next = pending_picks_; + pending_picks_ = pick; + } + pick = next; + } + GRPC_ERROR_UNREF(error); +} + +void RoundRobin::StartPickingLocked() { + started_picking_ = true; + subchannel_list_->StartWatchingLocked(); +} + +void RoundRobin::ExitIdleLocked() { + if (!started_picking_) { + StartPickingLocked(); + } +} + +void RoundRobin::ResetBackoffLocked() { + subchannel_list_->ResetBackoffLocked(); + if (latest_pending_subchannel_list_ != nullptr) { + latest_pending_subchannel_list_->ResetBackoffLocked(); + } +} + +bool RoundRobin::DoPickLocked(PickState* pick) { + const size_t next_ready_index = + subchannel_list_->GetNextReadySubchannelIndexLocked(); + if (next_ready_index < subchannel_list_->num_subchannels()) { + /* readily available, report right away */ + RoundRobinSubchannelData* sd = + subchannel_list_->subchannel(next_ready_index); + GPR_ASSERT(sd->connected_subchannel() != nullptr); + pick->connected_subchannel = sd->connected_subchannel()->Ref(); + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, + "[RR %p] Picked target <-- Subchannel %p (connected %p) (sl %p, " + "index %" PRIuPTR ")", + this, sd->subchannel(), pick->connected_subchannel.get(), + sd->subchannel_list(), next_ready_index); + } + /* only advance the last picked pointer if the selection was used */ + subchannel_list_->UpdateLastReadySubchannelIndexLocked(next_ready_index); + return true; + } + return false; +} + +void RoundRobin::DrainPendingPicksLocked() { + PickState* pick; + while ((pick = pending_picks_)) { + pending_picks_ = pick->next; + GPR_ASSERT(DoPickLocked(pick)); + GRPC_CLOSURE_SCHED(pick->on_complete, GRPC_ERROR_NONE); + } +} + +bool RoundRobin::PickLocked(PickState* pick, grpc_error** error) { + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, "[RR %p] Trying to pick (shutdown: %d)", this, shutdown_); + } + GPR_ASSERT(!shutdown_); + if (subchannel_list_ != nullptr) { + if (DoPickLocked(pick)) return true; + } + if (pick->on_complete == nullptr) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No pick result available but synchronous result required."); + return true; + } + /* no pick currently available. Save for later in list of pending picks */ + pick->next = pending_picks_; + pending_picks_ = pick; + if (!started_picking_) { + StartPickingLocked(); + } + return false; +} + +void RoundRobin::FillChildRefsForChannelz( + channelz::ChildRefsList* child_subchannels_to_fill, + channelz::ChildRefsList* ignored) { + MutexLock lock(&child_refs_mu_); + for (size_t i = 0; i < child_subchannels_.size(); ++i) { + // TODO(ncteisen): implement a de dup loop that is not O(n^2). Might + // have to implement lightweight set. For now, we don't care about + // performance when channelz requests are made. + bool found = false; + for (size_t j = 0; j < child_subchannels_to_fill->size(); ++j) { + if ((*child_subchannels_to_fill)[j] == child_subchannels_[i]) { + found = true; + break; + } + } + if (!found) { + child_subchannels_to_fill->push_back(child_subchannels_[i]); + } + } +} + +void RoundRobin::UpdateChildRefsLocked() { + channelz::ChildRefsList cs; + if (subchannel_list_ != nullptr) { + subchannel_list_->PopulateChildRefsList(&cs); + } + if (latest_pending_subchannel_list_ != nullptr) { + latest_pending_subchannel_list_->PopulateChildRefsList(&cs); + } + // atomically update the data that channelz will actually be looking at. + MutexLock lock(&child_refs_mu_); + child_subchannels_ = std::move(cs); +} + +void RoundRobin::RoundRobinSubchannelList::StartWatchingLocked() { + if (num_subchannels() == 0) return; + // Check current state of each subchannel synchronously, since any + // subchannel already used by some other channel may have a non-IDLE + // state. + for (size_t i = 0; i < num_subchannels(); ++i) { + grpc_error* error = GRPC_ERROR_NONE; + grpc_connectivity_state state = + subchannel(i)->CheckConnectivityStateLocked(&error); + if (state != GRPC_CHANNEL_IDLE) { + subchannel(i)->UpdateConnectivityStateLocked(state, error); + } + } + // Now set the LB policy's state based on the subchannels' states. + UpdateRoundRobinStateFromSubchannelStateCountsLocked(); + // Start connectivity watch for each subchannel. + for (size_t i = 0; i < num_subchannels(); i++) { + if (subchannel(i)->subchannel() != nullptr) { + subchannel(i)->StartConnectivityWatchLocked(); + } + } +} + +void RoundRobin::RoundRobinSubchannelList::UpdateStateCountersLocked( + grpc_connectivity_state old_state, grpc_connectivity_state new_state, + grpc_error* transient_failure_error) { + GPR_ASSERT(old_state != GRPC_CHANNEL_SHUTDOWN); + GPR_ASSERT(new_state != GRPC_CHANNEL_SHUTDOWN); + if (old_state == GRPC_CHANNEL_READY) { + GPR_ASSERT(num_ready_ > 0); + --num_ready_; + } else if (old_state == GRPC_CHANNEL_CONNECTING) { + GPR_ASSERT(num_connecting_ > 0); + --num_connecting_; + } else if (old_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + GPR_ASSERT(num_transient_failure_ > 0); + --num_transient_failure_; + } + if (new_state == GRPC_CHANNEL_READY) { + ++num_ready_; + } else if (new_state == GRPC_CHANNEL_CONNECTING) { + ++num_connecting_; + } else if (new_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + ++num_transient_failure_; + } + GRPC_ERROR_UNREF(last_transient_failure_error_); + last_transient_failure_error_ = transient_failure_error; +} + +// Sets the RR policy's connectivity state based on the current +// subchannel list. +void RoundRobin::RoundRobinSubchannelList:: + MaybeUpdateRoundRobinConnectivityStateLocked() { + RoundRobin* p = static_cast(policy()); + // Only set connectivity state if this is the current subchannel list. + if (p->subchannel_list_.get() != this) return; + /* In priority order. The first rule to match terminates the search (ie, if we + * are on rule n, all previous rules were unfulfilled). + * + * 1) RULE: ANY subchannel is READY => policy is READY. + * CHECK: subchannel_list->num_ready > 0. + * + * 2) RULE: ANY subchannel is CONNECTING => policy is CONNECTING. + * CHECK: sd->curr_connectivity_state == CONNECTING. + * + * 3) RULE: ALL subchannels are TRANSIENT_FAILURE => policy is + * TRANSIENT_FAILURE. + * CHECK: subchannel_list->num_transient_failures == + * subchannel_list->num_subchannels. + */ + if (num_ready_ > 0) { + /* 1) READY */ + grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_READY, + GRPC_ERROR_NONE, "rr_ready"); + } else if (num_connecting_ > 0) { + /* 2) CONNECTING */ + grpc_connectivity_state_set(&p->state_tracker_, GRPC_CHANNEL_CONNECTING, + GRPC_ERROR_NONE, "rr_connecting"); + } else if (num_transient_failure_ == num_subchannels()) { + /* 3) TRANSIENT_FAILURE */ + grpc_connectivity_state_set(&p->state_tracker_, + GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_REF(last_transient_failure_error_), + "rr_exhausted_subchannels"); + } +} + +void RoundRobin::RoundRobinSubchannelList:: + UpdateRoundRobinStateFromSubchannelStateCountsLocked() { + RoundRobin* p = static_cast(policy()); + AutoChildRefsUpdater guard(p); + if (num_ready_ > 0) { + if (p->subchannel_list_.get() != this) { + // Promote this list to p->subchannel_list_. + // This list must be p->latest_pending_subchannel_list_, because + // any previous update would have been shut down already and + // therefore we would not be receiving a notification for them. + GPR_ASSERT(p->latest_pending_subchannel_list_.get() == this); + GPR_ASSERT(!shutting_down()); + if (grpc_lb_round_robin_trace.enabled()) { + const size_t old_num_subchannels = + p->subchannel_list_ != nullptr + ? p->subchannel_list_->num_subchannels() + : 0; + gpr_log(GPR_INFO, + "[RR %p] phasing out subchannel list %p (size %" PRIuPTR + ") in favor of %p (size %" PRIuPTR ")", + p, p->subchannel_list_.get(), old_num_subchannels, this, + num_subchannels()); + } + p->subchannel_list_ = std::move(p->latest_pending_subchannel_list_); + } + // Drain pending picks. + p->DrainPendingPicksLocked(); + } + // Update the RR policy's connectivity state if needed. + MaybeUpdateRoundRobinConnectivityStateLocked(); +} + +void RoundRobin::RoundRobinSubchannelData::UpdateConnectivityStateLocked( + grpc_connectivity_state connectivity_state, grpc_error* error) { + RoundRobin* p = static_cast(subchannel_list()->policy()); + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log( + GPR_INFO, + "[RR %p] connectivity changed for subchannel %p, subchannel_list %p " + "(index %" PRIuPTR " of %" PRIuPTR "): prev_state=%s new_state=%s", + p, subchannel(), subchannel_list(), Index(), + subchannel_list()->num_subchannels(), + grpc_connectivity_state_name(last_connectivity_state_), + grpc_connectivity_state_name(connectivity_state)); + } + subchannel_list()->UpdateStateCountersLocked(last_connectivity_state_, + connectivity_state, error); + last_connectivity_state_ = connectivity_state; +} + +void RoundRobin::RoundRobinSubchannelData::ProcessConnectivityChangeLocked( + grpc_connectivity_state connectivity_state, grpc_error* error) { + RoundRobin* p = static_cast(subchannel_list()->policy()); + GPR_ASSERT(subchannel() != nullptr); + // If the new state is TRANSIENT_FAILURE, re-resolve. + // Only do this if we've started watching, not at startup time. + // Otherwise, if the subchannel was already in state TRANSIENT_FAILURE + // when the subchannel list was created, we'd wind up in a constant + // loop of re-resolution. + if (connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, + "[RR %p] Subchannel %p has gone into TRANSIENT_FAILURE. " + "Requesting re-resolution", + p, subchannel()); + } + p->TryReresolutionLocked(&grpc_lb_round_robin_trace, GRPC_ERROR_NONE); + } + // Update state counters. + UpdateConnectivityStateLocked(connectivity_state, error); + // Update overall state and renew notification. + subchannel_list()->UpdateRoundRobinStateFromSubchannelStateCountsLocked(); + RenewConnectivityWatchLocked(); +} + +/** Returns the index into p->subchannel_list->subchannels of the next + * subchannel in READY state, or p->subchannel_list->num_subchannels if no + * subchannel is READY. + * + * Note that this function does *not* update p->last_ready_subchannel_index. + * The caller must do that if it returns a pick. */ +size_t +RoundRobin::RoundRobinSubchannelList::GetNextReadySubchannelIndexLocked() { + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, + "[RR %p] getting next ready subchannel (out of %" PRIuPTR + "), last_ready_index=%" PRIuPTR, + policy(), num_subchannels(), last_ready_index_); + } + for (size_t i = 0; i < num_subchannels(); ++i) { + const size_t index = (i + last_ready_index_ + 1) % num_subchannels(); + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log( + GPR_INFO, + "[RR %p] checking subchannel %p, subchannel_list %p, index %" PRIuPTR + ": state=%s", + policy(), subchannel(index)->subchannel(), this, index, + grpc_connectivity_state_name( + subchannel(index)->connectivity_state())); + } + if (subchannel(index)->connectivity_state() == GRPC_CHANNEL_READY) { + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, + "[RR %p] found next ready subchannel (%p) at index %" PRIuPTR + " of subchannel_list %p", + policy(), subchannel(index)->subchannel(), index, this); + } + return index; + } + } + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, "[RR %p] no subchannels in ready state", this); + } + return num_subchannels(); +} + +// Sets last_ready_index_ to last_ready_index. +void RoundRobin::RoundRobinSubchannelList::UpdateLastReadySubchannelIndexLocked( + size_t last_ready_index) { + GPR_ASSERT(last_ready_index < num_subchannels()); + last_ready_index_ = last_ready_index; + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, + "[RR %p] setting last_ready_subchannel_index=%" PRIuPTR + " (SC %p, CSC %p)", + policy(), last_ready_index, + subchannel(last_ready_index)->subchannel(), + subchannel(last_ready_index)->connected_subchannel()); + } +} + +grpc_connectivity_state RoundRobin::CheckConnectivityLocked( + grpc_error** error) { + return grpc_connectivity_state_get(&state_tracker_, error); +} + +void RoundRobin::NotifyOnStateChangeLocked(grpc_connectivity_state* current, + grpc_closure* notify) { + grpc_connectivity_state_notify_on_state_change(&state_tracker_, current, + notify); +} + +void RoundRobin::UpdateLocked(const grpc_channel_args& args, + grpc_json* lb_config) { + AutoChildRefsUpdater guard(this); + const ServerAddressList* addresses = FindServerAddressListChannelArg(&args); + if (addresses == nullptr) { + gpr_log(GPR_ERROR, "[RR %p] update provided no addresses; ignoring", this); + // If we don't have a current subchannel list, go into TRANSIENT_FAILURE. + // Otherwise, keep using the current subchannel list (ignore this update). + if (subchannel_list_ == nullptr) { + grpc_connectivity_state_set( + &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), + "rr_update_missing"); + } + return; + } + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, "[RR %p] received update with %" PRIuPTR " addresses", + this, addresses->size()); + } + // Replace latest_pending_subchannel_list_. + if (latest_pending_subchannel_list_ != nullptr) { + if (grpc_lb_round_robin_trace.enabled()) { + gpr_log(GPR_INFO, + "[RR %p] Shutting down previous pending subchannel list %p", this, + latest_pending_subchannel_list_.get()); + } + } + latest_pending_subchannel_list_ = MakeOrphanable( + this, &grpc_lb_round_robin_trace, *addresses, combiner(), + client_channel_factory(), args); + // If we haven't started picking yet or the new list is empty, + // immediately promote the new list to the current list. + if (!started_picking_ || + latest_pending_subchannel_list_->num_subchannels() == 0) { + if (latest_pending_subchannel_list_->num_subchannels() == 0) { + grpc_connectivity_state_set( + &state_tracker_, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), + "rr_update_empty"); + } + subchannel_list_ = std::move(latest_pending_subchannel_list_); + } else { + // If we've started picking, start watching the new list. + latest_pending_subchannel_list_->StartWatchingLocked(); + } +} + +// +// factory +// + +class RoundRobinFactory : public LoadBalancingPolicyFactory { + public: + OrphanablePtr CreateLoadBalancingPolicy( + const LoadBalancingPolicy::Args& args) const override { + return OrphanablePtr(New(args)); + } + + const char* name() const override { return kRoundRobin; } +}; + +} // namespace + +} // namespace grpc_core + +void grpc_lb_policy_round_robin_init() { + grpc_core::LoadBalancingPolicyRegistry::Builder:: + RegisterLoadBalancingPolicyFactory( + grpc_core::UniquePtr( + grpc_core::New())); +} + +void grpc_lb_policy_round_robin_shutdown() {} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h new file mode 100644 index 00000000000000..6f31a643c1d583 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/subchannel_list.h @@ -0,0 +1,593 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H + +#include + +#include + +#include + +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/transport/connectivity_state.h" + +// Code for maintaining a list of subchannels within an LB policy. +// +// To use this, callers must create their own subclasses, like so: +/* + +class MySubchannelList; // Forward declaration. + +class MySubchannelData + : public SubchannelData { + public: + void ProcessConnectivityChangeLocked( + grpc_connectivity_state connectivity_state, grpc_error* error) override { + // ...code to handle connectivity changes... + } +}; + +class MySubchannelList + : public SubchannelList { +}; + +*/ +// All methods with a Locked() suffix must be called from within the +// client_channel combiner. + +namespace grpc_core { + +// Forward declaration. +template +class SubchannelList; + +// Stores data for a particular subchannel in a subchannel list. +// Callers must create a subclass that implements the +// ProcessConnectivityChangeLocked() method. +template +class SubchannelData { + public: + // Returns a pointer to the subchannel list containing this object. + SubchannelListType* subchannel_list() const { + return static_cast(subchannel_list_); + } + + // Returns the index into the subchannel list of this object. + size_t Index() const { + return static_cast(static_cast(this) - + subchannel_list_->subchannel(0)); + } + + // Returns a pointer to the subchannel. + grpc_subchannel* subchannel() const { return subchannel_; } + + // Returns the connected subchannel. Will be null if the subchannel + // is not connected. + ConnectedSubchannel* connected_subchannel() const { + return connected_subchannel_.get(); + } + + // Synchronously checks the subchannel's connectivity state. + // Must not be called while there is a connectivity notification + // pending (i.e., between calling StartConnectivityWatchLocked() or + // RenewConnectivityWatchLocked() and the resulting invocation of + // ProcessConnectivityChangeLocked()). + grpc_connectivity_state CheckConnectivityStateLocked(grpc_error** error) { + GPR_ASSERT(!connectivity_notification_pending_); + pending_connectivity_state_unsafe_ = grpc_subchannel_check_connectivity( + subchannel(), error, subchannel_list_->inhibit_health_checking()); + UpdateConnectedSubchannelLocked(); + return pending_connectivity_state_unsafe_; + } + + // Resets the connection backoff. + // TODO(roth): This method should go away when we move the backoff + // code out of the subchannel and into the LB policies. + void ResetBackoffLocked(); + + // Starts watching the connectivity state of the subchannel. + // ProcessConnectivityChangeLocked() will be called when the + // connectivity state changes. + void StartConnectivityWatchLocked(); + + // Renews watching the connectivity state of the subchannel. + void RenewConnectivityWatchLocked(); + + // Stops watching the connectivity state of the subchannel. + void StopConnectivityWatchLocked(); + + // Cancels watching the connectivity state of the subchannel. + // Must be called only while there is a connectivity notification + // pending (i.e., between calling StartConnectivityWatchLocked() or + // RenewConnectivityWatchLocked() and the resulting invocation of + // ProcessConnectivityChangeLocked()). + // From within ProcessConnectivityChangeLocked(), use + // StopConnectivityWatchLocked() instead. + void CancelConnectivityWatchLocked(const char* reason); + + // Cancels any pending connectivity watch and unrefs the subchannel. + void ShutdownLocked(); + + GRPC_ABSTRACT_BASE_CLASS + + protected: + SubchannelData( + SubchannelList* subchannel_list, + const ServerAddress& address, grpc_subchannel* subchannel, + grpc_combiner* combiner); + + virtual ~SubchannelData(); + + // After StartConnectivityWatchLocked() or RenewConnectivityWatchLocked() + // is called, this method will be invoked when the subchannel's connectivity + // state changes. + // Implementations must invoke either RenewConnectivityWatchLocked() or + // StopConnectivityWatchLocked() before returning. + virtual void ProcessConnectivityChangeLocked( + grpc_connectivity_state connectivity_state, + grpc_error* error) GRPC_ABSTRACT; + + // Unrefs the subchannel. + void UnrefSubchannelLocked(const char* reason); + + private: + // Updates connected_subchannel_ based on pending_connectivity_state_unsafe_. + // Returns true if the connectivity state should be reported. + bool UpdateConnectedSubchannelLocked(); + + static void OnConnectivityChangedLocked(void* arg, grpc_error* error); + + // Backpointer to owning subchannel list. Not owned. + SubchannelList* subchannel_list_; + + // The subchannel and connected subchannel. + grpc_subchannel* subchannel_; + RefCountedPtr connected_subchannel_; + + // Notification that connectivity has changed on subchannel. + grpc_closure connectivity_changed_closure_; + // Is a connectivity notification pending? + bool connectivity_notification_pending_ = false; + // Connectivity state to be updated by + // grpc_subchannel_notify_on_state_change(), not guarded by + // the combiner. + grpc_connectivity_state pending_connectivity_state_unsafe_; +}; + +// A list of subchannels. +template +class SubchannelList : public InternallyRefCounted { + public: + typedef InlinedVector SubchannelVector; + + // The number of subchannels in the list. + size_t num_subchannels() const { return subchannels_.size(); } + + // The data for the subchannel at a particular index. + SubchannelDataType* subchannel(size_t index) { return &subchannels_[index]; } + + // Returns true if the subchannel list is shutting down. + bool shutting_down() const { return shutting_down_; } + + // Populates refs_list with the uuids of this SubchannelLists's subchannels. + void PopulateChildRefsList(channelz::ChildRefsList* refs_list) { + for (size_t i = 0; i < subchannels_.size(); ++i) { + if (subchannels_[i].subchannel() != nullptr) { + grpc_core::channelz::SubchannelNode* subchannel_node = + grpc_subchannel_get_channelz_node(subchannels_[i].subchannel()); + if (subchannel_node != nullptr) { + refs_list->push_back(subchannel_node->uuid()); + } + } + } + } + + // Accessors. + LoadBalancingPolicy* policy() const { return policy_; } + TraceFlag* tracer() const { return tracer_; } + bool inhibit_health_checking() const { return inhibit_health_checking_; } + + // Resets connection backoff of all subchannels. + // TODO(roth): We will probably need to rethink this as part of moving + // the backoff code out of subchannels and into LB policies. + void ResetBackoffLocked(); + + // Note: Caller must ensure that this is invoked inside of the combiner. + void Orphan() override { + ShutdownLocked(); + InternallyRefCounted::Unref(DEBUG_LOCATION, "shutdown"); + } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + SubchannelList(LoadBalancingPolicy* policy, TraceFlag* tracer, + const ServerAddressList& addresses, grpc_combiner* combiner, + grpc_client_channel_factory* client_channel_factory, + const grpc_channel_args& args); + + virtual ~SubchannelList(); + + private: + // So New() can call our private ctor. + template + friend T* New(Args&&... args); + + // For accessing Ref() and Unref(). + friend class SubchannelData; + + void ShutdownLocked(); + + // Backpointer to owning policy. + LoadBalancingPolicy* policy_; + + TraceFlag* tracer_; + + bool inhibit_health_checking_; + + grpc_combiner* combiner_; + + // The list of subchannels. + SubchannelVector subchannels_; + + // Is this list shutting down? This may be true due to the shutdown of the + // policy itself or because a newer update has arrived while this one hadn't + // finished processing. + bool shutting_down_ = false; +}; + +// +// implementation -- no user-servicable parts below +// + +// +// SubchannelData +// + +template +SubchannelData::SubchannelData( + SubchannelList* subchannel_list, + const ServerAddress& address, grpc_subchannel* subchannel, + grpc_combiner* combiner) + : subchannel_list_(subchannel_list), + subchannel_(subchannel), + // We assume that the current state is IDLE. If not, we'll get a + // callback telling us that. + pending_connectivity_state_unsafe_(GRPC_CHANNEL_IDLE) { + GRPC_CLOSURE_INIT( + &connectivity_changed_closure_, + (&SubchannelData::OnConnectivityChangedLocked), + this, grpc_combiner_scheduler(combiner)); +} + +template +SubchannelData::~SubchannelData() { + UnrefSubchannelLocked("subchannel_data_destroy"); +} + +template +void SubchannelData:: + UnrefSubchannelLocked(const char* reason) { + if (subchannel_ != nullptr) { + if (subchannel_list_->tracer()->enabled()) { + gpr_log(GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR + " (subchannel %p): unreffing subchannel", + subchannel_list_->tracer()->name(), subchannel_list_->policy(), + subchannel_list_, Index(), subchannel_list_->num_subchannels(), + subchannel_); + } + GRPC_SUBCHANNEL_UNREF(subchannel_, reason); + subchannel_ = nullptr; + connected_subchannel_.reset(); + } +} + +template +void SubchannelData::ResetBackoffLocked() { + if (subchannel_ != nullptr) { + grpc_subchannel_reset_backoff(subchannel_); + } +} + +template +void SubchannelData::StartConnectivityWatchLocked() { + if (subchannel_list_->tracer()->enabled()) { + gpr_log(GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR + " (subchannel %p): starting watch: requesting connectivity change " + "notification (from %s)", + subchannel_list_->tracer()->name(), subchannel_list_->policy(), + subchannel_list_, Index(), subchannel_list_->num_subchannels(), + subchannel_, + grpc_connectivity_state_name(pending_connectivity_state_unsafe_)); + } + GPR_ASSERT(!connectivity_notification_pending_); + connectivity_notification_pending_ = true; + subchannel_list()->Ref(DEBUG_LOCATION, "connectivity_watch").release(); + grpc_subchannel_notify_on_state_change( + subchannel_, subchannel_list_->policy()->interested_parties(), + &pending_connectivity_state_unsafe_, &connectivity_changed_closure_, + subchannel_list_->inhibit_health_checking()); +} + +template +void SubchannelData::RenewConnectivityWatchLocked() { + if (subchannel_list_->tracer()->enabled()) { + gpr_log(GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR + " (subchannel %p): renewing watch: requesting connectivity change " + "notification (from %s)", + subchannel_list_->tracer()->name(), subchannel_list_->policy(), + subchannel_list_, Index(), subchannel_list_->num_subchannels(), + subchannel_, + grpc_connectivity_state_name(pending_connectivity_state_unsafe_)); + } + GPR_ASSERT(connectivity_notification_pending_); + grpc_subchannel_notify_on_state_change( + subchannel_, subchannel_list_->policy()->interested_parties(), + &pending_connectivity_state_unsafe_, &connectivity_changed_closure_, + subchannel_list_->inhibit_health_checking()); +} + +template +void SubchannelData::StopConnectivityWatchLocked() { + if (subchannel_list_->tracer()->enabled()) { + gpr_log(GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR + " (subchannel %p): stopping connectivity watch", + subchannel_list_->tracer()->name(), subchannel_list_->policy(), + subchannel_list_, Index(), subchannel_list_->num_subchannels(), + subchannel_); + } + GPR_ASSERT(connectivity_notification_pending_); + connectivity_notification_pending_ = false; + subchannel_list()->Unref(DEBUG_LOCATION, "connectivity_watch"); +} + +template +void SubchannelData:: + CancelConnectivityWatchLocked(const char* reason) { + if (subchannel_list_->tracer()->enabled()) { + gpr_log(GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR + " (subchannel %p): canceling connectivity watch (%s)", + subchannel_list_->tracer()->name(), subchannel_list_->policy(), + subchannel_list_, Index(), subchannel_list_->num_subchannels(), + subchannel_, reason); + } + GPR_ASSERT(connectivity_notification_pending_); + grpc_subchannel_notify_on_state_change( + subchannel_, nullptr, nullptr, &connectivity_changed_closure_, + subchannel_list_->inhibit_health_checking()); +} + +template +bool SubchannelData::UpdateConnectedSubchannelLocked() { + // If the subchannel is READY, take a ref to the connected subchannel. + if (pending_connectivity_state_unsafe_ == GRPC_CHANNEL_READY) { + connected_subchannel_ = + grpc_subchannel_get_connected_subchannel(subchannel_); + // If the subchannel became disconnected between the time that READY + // was reported and the time we got here (e.g., between when a + // notification callback is scheduled and when it was actually run in + // the combiner), then the connected subchannel may have disappeared out + // from under us. In that case, we don't actually want to consider the + // subchannel to be in state READY. Instead, we use IDLE as the + // basis for any future connectivity watch; this is the one state that + // the subchannel will never transition back into, so this ensures + // that we will get a notification for the next state, even if that state + // is READY again (e.g., if the subchannel has transitioned back to + // READY before the next watch gets requested). + if (connected_subchannel_ == nullptr) { + if (subchannel_list_->tracer()->enabled()) { + gpr_log(GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR + " (subchannel %p): state is READY but connected subchannel is " + "null; moving to state IDLE", + subchannel_list_->tracer()->name(), subchannel_list_->policy(), + subchannel_list_, Index(), subchannel_list_->num_subchannels(), + subchannel_); + } + pending_connectivity_state_unsafe_ = GRPC_CHANNEL_IDLE; + return false; + } + } else { + // For any state other than READY, unref the connected subchannel. + connected_subchannel_.reset(); + } + return true; +} + +template +void SubchannelData:: + OnConnectivityChangedLocked(void* arg, grpc_error* error) { + SubchannelData* sd = static_cast(arg); + if (sd->subchannel_list_->tracer()->enabled()) { + gpr_log( + GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR " of %" PRIuPTR + " (subchannel %p): connectivity changed: state=%s, error=%s, " + "shutting_down=%d", + sd->subchannel_list_->tracer()->name(), sd->subchannel_list_->policy(), + sd->subchannel_list_, sd->Index(), + sd->subchannel_list_->num_subchannels(), sd->subchannel_, + grpc_connectivity_state_name(sd->pending_connectivity_state_unsafe_), + grpc_error_string(error), sd->subchannel_list_->shutting_down()); + } + // If shutting down, unref subchannel and stop watching. + if (sd->subchannel_list_->shutting_down() || error == GRPC_ERROR_CANCELLED) { + sd->UnrefSubchannelLocked("connectivity_shutdown"); + sd->StopConnectivityWatchLocked(); + return; + } + // Get or release ref to connected subchannel. + if (!sd->UpdateConnectedSubchannelLocked()) { + // We don't want to report this connectivity state, so renew the watch. + sd->RenewConnectivityWatchLocked(); + return; + } + // Call the subclass's ProcessConnectivityChangeLocked() method. + sd->ProcessConnectivityChangeLocked(sd->pending_connectivity_state_unsafe_, + GRPC_ERROR_REF(error)); +} + +template +void SubchannelData::ShutdownLocked() { + // If there's a pending notification for this subchannel, cancel it; + // the callback is responsible for unreffing the subchannel. + // Otherwise, unref the subchannel directly. + if (connectivity_notification_pending_) { + CancelConnectivityWatchLocked("shutdown"); + } else if (subchannel_ != nullptr) { + UnrefSubchannelLocked("shutdown"); + } +} + +// +// SubchannelList +// + +template +SubchannelList::SubchannelList( + LoadBalancingPolicy* policy, TraceFlag* tracer, + const ServerAddressList& addresses, grpc_combiner* combiner, + grpc_client_channel_factory* client_channel_factory, + const grpc_channel_args& args) + : InternallyRefCounted(tracer), + policy_(policy), + tracer_(tracer), + combiner_(GRPC_COMBINER_REF(combiner, "subchannel_list")) { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, + "[%s %p] Creating subchannel list %p for %" PRIuPTR " subchannels", + tracer_->name(), policy, this, addresses.size()); + } + subchannels_.reserve(addresses.size()); + // We need to remove the LB addresses in order to be able to compare the + // subchannel keys of subchannels from a different batch of addresses. + // We also remove the inhibit-health-checking arg, since we are + // handling that here. + inhibit_health_checking_ = grpc_channel_arg_get_bool( + grpc_channel_args_find(&args, GRPC_ARG_INHIBIT_HEALTH_CHECKING), false); + static const char* keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS, + GRPC_ARG_SERVER_ADDRESS_LIST, + GRPC_ARG_INHIBIT_HEALTH_CHECKING}; + // Create a subchannel for each address. + grpc_subchannel_args sc_args; + for (size_t i = 0; i < addresses.size(); i++) { + // If there were any balancer addresses, we would have chosen grpclb + // policy, which does not use a SubchannelList. + GPR_ASSERT(!addresses[i].IsBalancer()); + memset(&sc_args, 0, sizeof(grpc_subchannel_args)); + InlinedVector args_to_add; + args_to_add.emplace_back( + grpc_create_subchannel_address_arg(&addresses[i].address())); + if (addresses[i].args() != nullptr) { + for (size_t j = 0; j < addresses[i].args()->num_args; ++j) { + args_to_add.emplace_back(addresses[i].args()->args[j]); + } + } + grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove( + &args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), + args_to_add.data(), args_to_add.size()); + gpr_free(args_to_add[0].value.string); + sc_args.args = new_args; + grpc_subchannel* subchannel = grpc_client_channel_factory_create_subchannel( + client_channel_factory, &sc_args); + grpc_channel_args_destroy(new_args); + if (subchannel == nullptr) { + // Subchannel could not be created. + if (tracer_->enabled()) { + char* address_uri = grpc_sockaddr_to_uri(&addresses[i].address()); + gpr_log(GPR_INFO, + "[%s %p] could not create subchannel for address uri %s, " + "ignoring", + tracer_->name(), policy_, address_uri); + gpr_free(address_uri); + } + continue; + } + if (tracer_->enabled()) { + char* address_uri = grpc_sockaddr_to_uri(&addresses[i].address()); + gpr_log(GPR_INFO, + "[%s %p] subchannel list %p index %" PRIuPTR + ": Created subchannel %p for address uri %s", + tracer_->name(), policy_, this, subchannels_.size(), subchannel, + address_uri); + gpr_free(address_uri); + } + subchannels_.emplace_back(this, addresses[i], subchannel, combiner); + } +} + +template +SubchannelList::~SubchannelList() { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "[%s %p] Destroying subchannel_list %p", tracer_->name(), + policy_, this); + } + GRPC_COMBINER_UNREF(combiner_, "subchannel_list"); +} + +template +void SubchannelList::ShutdownLocked() { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "[%s %p] Shutting down subchannel_list %p", + tracer_->name(), policy_, this); + } + GPR_ASSERT(!shutting_down_); + shutting_down_ = true; + for (size_t i = 0; i < subchannels_.size(); i++) { + SubchannelDataType* sd = &subchannels_[i]; + sd->ShutdownLocked(); + } +} + +template +void SubchannelList::ResetBackoffLocked() { + for (size_t i = 0; i < subchannels_.size(); i++) { + SubchannelDataType* sd = &subchannels_[i]; + sd->ResetBackoffLocked(); + } +} + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_SUBCHANNEL_LIST_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc new file mode 100644 index 00000000000000..8787f5bcc24982 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.cc @@ -0,0 +1,1676 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// Implementation of the gRPC LB policy. +/// +/// This policy takes as input a list of resolved addresses, which must +/// include at least one balancer address. +/// +/// An internal channel (\a lb_channel_) is created for the addresses +/// from that are balancers. This channel behaves just like a regular +/// channel that uses pick_first to select from the list of balancer +/// addresses. +/// +/// The first time the xDS policy gets a request for a pick or to exit the idle +/// state, \a StartPickingLocked() is called. This method is responsible for +/// instantiating the internal *streaming* call to the LB server (whichever +/// address pick_first chose). The call will be complete when either the +/// balancer sends status or when we cancel the call (e.g., because we are +/// shutting down). In needed, we retry the call. If we received at least one +/// valid message from the server, a new call attempt will be made immediately; +/// otherwise, we apply back-off delays between attempts. +/// +/// We maintain an internal child policy (round_robin) instance for distributing +/// requests across backends. Whenever we receive a new serverlist from +/// the balancer, we update the child policy with the new list of +/// addresses. +/// +/// Once a child policy instance is in place (and getting updated as +/// described), calls for a pick, or a cancellation will be serviced right away +/// by forwarding them to the child policy instance. Any time there's no child +/// policy available (i.e., right after the creation of the xDS policy), pick +/// requests are added to a list of pending picks to be flushed and serviced +/// when the child policy instance becomes available. +/// +/// \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the +/// high level design and details. + +// With the addition of a libuv endpoint, sockaddr.h now includes uv.h when +// using that endpoint. Because of various transitive includes in uv.h, +// including windows.h on Windows, uv.h must be included before other system +// headers. Therefore, sockaddr.h must always be included first. +#include + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds.h" +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h" +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h" +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h" +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/static_metadata.h" + +#define GRPC_XDS_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_XDS_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_XDS_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_XDS_RECONNECT_JITTER 0.2 +#define GRPC_XDS_DEFAULT_FALLBACK_TIMEOUT_MS 10000 + +namespace grpc_core { + +TraceFlag grpc_lb_xds_trace(false, "xds"); + +namespace { + +constexpr char kXds[] = "xds_experimental"; + +class XdsLb : public LoadBalancingPolicy { + public: + explicit XdsLb(const Args& args); + + const char* name() const override { return kXds; } + + void UpdateLocked(const grpc_channel_args& args, + grpc_json* lb_config) override; + bool PickLocked(PickState* pick, grpc_error** error) override; + void CancelPickLocked(PickState* pick, grpc_error* error) override; + void CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) override; + void NotifyOnStateChangeLocked(grpc_connectivity_state* state, + grpc_closure* closure) override; + grpc_connectivity_state CheckConnectivityLocked( + grpc_error** connectivity_error) override; + void HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) override; + void ExitIdleLocked() override; + void ResetBackoffLocked() override; + void FillChildRefsForChannelz( + channelz::ChildRefsList* child_subchannels, + channelz::ChildRefsList* child_channels) override; + + private: + /// Linked list of pending pick requests. It stores all information needed to + /// eventually call pick() on them. They mainly stay pending waiting for the + /// child policy to be created. + /// + /// Note that when a pick is sent to the child policy, we inject our own + /// on_complete callback, so that we can intercept the result before + /// invoking the original on_complete callback. This allows us to set the + /// LB token metadata and add client_stats to the call context. + /// See \a pending_pick_complete() for details. + struct PendingPick { + // The xds lb instance that created the wrapping. This instance is not + // owned; reference counts are untouched. It's used only for logging + // purposes. + XdsLb* xdslb_policy; + // The original pick. + PickState* pick; + // Our on_complete closure and the original one. + grpc_closure on_complete; + grpc_closure* original_on_complete; + // Stats for client-side load reporting. + RefCountedPtr client_stats; + // Next pending pick. + PendingPick* next = nullptr; + }; + + /// Contains a call to the LB server and all the data related to the call. + class BalancerCallState : public InternallyRefCounted { + public: + explicit BalancerCallState( + RefCountedPtr parent_xdslb_policy); + + // It's the caller's responsibility to ensure that Orphan() is called from + // inside the combiner. + void Orphan() override; + + void StartQuery(); + + XdsLbClientStats* client_stats() const { return client_stats_.get(); } + + bool seen_initial_response() const { return seen_initial_response_; } + + private: + // So Delete() can access our private dtor. + template + friend void grpc_core::Delete(T*); + + ~BalancerCallState(); + + XdsLb* xdslb_policy() const { + return static_cast(xdslb_policy_.get()); + } + + void ScheduleNextClientLoadReportLocked(); + void SendClientLoadReportLocked(); + + static bool LoadReportCountersAreZero(xds_grpclb_request* request); + + static void MaybeSendClientLoadReportLocked(void* arg, grpc_error* error); + static void OnInitialRequestSentLocked(void* arg, grpc_error* error); + static void OnBalancerMessageReceivedLocked(void* arg, grpc_error* error); + static void OnBalancerStatusReceivedLocked(void* arg, grpc_error* error); + + // The owning LB policy. + RefCountedPtr xdslb_policy_; + + // The streaming call to the LB server. Always non-NULL. + grpc_call* lb_call_ = nullptr; + + // recv_initial_metadata + grpc_metadata_array lb_initial_metadata_recv_; + + // send_message + grpc_byte_buffer* send_message_payload_ = nullptr; + grpc_closure lb_on_initial_request_sent_; + + // recv_message + grpc_byte_buffer* recv_message_payload_ = nullptr; + grpc_closure lb_on_balancer_message_received_; + bool seen_initial_response_ = false; + + // recv_trailing_metadata + grpc_closure lb_on_balancer_status_received_; + grpc_metadata_array lb_trailing_metadata_recv_; + grpc_status_code lb_call_status_; + grpc_slice lb_call_status_details_; + + // The stats for client-side load reporting associated with this LB call. + // Created after the first serverlist is received. + RefCountedPtr client_stats_; + grpc_millis client_stats_report_interval_ = 0; + grpc_timer client_load_report_timer_; + bool client_load_report_timer_callback_pending_ = false; + bool last_client_load_report_counters_were_zero_ = false; + bool client_load_report_is_due_ = false; + // The closure used for either the load report timer or the callback for + // completion of sending the load report. + grpc_closure client_load_report_closure_; + }; + + ~XdsLb(); + + void ShutdownLocked() override; + + // Helper function used in ctor and UpdateLocked(). + void ProcessChannelArgsLocked(const grpc_channel_args& args); + + // Methods for dealing with the balancer channel and call. + void StartPickingLocked(); + void StartBalancerCallLocked(); + static void OnFallbackTimerLocked(void* arg, grpc_error* error); + void StartBalancerCallRetryTimerLocked(); + static void OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error); + static void OnBalancerChannelConnectivityChangedLocked(void* arg, + grpc_error* error); + + // Pending pick methods. + static void PendingPickCleanup(PendingPick* pp); + PendingPick* PendingPickCreate(PickState* pick); + void AddPendingPick(PendingPick* pp); + static void OnPendingPickComplete(void* arg, grpc_error* error); + + // Methods for dealing with the child policy. + void CreateOrUpdateChildPolicyLocked(); + grpc_channel_args* CreateChildPolicyArgsLocked(); + void CreateChildPolicyLocked(const Args& args); + bool PickFromChildPolicyLocked(bool force_async, PendingPick* pp, + grpc_error** error); + void UpdateConnectivityStateFromChildPolicyLocked( + grpc_error* child_state_error); + static void OnChildPolicyConnectivityChangedLocked(void* arg, + grpc_error* error); + static void OnChildPolicyRequestReresolutionLocked(void* arg, + grpc_error* error); + + // Who the client is trying to communicate with. + const char* server_name_ = nullptr; + + // Current channel args from the resolver. + grpc_channel_args* args_ = nullptr; + + // Internal state. + bool started_picking_ = false; + bool shutting_down_ = false; + grpc_connectivity_state_tracker state_tracker_; + + // The channel for communicating with the LB server. + grpc_channel* lb_channel_ = nullptr; + // Mutex to protect the channel to the LB server. This is used when + // processing a channelz request. + gpr_mu lb_channel_mu_; + grpc_connectivity_state lb_channel_connectivity_; + grpc_closure lb_channel_on_connectivity_changed_; + // Are we already watching the LB channel's connectivity? + bool watching_lb_channel_ = false; + // Response generator to inject address updates into lb_channel_. + RefCountedPtr response_generator_; + + // The data associated with the current LB call. It holds a ref to this LB + // policy. It's initialized every time we query for backends. It's reset to + // NULL whenever the current LB call is no longer needed (e.g., the LB policy + // is shutting down, or the LB call has ended). A non-NULL lb_calld_ always + // contains a non-NULL lb_call_. + OrphanablePtr lb_calld_; + // Timeout in milliseconds for the LB call. 0 means no deadline. + int lb_call_timeout_ms_ = 0; + // Balancer call retry state. + BackOff lb_call_backoff_; + bool retry_timer_callback_pending_ = false; + grpc_timer lb_call_retry_timer_; + grpc_closure lb_on_call_retry_; + + // The deserialized response from the balancer. May be nullptr until one + // such response has arrived. + xds_grpclb_serverlist* serverlist_ = nullptr; + + // Timeout in milliseconds for before using fallback backend addresses. + // 0 means not using fallback. + int lb_fallback_timeout_ms_ = 0; + // The backend addresses from the resolver. + UniquePtr fallback_backend_addresses_; + // Fallback timer. + bool fallback_timer_callback_pending_ = false; + grpc_timer lb_fallback_timer_; + grpc_closure lb_on_fallback_; + + // Pending picks that are waiting on the xDS policy's connectivity. + PendingPick* pending_picks_ = nullptr; + + // The policy to use for the backends. + OrphanablePtr child_policy_; + grpc_connectivity_state child_connectivity_state_; + grpc_closure on_child_connectivity_changed_; + grpc_closure on_child_request_reresolution_; +}; + +// +// serverlist parsing code +// + +// Returns the backend addresses extracted from the given addresses. +UniquePtr ExtractBackendAddresses( + const ServerAddressList& addresses) { + auto backend_addresses = MakeUnique(); + for (size_t i = 0; i < addresses.size(); ++i) { + if (!addresses[i].IsBalancer()) { + backend_addresses->emplace_back(addresses[i]); + } + } + return backend_addresses; +} + +bool IsServerValid(const xds_grpclb_server* server, size_t idx, bool log) { + if (server->drop) return false; + const xds_grpclb_ip_address* ip = &server->ip_address; + if (GPR_UNLIKELY(server->port >> 16 != 0)) { + if (log) { + gpr_log(GPR_ERROR, + "Invalid port '%d' at index %lu of serverlist. Ignoring.", + server->port, (unsigned long)idx); + } + return false; + } + if (GPR_UNLIKELY(ip->size != 4 && ip->size != 16)) { + if (log) { + gpr_log(GPR_ERROR, + "Expected IP to be 4 or 16 bytes, got %d at index %lu of " + "serverlist. Ignoring", + ip->size, (unsigned long)idx); + } + return false; + } + return true; +} + +void ParseServer(const xds_grpclb_server* server, grpc_resolved_address* addr) { + memset(addr, 0, sizeof(*addr)); + if (server->drop) return; + const uint16_t netorder_port = grpc_htons((uint16_t)server->port); + /* the addresses are given in binary format (a in(6)_addr struct) in + * server->ip_address.bytes. */ + const xds_grpclb_ip_address* ip = &server->ip_address; + if (ip->size == 4) { + addr->len = static_cast(sizeof(grpc_sockaddr_in)); + grpc_sockaddr_in* addr4 = reinterpret_cast(&addr->addr); + addr4->sin_family = GRPC_AF_INET; + memcpy(&addr4->sin_addr, ip->bytes, ip->size); + addr4->sin_port = netorder_port; + } else if (ip->size == 16) { + addr->len = static_cast(sizeof(grpc_sockaddr_in6)); + grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)&addr->addr; + addr6->sin6_family = GRPC_AF_INET6; + memcpy(&addr6->sin6_addr, ip->bytes, ip->size); + addr6->sin6_port = netorder_port; + } +} + +// Returns addresses extracted from \a serverlist. +UniquePtr ProcessServerlist( + const xds_grpclb_serverlist* serverlist) { + auto addresses = MakeUnique(); + for (size_t i = 0; i < serverlist->num_servers; ++i) { + const xds_grpclb_server* server = serverlist->servers[i]; + if (!IsServerValid(serverlist->servers[i], i, false)) continue; + grpc_resolved_address addr; + ParseServer(server, &addr); + addresses->emplace_back(addr, nullptr); + } + return addresses; +} + +// +// XdsLb::BalancerCallState +// + +XdsLb::BalancerCallState::BalancerCallState( + RefCountedPtr parent_xdslb_policy) + : InternallyRefCounted(&grpc_lb_xds_trace), + xdslb_policy_(std::move(parent_xdslb_policy)) { + GPR_ASSERT(xdslb_policy_ != nullptr); + GPR_ASSERT(!xdslb_policy()->shutting_down_); + // Init the LB call. Note that the LB call will progress every time there's + // activity in xdslb_policy_->interested_parties(), which is comprised of + // the polling entities from client_channel. + GPR_ASSERT(xdslb_policy()->server_name_ != nullptr); + GPR_ASSERT(xdslb_policy()->server_name_[0] != '\0'); + const grpc_millis deadline = + xdslb_policy()->lb_call_timeout_ms_ == 0 + ? GRPC_MILLIS_INF_FUTURE + : ExecCtx::Get()->Now() + xdslb_policy()->lb_call_timeout_ms_; + lb_call_ = grpc_channel_create_pollset_set_call( + xdslb_policy()->lb_channel_, nullptr, GRPC_PROPAGATE_DEFAULTS, + xdslb_policy_->interested_parties(), + GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD, + nullptr, deadline, nullptr); + // Init the LB call request payload. + xds_grpclb_request* request = + xds_grpclb_request_create(xdslb_policy()->server_name_); + grpc_slice request_payload_slice = xds_grpclb_request_encode(request); + send_message_payload_ = + grpc_raw_byte_buffer_create(&request_payload_slice, 1); + grpc_slice_unref_internal(request_payload_slice); + xds_grpclb_request_destroy(request); + // Init other data associated with the LB call. + grpc_metadata_array_init(&lb_initial_metadata_recv_); + grpc_metadata_array_init(&lb_trailing_metadata_recv_); + GRPC_CLOSURE_INIT(&lb_on_initial_request_sent_, OnInitialRequestSentLocked, + this, grpc_combiner_scheduler(xdslb_policy()->combiner())); + GRPC_CLOSURE_INIT(&lb_on_balancer_message_received_, + OnBalancerMessageReceivedLocked, this, + grpc_combiner_scheduler(xdslb_policy()->combiner())); + GRPC_CLOSURE_INIT(&lb_on_balancer_status_received_, + OnBalancerStatusReceivedLocked, this, + grpc_combiner_scheduler(xdslb_policy()->combiner())); +} + +XdsLb::BalancerCallState::~BalancerCallState() { + GPR_ASSERT(lb_call_ != nullptr); + grpc_call_unref(lb_call_); + grpc_metadata_array_destroy(&lb_initial_metadata_recv_); + grpc_metadata_array_destroy(&lb_trailing_metadata_recv_); + grpc_byte_buffer_destroy(send_message_payload_); + grpc_byte_buffer_destroy(recv_message_payload_); + grpc_slice_unref_internal(lb_call_status_details_); +} + +void XdsLb::BalancerCallState::Orphan() { + GPR_ASSERT(lb_call_ != nullptr); + // If we are here because xdslb_policy wants to cancel the call, + // lb_on_balancer_status_received_ will complete the cancellation and clean + // up. Otherwise, we are here because xdslb_policy has to orphan a failed + // call, then the following cancellation will be a no-op. + grpc_call_cancel(lb_call_, nullptr); + if (client_load_report_timer_callback_pending_) { + grpc_timer_cancel(&client_load_report_timer_); + } + // Note that the initial ref is hold by lb_on_balancer_status_received_ + // instead of the caller of this function. So the corresponding unref happens + // in lb_on_balancer_status_received_ instead of here. +} + +void XdsLb::BalancerCallState::StartQuery() { + GPR_ASSERT(lb_call_ != nullptr); + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, "[xdslb %p] Starting LB call (lb_calld: %p, lb_call: %p)", + xdslb_policy_.get(), this, lb_call_); + } + // Create the ops. + grpc_call_error call_error; + grpc_op ops[3]; + memset(ops, 0, sizeof(ops)); + // Op: send initial metadata. + grpc_op* op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op->flags = 0; + op->reserved = nullptr; + op++; + // Op: send request message. + GPR_ASSERT(send_message_payload_ != nullptr); + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = send_message_payload_; + op->flags = 0; + op->reserved = nullptr; + op++; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "on_initial_request_sent"); + self.release(); + call_error = grpc_call_start_batch_and_execute( + lb_call_, ops, (size_t)(op - ops), &lb_on_initial_request_sent_); + GPR_ASSERT(GRPC_CALL_OK == call_error); + // Op: recv initial metadata. + op = ops; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &lb_initial_metadata_recv_; + op->flags = 0; + op->reserved = nullptr; + op++; + // Op: recv response. + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &recv_message_payload_; + op->flags = 0; + op->reserved = nullptr; + op++; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + self = Ref(DEBUG_LOCATION, "on_message_received"); + self.release(); + call_error = grpc_call_start_batch_and_execute( + lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_message_received_); + GPR_ASSERT(GRPC_CALL_OK == call_error); + // Op: recv server status. + op = ops; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = + &lb_trailing_metadata_recv_; + op->data.recv_status_on_client.status = &lb_call_status_; + op->data.recv_status_on_client.status_details = &lb_call_status_details_; + op->flags = 0; + op->reserved = nullptr; + op++; + // This callback signals the end of the LB call, so it relies on the initial + // ref instead of a new ref. When it's invoked, it's the initial ref that is + // unreffed. + call_error = grpc_call_start_batch_and_execute( + lb_call_, ops, (size_t)(op - ops), &lb_on_balancer_status_received_); + GPR_ASSERT(GRPC_CALL_OK == call_error); +} + +void XdsLb::BalancerCallState::ScheduleNextClientLoadReportLocked() { + const grpc_millis next_client_load_report_time = + ExecCtx::Get()->Now() + client_stats_report_interval_; + GRPC_CLOSURE_INIT(&client_load_report_closure_, + MaybeSendClientLoadReportLocked, this, + grpc_combiner_scheduler(xdslb_policy()->combiner())); + grpc_timer_init(&client_load_report_timer_, next_client_load_report_time, + &client_load_report_closure_); + client_load_report_timer_callback_pending_ = true; +} + +void XdsLb::BalancerCallState::MaybeSendClientLoadReportLocked( + void* arg, grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + XdsLb* xdslb_policy = lb_calld->xdslb_policy(); + lb_calld->client_load_report_timer_callback_pending_ = false; + if (error != GRPC_ERROR_NONE || lb_calld != xdslb_policy->lb_calld_.get()) { + lb_calld->Unref(DEBUG_LOCATION, "client_load_report"); + return; + } + // If we've already sent the initial request, then we can go ahead and send + // the load report. Otherwise, we need to wait until the initial request has + // been sent to send this (see OnInitialRequestSentLocked()). + if (lb_calld->send_message_payload_ == nullptr) { + lb_calld->SendClientLoadReportLocked(); + } else { + lb_calld->client_load_report_is_due_ = true; + } +} + +bool XdsLb::BalancerCallState::LoadReportCountersAreZero( + xds_grpclb_request* request) { + XdsLbClientStats::DroppedCallCounts* drop_entries = + static_cast( + request->client_stats.calls_finished_with_drop.arg); + return request->client_stats.num_calls_started == 0 && + request->client_stats.num_calls_finished == 0 && + request->client_stats.num_calls_finished_with_client_failed_to_send == + 0 && + request->client_stats.num_calls_finished_known_received == 0 && + (drop_entries == nullptr || drop_entries->empty()); +} + +// TODO(vpowar): Use LRS to send the client Load Report. +void XdsLb::BalancerCallState::SendClientLoadReportLocked() { + // Construct message payload. + GPR_ASSERT(send_message_payload_ == nullptr); + xds_grpclb_request* request = + xds_grpclb_load_report_request_create_locked(client_stats_.get()); + // Skip client load report if the counters were all zero in the last + // report and they are still zero in this one. + if (LoadReportCountersAreZero(request)) { + if (last_client_load_report_counters_were_zero_) { + xds_grpclb_request_destroy(request); + ScheduleNextClientLoadReportLocked(); + return; + } + last_client_load_report_counters_were_zero_ = true; + } else { + last_client_load_report_counters_were_zero_ = false; + } + // TODO(vpowar): Send the report on LRS stream. + xds_grpclb_request_destroy(request); +} + +void XdsLb::BalancerCallState::OnInitialRequestSentLocked(void* arg, + grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + grpc_byte_buffer_destroy(lb_calld->send_message_payload_); + lb_calld->send_message_payload_ = nullptr; + // If we attempted to send a client load report before the initial request was + // sent (and this lb_calld is still in use), send the load report now. + if (lb_calld->client_load_report_is_due_ && + lb_calld == lb_calld->xdslb_policy()->lb_calld_.get()) { + lb_calld->SendClientLoadReportLocked(); + lb_calld->client_load_report_is_due_ = false; + } + lb_calld->Unref(DEBUG_LOCATION, "on_initial_request_sent"); +} + +void XdsLb::BalancerCallState::OnBalancerMessageReceivedLocked( + void* arg, grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + XdsLb* xdslb_policy = lb_calld->xdslb_policy(); + // Empty payload means the LB call was cancelled. + if (lb_calld != xdslb_policy->lb_calld_.get() || + lb_calld->recv_message_payload_ == nullptr) { + lb_calld->Unref(DEBUG_LOCATION, "on_message_received"); + return; + } + grpc_byte_buffer_reader bbr; + grpc_byte_buffer_reader_init(&bbr, lb_calld->recv_message_payload_); + grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr); + grpc_byte_buffer_reader_destroy(&bbr); + grpc_byte_buffer_destroy(lb_calld->recv_message_payload_); + lb_calld->recv_message_payload_ = nullptr; + xds_grpclb_initial_response* initial_response; + xds_grpclb_serverlist* serverlist; + if (!lb_calld->seen_initial_response_ && + (initial_response = xds_grpclb_initial_response_parse(response_slice)) != + nullptr) { + // Have NOT seen initial response, look for initial response. + if (initial_response->has_client_stats_report_interval) { + lb_calld->client_stats_report_interval_ = GPR_MAX( + GPR_MS_PER_SEC, xds_grpclb_duration_to_millis( + &initial_response->client_stats_report_interval)); + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Received initial LB response message; " + "client load reporting interval = %" PRId64 " milliseconds", + xdslb_policy, lb_calld->client_stats_report_interval_); + } + } else if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Received initial LB response message; client load " + "reporting NOT enabled", + xdslb_policy); + } + xds_grpclb_initial_response_destroy(initial_response); + lb_calld->seen_initial_response_ = true; + } else if ((serverlist = xds_grpclb_response_parse_serverlist( + response_slice)) != nullptr) { + // Have seen initial response, look for serverlist. + GPR_ASSERT(lb_calld->lb_call_ != nullptr); + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Serverlist with %" PRIuPTR " servers received", + xdslb_policy, serverlist->num_servers); + for (size_t i = 0; i < serverlist->num_servers; ++i) { + grpc_resolved_address addr; + ParseServer(serverlist->servers[i], &addr); + char* ipport; + grpc_sockaddr_to_string(&ipport, &addr, false); + gpr_log(GPR_INFO, "[xdslb %p] Serverlist[%" PRIuPTR "]: %s", + xdslb_policy, i, ipport); + gpr_free(ipport); + } + } + /* update serverlist */ + if (serverlist->num_servers > 0) { + // Start sending client load report only after we start using the + // serverlist returned from the current LB call. + if (lb_calld->client_stats_report_interval_ > 0 && + lb_calld->client_stats_ == nullptr) { + lb_calld->client_stats_.reset(New()); + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = lb_calld->Ref(DEBUG_LOCATION, "client_load_report"); + self.release(); + lb_calld->ScheduleNextClientLoadReportLocked(); + } + if (xds_grpclb_serverlist_equals(xdslb_policy->serverlist_, serverlist)) { + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Incoming server list identical to current, " + "ignoring.", + xdslb_policy); + } + xds_grpclb_destroy_serverlist(serverlist); + } else { /* new serverlist */ + if (xdslb_policy->serverlist_ != nullptr) { + /* dispose of the old serverlist */ + xds_grpclb_destroy_serverlist(xdslb_policy->serverlist_); + } else { + /* or dispose of the fallback */ + xdslb_policy->fallback_backend_addresses_.reset(); + if (xdslb_policy->fallback_timer_callback_pending_) { + grpc_timer_cancel(&xdslb_policy->lb_fallback_timer_); + } + } + // and update the copy in the XdsLb instance. This + // serverlist instance will be destroyed either upon the next + // update or when the XdsLb instance is destroyed. + xdslb_policy->serverlist_ = serverlist; + xdslb_policy->CreateOrUpdateChildPolicyLocked(); + } + } else { + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, "[xdslb %p] Received empty server list, ignoring.", + xdslb_policy); + } + xds_grpclb_destroy_serverlist(serverlist); + } + } else { + // No valid initial response or serverlist found. + char* response_slice_str = + grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX); + gpr_log(GPR_ERROR, + "[xdslb %p] Invalid LB response received: '%s'. Ignoring.", + xdslb_policy, response_slice_str); + gpr_free(response_slice_str); + } + grpc_slice_unref_internal(response_slice); + if (!xdslb_policy->shutting_down_) { + // Keep listening for serverlist updates. + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_MESSAGE; + op.data.recv_message.recv_message = &lb_calld->recv_message_payload_; + op.flags = 0; + op.reserved = nullptr; + // Reuse the "OnBalancerMessageReceivedLocked" ref taken in StartQuery(). + const grpc_call_error call_error = grpc_call_start_batch_and_execute( + lb_calld->lb_call_, &op, 1, + &lb_calld->lb_on_balancer_message_received_); + GPR_ASSERT(GRPC_CALL_OK == call_error); + } else { + lb_calld->Unref(DEBUG_LOCATION, "on_message_received+xds_shutdown"); + } +} + +void XdsLb::BalancerCallState::OnBalancerStatusReceivedLocked( + void* arg, grpc_error* error) { + BalancerCallState* lb_calld = static_cast(arg); + XdsLb* xdslb_policy = lb_calld->xdslb_policy(); + GPR_ASSERT(lb_calld->lb_call_ != nullptr); + if (grpc_lb_xds_trace.enabled()) { + char* status_details = + grpc_slice_to_c_string(lb_calld->lb_call_status_details_); + gpr_log(GPR_INFO, + "[xdslb %p] Status from LB server received. Status = %d, details " + "= '%s', (lb_calld: %p, lb_call: %p), error '%s'", + xdslb_policy, lb_calld->lb_call_status_, status_details, lb_calld, + lb_calld->lb_call_, grpc_error_string(error)); + gpr_free(status_details); + } + xdslb_policy->TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_NONE); + // If this lb_calld is still in use, this call ended because of a failure so + // we want to retry connecting. Otherwise, we have deliberately ended this + // call and no further action is required. + if (lb_calld == xdslb_policy->lb_calld_.get()) { + xdslb_policy->lb_calld_.reset(); + GPR_ASSERT(!xdslb_policy->shutting_down_); + if (lb_calld->seen_initial_response_) { + // If we lose connection to the LB server, reset the backoff and restart + // the LB call immediately. + xdslb_policy->lb_call_backoff_.Reset(); + xdslb_policy->StartBalancerCallLocked(); + } else { + // If this LB call fails establishing any connection to the LB server, + // retry later. + xdslb_policy->StartBalancerCallRetryTimerLocked(); + } + } + lb_calld->Unref(DEBUG_LOCATION, "lb_call_ended"); +} + +// +// helper code for creating balancer channel +// + +UniquePtr ExtractBalancerAddresses( + const ServerAddressList& addresses) { + auto balancer_addresses = MakeUnique(); + for (size_t i = 0; i < addresses.size(); ++i) { + if (addresses[i].IsBalancer()) { + balancer_addresses->emplace_back(addresses[i]); + } + } + return balancer_addresses; +} + +/* Returns the channel args for the LB channel, used to create a bidirectional + * stream for the reception of load balancing updates. + * + * Inputs: + * - \a addresses: corresponding to the balancers. + * - \a response_generator: in order to propagate updates from the resolver + * above the grpclb policy. + * - \a args: other args inherited from the xds policy. */ +grpc_channel_args* BuildBalancerChannelArgs( + const ServerAddressList& addresses, + FakeResolverResponseGenerator* response_generator, + const grpc_channel_args* args) { + UniquePtr balancer_addresses = + ExtractBalancerAddresses(addresses); + // Channel args to remove. + static const char* args_to_remove[] = { + // LB policy name, since we want to use the default (pick_first) in + // the LB channel. + GRPC_ARG_LB_POLICY_NAME, + // The channel arg for the server URI, since that will be different for + // the LB channel than for the parent channel. The client channel + // factory will re-add this arg with the right value. + GRPC_ARG_SERVER_URI, + // The resolved addresses, which will be generated by the name resolver + // used in the LB channel. Note that the LB channel will use the fake + // resolver, so this won't actually generate a query to DNS (or some + // other name service). However, the addresses returned by the fake + // resolver will have is_balancer=false, whereas our own addresses have + // is_balancer=true. We need the LB channel to return addresses with + // is_balancer=false so that it does not wind up recursively using the + // xds LB policy, as per the special case logic in client_channel.c. + GRPC_ARG_SERVER_ADDRESS_LIST, + // The fake resolver response generator, because we are replacing it + // with the one from the xds policy, used to propagate updates to + // the LB channel. + GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR, + // The LB channel should use the authority indicated by the target + // authority table (see \a grpc_lb_policy_xds_modify_lb_channel_args), + // as opposed to the authority from the parent channel. + GRPC_ARG_DEFAULT_AUTHORITY, + // Just as for \a GRPC_ARG_DEFAULT_AUTHORITY, the LB channel should be + // treated as a stand-alone channel and not inherit this argument from the + // args of the parent channel. + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, + }; + // Channel args to add. + const grpc_arg args_to_add[] = { + // New server address list. + // Note that we pass these in both when creating the LB channel + // and via the fake resolver. The latter is what actually gets used. + CreateServerAddressListChannelArg(balancer_addresses.get()), + // The fake resolver response generator, which we use to inject + // address updates into the LB channel. + grpc_core::FakeResolverResponseGenerator::MakeChannelArg( + response_generator), + // A channel arg indicating the target is a xds load balancer. + grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_ADDRESS_IS_XDS_LOAD_BALANCER), 1), + // A channel arg indicating this is an internal channels, aka it is + // owned by components in Core, not by the user application. + grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL), 1), + }; + // Construct channel args. + grpc_channel_args* new_args = grpc_channel_args_copy_and_add_and_remove( + args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), args_to_add, + GPR_ARRAY_SIZE(args_to_add)); + // Make any necessary modifications for security. + return grpc_lb_policy_xds_modify_lb_channel_args(new_args); +} + +// +// ctor and dtor +// + +// TODO(vishalpowar): Use lb_config in args to configure LB policy. +XdsLb::XdsLb(const LoadBalancingPolicy::Args& args) + : LoadBalancingPolicy(args), + response_generator_(MakeRefCounted()), + lb_call_backoff_( + BackOff::Options() + .set_initial_backoff(GRPC_XDS_INITIAL_CONNECT_BACKOFF_SECONDS * + 1000) + .set_multiplier(GRPC_XDS_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(GRPC_XDS_RECONNECT_JITTER) + .set_max_backoff(GRPC_XDS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) { + // Initialization. + gpr_mu_init(&lb_channel_mu_); + grpc_subchannel_index_ref(); + GRPC_CLOSURE_INIT(&lb_channel_on_connectivity_changed_, + &XdsLb::OnBalancerChannelConnectivityChangedLocked, this, + grpc_combiner_scheduler(args.combiner)); + GRPC_CLOSURE_INIT(&on_child_connectivity_changed_, + &XdsLb::OnChildPolicyConnectivityChangedLocked, this, + grpc_combiner_scheduler(args.combiner)); + GRPC_CLOSURE_INIT(&on_child_request_reresolution_, + &XdsLb::OnChildPolicyRequestReresolutionLocked, this, + grpc_combiner_scheduler(args.combiner)); + grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, "xds"); + // Record server name. + const grpc_arg* arg = grpc_channel_args_find(args.args, GRPC_ARG_SERVER_URI); + const char* server_uri = grpc_channel_arg_get_string(arg); + GPR_ASSERT(server_uri != nullptr); + grpc_uri* uri = grpc_uri_parse(server_uri, true); + GPR_ASSERT(uri->path[0] != '\0'); + server_name_ = gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path); + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Will use '%s' as the server name for LB request.", this, + server_name_); + } + grpc_uri_destroy(uri); + // Record LB call timeout. + arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS); + lb_call_timeout_ms_ = grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX}); + // Record fallback timeout. + arg = grpc_channel_args_find(args.args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS); + lb_fallback_timeout_ms_ = grpc_channel_arg_get_integer( + arg, {GRPC_XDS_DEFAULT_FALLBACK_TIMEOUT_MS, 0, INT_MAX}); + // Process channel args. + ProcessChannelArgsLocked(*args.args); +} + +XdsLb::~XdsLb() { + GPR_ASSERT(pending_picks_ == nullptr); + gpr_mu_destroy(&lb_channel_mu_); + gpr_free((void*)server_name_); + grpc_channel_args_destroy(args_); + grpc_connectivity_state_destroy(&state_tracker_); + if (serverlist_ != nullptr) { + xds_grpclb_destroy_serverlist(serverlist_); + } + grpc_subchannel_index_unref(); +} + +void XdsLb::ShutdownLocked() { + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown"); + shutting_down_ = true; + lb_calld_.reset(); + if (retry_timer_callback_pending_) { + grpc_timer_cancel(&lb_call_retry_timer_); + } + if (fallback_timer_callback_pending_) { + grpc_timer_cancel(&lb_fallback_timer_); + } + child_policy_.reset(); + TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_CANCELLED); + // We destroy the LB channel here instead of in our destructor because + // destroying the channel triggers a last callback to + // OnBalancerChannelConnectivityChangedLocked(), and we need to be + // alive when that callback is invoked. + if (lb_channel_ != nullptr) { + gpr_mu_lock(&lb_channel_mu_); + grpc_channel_destroy(lb_channel_); + lb_channel_ = nullptr; + gpr_mu_unlock(&lb_channel_mu_); + } + grpc_connectivity_state_set(&state_tracker_, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_REF(error), "xds_shutdown"); + // Clear pending picks. + PendingPick* pp; + while ((pp = pending_picks_) != nullptr) { + pending_picks_ = pp->next; + pp->pick->connected_subchannel.reset(); + // Note: pp is deleted in this callback. + GRPC_CLOSURE_SCHED(&pp->on_complete, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +// +// public methods +// + +void XdsLb::HandOffPendingPicksLocked(LoadBalancingPolicy* new_policy) { + PendingPick* pp; + while ((pp = pending_picks_) != nullptr) { + pending_picks_ = pp->next; + pp->pick->on_complete = pp->original_on_complete; + grpc_error* error = GRPC_ERROR_NONE; + if (new_policy->PickLocked(pp->pick, &error)) { + // Synchronous return; schedule closure. + GRPC_CLOSURE_SCHED(pp->pick->on_complete, error); + } + Delete(pp); + } +} + +// Cancel a specific pending pick. +// +// A pick progresses as follows: +// - If there's a child policy available, it'll be handed over to child policy +// (in CreateChildPolicyLocked()). From that point onwards, it'll be the +// child policy's responsibility. For cancellations, that implies the pick +// needs to be also cancelled by the child policy instance. +// - Otherwise, without a child policy instance, picks stay pending at this +// policy's level (xds), inside the pending_picks_ list. To cancel these, +// we invoke the completion closure and set the pick's connected +// subchannel to nullptr right here. +void XdsLb::CancelPickLocked(PickState* pick, grpc_error* error) { + PendingPick* pp = pending_picks_; + pending_picks_ = nullptr; + while (pp != nullptr) { + PendingPick* next = pp->next; + if (pp->pick == pick) { + pick->connected_subchannel.reset(); + // Note: pp is deleted in this callback. + GRPC_CLOSURE_SCHED(&pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = pending_picks_; + pending_picks_ = pp; + } + pp = next; + } + if (child_policy_ != nullptr) { + child_policy_->CancelPickLocked(pick, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +// Cancel all pending picks. +// +// A pick progresses as follows: +// - If there's a child policy available, it'll be handed over to child policy +// (in CreateChildPolicyLocked()). From that point onwards, it'll be the +// child policy's responsibility. For cancellations, that implies the pick +// needs to be also cancelled by the child policy instance. +// - Otherwise, without a child policy instance, picks stay pending at this +// policy's level (xds), inside the pending_picks_ list. To cancel these, +// we invoke the completion closure and set the pick's connected +// subchannel to nullptr right here. +void XdsLb::CancelMatchingPicksLocked(uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error* error) { + PendingPick* pp = pending_picks_; + pending_picks_ = nullptr; + while (pp != nullptr) { + PendingPick* next = pp->next; + if ((*pp->pick->initial_metadata_flags & initial_metadata_flags_mask) == + initial_metadata_flags_eq) { + // Note: pp is deleted in this callback. + GRPC_CLOSURE_SCHED(&pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = pending_picks_; + pending_picks_ = pp; + } + pp = next; + } + if (child_policy_ != nullptr) { + child_policy_->CancelMatchingPicksLocked(initial_metadata_flags_mask, + initial_metadata_flags_eq, + GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +void XdsLb::ExitIdleLocked() { + if (!started_picking_) { + StartPickingLocked(); + } +} + +void XdsLb::ResetBackoffLocked() { + if (lb_channel_ != nullptr) { + grpc_channel_reset_connect_backoff(lb_channel_); + } + if (child_policy_ != nullptr) { + child_policy_->ResetBackoffLocked(); + } +} + +bool XdsLb::PickLocked(PickState* pick, grpc_error** error) { + PendingPick* pp = PendingPickCreate(pick); + bool pick_done = false; + if (child_policy_ != nullptr) { + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, "[xdslb %p] about to PICK from policy %p", this, + child_policy_.get()); + } + pick_done = PickFromChildPolicyLocked(false /* force_async */, pp, error); + } else { // child_policy_ == NULL + if (pick->on_complete == nullptr) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No pick result available but synchronous result required."); + pick_done = true; + } else { + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] No child policy. Adding to xds's pending picks", + this); + } + AddPendingPick(pp); + if (!started_picking_) { + StartPickingLocked(); + } + pick_done = false; + } + } + return pick_done; +} + +void XdsLb::FillChildRefsForChannelz(channelz::ChildRefsList* child_subchannels, + channelz::ChildRefsList* child_channels) { + // delegate to the child_policy_ to fill the children subchannels. + child_policy_->FillChildRefsForChannelz(child_subchannels, child_channels); + MutexLock lock(&lb_channel_mu_); + if (lb_channel_ != nullptr) { + grpc_core::channelz::ChannelNode* channel_node = + grpc_channel_get_channelz_node(lb_channel_); + if (channel_node != nullptr) { + child_channels->push_back(channel_node->uuid()); + } + } +} + +grpc_connectivity_state XdsLb::CheckConnectivityLocked( + grpc_error** connectivity_error) { + return grpc_connectivity_state_get(&state_tracker_, connectivity_error); +} + +void XdsLb::NotifyOnStateChangeLocked(grpc_connectivity_state* current, + grpc_closure* closure) { + grpc_connectivity_state_notify_on_state_change(&state_tracker_, current, + closure); +} + +void XdsLb::ProcessChannelArgsLocked(const grpc_channel_args& args) { + const ServerAddressList* addresses = FindServerAddressListChannelArg(&args); + if (addresses == nullptr) { + // Ignore this update. + gpr_log(GPR_ERROR, + "[xdslb %p] No valid LB addresses channel arg in update, ignoring.", + this); + return; + } + // Update fallback address list. + fallback_backend_addresses_ = ExtractBackendAddresses(*addresses); + // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args, + // since we use this to trigger the client_load_reporting filter. + static const char* args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME}; + grpc_arg new_arg = grpc_channel_arg_string_create( + (char*)GRPC_ARG_LB_POLICY_NAME, (char*)"xds"); + grpc_channel_args_destroy(args_); + args_ = grpc_channel_args_copy_and_add_and_remove( + &args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1); + // Construct args for balancer channel. + grpc_channel_args* lb_channel_args = + BuildBalancerChannelArgs(*addresses, response_generator_.get(), &args); + // Create balancer channel if needed. + if (lb_channel_ == nullptr) { + char* uri_str; + gpr_asprintf(&uri_str, "fake:///%s", server_name_); + gpr_mu_lock(&lb_channel_mu_); + lb_channel_ = grpc_client_channel_factory_create_channel( + client_channel_factory(), uri_str, + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, lb_channel_args); + gpr_mu_unlock(&lb_channel_mu_); + GPR_ASSERT(lb_channel_ != nullptr); + gpr_free(uri_str); + } + // Propagate updates to the LB channel (pick_first) through the fake + // resolver. + response_generator_->SetResponse(lb_channel_args); + grpc_channel_args_destroy(lb_channel_args); +} + +// TODO(vishalpowar): Use lb_config to configure LB policy. +void XdsLb::UpdateLocked(const grpc_channel_args& args, grpc_json* lb_config) { + ProcessChannelArgsLocked(args); + // Update the existing child policy. + // Note: We have disabled fallback mode in the code, so this child policy must + // have been created from a serverlist. + // TODO(vpowar): Handle the fallback_address changes when we add support for + // fallback in xDS. + if (child_policy_ != nullptr) CreateOrUpdateChildPolicyLocked(); + // Start watching the LB channel connectivity for connection, if not + // already doing so. + if (!watching_lb_channel_) { + lb_channel_connectivity_ = grpc_channel_check_connectivity_state( + lb_channel_, true /* try to connect */); + grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(lb_channel_)); + GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); + watching_lb_channel_ = true; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "watch_lb_channel_connectivity"); + self.release(); + grpc_client_channel_watch_connectivity_state( + client_channel_elem, + grpc_polling_entity_create_from_pollset_set(interested_parties()), + &lb_channel_connectivity_, &lb_channel_on_connectivity_changed_, + nullptr); + } +} + +// +// code for balancer channel and call +// + +void XdsLb::StartPickingLocked() { + // Start a timer to fall back. + if (lb_fallback_timeout_ms_ > 0 && serverlist_ == nullptr && + !fallback_timer_callback_pending_) { + grpc_millis deadline = ExecCtx::Get()->Now() + lb_fallback_timeout_ms_; + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "on_fallback_timer"); + self.release(); + GRPC_CLOSURE_INIT(&lb_on_fallback_, &XdsLb::OnFallbackTimerLocked, this, + grpc_combiner_scheduler(combiner())); + fallback_timer_callback_pending_ = true; + grpc_timer_init(&lb_fallback_timer_, deadline, &lb_on_fallback_); + } + started_picking_ = true; + StartBalancerCallLocked(); +} + +void XdsLb::StartBalancerCallLocked() { + GPR_ASSERT(lb_channel_ != nullptr); + if (shutting_down_) return; + // Init the LB call data. + GPR_ASSERT(lb_calld_ == nullptr); + lb_calld_ = MakeOrphanable(Ref()); + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Query for backends (lb_channel: %p, lb_calld: %p)", + this, lb_channel_, lb_calld_.get()); + } + lb_calld_->StartQuery(); +} + +void XdsLb::OnFallbackTimerLocked(void* arg, grpc_error* error) { + XdsLb* xdslb_policy = static_cast(arg); + xdslb_policy->fallback_timer_callback_pending_ = false; + // If we receive a serverlist after the timer fires but before this callback + // actually runs, don't fall back. + if (xdslb_policy->serverlist_ == nullptr && !xdslb_policy->shutting_down_ && + error == GRPC_ERROR_NONE) { + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Fallback timer fired. Not using fallback backends", + xdslb_policy); + } + } + xdslb_policy->Unref(DEBUG_LOCATION, "on_fallback_timer"); +} + +void XdsLb::StartBalancerCallRetryTimerLocked() { + grpc_millis next_try = lb_call_backoff_.NextAttemptTime(); + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, "[xdslb %p] Connection to LB server lost...", this); + grpc_millis timeout = next_try - ExecCtx::Get()->Now(); + if (timeout > 0) { + gpr_log(GPR_INFO, "[xdslb %p] ... retry_timer_active in %" PRId64 "ms.", + this, timeout); + } else { + gpr_log(GPR_INFO, "[xdslb %p] ... retry_timer_active immediately.", this); + } + } + // TODO(roth): We currently track this ref manually. Once the + // ClosureRef API is ready, we should pass the RefCountedPtr<> along + // with the callback. + auto self = Ref(DEBUG_LOCATION, "on_balancer_call_retry_timer"); + self.release(); + GRPC_CLOSURE_INIT(&lb_on_call_retry_, &XdsLb::OnBalancerCallRetryTimerLocked, + this, grpc_combiner_scheduler(combiner())); + retry_timer_callback_pending_ = true; + grpc_timer_init(&lb_call_retry_timer_, next_try, &lb_on_call_retry_); +} + +void XdsLb::OnBalancerCallRetryTimerLocked(void* arg, grpc_error* error) { + XdsLb* xdslb_policy = static_cast(arg); + xdslb_policy->retry_timer_callback_pending_ = false; + if (!xdslb_policy->shutting_down_ && error == GRPC_ERROR_NONE && + xdslb_policy->lb_calld_ == nullptr) { + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, "[xdslb %p] Restarting call to LB server", + xdslb_policy); + } + xdslb_policy->StartBalancerCallLocked(); + } + xdslb_policy->Unref(DEBUG_LOCATION, "on_balancer_call_retry_timer"); +} + +// Invoked as part of the update process. It continues watching the LB channel +// until it shuts down or becomes READY. It's invoked even if the LB channel +// stayed READY throughout the update (for example if the update is identical). +void XdsLb::OnBalancerChannelConnectivityChangedLocked(void* arg, + grpc_error* error) { + XdsLb* xdslb_policy = static_cast(arg); + if (xdslb_policy->shutting_down_) goto done; + // Re-initialize the lb_call. This should also take care of updating the + // child policy. Note that the current child policy, if any, will + // stay in effect until an update from the new lb_call is received. + switch (xdslb_policy->lb_channel_connectivity_) { + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_TRANSIENT_FAILURE: { + // Keep watching the LB channel. + grpc_channel_element* client_channel_elem = + grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(xdslb_policy->lb_channel_)); + GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); + grpc_client_channel_watch_connectivity_state( + client_channel_elem, + grpc_polling_entity_create_from_pollset_set( + xdslb_policy->interested_parties()), + &xdslb_policy->lb_channel_connectivity_, + &xdslb_policy->lb_channel_on_connectivity_changed_, nullptr); + break; + } + // The LB channel may be IDLE because it's shut down before the update. + // Restart the LB call to kick the LB channel into gear. + case GRPC_CHANNEL_IDLE: + case GRPC_CHANNEL_READY: + xdslb_policy->lb_calld_.reset(); + if (xdslb_policy->started_picking_) { + if (xdslb_policy->retry_timer_callback_pending_) { + grpc_timer_cancel(&xdslb_policy->lb_call_retry_timer_); + } + xdslb_policy->lb_call_backoff_.Reset(); + xdslb_policy->StartBalancerCallLocked(); + } + // Fall through. + case GRPC_CHANNEL_SHUTDOWN: + done: + xdslb_policy->watching_lb_channel_ = false; + xdslb_policy->Unref(DEBUG_LOCATION, + "watch_lb_channel_connectivity_cb_shutdown"); + } +} + +// +// PendingPick +// + +// Destroy function used when embedding client stats in call context. +void DestroyClientStats(void* arg) { + static_cast(arg)->Unref(); +} + +void XdsLb::PendingPickCleanup(PendingPick* pp) { + // If connected_subchannel is nullptr, no pick has been made by the + // child policy (e.g., all addresses failed to connect). + if (pp->pick->connected_subchannel != nullptr) { + // Pass on client stats via context. Passes ownership of the reference. + if (pp->client_stats != nullptr) { + pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].value = + pp->client_stats.release(); + pp->pick->subchannel_call_context[GRPC_GRPCLB_CLIENT_STATS].destroy = + DestroyClientStats; + } + } else { + pp->client_stats.reset(); + } +} + +/* The \a on_complete closure passed as part of the pick requires keeping a + * reference to its associated child policy instance. We wrap this closure in + * order to unref the child policy instance upon its invocation */ +void XdsLb::OnPendingPickComplete(void* arg, grpc_error* error) { + PendingPick* pp = static_cast(arg); + PendingPickCleanup(pp); + GRPC_CLOSURE_SCHED(pp->original_on_complete, GRPC_ERROR_REF(error)); + Delete(pp); +} + +XdsLb::PendingPick* XdsLb::PendingPickCreate(PickState* pick) { + PendingPick* pp = New(); + pp->xdslb_policy = this; + pp->pick = pick; + GRPC_CLOSURE_INIT(&pp->on_complete, &XdsLb::OnPendingPickComplete, pp, + grpc_schedule_on_exec_ctx); + pp->original_on_complete = pick->on_complete; + pick->on_complete = &pp->on_complete; + return pp; +} + +void XdsLb::AddPendingPick(PendingPick* pp) { + pp->next = pending_picks_; + pending_picks_ = pp; +} + +// +// code for interacting with the child policy +// + +// Performs a pick over \a child_policy_. Given that a pick can return +// immediately (ignoring its completion callback), we need to perform the +// cleanups this callback would otherwise be responsible for. +// If \a force_async is true, then we will manually schedule the +// completion callback even if the pick is available immediately. +bool XdsLb::PickFromChildPolicyLocked(bool force_async, PendingPick* pp, + grpc_error** error) { + // Set client_stats. + if (lb_calld_ != nullptr && lb_calld_->client_stats() != nullptr) { + pp->client_stats = lb_calld_->client_stats()->Ref(); + } + // Pick via the child policy. + bool pick_done = child_policy_->PickLocked(pp->pick, error); + if (pick_done) { + PendingPickCleanup(pp); + if (force_async) { + GRPC_CLOSURE_SCHED(pp->original_on_complete, *error); + *error = GRPC_ERROR_NONE; + pick_done = false; + } + Delete(pp); + } + // else, the pending pick will be registered and taken care of by the + // pending pick list inside the child policy. Eventually, + // OnPendingPickComplete() will be called, which will (among other + // things) add the LB token to the call's initial metadata. + return pick_done; +} + +void XdsLb::CreateChildPolicyLocked(const Args& args) { + GPR_ASSERT(child_policy_ == nullptr); + child_policy_ = LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy( + "round_robin", args); + if (GPR_UNLIKELY(child_policy_ == nullptr)) { + gpr_log(GPR_ERROR, "[xdslb %p] Failure creating a child policy", this); + return; + } + // TODO(roth): We currently track this ref manually. Once the new + // ClosureRef API is done, pass the RefCountedPtr<> along with the closure. + auto self = Ref(DEBUG_LOCATION, "on_child_reresolution_requested"); + self.release(); + child_policy_->SetReresolutionClosureLocked(&on_child_request_reresolution_); + grpc_error* child_state_error = nullptr; + child_connectivity_state_ = + child_policy_->CheckConnectivityLocked(&child_state_error); + // Connectivity state is a function of the child policy updated/created. + UpdateConnectivityStateFromChildPolicyLocked(child_state_error); + // Add the xDS's interested_parties pollset_set to that of the newly created + // child policy. This will make the child policy progress upon activity on + // xDS LB, which in turn is tied to the application's call. + grpc_pollset_set_add_pollset_set(child_policy_->interested_parties(), + interested_parties()); + // Subscribe to changes to the connectivity of the new child policy. + // TODO(roth): We currently track this ref manually. Once the new + // ClosureRef API is done, pass the RefCountedPtr<> along with the closure. + self = Ref(DEBUG_LOCATION, "on_child_connectivity_changed"); + self.release(); + child_policy_->NotifyOnStateChangeLocked(&child_connectivity_state_, + &on_child_connectivity_changed_); + child_policy_->ExitIdleLocked(); + // Send pending picks to child policy. + PendingPick* pp; + while ((pp = pending_picks_)) { + pending_picks_ = pp->next; + if (grpc_lb_xds_trace.enabled()) { + gpr_log( + GPR_INFO, + "[xdslb %p] Pending pick about to (async) PICK from child policy %p", + this, child_policy_.get()); + } + grpc_error* error = GRPC_ERROR_NONE; + PickFromChildPolicyLocked(true /* force_async */, pp, &error); + } +} + +grpc_channel_args* XdsLb::CreateChildPolicyArgsLocked() { + bool is_backend_from_grpclb_load_balancer = false; + // This should never be invoked if we do not have serverlist_, as fallback + // mode is disabled for xDS plugin. + GPR_ASSERT(serverlist_ != nullptr); + GPR_ASSERT(serverlist_->num_servers > 0); + UniquePtr addresses = ProcessServerlist(serverlist_); + GPR_ASSERT(addresses != nullptr); + is_backend_from_grpclb_load_balancer = true; + // Replace the server address list in the channel args that we pass down to + // the subchannel. + static const char* keys_to_remove[] = {GRPC_ARG_SERVER_ADDRESS_LIST}; + const grpc_arg args_to_add[] = { + CreateServerAddressListChannelArg(addresses.get()), + // A channel arg indicating if the target is a backend inferred from a + // grpclb load balancer. + grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_ADDRESS_IS_BACKEND_FROM_XDS_LOAD_BALANCER), + is_backend_from_grpclb_load_balancer), + }; + grpc_channel_args* args = grpc_channel_args_copy_and_add_and_remove( + args_, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), args_to_add, + GPR_ARRAY_SIZE(args_to_add)); + return args; +} + +void XdsLb::CreateOrUpdateChildPolicyLocked() { + if (shutting_down_) return; + grpc_channel_args* args = CreateChildPolicyArgsLocked(); + GPR_ASSERT(args != nullptr); + if (child_policy_ != nullptr) { + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, "[xdslb %p] Updating the child policy %p", this, + child_policy_.get()); + } + // TODO(vishalpowar): Pass the correct LB config. + child_policy_->UpdateLocked(*args, nullptr); + } else { + LoadBalancingPolicy::Args lb_policy_args; + lb_policy_args.combiner = combiner(); + lb_policy_args.client_channel_factory = client_channel_factory(); + lb_policy_args.args = args; + CreateChildPolicyLocked(lb_policy_args); + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, "[xdslb %p] Created a new child policy %p", this, + child_policy_.get()); + } + } + grpc_channel_args_destroy(args); +} + +void XdsLb::OnChildPolicyRequestReresolutionLocked(void* arg, + grpc_error* error) { + XdsLb* xdslb_policy = static_cast(arg); + if (xdslb_policy->shutting_down_ || error != GRPC_ERROR_NONE) { + xdslb_policy->Unref(DEBUG_LOCATION, "on_child_reresolution_requested"); + return; + } + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Re-resolution requested from child policy " + "(%p).", + xdslb_policy, xdslb_policy->child_policy_.get()); + } + // If we are talking to a balancer, we expect to get updated addresses form + // the balancer, so we can ignore the re-resolution request from the child + // policy. + // Otherwise, handle the re-resolution request using the xds policy's + // original re-resolution closure. + if (xdslb_policy->lb_calld_ == nullptr || + !xdslb_policy->lb_calld_->seen_initial_response()) { + xdslb_policy->TryReresolutionLocked(&grpc_lb_xds_trace, GRPC_ERROR_NONE); + } + // Give back the wrapper closure to the child policy. + xdslb_policy->child_policy_->SetReresolutionClosureLocked( + &xdslb_policy->on_child_request_reresolution_); +} + +void XdsLb::UpdateConnectivityStateFromChildPolicyLocked( + grpc_error* child_state_error) { + const grpc_connectivity_state curr_glb_state = + grpc_connectivity_state_check(&state_tracker_); + /* The new connectivity status is a function of the previous one and the new + * input coming from the status of the child policy. + * + * current state (xds's) + * | + * v || I | C | R | TF | SD | <- new state (child policy's) + * ===++====+=====+=====+======+======+ + * I || I | C | R | [I] | [I] | + * ---++----+-----+-----+------+------+ + * C || I | C | R | [C] | [C] | + * ---++----+-----+-----+------+------+ + * R || I | C | R | [R] | [R] | + * ---++----+-----+-----+------+------+ + * TF || I | C | R | [TF] | [TF] | + * ---++----+-----+-----+------+------+ + * SD || NA | NA | NA | NA | NA | (*) + * ---++----+-----+-----+------+------+ + * + * A [STATE] indicates that the old child policy is kept. In those cases, + * STATE is the current state of xds, which is left untouched. + * + * In summary, if the new state is TRANSIENT_FAILURE or SHUTDOWN, stick to + * the previous child policy instance. + * + * Note that the status is never updated to SHUTDOWN as a result of calling + * this function. Only glb_shutdown() has the power to set that state. + * + * (*) This function mustn't be called during shutting down. */ + GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN); + switch (child_connectivity_state_) { + case GRPC_CHANNEL_TRANSIENT_FAILURE: + case GRPC_CHANNEL_SHUTDOWN: + GPR_ASSERT(child_state_error != GRPC_ERROR_NONE); + break; + case GRPC_CHANNEL_IDLE: + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_READY: + GPR_ASSERT(child_state_error == GRPC_ERROR_NONE); + } + if (grpc_lb_xds_trace.enabled()) { + gpr_log(GPR_INFO, + "[xdslb %p] Setting xds's state to %s from child policy %p state.", + this, grpc_connectivity_state_name(child_connectivity_state_), + child_policy_.get()); + } + grpc_connectivity_state_set(&state_tracker_, child_connectivity_state_, + child_state_error, + "update_lb_connectivity_status_locked"); +} + +void XdsLb::OnChildPolicyConnectivityChangedLocked(void* arg, + grpc_error* error) { + XdsLb* xdslb_policy = static_cast(arg); + if (xdslb_policy->shutting_down_) { + xdslb_policy->Unref(DEBUG_LOCATION, "on_child_connectivity_changed"); + return; + } + xdslb_policy->UpdateConnectivityStateFromChildPolicyLocked( + GRPC_ERROR_REF(error)); + // Resubscribe. Reuse the "on_child_connectivity_changed" ref. + xdslb_policy->child_policy_->NotifyOnStateChangeLocked( + &xdslb_policy->child_connectivity_state_, + &xdslb_policy->on_child_connectivity_changed_); +} + +// +// factory +// + +class XdsFactory : public LoadBalancingPolicyFactory { + public: + OrphanablePtr CreateLoadBalancingPolicy( + const LoadBalancingPolicy::Args& args) const override { + /* Count the number of gRPC-LB addresses. There must be at least one. */ + const ServerAddressList* addresses = + FindServerAddressListChannelArg(args.args); + if (addresses == nullptr) return nullptr; + bool found_balancer_address = false; + for (size_t i = 0; i < addresses->size(); ++i) { + if ((*addresses)[i].IsBalancer()) { + found_balancer_address = true; + break; + } + } + if (!found_balancer_address) return nullptr; + return OrphanablePtr(New(args)); + } + + const char* name() const override { return kXds; } +}; + +} // namespace + +} // namespace grpc_core + +// +// Plugin registration +// + +void grpc_lb_policy_xds_init() { + grpc_core::LoadBalancingPolicyRegistry::Builder:: + RegisterLoadBalancingPolicyFactory( + grpc_core::UniquePtr( + grpc_core::New())); +} + +void grpc_lb_policy_xds_shutdown() {} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.h new file mode 100644 index 00000000000000..8b20680f2d6b95 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_H + +#include + +/** Channel arg indicating if a target corresponding to the address is grpclb + * loadbalancer. The type of this arg is an integer and the value is treated as + * a bool. */ +#define GRPC_ARG_ADDRESS_IS_XDS_LOAD_BALANCER \ + "grpc.address_is_xds_load_balancer" +/** Channel arg indicating if a target corresponding to the address is a backend + * received from a balancer. The type of this arg is an integer and the value is + * treated as a bool. */ +#define GRPC_ARG_ADDRESS_IS_BACKEND_FROM_XDS_LOAD_BALANCER \ + "grpc.address_is_backend_from_xds_load_balancer" + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc new file mode 100644 index 00000000000000..0aa145a24e59a5 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.cc @@ -0,0 +1,26 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h" + +grpc_channel_args* grpc_lb_policy_xds_modify_lb_channel_args( + grpc_channel_args* args) { + return args; +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h new file mode 100644 index 00000000000000..f713b7f563dde0 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_H + +#include + +#include + +/// Makes any necessary modifications to \a args for use in the xds +/// balancer channel. +/// +/// Takes ownership of \a args. +/// +/// Caller takes ownership of the returned args. +grpc_channel_args* grpc_lb_policy_xds_modify_lb_channel_args( + grpc_channel_args* args); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CHANNEL_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_secure.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_secure.cc new file mode 100644 index 00000000000000..55c646e6eed981 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_channel_secure.cc @@ -0,0 +1,104 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_channel.h" + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/transport/target_authority_table.h" +#include "src/core/lib/slice/slice_internal.h" + +namespace grpc_core { +namespace { + +int BalancerNameCmp(const grpc_core::UniquePtr& a, + const grpc_core::UniquePtr& b) { + return strcmp(a.get(), b.get()); +} + +RefCountedPtr CreateTargetAuthorityTable( + const ServerAddressList& addresses) { + TargetAuthorityTable::Entry* target_authority_entries = + static_cast( + gpr_zalloc(sizeof(*target_authority_entries) * addresses.size())); + for (size_t i = 0; i < addresses.size(); ++i) { + char* addr_str; + GPR_ASSERT( + grpc_sockaddr_to_string(&addr_str, &addresses[i].address(), true) > 0); + target_authority_entries[i].key = grpc_slice_from_copied_string(addr_str); + gpr_free(addr_str); + char* balancer_name = grpc_channel_arg_get_string(grpc_channel_args_find( + addresses[i].args(), GRPC_ARG_ADDRESS_BALANCER_NAME)); + target_authority_entries[i].value.reset(gpr_strdup(balancer_name)); + } + RefCountedPtr target_authority_table = + TargetAuthorityTable::Create(addresses.size(), target_authority_entries, + BalancerNameCmp); + gpr_free(target_authority_entries); + return target_authority_table; +} + +} // namespace +} // namespace grpc_core + +grpc_channel_args* grpc_lb_policy_xds_modify_lb_channel_args( + grpc_channel_args* args) { + const char* args_to_remove[1]; + size_t num_args_to_remove = 0; + grpc_arg args_to_add[2]; + size_t num_args_to_add = 0; + // Add arg for targets info table. + grpc_core::ServerAddressList* addresses = + grpc_core::FindServerAddressListChannelArg(args); + GPR_ASSERT(addresses != nullptr); + grpc_core::RefCountedPtr + target_authority_table = + grpc_core::CreateTargetAuthorityTable(*addresses); + args_to_add[num_args_to_add++] = + grpc_core::CreateTargetAuthorityTableChannelArg( + target_authority_table.get()); + // Substitute the channel credentials with a version without call + // credentials: the load balancer is not necessarily trusted to handle + // bearer token credentials. + grpc_channel_credentials* channel_credentials = + grpc_channel_credentials_find_in_args(args); + grpc_core::RefCountedPtr creds_sans_call_creds; + if (channel_credentials != nullptr) { + creds_sans_call_creds = + channel_credentials->duplicate_without_call_credentials(); + GPR_ASSERT(creds_sans_call_creds != nullptr); + args_to_remove[num_args_to_remove++] = GRPC_ARG_CHANNEL_CREDENTIALS; + args_to_add[num_args_to_add++] = + grpc_channel_credentials_to_arg(creds_sans_call_creds.get()); + } + grpc_channel_args* result = grpc_channel_args_copy_and_add_and_remove( + args, args_to_remove, num_args_to_remove, args_to_add, num_args_to_add); + // Clean up. + grpc_channel_args_destroy(args); + return result; +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc new file mode 100644 index 00000000000000..cdf5408be36c99 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.cc @@ -0,0 +1,85 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h" + +#include +#include +#include + +namespace grpc_core { + +void XdsLbClientStats::AddCallStarted() { + gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1); +} + +void XdsLbClientStats::AddCallFinished(bool finished_with_client_failed_to_send, + bool finished_known_received) { + gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1); + if (finished_with_client_failed_to_send) { + gpr_atm_full_fetch_add(&num_calls_finished_with_client_failed_to_send_, + (gpr_atm)1); + } + if (finished_known_received) { + gpr_atm_full_fetch_add(&num_calls_finished_known_received_, (gpr_atm)1); + } +} + +void XdsLbClientStats::AddCallDroppedLocked(char* token) { + // Increment num_calls_started and num_calls_finished. + gpr_atm_full_fetch_add(&num_calls_started_, (gpr_atm)1); + gpr_atm_full_fetch_add(&num_calls_finished_, (gpr_atm)1); + // Record the drop. + if (drop_token_counts_ == nullptr) { + drop_token_counts_.reset(New()); + } + for (size_t i = 0; i < drop_token_counts_->size(); ++i) { + if (strcmp((*drop_token_counts_)[i].token.get(), token) == 0) { + ++(*drop_token_counts_)[i].count; + return; + } + } + // Not found, so add a new entry. + drop_token_counts_->emplace_back(UniquePtr(gpr_strdup(token)), 1); +} + +namespace { + +void AtomicGetAndResetCounter(int64_t* value, gpr_atm* counter) { + *value = static_cast(gpr_atm_full_xchg(counter, (gpr_atm)0)); +} + +} // namespace + +void XdsLbClientStats::GetLocked( + int64_t* num_calls_started, int64_t* num_calls_finished, + int64_t* num_calls_finished_with_client_failed_to_send, + int64_t* num_calls_finished_known_received, + UniquePtr* drop_token_counts) { + AtomicGetAndResetCounter(num_calls_started, &num_calls_started_); + AtomicGetAndResetCounter(num_calls_finished, &num_calls_finished_); + AtomicGetAndResetCounter(num_calls_finished_with_client_failed_to_send, + &num_calls_finished_with_client_failed_to_send_); + AtomicGetAndResetCounter(num_calls_finished_known_received, + &num_calls_finished_known_received_); + *drop_token_counts = std::move(drop_token_counts_); +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h new file mode 100644 index 00000000000000..fa0b9f4b635700 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h @@ -0,0 +1,72 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CLIENT_STATS_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CLIENT_STATS_H + +#include + +#include + +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" + +namespace grpc_core { + +class XdsLbClientStats : public RefCounted { + public: + struct DropTokenCount { + UniquePtr token; + int64_t count; + + DropTokenCount(UniquePtr token, int64_t count) + : token(std::move(token)), count(count) {} + }; + + typedef InlinedVector DroppedCallCounts; + + XdsLbClientStats() {} + + void AddCallStarted(); + void AddCallFinished(bool finished_with_client_failed_to_send, + bool finished_known_received); + + // This method is not thread-safe; caller must synchronize. + void AddCallDroppedLocked(char* token); + + // This method is not thread-safe; caller must synchronize. + void GetLocked(int64_t* num_calls_started, int64_t* num_calls_finished, + int64_t* num_calls_finished_with_client_failed_to_send, + int64_t* num_calls_finished_known_received, + UniquePtr* drop_token_counts); + + private: + // This field must only be accessed via *_locked() methods. + UniquePtr drop_token_counts_; + // These fields may be accessed from multiple threads at a time. + gpr_atm num_calls_started_ = 0; + gpr_atm num_calls_finished_ = 0; + gpr_atm num_calls_finished_with_client_failed_to_send_ = 0; + gpr_atm num_calls_finished_known_received_ = 0; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_CLIENT_STATS_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc new file mode 100644 index 00000000000000..79b7bdbe3389c2 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.cc @@ -0,0 +1,307 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "pb_decode.h" +#include "pb_encode.h" +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h" + +#include + +/* invoked once for every Server in ServerList */ +static bool count_serverlist(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + xds_grpclb_serverlist* sl = static_cast(*arg); + xds_grpclb_server server; + if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, &server))) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; + } + ++sl->num_servers; + return true; +} + +typedef struct decode_serverlist_arg { + /* The decoding callback is invoked once per server in serverlist. Remember + * which index of the serverlist are we currently decoding */ + size_t decoding_idx; + /* The decoded serverlist */ + xds_grpclb_serverlist* serverlist; +} decode_serverlist_arg; + +/* invoked once for every Server in ServerList */ +static bool decode_serverlist(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + decode_serverlist_arg* dec_arg = static_cast(*arg); + GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx); + xds_grpclb_server* server = + static_cast(gpr_zalloc(sizeof(xds_grpclb_server))); + if (GPR_UNLIKELY(!pb_decode(stream, grpc_lb_v1_Server_fields, server))) { + gpr_free(server); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; + } + dec_arg->serverlist->servers[dec_arg->decoding_idx++] = server; + return true; +} + +xds_grpclb_request* xds_grpclb_request_create(const char* lb_service_name) { + xds_grpclb_request* req = + static_cast(gpr_malloc(sizeof(xds_grpclb_request))); + req->has_client_stats = false; + req->has_initial_request = true; + req->initial_request.has_name = true; + strncpy(req->initial_request.name, lb_service_name, + XDS_SERVICE_NAME_MAX_LENGTH); + return req; +} + +static void populate_timestamp(gpr_timespec timestamp, + xds_grpclb_timestamp* timestamp_pb) { + timestamp_pb->has_seconds = true; + timestamp_pb->seconds = timestamp.tv_sec; + timestamp_pb->has_nanos = true; + timestamp_pb->nanos = timestamp.tv_nsec; +} + +static bool encode_string(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + char* str = static_cast(*arg); + if (!pb_encode_tag_for_field(stream, field)) return false; + return pb_encode_string(stream, reinterpret_cast(str), strlen(str)); +} + +static bool encode_drops(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + grpc_core::XdsLbClientStats::DroppedCallCounts* drop_entries = + static_cast(*arg); + if (drop_entries == nullptr) return true; + for (size_t i = 0; i < drop_entries->size(); ++i) { + if (!pb_encode_tag_for_field(stream, field)) return false; + grpc_lb_v1_ClientStatsPerToken drop_message; + drop_message.load_balance_token.funcs.encode = encode_string; + drop_message.load_balance_token.arg = (*drop_entries)[i].token.get(); + drop_message.has_num_calls = true; + drop_message.num_calls = (*drop_entries)[i].count; + if (!pb_encode_submessage(stream, grpc_lb_v1_ClientStatsPerToken_fields, + &drop_message)) { + return false; + } + } + return true; +} + +xds_grpclb_request* xds_grpclb_load_report_request_create_locked( + grpc_core::XdsLbClientStats* client_stats) { + xds_grpclb_request* req = + static_cast(gpr_zalloc(sizeof(xds_grpclb_request))); + req->has_client_stats = true; + req->client_stats.has_timestamp = true; + populate_timestamp(gpr_now(GPR_CLOCK_REALTIME), &req->client_stats.timestamp); + req->client_stats.has_num_calls_started = true; + req->client_stats.has_num_calls_finished = true; + req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; + req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; + req->client_stats.has_num_calls_finished_known_received = true; + req->client_stats.calls_finished_with_drop.funcs.encode = encode_drops; + grpc_core::UniquePtr + drop_counts; + client_stats->GetLocked( + &req->client_stats.num_calls_started, + &req->client_stats.num_calls_finished, + &req->client_stats.num_calls_finished_with_client_failed_to_send, + &req->client_stats.num_calls_finished_known_received, &drop_counts); + // Will be deleted in xds_grpclb_request_destroy(). + req->client_stats.calls_finished_with_drop.arg = drop_counts.release(); + return req; +} + +grpc_slice xds_grpclb_request_encode(const xds_grpclb_request* request) { + size_t encoded_length; + pb_ostream_t sizestream; + pb_ostream_t outputstream; + grpc_slice slice; + memset(&sizestream, 0, sizeof(pb_ostream_t)); + pb_encode(&sizestream, grpc_lb_v1_LoadBalanceRequest_fields, request); + encoded_length = sizestream.bytes_written; + + slice = GRPC_SLICE_MALLOC(encoded_length); + outputstream = + pb_ostream_from_buffer(GRPC_SLICE_START_PTR(slice), encoded_length); + GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v1_LoadBalanceRequest_fields, + request) != 0); + return slice; +} + +void xds_grpclb_request_destroy(xds_grpclb_request* request) { + if (request->has_client_stats) { + grpc_core::XdsLbClientStats::DroppedCallCounts* drop_entries = + static_cast( + request->client_stats.calls_finished_with_drop.arg); + grpc_core::Delete(drop_entries); + } + gpr_free(request); +} + +typedef grpc_lb_v1_LoadBalanceResponse xds_grpclb_response; +xds_grpclb_initial_response* xds_grpclb_initial_response_parse( + grpc_slice encoded_xds_grpclb_response) { + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_xds_grpclb_response), + GRPC_SLICE_LENGTH(encoded_xds_grpclb_response)); + xds_grpclb_response res; + memset(&res, 0, sizeof(xds_grpclb_response)); + if (GPR_UNLIKELY( + !pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res))) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return nullptr; + } + + if (!res.has_initial_response) return nullptr; + + xds_grpclb_initial_response* initial_res = + static_cast( + gpr_malloc(sizeof(xds_grpclb_initial_response))); + memcpy(initial_res, &res.initial_response, + sizeof(xds_grpclb_initial_response)); + + return initial_res; +} + +xds_grpclb_serverlist* xds_grpclb_response_parse_serverlist( + grpc_slice encoded_xds_grpclb_response) { + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_xds_grpclb_response), + GRPC_SLICE_LENGTH(encoded_xds_grpclb_response)); + pb_istream_t stream_at_start = stream; + xds_grpclb_serverlist* sl = static_cast( + gpr_zalloc(sizeof(xds_grpclb_serverlist))); + xds_grpclb_response res; + memset(&res, 0, sizeof(xds_grpclb_response)); + // First pass: count number of servers. + res.server_list.servers.funcs.decode = count_serverlist; + res.server_list.servers.arg = sl; + bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res); + if (GPR_UNLIKELY(!status)) { + gpr_free(sl); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return nullptr; + } + // Second pass: populate servers. + if (sl->num_servers > 0) { + sl->servers = static_cast( + gpr_zalloc(sizeof(xds_grpclb_server*) * sl->num_servers)); + decode_serverlist_arg decode_arg; + memset(&decode_arg, 0, sizeof(decode_arg)); + decode_arg.serverlist = sl; + res.server_list.servers.funcs.decode = decode_serverlist; + res.server_list.servers.arg = &decode_arg; + status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, + &res); + if (GPR_UNLIKELY(!status)) { + xds_grpclb_destroy_serverlist(sl); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return nullptr; + } + } + return sl; +} + +void xds_grpclb_destroy_serverlist(xds_grpclb_serverlist* serverlist) { + if (serverlist == nullptr) { + return; + } + for (size_t i = 0; i < serverlist->num_servers; i++) { + gpr_free(serverlist->servers[i]); + } + gpr_free(serverlist->servers); + gpr_free(serverlist); +} + +xds_grpclb_serverlist* xds_grpclb_serverlist_copy( + const xds_grpclb_serverlist* sl) { + xds_grpclb_serverlist* copy = static_cast( + gpr_zalloc(sizeof(xds_grpclb_serverlist))); + copy->num_servers = sl->num_servers; + copy->servers = static_cast( + gpr_malloc(sizeof(xds_grpclb_server*) * sl->num_servers)); + for (size_t i = 0; i < sl->num_servers; i++) { + copy->servers[i] = + static_cast(gpr_malloc(sizeof(xds_grpclb_server))); + memcpy(copy->servers[i], sl->servers[i], sizeof(xds_grpclb_server)); + } + return copy; +} + +bool xds_grpclb_serverlist_equals(const xds_grpclb_serverlist* lhs, + const xds_grpclb_serverlist* rhs) { + if (lhs == nullptr || rhs == nullptr) { + return false; + } + if (lhs->num_servers != rhs->num_servers) { + return false; + } + for (size_t i = 0; i < lhs->num_servers; i++) { + if (!xds_grpclb_server_equals(lhs->servers[i], rhs->servers[i])) { + return false; + } + } + return true; +} + +bool xds_grpclb_server_equals(const xds_grpclb_server* lhs, + const xds_grpclb_server* rhs) { + return memcmp(lhs, rhs, sizeof(xds_grpclb_server)) == 0; +} + +int xds_grpclb_duration_compare(const xds_grpclb_duration* lhs, + const xds_grpclb_duration* rhs) { + GPR_ASSERT(lhs && rhs); + if (lhs->has_seconds && rhs->has_seconds) { + if (lhs->seconds < rhs->seconds) return -1; + if (lhs->seconds > rhs->seconds) return 1; + } else if (lhs->has_seconds) { + return 1; + } else if (rhs->has_seconds) { + return -1; + } + + GPR_ASSERT(lhs->seconds == rhs->seconds); + if (lhs->has_nanos && rhs->has_nanos) { + if (lhs->nanos < rhs->nanos) return -1; + if (lhs->nanos > rhs->nanos) return 1; + } else if (lhs->has_nanos) { + return 1; + } else if (rhs->has_nanos) { + return -1; + } + + return 0; +} + +grpc_millis xds_grpclb_duration_to_millis(xds_grpclb_duration* duration_pb) { + return static_cast( + (duration_pb->has_seconds ? duration_pb->seconds : 0) * GPR_MS_PER_SEC + + (duration_pb->has_nanos ? duration_pb->nanos : 0) / GPR_NS_PER_MS); +} + +void xds_grpclb_initial_response_destroy( + xds_grpclb_initial_response* response) { + gpr_free(response); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h new file mode 100644 index 00000000000000..6704995641705a --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy/xds/xds_load_balancer_api.h @@ -0,0 +1,89 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_LOAD_BALANCER_API_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_LOAD_BALANCER_API_H + +#include + +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" +#include "src/core/ext/filters/client_channel/lb_policy/xds/xds_client_stats.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +#define XDS_SERVICE_NAME_MAX_LENGTH 128 + +typedef grpc_lb_v1_Server_ip_address_t xds_grpclb_ip_address; +typedef grpc_lb_v1_LoadBalanceRequest xds_grpclb_request; +typedef grpc_lb_v1_InitialLoadBalanceResponse xds_grpclb_initial_response; +typedef grpc_lb_v1_Server xds_grpclb_server; +typedef google_protobuf_Duration xds_grpclb_duration; +typedef google_protobuf_Timestamp xds_grpclb_timestamp; + +typedef struct { + xds_grpclb_server** servers; + size_t num_servers; +} xds_grpclb_serverlist; + +/** Create a request for a gRPC LB service under \a lb_service_name */ +xds_grpclb_request* xds_grpclb_request_create(const char* lb_service_name); +xds_grpclb_request* xds_grpclb_load_report_request_create_locked( + grpc_core::XdsLbClientStats* client_stats); + +/** Protocol Buffers v3-encode \a request */ +grpc_slice xds_grpclb_request_encode(const xds_grpclb_request* request); + +/** Destroy \a request */ +void xds_grpclb_request_destroy(xds_grpclb_request* request); + +/** Parse (ie, decode) the bytes in \a encoded_xds_grpclb_response as a \a + * xds_grpclb_initial_response */ +xds_grpclb_initial_response* xds_grpclb_initial_response_parse( + grpc_slice encoded_xds_grpclb_response); + +/** Parse the list of servers from an encoded \a xds_grpclb_response */ +xds_grpclb_serverlist* xds_grpclb_response_parse_serverlist( + grpc_slice encoded_xds_grpclb_response); + +/** Return a copy of \a sl. The caller is responsible for calling \a + * xds_grpclb_destroy_serverlist on the returned copy. */ +xds_grpclb_serverlist* xds_grpclb_serverlist_copy( + const xds_grpclb_serverlist* sl); + +bool xds_grpclb_serverlist_equals(const xds_grpclb_serverlist* lhs, + const xds_grpclb_serverlist* rhs); + +bool xds_grpclb_server_equals(const xds_grpclb_server* lhs, + const xds_grpclb_server* rhs); + +/** Destroy \a serverlist */ +void xds_grpclb_destroy_serverlist(xds_grpclb_serverlist* serverlist); + +/** Compare \a lhs against \a rhs and return 0 if \a lhs and \a rhs are equal, + * < 0 if \a lhs represents a duration shorter than \a rhs and > 0 otherwise */ +int xds_grpclb_duration_compare(const xds_grpclb_duration* lhs, + const xds_grpclb_duration* rhs); + +grpc_millis xds_grpclb_duration_to_millis(xds_grpclb_duration* duration_pb); + +/** Destroy \a initial_response */ +void xds_grpclb_initial_response_destroy(xds_grpclb_initial_response* response); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_XDS_XDS_LOAD_BALANCER_API_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_factory.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_factory.h new file mode 100644 index 00000000000000..a165ebafaba836 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_factory.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H + +#include + +#include "src/core/ext/filters/client_channel/lb_policy.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/orphanable.h" + +namespace grpc_core { + +class LoadBalancingPolicyFactory { + public: + /// Returns a new LB policy instance. + virtual OrphanablePtr CreateLoadBalancingPolicy( + const LoadBalancingPolicy::Args& args) const GRPC_ABSTRACT; + + /// Returns the LB policy name that this factory provides. + /// Caller does NOT take ownership of result. + virtual const char* name() const GRPC_ABSTRACT; + + virtual ~LoadBalancingPolicyFactory() {} + + GRPC_ABSTRACT_BASE_CLASS +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.cc b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.cc new file mode 100644 index 00000000000000..ad459c9c8cf8f1 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.cc @@ -0,0 +1,102 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" + +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/inlined_vector.h" + +namespace grpc_core { + +namespace { + +class RegistryState { + public: + RegistryState() {} + + void RegisterLoadBalancingPolicyFactory( + UniquePtr factory) { + for (size_t i = 0; i < factories_.size(); ++i) { + GPR_ASSERT(strcmp(factories_[i]->name(), factory->name()) != 0); + } + factories_.push_back(std::move(factory)); + } + + LoadBalancingPolicyFactory* GetLoadBalancingPolicyFactory( + const char* name) const { + for (size_t i = 0; i < factories_.size(); ++i) { + if (strcmp(name, factories_[i]->name()) == 0) { + return factories_[i].get(); + } + } + return nullptr; + } + + private: + InlinedVector, 10> factories_; +}; + +RegistryState* g_state = nullptr; + +} // namespace + +// +// LoadBalancingPolicyRegistry::Builder +// + +void LoadBalancingPolicyRegistry::Builder::InitRegistry() { + if (g_state == nullptr) g_state = New(); +} + +void LoadBalancingPolicyRegistry::Builder::ShutdownRegistry() { + Delete(g_state); + g_state = nullptr; +} + +void LoadBalancingPolicyRegistry::Builder::RegisterLoadBalancingPolicyFactory( + UniquePtr factory) { + InitRegistry(); + g_state->RegisterLoadBalancingPolicyFactory(std::move(factory)); +} + +// +// LoadBalancingPolicyRegistry +// + +OrphanablePtr +LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy( + const char* name, const LoadBalancingPolicy::Args& args) { + GPR_ASSERT(g_state != nullptr); + // Find factory. + LoadBalancingPolicyFactory* factory = + g_state->GetLoadBalancingPolicyFactory(name); + if (factory == nullptr) return nullptr; // Specified name not found. + // Create policy via factory. + return factory->CreateLoadBalancingPolicy(args); +} + +bool LoadBalancingPolicyRegistry::LoadBalancingPolicyExists(const char* name) { + GPR_ASSERT(g_state != nullptr); + return g_state->GetLoadBalancingPolicyFactory(name) != nullptr; +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.h b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.h new file mode 100644 index 00000000000000..338f7c9f69693c --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/lb_policy_registry.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H + +#include + +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/orphanable.h" + +namespace grpc_core { + +class LoadBalancingPolicyRegistry { + public: + /// Methods used to create and populate the LoadBalancingPolicyRegistry. + /// NOT THREAD SAFE -- to be used only during global gRPC + /// initialization and shutdown. + class Builder { + public: + /// Global initialization and shutdown hooks. + static void InitRegistry(); + static void ShutdownRegistry(); + + /// Registers an LB policy factory. The factory will be used to create an + /// LB policy whose name matches that of the factory. + static void RegisterLoadBalancingPolicyFactory( + UniquePtr factory); + }; + + /// Creates an LB policy of the type specified by \a name. + static OrphanablePtr CreateLoadBalancingPolicy( + const char* name, const LoadBalancingPolicy::Args& args); + + /// Returns true if the LB policy factory specified by \a name exists in this + /// registry. + static bool LoadBalancingPolicyExists(const char* name); +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/parse_address.cc b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.cc new file mode 100644 index 00000000000000..707beb88769a59 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.cc @@ -0,0 +1,215 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" + +#include +#include +#ifdef GRPC_HAVE_UNIX_SOCKET +#include +#endif + +#include +#include +#include + +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" + +#ifdef GRPC_HAVE_UNIX_SOCKET + +bool grpc_parse_unix(const grpc_uri* uri, + grpc_resolved_address* resolved_addr) { + if (strcmp("unix", uri->scheme) != 0) { + gpr_log(GPR_ERROR, "Expected 'unix' scheme, got '%s'", uri->scheme); + return false; + } + struct sockaddr_un* un = + reinterpret_cast(resolved_addr->addr); + const size_t maxlen = sizeof(un->sun_path); + const size_t path_len = strnlen(uri->path, maxlen); + if (path_len == maxlen) return false; + un->sun_family = AF_UNIX; + strcpy(un->sun_path, uri->path); + resolved_addr->len = static_cast(sizeof(*un)); + return true; +} + +#else /* GRPC_HAVE_UNIX_SOCKET */ + +bool grpc_parse_unix(const grpc_uri* uri, + grpc_resolved_address* resolved_addr) { + abort(); +} + +#endif /* GRPC_HAVE_UNIX_SOCKET */ + +bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr, + bool log_errors) { + bool success = false; + // Split host and port. + char* host; + char* port; + if (!gpr_split_host_port(hostport, &host, &port)) return false; + // Parse IP address. + memset(addr, 0, sizeof(*addr)); + addr->len = static_cast(sizeof(grpc_sockaddr_in)); + grpc_sockaddr_in* in = reinterpret_cast(addr->addr); + in->sin_family = GRPC_AF_INET; + if (grpc_inet_pton(GRPC_AF_INET, host, &in->sin_addr) == 0) { + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host); + goto done; + } + // Parse port. + if (port == nullptr) { + if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv4 scheme"); + goto done; + } + int port_num; + if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port); + goto done; + } + in->sin_port = grpc_htons(static_cast(port_num)); + success = true; +done: + gpr_free(host); + gpr_free(port); + return success; +} + +bool grpc_parse_ipv4(const grpc_uri* uri, + grpc_resolved_address* resolved_addr) { + if (strcmp("ipv4", uri->scheme) != 0) { + gpr_log(GPR_ERROR, "Expected 'ipv4' scheme, got '%s'", uri->scheme); + return false; + } + const char* host_port = uri->path; + if (*host_port == '/') ++host_port; + return grpc_parse_ipv4_hostport(host_port, resolved_addr, + true /* log_errors */); +} + +bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr, + bool log_errors) { + bool success = false; + // Split host and port. + char* host; + char* port; + if (!gpr_split_host_port(hostport, &host, &port)) return false; + // Parse IP address. + memset(addr, 0, sizeof(*addr)); + addr->len = static_cast(sizeof(grpc_sockaddr_in6)); + grpc_sockaddr_in6* in6 = reinterpret_cast(addr->addr); + in6->sin6_family = GRPC_AF_INET6; + // Handle the RFC6874 syntax for IPv6 zone identifiers. + char* host_end = static_cast(gpr_memrchr(host, '%', strlen(host))); + if (host_end != nullptr) { + GPR_ASSERT(host_end >= host); + char host_without_scope[GRPC_INET6_ADDRSTRLEN + 1]; + size_t host_without_scope_len = static_cast(host_end - host); + uint32_t sin6_scope_id = 0; + if (host_without_scope_len > GRPC_INET6_ADDRSTRLEN) { + if (log_errors) { + gpr_log( + GPR_ERROR, + "invalid ipv6 address length %zu. Length cannot be greater than " + "GRPC_INET6_ADDRSTRLEN i.e %d)", + host_without_scope_len, GRPC_INET6_ADDRSTRLEN); + } + goto done; + } + strncpy(host_without_scope, host, host_without_scope_len); + host_without_scope[host_without_scope_len] = '\0'; + if (grpc_inet_pton(GRPC_AF_INET6, host_without_scope, &in6->sin6_addr) == + 0) { + if (log_errors) { + gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope); + } + goto done; + } + if (gpr_parse_bytes_to_uint32(host_end + 1, + strlen(host) - host_without_scope_len - 1, + &sin6_scope_id) == 0) { + if (log_errors) { + gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1); + } + goto done; + } + // Handle "sin6_scope_id" being type "u_long". See grpc issue #10027. + in6->sin6_scope_id = sin6_scope_id; + } else { + if (grpc_inet_pton(GRPC_AF_INET6, host, &in6->sin6_addr) == 0) { + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); + goto done; + } + } + // Parse port. + if (port == nullptr) { + if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv6 scheme"); + goto done; + } + int port_num; + if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port); + goto done; + } + in6->sin6_port = grpc_htons(static_cast(port_num)); + success = true; +done: + gpr_free(host); + gpr_free(port); + return success; +} + +bool grpc_parse_ipv6(const grpc_uri* uri, + grpc_resolved_address* resolved_addr) { + if (strcmp("ipv6", uri->scheme) != 0) { + gpr_log(GPR_ERROR, "Expected 'ipv6' scheme, got '%s'", uri->scheme); + return false; + } + const char* host_port = uri->path; + if (*host_port == '/') ++host_port; + return grpc_parse_ipv6_hostport(host_port, resolved_addr, + true /* log_errors */); +} + +bool grpc_parse_uri(const grpc_uri* uri, grpc_resolved_address* resolved_addr) { + if (strcmp("unix", uri->scheme) == 0) { + return grpc_parse_unix(uri, resolved_addr); + } else if (strcmp("ipv4", uri->scheme) == 0) { + return grpc_parse_ipv4(uri, resolved_addr); + } else if (strcmp("ipv6", uri->scheme) == 0) { + return grpc_parse_ipv6(uri, resolved_addr); + } + gpr_log(GPR_ERROR, "Can't parse scheme '%s'", uri->scheme); + return false; +} + +uint16_t grpc_strhtons(const char* port) { + if (strcmp(port, "http") == 0) { + return htons(80); + } else if (strcmp(port, "https") == 0) { + return htons(443); + } + return htons(static_cast(atoi(port))); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/parse_address.h b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.h new file mode 100644 index 00000000000000..5c050a2333db67 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/parse_address.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H + +#include + +#include + +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/uri/uri_parser.h" + +/** Populate \a resolved_addr from \a uri, whose path is expected to contain a + * unix socket path. Returns true upon success. */ +bool grpc_parse_unix(const grpc_uri* uri, grpc_resolved_address* resolved_addr); + +/** Populate \a resolved_addr from \a uri, whose path is expected to contain an + * IPv4 host:port pair. Returns true upon success. */ +bool grpc_parse_ipv4(const grpc_uri* uri, grpc_resolved_address* resolved_addr); + +/** Populate \a resolved_addr from \a uri, whose path is expected to contain an + * IPv6 host:port pair. Returns true upon success. */ +bool grpc_parse_ipv6(const grpc_uri* uri, grpc_resolved_address* resolved_addr); + +/** Populate \a resolved_addr from \a uri. Returns true upon success. */ +bool grpc_parse_uri(const grpc_uri* uri, grpc_resolved_address* resolved_addr); + +/** Parse bare IPv4 or IPv6 "IP:port" strings. */ +bool grpc_parse_ipv4_hostport(const char* hostport, grpc_resolved_address* addr, + bool log_errors); +bool grpc_parse_ipv6_hostport(const char* hostport, grpc_resolved_address* addr, + bool log_errors); + +/* Converts named or numeric port to a uint16 suitable for use in a sockaddr. */ +uint16_t grpc_strhtons(const char* port); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.cc b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.cc new file mode 100644 index 00000000000000..c4da06778d2154 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/proxy_mapper.h" + +void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable, + grpc_proxy_mapper* mapper) { + mapper->vtable = vtable; +} + +bool grpc_proxy_mapper_map_name(grpc_proxy_mapper* mapper, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + return mapper->vtable->map_name(mapper, server_uri, args, name_to_resolve, + new_args); +} + +bool grpc_proxy_mapper_map_address(grpc_proxy_mapper* mapper, + const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args) { + return mapper->vtable->map_address(mapper, address, args, new_address, + new_args); +} + +void grpc_proxy_mapper_destroy(grpc_proxy_mapper* mapper) { + mapper->vtable->destroy(mapper); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.h b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.h new file mode 100644 index 00000000000000..634b0ed7bf5523 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper.h @@ -0,0 +1,74 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H + +#include + +#include + +#include + +#include "src/core/lib/iomgr/resolve_address.h" + +typedef struct grpc_proxy_mapper grpc_proxy_mapper; + +typedef struct { + /// Determines the proxy name to resolve for \a server_uri. + /// If no proxy is needed, returns false. + /// Otherwise, sets \a name_to_resolve, optionally sets \a new_args, + /// and returns true. + bool (*map_name)(grpc_proxy_mapper* mapper, const char* server_uri, + const grpc_channel_args* args, char** name_to_resolve, + grpc_channel_args** new_args); + /// Determines the proxy address to use to contact \a address. + /// If no proxy is needed, returns false. + /// Otherwise, sets \a new_address, optionally sets \a new_args, and + /// returns true. + bool (*map_address)(grpc_proxy_mapper* mapper, + const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args); + /// Destroys \a mapper. + void (*destroy)(grpc_proxy_mapper* mapper); +} grpc_proxy_mapper_vtable; + +struct grpc_proxy_mapper { + const grpc_proxy_mapper_vtable* vtable; +}; + +void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable, + grpc_proxy_mapper* mapper); + +bool grpc_proxy_mapper_map_name(grpc_proxy_mapper* mapper, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args); + +bool grpc_proxy_mapper_map_address(grpc_proxy_mapper* mapper, + const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args); + +void grpc_proxy_mapper_destroy(grpc_proxy_mapper* mapper); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.cc b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.cc new file mode 100644 index 00000000000000..a02a5f5e2cdadc --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" + +#include + +#include + +// +// grpc_proxy_mapper_list +// + +typedef struct { + grpc_proxy_mapper** list; + size_t num_mappers; +} grpc_proxy_mapper_list; + +static void grpc_proxy_mapper_list_register(grpc_proxy_mapper_list* list, + bool at_start, + grpc_proxy_mapper* mapper) { + list->list = static_cast(gpr_realloc( + list->list, (list->num_mappers + 1) * sizeof(grpc_proxy_mapper*))); + if (at_start) { + memmove(list->list + 1, list->list, + sizeof(grpc_proxy_mapper*) * list->num_mappers); + list->list[0] = mapper; + } else { + list->list[list->num_mappers] = mapper; + } + ++list->num_mappers; +} + +static bool grpc_proxy_mapper_list_map_name(grpc_proxy_mapper_list* list, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + for (size_t i = 0; i < list->num_mappers; ++i) { + if (grpc_proxy_mapper_map_name(list->list[i], server_uri, args, + name_to_resolve, new_args)) { + return true; + } + } + return false; +} + +static bool grpc_proxy_mapper_list_map_address( + grpc_proxy_mapper_list* list, const grpc_resolved_address* address, + const grpc_channel_args* args, grpc_resolved_address** new_address, + grpc_channel_args** new_args) { + for (size_t i = 0; i < list->num_mappers; ++i) { + if (grpc_proxy_mapper_map_address(list->list[i], address, args, new_address, + new_args)) { + return true; + } + } + return false; +} + +static void grpc_proxy_mapper_list_destroy(grpc_proxy_mapper_list* list) { + for (size_t i = 0; i < list->num_mappers; ++i) { + grpc_proxy_mapper_destroy(list->list[i]); + } + gpr_free(list->list); + // Clean up in case we re-initialze later. + // TODO(ctiller): This should ideally live in + // grpc_proxy_mapper_registry_init(). However, if we did this there, + // then we would do it AFTER we start registering proxy mappers from + // third-party plugins, so they'd never show up (and would leak memory). + // We probably need some sort of dependency system for plugins to fix + // this. + memset(list, 0, sizeof(*list)); +} + +// +// plugin +// + +static grpc_proxy_mapper_list g_proxy_mapper_list; + +void grpc_proxy_mapper_registry_init() {} + +void grpc_proxy_mapper_registry_shutdown() { + grpc_proxy_mapper_list_destroy(&g_proxy_mapper_list); +} + +void grpc_proxy_mapper_register(bool at_start, grpc_proxy_mapper* mapper) { + grpc_proxy_mapper_list_register(&g_proxy_mapper_list, at_start, mapper); +} + +bool grpc_proxy_mappers_map_name(const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + return grpc_proxy_mapper_list_map_name(&g_proxy_mapper_list, server_uri, args, + name_to_resolve, new_args); +} +bool grpc_proxy_mappers_map_address(const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args) { + return grpc_proxy_mapper_list_map_address(&g_proxy_mapper_list, address, args, + new_address, new_args); +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.h b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.h new file mode 100644 index 00000000000000..326b582b9933b3 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/proxy_mapper_registry.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H + +#include + +#include "src/core/ext/filters/client_channel/proxy_mapper.h" + +void grpc_proxy_mapper_registry_init(); +void grpc_proxy_mapper_registry_shutdown(); + +/// Registers a new proxy mapper. Takes ownership. +/// If \a at_start is true, the new mapper will be at the beginning of +/// the list. Otherwise, it will be added to the end. +void grpc_proxy_mapper_register(bool at_start, grpc_proxy_mapper* mapper); + +bool grpc_proxy_mappers_map_name(const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args); + +bool grpc_proxy_mappers_map_address(const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/request_routing.cc b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.cc new file mode 100644 index 00000000000000..f9a7e164e759b9 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.cc @@ -0,0 +1,936 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/request_routing.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/backup_poller.h" +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/retry_throttle.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/filters/deadline/deadline_filter.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/channel/status_util.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/service_config.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_metadata.h" + +namespace grpc_core { + +// +// RequestRouter::Request::ResolverResultWaiter +// + +// Handles waiting for a resolver result. +// Used only for the first call on an idle channel. +class RequestRouter::Request::ResolverResultWaiter { + public: + explicit ResolverResultWaiter(Request* request) + : request_router_(request->request_router_), + request_(request), + tracer_enabled_(request_router_->tracer_->enabled()) { + if (tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: deferring pick pending resolver " + "result", + request_router_, request); + } + // Add closure to be run when a resolver result is available. + GRPC_CLOSURE_INIT(&done_closure_, &DoneLocked, this, + grpc_combiner_scheduler(request_router_->combiner_)); + AddToWaitingList(); + // Set cancellation closure, so that we abort if the call is cancelled. + GRPC_CLOSURE_INIT(&cancel_closure_, &CancelLocked, this, + grpc_combiner_scheduler(request_router_->combiner_)); + grpc_call_combiner_set_notify_on_cancel(request->call_combiner_, + &cancel_closure_); + } + + private: + // Adds done_closure_ to + // request_router_->waiting_for_resolver_result_closures_. + void AddToWaitingList() { + grpc_closure_list_append( + &request_router_->waiting_for_resolver_result_closures_, &done_closure_, + GRPC_ERROR_NONE); + } + + // Invoked when a resolver result is available. + static void DoneLocked(void* arg, grpc_error* error) { + ResolverResultWaiter* self = static_cast(arg); + RequestRouter* request_router = self->request_router_; + // If CancelLocked() has already run, delete ourselves without doing + // anything. Note that the call stack may have already been destroyed, + // so it's not safe to access anything in state_. + if (GPR_UNLIKELY(self->finished_)) { + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p: call cancelled before resolver result", + request_router); + } + Delete(self); + return; + } + // Otherwise, process the resolver result. + Request* request = self->request_; + if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) { + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: resolver failed to return data", + request_router, request); + } + GRPC_CLOSURE_RUN(request->on_route_done_, GRPC_ERROR_REF(error)); + } else if (GPR_UNLIKELY(request_router->resolver_ == nullptr)) { + // Shutting down. + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, "request_router=%p request=%p: resolver disconnected", + request_router, request); + } + GRPC_CLOSURE_RUN(request->on_route_done_, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected")); + } else if (GPR_UNLIKELY(request_router->lb_policy_ == nullptr)) { + // Transient resolver failure. + // If call has wait_for_ready=true, try again; otherwise, fail. + if (*request->pick_.initial_metadata_flags & + GRPC_INITIAL_METADATA_WAIT_FOR_READY) { + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: resolver returned but no LB " + "policy; wait_for_ready=true; trying again", + request_router, request); + } + // Re-add ourselves to the waiting list. + self->AddToWaitingList(); + // Return early so that we don't set finished_ to true below. + return; + } else { + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: resolver returned but no LB " + "policy; wait_for_ready=false; failing", + request_router, request); + } + GRPC_CLOSURE_RUN( + request->on_route_done_, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Name resolution failure"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } + } else { + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: resolver returned, doing LB " + "pick", + request_router, request); + } + request->ProcessServiceConfigAndStartLbPickLocked(); + } + self->finished_ = true; + } + + // Invoked when the call is cancelled. + // Note: This runs under the client_channel combiner, but will NOT be + // holding the call combiner. + static void CancelLocked(void* arg, grpc_error* error) { + ResolverResultWaiter* self = static_cast(arg); + RequestRouter* request_router = self->request_router_; + // If DoneLocked() has already run, delete ourselves without doing anything. + if (self->finished_) { + Delete(self); + return; + } + Request* request = self->request_; + // If we are being cancelled, immediately invoke on_route_done_ + // to propagate the error back to the caller. + if (error != GRPC_ERROR_NONE) { + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: cancelling call waiting for " + "name resolution", + request_router, request); + } + // Note: Although we are not in the call combiner here, we are + // basically stealing the call combiner from the pending pick, so + // it's safe to run on_route_done_ here -- we are essentially + // calling it here instead of calling it in DoneLocked(). + GRPC_CLOSURE_RUN(request->on_route_done_, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick cancelled", &error, 1)); + } + self->finished_ = true; + } + + RequestRouter* request_router_; + Request* request_; + const bool tracer_enabled_; + grpc_closure done_closure_; + grpc_closure cancel_closure_; + bool finished_ = false; +}; + +// +// RequestRouter::Request::AsyncPickCanceller +// + +// Handles the call combiner cancellation callback for an async LB pick. +class RequestRouter::Request::AsyncPickCanceller { + public: + explicit AsyncPickCanceller(Request* request) + : request_router_(request->request_router_), + request_(request), + tracer_enabled_(request_router_->tracer_->enabled()) { + GRPC_CALL_STACK_REF(request->owning_call_, "pick_callback_cancel"); + // Set cancellation closure, so that we abort if the call is cancelled. + GRPC_CLOSURE_INIT(&cancel_closure_, &CancelLocked, this, + grpc_combiner_scheduler(request_router_->combiner_)); + grpc_call_combiner_set_notify_on_cancel(request->call_combiner_, + &cancel_closure_); + } + + void MarkFinishedLocked() { + finished_ = true; + GRPC_CALL_STACK_UNREF(request_->owning_call_, "pick_callback_cancel"); + } + + private: + // Invoked when the call is cancelled. + // Note: This runs under the client_channel combiner, but will NOT be + // holding the call combiner. + static void CancelLocked(void* arg, grpc_error* error) { + AsyncPickCanceller* self = static_cast(arg); + Request* request = self->request_; + RequestRouter* request_router = self->request_router_; + if (!self->finished_) { + // Note: request_router->lb_policy_ may have changed since we started our + // pick, in which case we will be cancelling the pick on a policy other + // than the one we started it on. However, this will just be a no-op. + if (error != GRPC_ERROR_NONE && request_router->lb_policy_ != nullptr) { + if (self->tracer_enabled_) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: cancelling pick from LB " + "policy %p", + request_router, request, request_router->lb_policy_.get()); + } + request_router->lb_policy_->CancelPickLocked(&request->pick_, + GRPC_ERROR_REF(error)); + } + request->pick_canceller_ = nullptr; + GRPC_CALL_STACK_UNREF(request->owning_call_, "pick_callback_cancel"); + } + Delete(self); + } + + RequestRouter* request_router_; + Request* request_; + const bool tracer_enabled_; + grpc_closure cancel_closure_; + bool finished_ = false; +}; + +// +// RequestRouter::Request +// + +RequestRouter::Request::Request(grpc_call_stack* owning_call, + grpc_call_combiner* call_combiner, + grpc_polling_entity* pollent, + grpc_metadata_batch* send_initial_metadata, + uint32_t* send_initial_metadata_flags, + ApplyServiceConfigCallback apply_service_config, + void* apply_service_config_user_data, + grpc_closure* on_route_done) + : owning_call_(owning_call), + call_combiner_(call_combiner), + pollent_(pollent), + apply_service_config_(apply_service_config), + apply_service_config_user_data_(apply_service_config_user_data), + on_route_done_(on_route_done) { + pick_.initial_metadata = send_initial_metadata; + pick_.initial_metadata_flags = send_initial_metadata_flags; +} + +RequestRouter::Request::~Request() { + if (pick_.connected_subchannel != nullptr) { + pick_.connected_subchannel.reset(); + } + for (size_t i = 0; i < GRPC_CONTEXT_COUNT; ++i) { + if (pick_.subchannel_call_context[i].destroy != nullptr) { + pick_.subchannel_call_context[i].destroy( + pick_.subchannel_call_context[i].value); + } + } +} + +// Invoked once resolver results are available. +void RequestRouter::Request::ProcessServiceConfigAndStartLbPickLocked() { + // Get service config data if needed. + if (!apply_service_config_(apply_service_config_user_data_)) return; + // Start LB pick. + StartLbPickLocked(); +} + +void RequestRouter::Request::MaybeAddCallToInterestedPartiesLocked() { + if (!pollent_added_to_interested_parties_) { + pollent_added_to_interested_parties_ = true; + grpc_polling_entity_add_to_pollset_set( + pollent_, request_router_->interested_parties_); + } +} + +void RequestRouter::Request::MaybeRemoveCallFromInterestedPartiesLocked() { + if (pollent_added_to_interested_parties_) { + pollent_added_to_interested_parties_ = false; + grpc_polling_entity_del_from_pollset_set( + pollent_, request_router_->interested_parties_); + } +} + +// Starts a pick on the LB policy. +void RequestRouter::Request::StartLbPickLocked() { + if (request_router_->tracer_->enabled()) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: starting pick on lb_policy=%p", + request_router_, this, request_router_->lb_policy_.get()); + } + GRPC_CLOSURE_INIT(&on_pick_done_, &LbPickDoneLocked, this, + grpc_combiner_scheduler(request_router_->combiner_)); + pick_.on_complete = &on_pick_done_; + GRPC_CALL_STACK_REF(owning_call_, "pick_callback"); + grpc_error* error = GRPC_ERROR_NONE; + const bool pick_done = + request_router_->lb_policy_->PickLocked(&pick_, &error); + if (pick_done) { + // Pick completed synchronously. + if (request_router_->tracer_->enabled()) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: pick completed synchronously", + request_router_, this); + } + GRPC_CLOSURE_RUN(on_route_done_, error); + GRPC_CALL_STACK_UNREF(owning_call_, "pick_callback"); + } else { + // Pick will be returned asynchronously. + // Add the request's polling entity to the request_router's + // interested_parties, so that the I/O of the LB policy can be done + // under it. It will be removed in LbPickDoneLocked(). + MaybeAddCallToInterestedPartiesLocked(); + // Request notification on call cancellation. + // We allocate a separate object to track cancellation, since the + // cancellation closure might still be pending when we need to reuse + // the memory in which this Request object is stored for a subsequent + // retry attempt. + pick_canceller_ = New(this); + } +} + +// Callback invoked by LoadBalancingPolicy::PickLocked() for async picks. +// Unrefs the LB policy and invokes on_route_done_. +void RequestRouter::Request::LbPickDoneLocked(void* arg, grpc_error* error) { + Request* self = static_cast(arg); + RequestRouter* request_router = self->request_router_; + if (request_router->tracer_->enabled()) { + gpr_log(GPR_INFO, + "request_router=%p request=%p: pick completed asynchronously", + request_router, self); + } + self->MaybeRemoveCallFromInterestedPartiesLocked(); + if (self->pick_canceller_ != nullptr) { + self->pick_canceller_->MarkFinishedLocked(); + } + GRPC_CLOSURE_RUN(self->on_route_done_, GRPC_ERROR_REF(error)); + GRPC_CALL_STACK_UNREF(self->owning_call_, "pick_callback"); +} + +// +// RequestRouter::LbConnectivityWatcher +// + +class RequestRouter::LbConnectivityWatcher { + public: + LbConnectivityWatcher(RequestRouter* request_router, + grpc_connectivity_state state, + LoadBalancingPolicy* lb_policy, + grpc_channel_stack* owning_stack, + grpc_combiner* combiner) + : request_router_(request_router), + state_(state), + lb_policy_(lb_policy), + owning_stack_(owning_stack) { + GRPC_CHANNEL_STACK_REF(owning_stack_, "LbConnectivityWatcher"); + GRPC_CLOSURE_INIT(&on_changed_, &OnLbPolicyStateChangedLocked, this, + grpc_combiner_scheduler(combiner)); + lb_policy_->NotifyOnStateChangeLocked(&state_, &on_changed_); + } + + ~LbConnectivityWatcher() { + GRPC_CHANNEL_STACK_UNREF(owning_stack_, "LbConnectivityWatcher"); + } + + private: + static void OnLbPolicyStateChangedLocked(void* arg, grpc_error* error) { + LbConnectivityWatcher* self = static_cast(arg); + // If the notification is not for the current policy, we're stale, + // so delete ourselves. + if (self->lb_policy_ != self->request_router_->lb_policy_.get()) { + Delete(self); + return; + } + // Otherwise, process notification. + if (self->request_router_->tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: lb_policy=%p state changed to %s", + self->request_router_, self->lb_policy_, + grpc_connectivity_state_name(self->state_)); + } + self->request_router_->SetConnectivityStateLocked( + self->state_, GRPC_ERROR_REF(error), "lb_changed"); + // If shutting down, terminate watch. + if (self->state_ == GRPC_CHANNEL_SHUTDOWN) { + Delete(self); + return; + } + // Renew watch. + self->lb_policy_->NotifyOnStateChangeLocked(&self->state_, + &self->on_changed_); + } + + RequestRouter* request_router_; + grpc_connectivity_state state_; + // LB policy address. No ref held, so not safe to dereference unless + // it happens to match request_router->lb_policy_. + LoadBalancingPolicy* lb_policy_; + grpc_channel_stack* owning_stack_; + grpc_closure on_changed_; +}; + +// +// RequestRounter::ReresolutionRequestHandler +// + +class RequestRouter::ReresolutionRequestHandler { + public: + ReresolutionRequestHandler(RequestRouter* request_router, + LoadBalancingPolicy* lb_policy, + grpc_channel_stack* owning_stack, + grpc_combiner* combiner) + : request_router_(request_router), + lb_policy_(lb_policy), + owning_stack_(owning_stack) { + GRPC_CHANNEL_STACK_REF(owning_stack_, "ReresolutionRequestHandler"); + GRPC_CLOSURE_INIT(&closure_, &OnRequestReresolutionLocked, this, + grpc_combiner_scheduler(combiner)); + lb_policy_->SetReresolutionClosureLocked(&closure_); + } + + private: + static void OnRequestReresolutionLocked(void* arg, grpc_error* error) { + ReresolutionRequestHandler* self = + static_cast(arg); + RequestRouter* request_router = self->request_router_; + // If this invocation is for a stale LB policy, treat it as an LB shutdown + // signal. + if (self->lb_policy_ != request_router->lb_policy_.get() || + error != GRPC_ERROR_NONE || request_router->resolver_ == nullptr) { + GRPC_CHANNEL_STACK_UNREF(request_router->owning_stack_, + "ReresolutionRequestHandler"); + Delete(self); + return; + } + if (request_router->tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: started name re-resolving", + request_router); + } + request_router->resolver_->RequestReresolutionLocked(); + // Give back the closure to the LB policy. + self->lb_policy_->SetReresolutionClosureLocked(&self->closure_); + } + + RequestRouter* request_router_; + // LB policy address. No ref held, so not safe to dereference unless + // it happens to match request_router->lb_policy_. + LoadBalancingPolicy* lb_policy_; + grpc_channel_stack* owning_stack_; + grpc_closure closure_; +}; + +// +// RequestRouter +// + +RequestRouter::RequestRouter( + grpc_channel_stack* owning_stack, grpc_combiner* combiner, + grpc_client_channel_factory* client_channel_factory, + grpc_pollset_set* interested_parties, TraceFlag* tracer, + ProcessResolverResultCallback process_resolver_result, + void* process_resolver_result_user_data, const char* target_uri, + const grpc_channel_args* args, grpc_error** error) + : owning_stack_(owning_stack), + combiner_(combiner), + client_channel_factory_(client_channel_factory), + interested_parties_(interested_parties), + tracer_(tracer), + process_resolver_result_(process_resolver_result), + process_resolver_result_user_data_(process_resolver_result_user_data) { + GRPC_CLOSURE_INIT(&on_resolver_result_changed_, + &RequestRouter::OnResolverResultChangedLocked, this, + grpc_combiner_scheduler(combiner)); + grpc_connectivity_state_init(&state_tracker_, GRPC_CHANNEL_IDLE, + "request_router"); + grpc_channel_args* new_args = nullptr; + if (process_resolver_result == nullptr) { + grpc_arg arg = grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION), 0); + new_args = grpc_channel_args_copy_and_add(args, &arg, 1); + } + resolver_ = ResolverRegistry::CreateResolver( + target_uri, (new_args == nullptr ? args : new_args), interested_parties_, + combiner_); + grpc_channel_args_destroy(new_args); + if (resolver_ == nullptr) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("resolver creation failed"); + } +} + +RequestRouter::~RequestRouter() { + if (resolver_ != nullptr) { + // The only way we can get here is if we never started resolving, + // because we take a ref to the channel stack when we start + // resolving and do not release it until the resolver callback is + // invoked after the resolver shuts down. + resolver_.reset(); + } + if (lb_policy_ != nullptr) { + grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(), + interested_parties_); + lb_policy_.reset(); + } + if (client_channel_factory_ != nullptr) { + grpc_client_channel_factory_unref(client_channel_factory_); + } + grpc_connectivity_state_destroy(&state_tracker_); +} + +namespace { + +const char* GetChannelConnectivityStateChangeString( + grpc_connectivity_state state) { + switch (state) { + case GRPC_CHANNEL_IDLE: + return "Channel state change to IDLE"; + case GRPC_CHANNEL_CONNECTING: + return "Channel state change to CONNECTING"; + case GRPC_CHANNEL_READY: + return "Channel state change to READY"; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + return "Channel state change to TRANSIENT_FAILURE"; + case GRPC_CHANNEL_SHUTDOWN: + return "Channel state change to SHUTDOWN"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +} // namespace + +void RequestRouter::SetConnectivityStateLocked(grpc_connectivity_state state, + grpc_error* error, + const char* reason) { + if (lb_policy_ != nullptr) { + if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + // Cancel picks with wait_for_ready=false. + lb_policy_->CancelMatchingPicksLocked( + /* mask= */ GRPC_INITIAL_METADATA_WAIT_FOR_READY, + /* check= */ 0, GRPC_ERROR_REF(error)); + } else if (state == GRPC_CHANNEL_SHUTDOWN) { + // Cancel all picks. + lb_policy_->CancelMatchingPicksLocked(/* mask= */ 0, /* check= */ 0, + GRPC_ERROR_REF(error)); + } + } + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: setting connectivity state to %s", + this, grpc_connectivity_state_name(state)); + } + if (channelz_node_ != nullptr) { + channelz_node_->AddTraceEvent( + channelz::ChannelTrace::Severity::Info, + grpc_slice_from_static_string( + GetChannelConnectivityStateChangeString(state))); + } + grpc_connectivity_state_set(&state_tracker_, state, error, reason); +} + +void RequestRouter::StartResolvingLocked() { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: starting name resolution", this); + } + GPR_ASSERT(!started_resolving_); + started_resolving_ = true; + GRPC_CHANNEL_STACK_REF(owning_stack_, "resolver"); + resolver_->NextLocked(&resolver_result_, &on_resolver_result_changed_); +} + +// Invoked from the resolver NextLocked() callback when the resolver +// is shutting down. +void RequestRouter::OnResolverShutdownLocked(grpc_error* error) { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: shutting down", this); + } + if (lb_policy_ != nullptr) { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: shutting down lb_policy=%p", this, + lb_policy_.get()); + } + grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(), + interested_parties_); + lb_policy_.reset(); + } + if (resolver_ != nullptr) { + // This should never happen; it can only be triggered by a resolver + // implementation spotaneously deciding to report shutdown without + // being orphaned. This code is included just to be defensive. + if (tracer_->enabled()) { + gpr_log(GPR_INFO, + "request_router=%p: spontaneous shutdown from resolver %p", this, + resolver_.get()); + } + resolver_.reset(); + SetConnectivityStateLocked(GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Resolver spontaneous shutdown", &error, 1), + "resolver_spontaneous_shutdown"); + } + grpc_closure_list_fail_all(&waiting_for_resolver_result_closures_, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Channel disconnected", &error, 1)); + GRPC_CLOSURE_LIST_SCHED(&waiting_for_resolver_result_closures_); + GRPC_CHANNEL_STACK_UNREF(owning_stack_, "resolver"); + grpc_channel_args_destroy(resolver_result_); + resolver_result_ = nullptr; + GRPC_ERROR_UNREF(error); +} + +// Creates a new LB policy, replacing any previous one. +// If the new policy is created successfully, sets *connectivity_state and +// *connectivity_error to its initial connectivity state; otherwise, +// leaves them unchanged. +void RequestRouter::CreateNewLbPolicyLocked( + const char* lb_policy_name, grpc_json* lb_config, + grpc_connectivity_state* connectivity_state, + grpc_error** connectivity_error, TraceStringVector* trace_strings) { + LoadBalancingPolicy::Args lb_policy_args; + lb_policy_args.combiner = combiner_; + lb_policy_args.client_channel_factory = client_channel_factory_; + lb_policy_args.args = resolver_result_; + lb_policy_args.lb_config = lb_config; + OrphanablePtr new_lb_policy = + LoadBalancingPolicyRegistry::CreateLoadBalancingPolicy(lb_policy_name, + lb_policy_args); + if (GPR_UNLIKELY(new_lb_policy == nullptr)) { + gpr_log(GPR_ERROR, "could not create LB policy \"%s\"", lb_policy_name); + if (channelz_node_ != nullptr) { + char* str; + gpr_asprintf(&str, "Could not create LB policy \'%s\'", lb_policy_name); + trace_strings->push_back(str); + } + } else { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: created new LB policy \"%s\" (%p)", + this, lb_policy_name, new_lb_policy.get()); + } + if (channelz_node_ != nullptr) { + char* str; + gpr_asprintf(&str, "Created new LB policy \'%s\'", lb_policy_name); + trace_strings->push_back(str); + } + // Swap out the LB policy and update the fds in interested_parties_. + if (lb_policy_ != nullptr) { + if (tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: shutting down lb_policy=%p", this, + lb_policy_.get()); + } + grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(), + interested_parties_); + lb_policy_->HandOffPendingPicksLocked(new_lb_policy.get()); + } + lb_policy_ = std::move(new_lb_policy); + grpc_pollset_set_add_pollset_set(lb_policy_->interested_parties(), + interested_parties_); + // Create re-resolution request handler for the new LB policy. It + // will delete itself when no longer needed. + New(this, lb_policy_.get(), owning_stack_, + combiner_); + // Get the new LB policy's initial connectivity state and start a + // connectivity watch. + GRPC_ERROR_UNREF(*connectivity_error); + *connectivity_state = + lb_policy_->CheckConnectivityLocked(connectivity_error); + if (exit_idle_when_lb_policy_arrives_) { + lb_policy_->ExitIdleLocked(); + exit_idle_when_lb_policy_arrives_ = false; + } + // Create new watcher. It will delete itself when done. + New(this, *connectivity_state, lb_policy_.get(), + owning_stack_, combiner_); + } +} + +void RequestRouter::MaybeAddTraceMessagesForAddressChangesLocked( + TraceStringVector* trace_strings) { + const ServerAddressList* addresses = + FindServerAddressListChannelArg(resolver_result_); + const bool resolution_contains_addresses = + addresses != nullptr && addresses->size() > 0; + if (!resolution_contains_addresses && + previous_resolution_contained_addresses_) { + trace_strings->push_back(gpr_strdup("Address list became empty")); + } else if (resolution_contains_addresses && + !previous_resolution_contained_addresses_) { + trace_strings->push_back(gpr_strdup("Address list became non-empty")); + } + previous_resolution_contained_addresses_ = resolution_contains_addresses; +} + +void RequestRouter::ConcatenateAndAddChannelTraceLocked( + TraceStringVector* trace_strings) const { + if (!trace_strings->empty()) { + gpr_strvec v; + gpr_strvec_init(&v); + gpr_strvec_add(&v, gpr_strdup("Resolution event: ")); + bool is_first = 1; + for (size_t i = 0; i < trace_strings->size(); ++i) { + if (!is_first) gpr_strvec_add(&v, gpr_strdup(", ")); + is_first = false; + gpr_strvec_add(&v, (*trace_strings)[i]); + } + char* flat; + size_t flat_len = 0; + flat = gpr_strvec_flatten(&v, &flat_len); + channelz_node_->AddTraceEvent( + grpc_core::channelz::ChannelTrace::Severity::Info, + grpc_slice_new(flat, flat_len, gpr_free)); + gpr_strvec_destroy(&v); + } +} + +// Callback invoked when a resolver result is available. +void RequestRouter::OnResolverResultChangedLocked(void* arg, + grpc_error* error) { + RequestRouter* self = static_cast(arg); + if (self->tracer_->enabled()) { + const char* disposition = + self->resolver_result_ != nullptr + ? "" + : (error == GRPC_ERROR_NONE ? " (transient error)" + : " (resolver shutdown)"); + gpr_log(GPR_INFO, + "request_router=%p: got resolver result: resolver_result=%p " + "error=%s%s", + self, self->resolver_result_, grpc_error_string(error), + disposition); + } + // Handle shutdown. + if (error != GRPC_ERROR_NONE || self->resolver_ == nullptr) { + self->OnResolverShutdownLocked(GRPC_ERROR_REF(error)); + return; + } + // Data used to set the channel's connectivity state. + bool set_connectivity_state = true; + // We only want to trace the address resolution in the follow cases: + // (a) Address resolution resulted in service config change. + // (b) Address resolution that causes number of backends to go from + // zero to non-zero. + // (c) Address resolution that causes number of backends to go from + // non-zero to zero. + // (d) Address resolution that causes a new LB policy to be created. + // + // we track a list of strings to eventually be concatenated and traced. + TraceStringVector trace_strings; + grpc_connectivity_state connectivity_state = GRPC_CHANNEL_TRANSIENT_FAILURE; + grpc_error* connectivity_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("No load balancing policy"); + // resolver_result_ will be null in the case of a transient + // resolution error. In that case, we don't have any new result to + // process, which means that we keep using the previous result (if any). + if (self->resolver_result_ == nullptr) { + if (self->tracer_->enabled()) { + gpr_log(GPR_INFO, "request_router=%p: resolver transient failure", self); + } + // Don't override connectivity state if we already have an LB policy. + if (self->lb_policy_ != nullptr) set_connectivity_state = false; + } else { + // Parse the resolver result. + const char* lb_policy_name = nullptr; + grpc_json* lb_policy_config = nullptr; + const bool service_config_changed = self->process_resolver_result_( + self->process_resolver_result_user_data_, *self->resolver_result_, + &lb_policy_name, &lb_policy_config); + GPR_ASSERT(lb_policy_name != nullptr); + // Check to see if we're already using the right LB policy. + const bool lb_policy_name_changed = + self->lb_policy_ == nullptr || + strcmp(self->lb_policy_->name(), lb_policy_name) != 0; + if (self->lb_policy_ != nullptr && !lb_policy_name_changed) { + // Continue using the same LB policy. Update with new addresses. + if (self->tracer_->enabled()) { + gpr_log(GPR_INFO, + "request_router=%p: updating existing LB policy \"%s\" (%p)", + self, lb_policy_name, self->lb_policy_.get()); + } + self->lb_policy_->UpdateLocked(*self->resolver_result_, lb_policy_config); + // No need to set the channel's connectivity state; the existing + // watch on the LB policy will take care of that. + set_connectivity_state = false; + } else { + // Instantiate new LB policy. + self->CreateNewLbPolicyLocked(lb_policy_name, lb_policy_config, + &connectivity_state, &connectivity_error, + &trace_strings); + } + // Add channel trace event. + if (self->channelz_node_ != nullptr) { + if (service_config_changed) { + // TODO(ncteisen): might be worth somehow including a snippet of the + // config in the trace, at the risk of bloating the trace logs. + trace_strings.push_back(gpr_strdup("Service config changed")); + } + self->MaybeAddTraceMessagesForAddressChangesLocked(&trace_strings); + self->ConcatenateAndAddChannelTraceLocked(&trace_strings); + } + // Clean up. + grpc_channel_args_destroy(self->resolver_result_); + self->resolver_result_ = nullptr; + } + // Set the channel's connectivity state if needed. + if (set_connectivity_state) { + self->SetConnectivityStateLocked(connectivity_state, connectivity_error, + "resolver_result"); + } else { + GRPC_ERROR_UNREF(connectivity_error); + } + // Invoke closures that were waiting for results and renew the watch. + GRPC_CLOSURE_LIST_SCHED(&self->waiting_for_resolver_result_closures_); + self->resolver_->NextLocked(&self->resolver_result_, + &self->on_resolver_result_changed_); +} + +void RequestRouter::RouteCallLocked(Request* request) { + GPR_ASSERT(request->pick_.connected_subchannel == nullptr); + request->request_router_ = this; + if (lb_policy_ != nullptr) { + // We already have resolver results, so process the service config + // and start an LB pick. + request->ProcessServiceConfigAndStartLbPickLocked(); + } else if (resolver_ == nullptr) { + GRPC_CLOSURE_RUN(request->on_route_done_, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected")); + } else { + // We do not yet have an LB policy, so wait for a resolver result. + if (!started_resolving_) { + StartResolvingLocked(); + } + // Create a new waiter, which will delete itself when done. + New(request); + // Add the request's polling entity to the request_router's + // interested_parties, so that the I/O of the resolver can be done + // under it. It will be removed in LbPickDoneLocked(). + request->MaybeAddCallToInterestedPartiesLocked(); + } +} + +void RequestRouter::ShutdownLocked(grpc_error* error) { + if (resolver_ != nullptr) { + SetConnectivityStateLocked(GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(error), + "disconnect"); + resolver_.reset(); + if (!started_resolving_) { + grpc_closure_list_fail_all(&waiting_for_resolver_result_closures_, + GRPC_ERROR_REF(error)); + GRPC_CLOSURE_LIST_SCHED(&waiting_for_resolver_result_closures_); + } + if (lb_policy_ != nullptr) { + grpc_pollset_set_del_pollset_set(lb_policy_->interested_parties(), + interested_parties_); + lb_policy_.reset(); + } + } + GRPC_ERROR_UNREF(error); +} + +grpc_connectivity_state RequestRouter::GetConnectivityState() { + return grpc_connectivity_state_check(&state_tracker_); +} + +void RequestRouter::NotifyOnConnectivityStateChange( + grpc_connectivity_state* state, grpc_closure* closure) { + grpc_connectivity_state_notify_on_state_change(&state_tracker_, state, + closure); +} + +void RequestRouter::ExitIdleLocked() { + if (lb_policy_ != nullptr) { + lb_policy_->ExitIdleLocked(); + } else { + exit_idle_when_lb_policy_arrives_ = true; + if (!started_resolving_ && resolver_ != nullptr) { + StartResolvingLocked(); + } + } +} + +void RequestRouter::ResetConnectionBackoffLocked() { + if (resolver_ != nullptr) { + resolver_->ResetBackoffLocked(); + resolver_->RequestReresolutionLocked(); + } + if (lb_policy_ != nullptr) { + lb_policy_->ResetBackoffLocked(); + } +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/request_routing.h b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.h new file mode 100644 index 00000000000000..0c671229c8e889 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/request_routing.h @@ -0,0 +1,177 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_REQUEST_ROUTING_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_REQUEST_ROUTING_H + +#include + +#include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/ext/filters/client_channel/lb_policy.h" +#include "src/core/ext/filters/client_channel/resolver.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/iomgr/call_combiner.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/metadata_batch.h" + +namespace grpc_core { + +class RequestRouter { + public: + class Request { + public: + // Synchronous callback that applies the service config to a call. + // Returns false if the call should be failed. + typedef bool (*ApplyServiceConfigCallback)(void* user_data); + + Request(grpc_call_stack* owning_call, grpc_call_combiner* call_combiner, + grpc_polling_entity* pollent, + grpc_metadata_batch* send_initial_metadata, + uint32_t* send_initial_metadata_flags, + ApplyServiceConfigCallback apply_service_config, + void* apply_service_config_user_data, grpc_closure* on_route_done); + + ~Request(); + + // TODO(roth): It seems a bit ugly to expose this member in a + // non-const way. Find a better API to avoid this. + LoadBalancingPolicy::PickState* pick() { return &pick_; } + + private: + friend class RequestRouter; + + class ResolverResultWaiter; + class AsyncPickCanceller; + + void ProcessServiceConfigAndStartLbPickLocked(); + void StartLbPickLocked(); + static void LbPickDoneLocked(void* arg, grpc_error* error); + + void MaybeAddCallToInterestedPartiesLocked(); + void MaybeRemoveCallFromInterestedPartiesLocked(); + + // Populated by caller. + grpc_call_stack* owning_call_; + grpc_call_combiner* call_combiner_; + grpc_polling_entity* pollent_; + ApplyServiceConfigCallback apply_service_config_; + void* apply_service_config_user_data_; + grpc_closure* on_route_done_; + LoadBalancingPolicy::PickState pick_; + + // Internal state. + RequestRouter* request_router_ = nullptr; + bool pollent_added_to_interested_parties_ = false; + grpc_closure on_pick_done_; + AsyncPickCanceller* pick_canceller_ = nullptr; + }; + + // Synchronous callback that takes the service config JSON string and + // LB policy name. + // Returns true if the service config has changed since the last result. + typedef bool (*ProcessResolverResultCallback)(void* user_data, + const grpc_channel_args& args, + const char** lb_policy_name, + grpc_json** lb_policy_config); + + RequestRouter(grpc_channel_stack* owning_stack, grpc_combiner* combiner, + grpc_client_channel_factory* client_channel_factory, + grpc_pollset_set* interested_parties, TraceFlag* tracer, + ProcessResolverResultCallback process_resolver_result, + void* process_resolver_result_user_data, const char* target_uri, + const grpc_channel_args* args, grpc_error** error); + + ~RequestRouter(); + + void set_channelz_node(channelz::ClientChannelNode* channelz_node) { + channelz_node_ = channelz_node; + } + + void RouteCallLocked(Request* request); + + // TODO(roth): Add methods to cancel picks. + + void ShutdownLocked(grpc_error* error); + + void ExitIdleLocked(); + void ResetConnectionBackoffLocked(); + + grpc_connectivity_state GetConnectivityState(); + void NotifyOnConnectivityStateChange(grpc_connectivity_state* state, + grpc_closure* closure); + + LoadBalancingPolicy* lb_policy() const { return lb_policy_.get(); } + + private: + using TraceStringVector = grpc_core::InlinedVector; + + class ReresolutionRequestHandler; + class LbConnectivityWatcher; + + void StartResolvingLocked(); + void OnResolverShutdownLocked(grpc_error* error); + void CreateNewLbPolicyLocked(const char* lb_policy_name, grpc_json* lb_config, + grpc_connectivity_state* connectivity_state, + grpc_error** connectivity_error, + TraceStringVector* trace_strings); + void MaybeAddTraceMessagesForAddressChangesLocked( + TraceStringVector* trace_strings); + void ConcatenateAndAddChannelTraceLocked( + TraceStringVector* trace_strings) const; + static void OnResolverResultChangedLocked(void* arg, grpc_error* error); + + void SetConnectivityStateLocked(grpc_connectivity_state state, + grpc_error* error, const char* reason); + + // Passed in from caller at construction time. + grpc_channel_stack* owning_stack_; + grpc_combiner* combiner_; + grpc_client_channel_factory* client_channel_factory_; + grpc_pollset_set* interested_parties_; + TraceFlag* tracer_; + + channelz::ClientChannelNode* channelz_node_ = nullptr; + + // Resolver and associated state. + OrphanablePtr resolver_; + ProcessResolverResultCallback process_resolver_result_; + void* process_resolver_result_user_data_; + bool started_resolving_ = false; + grpc_channel_args* resolver_result_ = nullptr; + bool previous_resolution_contained_addresses_ = false; + grpc_closure_list waiting_for_resolver_result_closures_; + grpc_closure on_resolver_result_changed_; + + // LB policy and associated state. + OrphanablePtr lb_policy_; + bool exit_idle_when_lb_policy_arrives_ = false; + + grpc_connectivity_state_tracker state_tracker_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_REQUEST_ROUTING_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver.cc new file mode 100644 index 00000000000000..601b08be24668d --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver.cc @@ -0,0 +1,35 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/resolver.h" +#include "src/core/lib/iomgr/combiner.h" + +grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount(false, + "resolver_refcount"); + +namespace grpc_core { + +Resolver::Resolver(grpc_combiner* combiner) + : InternallyRefCounted(&grpc_trace_resolver_refcount), + combiner_(GRPC_COMBINER_REF(combiner, "resolver")) {} + +Resolver::~Resolver() { GRPC_COMBINER_UNREF(combiner_, "resolver"); } + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver.h new file mode 100644 index 00000000000000..9da849a10173af --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver.h @@ -0,0 +1,133 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H + +#include + +#include + +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/iomgr.h" + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_resolver_refcount; + +namespace grpc_core { + +/// Interface for name resolution. +/// +/// This interface is designed to support both push-based and pull-based +/// mechanisms. A push-based mechanism is one where the resolver will +/// subscribe to updates for a given name, and the name service will +/// proactively send new data to the resolver whenever the data associated +/// with the name changes. A pull-based mechanism is one where the resolver +/// needs to query the name service again to get updated information (e.g., +/// DNS). +/// +/// Note: All methods with a "Locked" suffix must be called from the +/// combiner passed to the constructor. +class Resolver : public InternallyRefCounted { + public: + // Not copyable nor movable. + Resolver(const Resolver&) = delete; + Resolver& operator=(const Resolver&) = delete; + + /// Requests a callback when a new result becomes available. + /// When the new result is available, sets \a *result to the new result + /// and schedules \a on_complete for execution. + /// Upon transient failure, sets \a *result to nullptr and schedules + /// \a on_complete with no error. + /// If resolution is fatally broken, sets \a *result to nullptr and + /// schedules \a on_complete with an error. + /// TODO(roth): When we have time, improve the way this API represents + /// transient failure vs. shutdown. + /// + /// Note that the client channel will almost always have a request + /// to \a NextLocked() pending. When it gets the callback, it will + /// process the new result and then immediately make another call to + /// \a NextLocked(). This allows push-based resolvers to provide new + /// data as soon as it becomes available. + virtual void NextLocked(grpc_channel_args** result, + grpc_closure* on_complete) GRPC_ABSTRACT; + + /// Asks the resolver to obtain an updated resolver result, if + /// applicable. + /// + /// This is useful for pull-based implementations to decide when to + /// re-resolve. However, the implementation is not required to + /// re-resolve immediately upon receiving this call; it may instead + /// elect to delay based on some configured minimum time between + /// queries, to avoid hammering the name service with queries. + /// + /// For push-based implementations, this may be a no-op. + /// + /// If this causes new data to become available, then the currently + /// pending call to \a NextLocked() will return the new result. + virtual void RequestReresolutionLocked() {} + + /// Resets the re-resolution backoff, if any. + /// This needs to be implemented only by pull-based implementations; + /// for push-based implementations, it will be a no-op. + /// TODO(roth): Pull the backoff code out of resolver and into + /// client_channel, so that it can be shared across resolver + /// implementations. At that point, this method can go away. + virtual void ResetBackoffLocked() {} + + void Orphan() override { + // Invoke ShutdownAndUnrefLocked() inside of the combiner. + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(&Resolver::ShutdownAndUnrefLocked, this, + grpc_combiner_scheduler(combiner_)), + GRPC_ERROR_NONE); + } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE + + /// Does NOT take ownership of the reference to \a combiner. + // TODO(roth): Once we have a C++-like interface for combiners, this + // API should change to take a RefCountedPtr<>, so that we always take + // ownership of a new ref. + explicit Resolver(grpc_combiner* combiner); + + virtual ~Resolver(); + + /// Shuts down the resolver. If there is a pending call to + /// NextLocked(), the callback will be scheduled with an error. + virtual void ShutdownLocked() GRPC_ABSTRACT; + + grpc_combiner* combiner() const { return combiner_; } + + private: + static void ShutdownAndUnrefLocked(void* arg, grpc_error* ignored) { + Resolver* resolver = static_cast(arg); + resolver->ShutdownLocked(); + resolver->Unref(); + } + + grpc_combiner* combiner_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/README.md b/third_party/grpc/src/core/ext/filters/client_channel/resolver/README.md new file mode 100644 index 00000000000000..b0e234e96a5614 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/README.md @@ -0,0 +1,4 @@ +# Resolver + +Implementations of various name resolution schemes. +See the [naming spec](/doc/naming.md). diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc new file mode 100644 index 00000000000000..abacd0c960d42c --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc @@ -0,0 +1,516 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#if GRPC_ARES == 1 && !defined(GRPC_UV) + +#include +#include +#include + +#include +#include + +#include + +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/gethostname.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/transport/service_config.h" + +#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_DNS_RECONNECT_JITTER 0.2 + +namespace grpc_core { + +namespace { + +const char kDefaultPort[] = "https"; + +class AresDnsResolver : public Resolver { + public: + explicit AresDnsResolver(const ResolverArgs& args); + + void NextLocked(grpc_channel_args** result, + grpc_closure* on_complete) override; + + void RequestReresolutionLocked() override; + + void ResetBackoffLocked() override; + + void ShutdownLocked() override; + + private: + virtual ~AresDnsResolver(); + + void MaybeStartResolvingLocked(); + void StartResolvingLocked(); + void MaybeFinishNextLocked(); + + static void OnNextResolutionLocked(void* arg, grpc_error* error); + static void OnResolvedLocked(void* arg, grpc_error* error); + + /// DNS server to use (if not system default) + char* dns_server_; + /// name to resolve (usually the same as target_name) + char* name_to_resolve_; + /// channel args + grpc_channel_args* channel_args_; + /// whether to request the service config + bool request_service_config_; + /// pollset_set to drive the name resolution process + grpc_pollset_set* interested_parties_; + /// closures used by the combiner + grpc_closure on_next_resolution_; + grpc_closure on_resolved_; + /// are we currently resolving? + bool resolving_ = false; + /// the pending resolving request + grpc_ares_request* pending_request_ = nullptr; + /// which version of the result have we published? + int published_version_ = 0; + /// which version of the result is current? + int resolved_version_ = 0; + /// pending next completion, or NULL + grpc_closure* next_completion_ = nullptr; + /// target result address for next completion + grpc_channel_args** target_result_ = nullptr; + /// current (fully resolved) result + grpc_channel_args* resolved_result_ = nullptr; + /// next resolution timer + bool have_next_resolution_timer_ = false; + grpc_timer next_resolution_timer_; + /// min interval between DNS requests + grpc_millis min_time_between_resolutions_; + /// timestamp of last DNS request + grpc_millis last_resolution_timestamp_ = -1; + /// retry backoff state + BackOff backoff_; + /// currently resolving addresses + UniquePtr addresses_; + /// currently resolving service config + char* service_config_json_ = nullptr; + // has shutdown been initiated + bool shutdown_initiated_ = false; + // timeout in milliseconds for active DNS queries + int query_timeout_ms_; +}; + +AresDnsResolver::AresDnsResolver(const ResolverArgs& args) + : Resolver(args.combiner), + backoff_( + BackOff::Options() + .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * + 1000) + .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(GRPC_DNS_RECONNECT_JITTER) + .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) { + // Get name to resolve from URI path. + const char* path = args.uri->path; + if (path[0] == '/') ++path; + name_to_resolve_ = gpr_strdup(path); + // Get DNS server from URI authority. + dns_server_ = nullptr; + if (0 != strcmp(args.uri->authority, "")) { + dns_server_ = gpr_strdup(args.uri->authority); + } + channel_args_ = grpc_channel_args_copy(args.args); + const grpc_arg* arg = grpc_channel_args_find( + channel_args_, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION); + grpc_integer_options integer_options = {false, false, true}; + request_service_config_ = !grpc_channel_arg_get_integer(arg, integer_options); + arg = grpc_channel_args_find(channel_args_, + GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS); + min_time_between_resolutions_ = + grpc_channel_arg_get_integer(arg, {1000, 0, INT_MAX}); + interested_parties_ = grpc_pollset_set_create(); + if (args.pollset_set != nullptr) { + grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set); + } + GRPC_CLOSURE_INIT(&on_next_resolution_, OnNextResolutionLocked, this, + grpc_combiner_scheduler(combiner())); + GRPC_CLOSURE_INIT(&on_resolved_, OnResolvedLocked, this, + grpc_combiner_scheduler(combiner())); + const grpc_arg* query_timeout_ms_arg = + grpc_channel_args_find(channel_args_, GRPC_ARG_DNS_ARES_QUERY_TIMEOUT_MS); + query_timeout_ms_ = grpc_channel_arg_get_integer( + query_timeout_ms_arg, + {GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS, 0, INT_MAX}); +} + +AresDnsResolver::~AresDnsResolver() { + GRPC_CARES_TRACE_LOG("resolver:%p destroying AresDnsResolver", this); + if (resolved_result_ != nullptr) { + grpc_channel_args_destroy(resolved_result_); + } + grpc_pollset_set_destroy(interested_parties_); + gpr_free(dns_server_); + gpr_free(name_to_resolve_); + grpc_channel_args_destroy(channel_args_); +} + +void AresDnsResolver::NextLocked(grpc_channel_args** target_result, + grpc_closure* on_complete) { + GRPC_CARES_TRACE_LOG("resolver:%p AresDnsResolver::NextLocked() is called.", + this); + GPR_ASSERT(next_completion_ == nullptr); + next_completion_ = on_complete; + target_result_ = target_result; + if (resolved_version_ == 0 && !resolving_) { + MaybeStartResolvingLocked(); + } else { + MaybeFinishNextLocked(); + } +} + +void AresDnsResolver::RequestReresolutionLocked() { + if (!resolving_) { + MaybeStartResolvingLocked(); + } +} + +void AresDnsResolver::ResetBackoffLocked() { + if (have_next_resolution_timer_) { + grpc_timer_cancel(&next_resolution_timer_); + } + backoff_.Reset(); +} + +void AresDnsResolver::ShutdownLocked() { + shutdown_initiated_ = true; + if (have_next_resolution_timer_) { + grpc_timer_cancel(&next_resolution_timer_); + } + if (pending_request_ != nullptr) { + grpc_cancel_ares_request_locked(pending_request_); + } + if (next_completion_ != nullptr) { + *target_result_ = nullptr; + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Resolver Shutdown")); + next_completion_ = nullptr; + } +} + +void AresDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) { + AresDnsResolver* r = static_cast(arg); + GRPC_CARES_TRACE_LOG( + "resolver:%p re-resolution timer fired. error: %s. shutdown_initiated_: " + "%d", + r, grpc_error_string(error), r->shutdown_initiated_); + r->have_next_resolution_timer_ = false; + if (error == GRPC_ERROR_NONE && !r->shutdown_initiated_) { + if (!r->resolving_) { + GRPC_CARES_TRACE_LOG( + "resolver:%p start resolving due to re-resolution timer", r); + r->StartResolvingLocked(); + } + } + r->Unref(DEBUG_LOCATION, "next_resolution_timer"); +} + +bool ValueInJsonArray(grpc_json* array, const char* value) { + for (grpc_json* entry = array->child; entry != nullptr; entry = entry->next) { + if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) { + return true; + } + } + return false; +} + +char* ChooseServiceConfig(char* service_config_choice_json) { + grpc_json* choices_json = grpc_json_parse_string(service_config_choice_json); + if (choices_json == nullptr || choices_json->type != GRPC_JSON_ARRAY) { + gpr_log(GPR_ERROR, "cannot parse service config JSON string"); + return nullptr; + } + char* service_config = nullptr; + for (grpc_json* choice = choices_json->child; choice != nullptr; + choice = choice->next) { + if (choice->type != GRPC_JSON_OBJECT) { + gpr_log(GPR_ERROR, "cannot parse service config JSON string"); + break; + } + grpc_json* service_config_json = nullptr; + for (grpc_json* field = choice->child; field != nullptr; + field = field->next) { + // Check client language, if specified. + if (strcmp(field->key, "clientLanguage") == 0) { + if (field->type != GRPC_JSON_ARRAY || !ValueInJsonArray(field, "c++")) { + service_config_json = nullptr; + break; + } + } + // Check client hostname, if specified. + if (strcmp(field->key, "clientHostname") == 0) { + char* hostname = grpc_gethostname(); + if (hostname == nullptr || field->type != GRPC_JSON_ARRAY || + !ValueInJsonArray(field, hostname)) { + service_config_json = nullptr; + break; + } + } + // Check percentage, if specified. + if (strcmp(field->key, "percentage") == 0) { + if (field->type != GRPC_JSON_NUMBER) { + service_config_json = nullptr; + break; + } + int random_pct = rand() % 100; + int percentage; + if (sscanf(field->value, "%d", &percentage) != 1 || + random_pct > percentage || percentage == 0) { + service_config_json = nullptr; + break; + } + } + // Save service config. + if (strcmp(field->key, "serviceConfig") == 0) { + if (field->type == GRPC_JSON_OBJECT) { + service_config_json = field; + } + } + } + if (service_config_json != nullptr) { + service_config = grpc_json_dump_to_string(service_config_json, 0); + break; + } + } + grpc_json_destroy(choices_json); + return service_config; +} + +void AresDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { + AresDnsResolver* r = static_cast(arg); + grpc_channel_args* result = nullptr; + GPR_ASSERT(r->resolving_); + r->resolving_ = false; + gpr_free(r->pending_request_); + r->pending_request_ = nullptr; + if (r->addresses_ != nullptr) { + static const char* args_to_remove[1]; + size_t num_args_to_remove = 0; + grpc_arg args_to_add[2]; + size_t num_args_to_add = 0; + args_to_add[num_args_to_add++] = + CreateServerAddressListChannelArg(r->addresses_.get()); + char* service_config_string = nullptr; + if (r->service_config_json_ != nullptr) { + service_config_string = ChooseServiceConfig(r->service_config_json_); + gpr_free(r->service_config_json_); + if (service_config_string != nullptr) { + GRPC_CARES_TRACE_LOG("resolver:%p selected service config choice: %s", + r, service_config_string); + args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG; + args_to_add[num_args_to_add++] = grpc_channel_arg_string_create( + (char*)GRPC_ARG_SERVICE_CONFIG, service_config_string); + } + } + result = grpc_channel_args_copy_and_add_and_remove( + r->channel_args_, args_to_remove, num_args_to_remove, args_to_add, + num_args_to_add); + gpr_free(service_config_string); + r->addresses_.reset(); + // Reset backoff state so that we start from the beginning when the + // next request gets triggered. + r->backoff_.Reset(); + } else if (!r->shutdown_initiated_) { + const char* msg = grpc_error_string(error); + GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed: %s", r, msg); + grpc_millis next_try = r->backoff_.NextAttemptTime(); + grpc_millis timeout = next_try - ExecCtx::Get()->Now(); + GRPC_CARES_TRACE_LOG("resolver:%p dns resolution failed (will retry): %s", + r, grpc_error_string(error)); + GPR_ASSERT(!r->have_next_resolution_timer_); + r->have_next_resolution_timer_ = true; + // TODO(roth): We currently deal with this ref manually. Once the + // new closure API is done, find a way to track this ref with the timer + // callback as part of the type system. + RefCountedPtr self = r->Ref(DEBUG_LOCATION, "retry-timer"); + self.release(); + if (timeout > 0) { + GRPC_CARES_TRACE_LOG("resolver:%p retrying in %" PRId64 " milliseconds", + r, timeout); + } else { + GRPC_CARES_TRACE_LOG("resolver:%p retrying immediately", r); + } + grpc_timer_init(&r->next_resolution_timer_, next_try, + &r->on_next_resolution_); + } + if (r->resolved_result_ != nullptr) { + grpc_channel_args_destroy(r->resolved_result_); + } + r->resolved_result_ = result; + ++r->resolved_version_; + r->MaybeFinishNextLocked(); + r->Unref(DEBUG_LOCATION, "dns-resolving"); +} + +void AresDnsResolver::MaybeStartResolvingLocked() { + // If there is an existing timer, the time it fires is the earliest time we + // can start the next resolution. + if (have_next_resolution_timer_) return; + if (last_resolution_timestamp_ >= 0) { + const grpc_millis earliest_next_resolution = + last_resolution_timestamp_ + min_time_between_resolutions_; + const grpc_millis ms_until_next_resolution = + earliest_next_resolution - grpc_core::ExecCtx::Get()->Now(); + if (ms_until_next_resolution > 0) { + const grpc_millis last_resolution_ago = + grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_; + GRPC_CARES_TRACE_LOG( + "resolver:%p In cooldown from last resolution (from %" PRId64 + " ms ago). Will resolve again in %" PRId64 " ms", + this, last_resolution_ago, ms_until_next_resolution); + have_next_resolution_timer_ = true; + // TODO(roth): We currently deal with this ref manually. Once the + // new closure API is done, find a way to track this ref with the timer + // callback as part of the type system. + RefCountedPtr self = + Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown"); + self.release(); + grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution, + &on_next_resolution_); + return; + } + } + StartResolvingLocked(); +} + +void AresDnsResolver::StartResolvingLocked() { + // TODO(roth): We currently deal with this ref manually. Once the + // new closure API is done, find a way to track this ref with the timer + // callback as part of the type system. + RefCountedPtr self = Ref(DEBUG_LOCATION, "dns-resolving"); + self.release(); + GPR_ASSERT(!resolving_); + resolving_ = true; + service_config_json_ = nullptr; + pending_request_ = grpc_dns_lookup_ares_locked( + dns_server_, name_to_resolve_, kDefaultPort, interested_parties_, + &on_resolved_, &addresses_, true /* check_grpclb */, + request_service_config_ ? &service_config_json_ : nullptr, + query_timeout_ms_, combiner()); + last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now(); + GRPC_CARES_TRACE_LOG("resolver:%p Started resolving. pending_request_:%p", + this, pending_request_); +} + +void AresDnsResolver::MaybeFinishNextLocked() { + if (next_completion_ != nullptr && resolved_version_ != published_version_) { + *target_result_ = resolved_result_ == nullptr + ? nullptr + : grpc_channel_args_copy(resolved_result_); + GRPC_CARES_TRACE_LOG("resolver:%p AresDnsResolver::MaybeFinishNextLocked()", + this); + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE); + next_completion_ = nullptr; + published_version_ = resolved_version_; + } +} + +// +// Factory +// + +class AresDnsResolverFactory : public ResolverFactory { + public: + OrphanablePtr CreateResolver( + const ResolverArgs& args) const override { + return OrphanablePtr(New(args)); + } + + const char* scheme() const override { return "dns"; } +}; + +} // namespace + +} // namespace grpc_core + +extern grpc_address_resolver_vtable* grpc_resolve_address_impl; +static grpc_address_resolver_vtable* default_resolver; + +static grpc_error* blocking_resolve_address_ares( + const char* name, const char* default_port, + grpc_resolved_addresses** addresses) { + return default_resolver->blocking_resolve_address(name, default_port, + addresses); +} + +static grpc_address_resolver_vtable ares_resolver = { + grpc_resolve_address_ares, blocking_resolve_address_ares}; + +static bool should_use_ares(const char* resolver_env) { + return resolver_env != nullptr && gpr_stricmp(resolver_env, "ares") == 0; +} + +void grpc_resolver_dns_ares_init() { + char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER"); + /* TODO(zyc): Turn on c-ares based resolver by default after the address + sorter and the CNAME support are added. */ + if (should_use_ares(resolver_env)) { + gpr_log(GPR_DEBUG, "Using ares dns resolver"); + address_sorting_init(); + grpc_error* error = grpc_ares_init(); + if (error != GRPC_ERROR_NONE) { + GRPC_LOG_IF_ERROR("ares_library_init() failed", error); + return; + } + if (default_resolver == nullptr) { + default_resolver = grpc_resolve_address_impl; + } + grpc_set_resolver_impl(&ares_resolver); + grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( + grpc_core::UniquePtr( + grpc_core::New())); + } + gpr_free(resolver_env); +} + +void grpc_resolver_dns_ares_shutdown() { + char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER"); + if (should_use_ares(resolver_env)) { + address_sorting_shutdown(); + grpc_ares_cleanup(); + } + gpr_free(resolver_env); +} + +#else /* GRPC_ARES == 1 && !defined(GRPC_UV) */ + +void grpc_resolver_dns_ares_init(void) {} + +void grpc_resolver_dns_ares_shutdown(void) {} + +#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc new file mode 100644 index 00000000000000..d99c2e3004712d --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.cc @@ -0,0 +1,368 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "src/core/lib/iomgr/port.h" +#if GRPC_ARES == 1 && !defined(GRPC_UV) + +#include +#include + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" + +#include +#include +#include +#include +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/timer.h" + +typedef struct fd_node { + /** the owner of this fd node */ + grpc_ares_ev_driver* ev_driver; + /** a closure wrapping on_readable_locked, which should be + invoked when the grpc_fd in this node becomes readable. */ + grpc_closure read_closure; + /** a closure wrapping on_writable_locked, which should be + invoked when the grpc_fd in this node becomes writable. */ + grpc_closure write_closure; + /** next fd node in the list */ + struct fd_node* next; + + /** wrapped fd that's polled by grpc's poller for the current platform */ + grpc_core::GrpcPolledFd* grpc_polled_fd; + /** if the readable closure has been registered */ + bool readable_registered; + /** if the writable closure has been registered */ + bool writable_registered; + /** if the fd has been shutdown yet from grpc iomgr perspective */ + bool already_shutdown; +} fd_node; + +struct grpc_ares_ev_driver { + /** the ares_channel owned by this event driver */ + ares_channel channel; + /** pollset set for driving the IO events of the channel */ + grpc_pollset_set* pollset_set; + /** refcount of the event driver */ + gpr_refcount refs; + + /** combiner to synchronize c-ares and I/O callbacks on */ + grpc_combiner* combiner; + /** a list of grpc_fd that this event driver is currently using. */ + fd_node* fds; + /** is this event driver currently working? */ + bool working; + /** is this event driver being shut down */ + bool shutting_down; + /** request object that's using this ev driver */ + grpc_ares_request* request; + /** Owned by the ev_driver. Creates new GrpcPolledFd's */ + grpc_core::UniquePtr polled_fd_factory; + /** query timeout in milliseconds */ + int query_timeout_ms; + /** alarm to cancel active queries */ + grpc_timer query_timeout; + /** cancels queries on a timeout */ + grpc_closure on_timeout_locked; +}; + +static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver); + +static grpc_ares_ev_driver* grpc_ares_ev_driver_ref( + grpc_ares_ev_driver* ev_driver) { + GRPC_CARES_TRACE_LOG("request:%p Ref ev_driver %p", ev_driver->request, + ev_driver); + gpr_ref(&ev_driver->refs); + return ev_driver; +} + +static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver* ev_driver) { + GRPC_CARES_TRACE_LOG("request:%p Unref ev_driver %p", ev_driver->request, + ev_driver); + if (gpr_unref(&ev_driver->refs)) { + GRPC_CARES_TRACE_LOG("request:%p destroy ev_driver %p", ev_driver->request, + ev_driver); + GPR_ASSERT(ev_driver->fds == nullptr); + GRPC_COMBINER_UNREF(ev_driver->combiner, "free ares event driver"); + ares_destroy(ev_driver->channel); + grpc_ares_complete_request_locked(ev_driver->request); + grpc_core::Delete(ev_driver); + } +} + +static void fd_node_destroy_locked(fd_node* fdn) { + GRPC_CARES_TRACE_LOG("request:%p delete fd: %s", fdn->ev_driver->request, + fdn->grpc_polled_fd->GetName()); + GPR_ASSERT(!fdn->readable_registered); + GPR_ASSERT(!fdn->writable_registered); + GPR_ASSERT(fdn->already_shutdown); + grpc_core::Delete(fdn->grpc_polled_fd); + gpr_free(fdn); +} + +static void fd_node_shutdown_locked(fd_node* fdn, const char* reason) { + if (!fdn->already_shutdown) { + fdn->already_shutdown = true; + fdn->grpc_polled_fd->ShutdownLocked( + GRPC_ERROR_CREATE_FROM_STATIC_STRING(reason)); + } +} + +static void on_timeout_locked(void* arg, grpc_error* error); + +grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver, + grpc_pollset_set* pollset_set, + int query_timeout_ms, + grpc_combiner* combiner, + grpc_ares_request* request) { + *ev_driver = grpc_core::New(); + ares_options opts; + memset(&opts, 0, sizeof(opts)); + opts.flags |= ARES_FLAG_STAYOPEN; + int status = ares_init_options(&(*ev_driver)->channel, &opts, ARES_OPT_FLAGS); + GRPC_CARES_TRACE_LOG("request:%p grpc_ares_ev_driver_create_locked", request); + if (status != ARES_SUCCESS) { + char* err_msg; + gpr_asprintf(&err_msg, "Failed to init ares channel. C-ares error: %s", + ares_strerror(status)); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); + gpr_free(err_msg); + gpr_free(*ev_driver); + return err; + } + (*ev_driver)->combiner = GRPC_COMBINER_REF(combiner, "ares event driver"); + gpr_ref_init(&(*ev_driver)->refs, 1); + (*ev_driver)->pollset_set = pollset_set; + (*ev_driver)->fds = nullptr; + (*ev_driver)->working = false; + (*ev_driver)->shutting_down = false; + (*ev_driver)->request = request; + (*ev_driver)->polled_fd_factory = + grpc_core::NewGrpcPolledFdFactory((*ev_driver)->combiner); + (*ev_driver) + ->polled_fd_factory->ConfigureAresChannelLocked((*ev_driver)->channel); + GRPC_CLOSURE_INIT(&(*ev_driver)->on_timeout_locked, on_timeout_locked, + *ev_driver, grpc_combiner_scheduler(combiner)); + (*ev_driver)->query_timeout_ms = query_timeout_ms; + return GRPC_ERROR_NONE; +} + +void grpc_ares_ev_driver_on_queries_complete_locked( + grpc_ares_ev_driver* ev_driver) { + // We mark the event driver as being shut down. If the event driver + // is working, grpc_ares_notify_on_event_locked will shut down the + // fds; if it's not working, there are no fds to shut down. + ev_driver->shutting_down = true; + grpc_timer_cancel(&ev_driver->query_timeout); + grpc_ares_ev_driver_unref(ev_driver); +} + +void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver) { + ev_driver->shutting_down = true; + fd_node* fn = ev_driver->fds; + while (fn != nullptr) { + fd_node_shutdown_locked(fn, "grpc_ares_ev_driver_shutdown"); + fn = fn->next; + } +} + +// Search fd in the fd_node list head. This is an O(n) search, the max possible +// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests. +static fd_node* pop_fd_node_locked(fd_node** head, ares_socket_t as) { + fd_node dummy_head; + dummy_head.next = *head; + fd_node* node = &dummy_head; + while (node->next != nullptr) { + if (node->next->grpc_polled_fd->GetWrappedAresSocketLocked() == as) { + fd_node* ret = node->next; + node->next = node->next->next; + *head = dummy_head.next; + return ret; + } + node = node->next; + } + return nullptr; +} + +static void on_timeout_locked(void* arg, grpc_error* error) { + grpc_ares_ev_driver* driver = static_cast(arg); + GRPC_CARES_TRACE_LOG( + "request:%p ev_driver=%p on_timeout_locked. driver->shutting_down=%d. " + "err=%s", + driver->request, driver, driver->shutting_down, grpc_error_string(error)); + if (!driver->shutting_down && error == GRPC_ERROR_NONE) { + grpc_ares_ev_driver_shutdown_locked(driver); + } + grpc_ares_ev_driver_unref(driver); +} + +static void on_readable_locked(void* arg, grpc_error* error) { + fd_node* fdn = static_cast(arg); + grpc_ares_ev_driver* ev_driver = fdn->ev_driver; + const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked(); + fdn->readable_registered = false; + GRPC_CARES_TRACE_LOG("request:%p readable on %s", fdn->ev_driver->request, + fdn->grpc_polled_fd->GetName()); + if (error == GRPC_ERROR_NONE) { + do { + ares_process_fd(ev_driver->channel, as, ARES_SOCKET_BAD); + } while (fdn->grpc_polled_fd->IsFdStillReadableLocked()); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + grpc_ares_notify_on_event_locked(ev_driver); + grpc_ares_ev_driver_unref(ev_driver); +} + +static void on_writable_locked(void* arg, grpc_error* error) { + fd_node* fdn = static_cast(arg); + grpc_ares_ev_driver* ev_driver = fdn->ev_driver; + const ares_socket_t as = fdn->grpc_polled_fd->GetWrappedAresSocketLocked(); + fdn->writable_registered = false; + GRPC_CARES_TRACE_LOG("request:%p writable on %s", ev_driver->request, + fdn->grpc_polled_fd->GetName()); + if (error == GRPC_ERROR_NONE) { + ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, as); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + grpc_ares_notify_on_event_locked(ev_driver); + grpc_ares_ev_driver_unref(ev_driver); +} + +ares_channel* grpc_ares_ev_driver_get_channel_locked( + grpc_ares_ev_driver* ev_driver) { + return &ev_driver->channel; +} + +// Get the file descriptors used by the ev_driver's ares channel, register +// driver_closure with these filedescriptors. +static void grpc_ares_notify_on_event_locked(grpc_ares_ev_driver* ev_driver) { + fd_node* new_list = nullptr; + if (!ev_driver->shutting_down) { + ares_socket_t socks[ARES_GETSOCK_MAXNUM]; + int socks_bitmask = + ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM); + for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) { + if (ARES_GETSOCK_READABLE(socks_bitmask, i) || + ARES_GETSOCK_WRITABLE(socks_bitmask, i)) { + fd_node* fdn = pop_fd_node_locked(&ev_driver->fds, socks[i]); + // Create a new fd_node if sock[i] is not in the fd_node list. + if (fdn == nullptr) { + fdn = static_cast(gpr_malloc(sizeof(fd_node))); + fdn->grpc_polled_fd = + ev_driver->polled_fd_factory->NewGrpcPolledFdLocked( + socks[i], ev_driver->pollset_set, ev_driver->combiner); + GRPC_CARES_TRACE_LOG("request:%p new fd: %s", ev_driver->request, + fdn->grpc_polled_fd->GetName()); + fdn->ev_driver = ev_driver; + fdn->readable_registered = false; + fdn->writable_registered = false; + fdn->already_shutdown = false; + GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_locked, fdn, + grpc_combiner_scheduler(ev_driver->combiner)); + GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable_locked, fdn, + grpc_combiner_scheduler(ev_driver->combiner)); + } + fdn->next = new_list; + new_list = fdn; + // Register read_closure if the socket is readable and read_closure has + // not been registered with this socket. + if (ARES_GETSOCK_READABLE(socks_bitmask, i) && + !fdn->readable_registered) { + grpc_ares_ev_driver_ref(ev_driver); + GRPC_CARES_TRACE_LOG("request:%p notify read on: %s", + ev_driver->request, + fdn->grpc_polled_fd->GetName()); + fdn->grpc_polled_fd->RegisterForOnReadableLocked(&fdn->read_closure); + fdn->readable_registered = true; + } + // Register write_closure if the socket is writable and write_closure + // has not been registered with this socket. + if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) && + !fdn->writable_registered) { + GRPC_CARES_TRACE_LOG("request:%p notify write on: %s", + ev_driver->request, + fdn->grpc_polled_fd->GetName()); + grpc_ares_ev_driver_ref(ev_driver); + fdn->grpc_polled_fd->RegisterForOnWriteableLocked( + &fdn->write_closure); + fdn->writable_registered = true; + } + } + } + } + // Any remaining fds in ev_driver->fds were not returned by ares_getsock() and + // are therefore no longer in use, so they can be shut down and removed from + // the list. + while (ev_driver->fds != nullptr) { + fd_node* cur = ev_driver->fds; + ev_driver->fds = ev_driver->fds->next; + fd_node_shutdown_locked(cur, "c-ares fd shutdown"); + if (!cur->readable_registered && !cur->writable_registered) { + fd_node_destroy_locked(cur); + } else { + cur->next = new_list; + new_list = cur; + } + } + ev_driver->fds = new_list; + // If the ev driver has no working fd, all the tasks are done. + if (new_list == nullptr) { + ev_driver->working = false; + GRPC_CARES_TRACE_LOG("request:%p ev driver stop working", + ev_driver->request); + } +} + +void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver) { + if (!ev_driver->working) { + ev_driver->working = true; + grpc_ares_notify_on_event_locked(ev_driver); + grpc_millis timeout = + ev_driver->query_timeout_ms == 0 + ? GRPC_MILLIS_INF_FUTURE + : ev_driver->query_timeout_ms + grpc_core::ExecCtx::Get()->Now(); + GRPC_CARES_TRACE_LOG( + "request:%p ev_driver=%p grpc_ares_ev_driver_start_locked. timeout in " + "%" PRId64 " ms", + ev_driver->request, ev_driver, timeout); + grpc_ares_ev_driver_ref(ev_driver); + grpc_timer_init(&ev_driver->query_timeout, timeout, + &ev_driver->on_timeout_locked); + } +} + +#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h new file mode 100644 index 00000000000000..b8cefd9470ec8e --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H + +#include + +#include +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/iomgr/pollset_set.h" + +typedef struct grpc_ares_ev_driver grpc_ares_ev_driver; + +/* Start \a ev_driver. It will keep working until all IO on its ares_channel is + done, or grpc_ares_ev_driver_destroy() is called. It may notify the callbacks + bound to its ares_channel when necessary. */ +void grpc_ares_ev_driver_start_locked(grpc_ares_ev_driver* ev_driver); + +/* Returns the ares_channel owned by \a ev_driver. To bind a c-ares query to + \a ev_driver, use the ares_channel owned by \a ev_driver as the arg of the + query. */ +ares_channel* grpc_ares_ev_driver_get_channel_locked( + grpc_ares_ev_driver* ev_driver); + +/* Creates a new grpc_ares_ev_driver. Returns GRPC_ERROR_NONE if \a ev_driver is + created successfully. */ +grpc_error* grpc_ares_ev_driver_create_locked(grpc_ares_ev_driver** ev_driver, + grpc_pollset_set* pollset_set, + int query_timeout_ms, + grpc_combiner* combiner, + grpc_ares_request* request); + +/* Called back when all DNS lookups have completed. */ +void grpc_ares_ev_driver_on_queries_complete_locked( + grpc_ares_ev_driver* ev_driver); + +/* Shutdown all the grpc_fds used by \a ev_driver */ +void grpc_ares_ev_driver_shutdown_locked(grpc_ares_ev_driver* ev_driver); + +namespace grpc_core { + +/* A wrapped fd that integrates with the grpc iomgr of the current platform. + * A GrpcPolledFd knows how to create grpc platform-specific iomgr endpoints + * from "ares_socket_t" sockets, and then sign up for readability/writeability + * with that poller, and do shutdown and destruction. */ +class GrpcPolledFd { + public: + virtual ~GrpcPolledFd() {} + /* Called when c-ares library is interested and there's no pending callback */ + virtual void RegisterForOnReadableLocked(grpc_closure* read_closure) + GRPC_ABSTRACT; + /* Called when c-ares library is interested and there's no pending callback */ + virtual void RegisterForOnWriteableLocked(grpc_closure* write_closure) + GRPC_ABSTRACT; + /* Indicates if there is data left even after just being read from */ + virtual bool IsFdStillReadableLocked() GRPC_ABSTRACT; + /* Called once and only once. Must cause cancellation of any pending + * read/write callbacks. */ + virtual void ShutdownLocked(grpc_error* error) GRPC_ABSTRACT; + /* Get the underlying ares_socket_t that this was created from */ + virtual ares_socket_t GetWrappedAresSocketLocked() GRPC_ABSTRACT; + /* A unique name, for logging */ + virtual const char* GetName() GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; + +/* A GrpcPolledFdFactory is 1-to-1 with and owned by the + * ares event driver. It knows how to create GrpcPolledFd's + * for the current platform, and the ares driver uses it for all of + * its fd's. */ +class GrpcPolledFdFactory { + public: + virtual ~GrpcPolledFdFactory() {} + /* Creates a new wrapped fd for the current platform */ + virtual GrpcPolledFd* NewGrpcPolledFdLocked( + ares_socket_t as, grpc_pollset_set* driver_pollset_set, + grpc_combiner* combiner) GRPC_ABSTRACT; + /* Optionally configures the ares channel after creation */ + virtual void ConfigureAresChannelLocked(ares_channel channel) GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; + +UniquePtr NewGrpcPolledFdFactory(grpc_combiner* combiner); + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc new file mode 100644 index 00000000000000..aa58e1aaf50671 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc @@ -0,0 +1,106 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "src/core/lib/iomgr/port.h" +#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" + +#include +#include +#include +#include +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +namespace grpc_core { + +class GrpcPolledFdPosix : public GrpcPolledFd { + public: + GrpcPolledFdPosix(ares_socket_t as, grpc_pollset_set* driver_pollset_set) + : as_(as) { + gpr_asprintf(&name_, "c-ares fd: %d", (int)as); + fd_ = grpc_fd_create((int)as, name_, false); + driver_pollset_set_ = driver_pollset_set; + grpc_pollset_set_add_fd(driver_pollset_set_, fd_); + } + + ~GrpcPolledFdPosix() { + gpr_free(name_); + grpc_pollset_set_del_fd(driver_pollset_set_, fd_); + /* c-ares library will close the fd inside grpc_fd. This fd may be picked up + immediately by another thread, and should not be closed by the following + grpc_fd_orphan. */ + int dummy_release_fd; + grpc_fd_orphan(fd_, nullptr, &dummy_release_fd, "c-ares query finished"); + } + + void RegisterForOnReadableLocked(grpc_closure* read_closure) override { + grpc_fd_notify_on_read(fd_, read_closure); + } + + void RegisterForOnWriteableLocked(grpc_closure* write_closure) override { + grpc_fd_notify_on_write(fd_, write_closure); + } + + bool IsFdStillReadableLocked() override { + size_t bytes_available = 0; + return ioctl(grpc_fd_wrapped_fd(fd_), FIONREAD, &bytes_available) == 0 && + bytes_available > 0; + } + + void ShutdownLocked(grpc_error* error) override { + grpc_fd_shutdown(fd_, error); + } + + ares_socket_t GetWrappedAresSocketLocked() override { return as_; } + + const char* GetName() override { return name_; } + + char* name_; + ares_socket_t as_; + grpc_fd* fd_; + grpc_pollset_set* driver_pollset_set_; +}; + +class GrpcPolledFdFactoryPosix : public GrpcPolledFdFactory { + public: + GrpcPolledFd* NewGrpcPolledFdLocked(ares_socket_t as, + grpc_pollset_set* driver_pollset_set, + grpc_combiner* combiner) override { + return New(as, driver_pollset_set); + } + + void ConfigureAresChannelLocked(ares_channel channel) override {} +}; + +UniquePtr NewGrpcPolledFdFactory(grpc_combiner* combiner) { + return UniquePtr(New()); +} + +} // namespace grpc_core + +#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc new file mode 100644 index 00000000000000..02121aa0ab4755 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_windows.cc @@ -0,0 +1,537 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "src/core/lib/iomgr/port.h" +#if GRPC_ARES == 1 && defined(GPR_WINDOWS) + +#include + +#include +#include +#include +#include +#include +#include +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_windows.h" +#include "src/core/lib/slice/slice_internal.h" + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" + +/* TODO(apolcyn): remove this hack after fixing upstream. + * Our grpc/c-ares code on Windows uses the ares_set_socket_functions API, + * which uses "struct iovec" type, which on Windows is defined inside of + * a c-ares header that is not public. + * See https://github.com/c-ares/c-ares/issues/206. */ +struct iovec { + void* iov_base; + size_t iov_len; +}; + +namespace grpc_core { + +/* c-ares creates its own sockets and is meant to read them when readable and + * write them when writeable. To fit this socket usage model into the grpc + * windows poller (which gives notifications when attempted reads and writes are + * actually fulfilled rather than possible), this GrpcPolledFdWindows class + * takes advantage of the ares_set_socket_functions API and acts as a virtual + * socket. It holds its own read and write buffers which are written to and read + * from c-ares and are used with the grpc windows poller, and it, e.g., + * manufactures virtual socket error codes when it e.g. needs to tell the c-ares + * library to wait for an async read. */ +class GrpcPolledFdWindows : public GrpcPolledFd { + public: + enum WriteState { + WRITE_IDLE, + WRITE_REQUESTED, + WRITE_PENDING, + WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY, + }; + + GrpcPolledFdWindows(ares_socket_t as, grpc_combiner* combiner) + : read_buf_(grpc_empty_slice()), + write_buf_(grpc_empty_slice()), + write_state_(WRITE_IDLE), + gotten_into_driver_list_(false) { + gpr_asprintf(&name_, "c-ares socket: %" PRIdPTR, as); + winsocket_ = grpc_winsocket_create(as, name_); + combiner_ = GRPC_COMBINER_REF(combiner, name_); + GRPC_CLOSURE_INIT(&outer_read_closure_, + &GrpcPolledFdWindows::OnIocpReadable, this, + grpc_combiner_scheduler(combiner_)); + GRPC_CLOSURE_INIT(&outer_write_closure_, + &GrpcPolledFdWindows::OnIocpWriteable, this, + grpc_combiner_scheduler(combiner_)); + } + + ~GrpcPolledFdWindows() { + GRPC_COMBINER_UNREF(combiner_, name_); + grpc_slice_unref_internal(read_buf_); + grpc_slice_unref_internal(write_buf_); + GPR_ASSERT(read_closure_ == nullptr); + GPR_ASSERT(write_closure_ == nullptr); + grpc_winsocket_destroy(winsocket_); + gpr_free(name_); + } + + void ScheduleAndNullReadClosure(grpc_error* error) { + GRPC_CLOSURE_SCHED(read_closure_, error); + read_closure_ = nullptr; + } + + void ScheduleAndNullWriteClosure(grpc_error* error) { + GRPC_CLOSURE_SCHED(write_closure_, error); + write_closure_ = nullptr; + } + + void RegisterForOnReadableLocked(grpc_closure* read_closure) override { + GPR_ASSERT(read_closure_ == nullptr); + read_closure_ = read_closure; + GPR_ASSERT(GRPC_SLICE_LENGTH(read_buf_) == 0); + grpc_slice_unref_internal(read_buf_); + read_buf_ = GRPC_SLICE_MALLOC(4192); + WSABUF buffer; + buffer.buf = (char*)GRPC_SLICE_START_PTR(read_buf_); + buffer.len = GRPC_SLICE_LENGTH(read_buf_); + memset(&winsocket_->read_info.overlapped, 0, sizeof(OVERLAPPED)); + recv_from_source_addr_len_ = sizeof(recv_from_source_addr_); + DWORD flags = 0; + if (WSARecvFrom(grpc_winsocket_wrapped_socket(winsocket_), &buffer, 1, + nullptr, &flags, (sockaddr*)recv_from_source_addr_, + &recv_from_source_addr_len_, + &winsocket_->read_info.overlapped, nullptr)) { + char* msg = gpr_format_message(WSAGetLastError()); + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + GRPC_CARES_TRACE_LOG( + "RegisterForOnReadableLocked: WSARecvFrom error:|%s|. fd:|%s|", msg, + GetName()); + gpr_free(msg); + if (WSAGetLastError() != WSA_IO_PENDING) { + ScheduleAndNullReadClosure(error); + return; + } + } + grpc_socket_notify_on_read(winsocket_, &outer_read_closure_); + } + + void RegisterForOnWriteableLocked(grpc_closure* write_closure) override { + GRPC_CARES_TRACE_LOG( + "RegisterForOnWriteableLocked. fd:|%s|. Current write state: %d", + GetName(), write_state_); + GPR_ASSERT(write_closure_ == nullptr); + write_closure_ = write_closure; + switch (write_state_) { + case WRITE_IDLE: + ScheduleAndNullWriteClosure(GRPC_ERROR_NONE); + break; + case WRITE_REQUESTED: + write_state_ = WRITE_PENDING; + SendWriteBuf(nullptr, &winsocket_->write_info.overlapped); + grpc_socket_notify_on_write(winsocket_, &outer_write_closure_); + break; + case WRITE_PENDING: + case WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY: + abort(); + } + } + + bool IsFdStillReadableLocked() override { + return GRPC_SLICE_LENGTH(read_buf_) > 0; + } + + void ShutdownLocked(grpc_error* error) override { + grpc_winsocket_shutdown(winsocket_); + } + + ares_socket_t GetWrappedAresSocketLocked() override { + return grpc_winsocket_wrapped_socket(winsocket_); + } + + const char* GetName() override { return name_; } + + ares_ssize_t RecvFrom(void* data, ares_socket_t data_len, int flags, + struct sockaddr* from, ares_socklen_t* from_len) { + GRPC_CARES_TRACE_LOG( + "RecvFrom called on fd:|%s|. Current read buf length:|%d|", GetName(), + GRPC_SLICE_LENGTH(read_buf_)); + if (GRPC_SLICE_LENGTH(read_buf_) == 0) { + WSASetLastError(WSAEWOULDBLOCK); + return -1; + } + ares_ssize_t bytes_read = 0; + for (size_t i = 0; i < GRPC_SLICE_LENGTH(read_buf_) && i < data_len; i++) { + ((char*)data)[i] = GRPC_SLICE_START_PTR(read_buf_)[i]; + bytes_read++; + } + read_buf_ = grpc_slice_sub_no_ref(read_buf_, bytes_read, + GRPC_SLICE_LENGTH(read_buf_)); + /* c-ares overloads this recv_from virtual socket function to receive + * data on both UDP and TCP sockets, and from is nullptr for TCP. */ + if (from != nullptr) { + GPR_ASSERT(*from_len <= recv_from_source_addr_len_); + memcpy(from, &recv_from_source_addr_, recv_from_source_addr_len_); + *from_len = recv_from_source_addr_len_; + } + return bytes_read; + } + + grpc_slice FlattenIovec(const struct iovec* iov, int iov_count) { + int total = 0; + for (int i = 0; i < iov_count; i++) { + total += iov[i].iov_len; + } + grpc_slice out = GRPC_SLICE_MALLOC(total); + size_t cur = 0; + for (int i = 0; i < iov_count; i++) { + for (int k = 0; k < iov[i].iov_len; k++) { + GRPC_SLICE_START_PTR(out)[cur++] = ((char*)iov[i].iov_base)[k]; + } + } + return out; + } + + int SendWriteBuf(LPDWORD bytes_sent_ptr, LPWSAOVERLAPPED overlapped) { + WSABUF buf; + buf.len = GRPC_SLICE_LENGTH(write_buf_); + buf.buf = (char*)GRPC_SLICE_START_PTR(write_buf_); + DWORD flags = 0; + int out = WSASend(grpc_winsocket_wrapped_socket(winsocket_), &buf, 1, + bytes_sent_ptr, flags, overlapped, nullptr); + GRPC_CARES_TRACE_LOG( + "WSASend: name:%s. buf len:%d. bytes sent: %d. overlapped %p. return " + "val: %d", + GetName(), buf.len, *bytes_sent_ptr, overlapped, out); + return out; + } + + ares_ssize_t TrySendWriteBufSyncNonBlocking() { + GPR_ASSERT(write_state_ == WRITE_IDLE); + ares_ssize_t total_sent; + DWORD bytes_sent = 0; + if (SendWriteBuf(&bytes_sent, nullptr) != 0) { + char* msg = gpr_format_message(WSAGetLastError()); + GRPC_CARES_TRACE_LOG( + "TrySendWriteBufSyncNonBlocking: SendWriteBuf error:|%s|. fd:|%s|", + msg, GetName()); + gpr_free(msg); + if (WSAGetLastError() == WSA_IO_PENDING) { + WSASetLastError(WSAEWOULDBLOCK); + write_state_ = WRITE_REQUESTED; + } + } + write_buf_ = grpc_slice_sub_no_ref(write_buf_, bytes_sent, + GRPC_SLICE_LENGTH(write_buf_)); + return bytes_sent; + } + + ares_ssize_t SendV(const struct iovec* iov, int iov_count) { + GRPC_CARES_TRACE_LOG("SendV called on fd:|%s|. Current write state: %d", + GetName(), write_state_); + switch (write_state_) { + case WRITE_IDLE: + GPR_ASSERT(GRPC_SLICE_LENGTH(write_buf_) == 0); + grpc_slice_unref_internal(write_buf_); + write_buf_ = FlattenIovec(iov, iov_count); + return TrySendWriteBufSyncNonBlocking(); + case WRITE_REQUESTED: + case WRITE_PENDING: + WSASetLastError(WSAEWOULDBLOCK); + return -1; + case WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY: + grpc_slice currently_attempted = FlattenIovec(iov, iov_count); + GPR_ASSERT(GRPC_SLICE_LENGTH(currently_attempted) >= + GRPC_SLICE_LENGTH(write_buf_)); + ares_ssize_t total_sent = 0; + for (size_t i = 0; i < GRPC_SLICE_LENGTH(write_buf_); i++) { + GPR_ASSERT(GRPC_SLICE_START_PTR(currently_attempted)[i] == + GRPC_SLICE_START_PTR(write_buf_)[i]); + total_sent++; + } + grpc_slice_unref_internal(write_buf_); + write_buf_ = + grpc_slice_sub_no_ref(currently_attempted, total_sent, + GRPC_SLICE_LENGTH(currently_attempted)); + write_state_ = WRITE_IDLE; + total_sent += TrySendWriteBufSyncNonBlocking(); + return total_sent; + } + abort(); + } + + int Connect(const struct sockaddr* target, ares_socklen_t target_len) { + SOCKET s = grpc_winsocket_wrapped_socket(winsocket_); + GRPC_CARES_TRACE_LOG("Connect: fd:|%s|", GetName()); + int out = + WSAConnect(s, target, target_len, nullptr, nullptr, nullptr, nullptr); + if (out != 0) { + char* msg = gpr_format_message(WSAGetLastError()); + GRPC_CARES_TRACE_LOG("Connect error code:|%d|, msg:|%s|. fd:|%s|", + WSAGetLastError(), msg, GetName()); + gpr_free(msg); + // c-ares expects a posix-style connect API + out = -1; + } + return out; + } + + static void OnIocpReadable(void* arg, grpc_error* error) { + GrpcPolledFdWindows* polled_fd = static_cast(arg); + polled_fd->OnIocpReadableInner(error); + } + + void OnIocpReadableInner(grpc_error* error) { + if (error == GRPC_ERROR_NONE) { + if (winsocket_->read_info.wsa_error != 0) { + /* WSAEMSGSIZE would be due to receiving more data + * than our read buffer's fixed capacity. Assume that + * the connection is TCP and read the leftovers + * in subsequent c-ares reads. */ + if (winsocket_->read_info.wsa_error != WSAEMSGSIZE) { + GRPC_ERROR_UNREF(error); + char* msg = gpr_format_message(winsocket_->read_info.wsa_error); + GRPC_CARES_TRACE_LOG( + "OnIocpReadableInner. winsocket error:|%s|. fd:|%s|", msg, + GetName()); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } + } + } + if (error == GRPC_ERROR_NONE) { + read_buf_ = grpc_slice_sub_no_ref(read_buf_, 0, + winsocket_->read_info.bytes_transfered); + } else { + grpc_slice_unref_internal(read_buf_); + read_buf_ = grpc_empty_slice(); + } + GRPC_CARES_TRACE_LOG( + "OnIocpReadable finishing. read buf length now:|%d|. :fd:|%s|", + GRPC_SLICE_LENGTH(read_buf_), GetName()); + ScheduleAndNullReadClosure(error); + } + + static void OnIocpWriteable(void* arg, grpc_error* error) { + GrpcPolledFdWindows* polled_fd = static_cast(arg); + polled_fd->OnIocpWriteableInner(error); + } + + void OnIocpWriteableInner(grpc_error* error) { + GRPC_CARES_TRACE_LOG("OnIocpWriteableInner. fd:|%s|", GetName()); + if (error == GRPC_ERROR_NONE) { + if (winsocket_->write_info.wsa_error != 0) { + char* msg = gpr_format_message(winsocket_->write_info.wsa_error); + GRPC_CARES_TRACE_LOG( + "OnIocpWriteableInner. winsocket error:|%s|. fd:|%s|", msg, + GetName()); + GRPC_ERROR_UNREF(error); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } + } + GPR_ASSERT(write_state_ == WRITE_PENDING); + if (error == GRPC_ERROR_NONE) { + write_state_ = WRITE_WAITING_FOR_VERIFICATION_UPON_RETRY; + write_buf_ = grpc_slice_sub_no_ref( + write_buf_, 0, winsocket_->write_info.bytes_transfered); + } else { + grpc_slice_unref_internal(write_buf_); + write_buf_ = grpc_empty_slice(); + } + ScheduleAndNullWriteClosure(error); + } + + bool gotten_into_driver_list() const { return gotten_into_driver_list_; } + void set_gotten_into_driver_list() { gotten_into_driver_list_ = true; } + + grpc_combiner* combiner_; + char recv_from_source_addr_[200]; + ares_socklen_t recv_from_source_addr_len_; + grpc_slice read_buf_; + grpc_slice write_buf_; + grpc_closure* read_closure_ = nullptr; + grpc_closure* write_closure_ = nullptr; + grpc_closure outer_read_closure_; + grpc_closure outer_write_closure_; + grpc_winsocket* winsocket_; + WriteState write_state_; + char* name_ = nullptr; + bool gotten_into_driver_list_; +}; + +struct SockToPolledFdEntry { + SockToPolledFdEntry(SOCKET s, GrpcPolledFdWindows* fd) + : socket(s), polled_fd(fd) {} + SOCKET socket; + GrpcPolledFdWindows* polled_fd; + SockToPolledFdEntry* next = nullptr; +}; + +/* A SockToPolledFdMap can make ares_socket_t types (SOCKET's on windows) + * to GrpcPolledFdWindow's, and is used to find the appropriate + * GrpcPolledFdWindows to handle a virtual socket call when c-ares makes that + * socket call on the ares_socket_t type. Instances are owned by and one-to-one + * with a GrpcPolledFdWindows factory and event driver */ +class SockToPolledFdMap { + public: + SockToPolledFdMap(grpc_combiner* combiner) { + combiner_ = GRPC_COMBINER_REF(combiner, "sock to polled fd map"); + } + + ~SockToPolledFdMap() { + GPR_ASSERT(head_ == nullptr); + GRPC_COMBINER_UNREF(combiner_, "sock to polled fd map"); + } + + void AddNewSocket(SOCKET s, GrpcPolledFdWindows* polled_fd) { + SockToPolledFdEntry* new_node = New(s, polled_fd); + new_node->next = head_; + head_ = new_node; + } + + GrpcPolledFdWindows* LookupPolledFd(SOCKET s) { + for (SockToPolledFdEntry* node = head_; node != nullptr; + node = node->next) { + if (node->socket == s) { + GPR_ASSERT(node->polled_fd != nullptr); + return node->polled_fd; + } + } + abort(); + } + + void RemoveEntry(SOCKET s) { + GPR_ASSERT(head_ != nullptr); + SockToPolledFdEntry** prev = &head_; + for (SockToPolledFdEntry* node = head_; node != nullptr; + node = node->next) { + if (node->socket == s) { + *prev = node->next; + Delete(node); + return; + } + prev = &node->next; + } + abort(); + } + + /* These virtual socket functions are called from within the c-ares + * library. These methods generally dispatch those socket calls to the + * appropriate methods. The virtual "socket" and "close" methods are + * special and instead create/add and remove/destroy GrpcPolledFdWindows + * objects. + */ + static ares_socket_t Socket(int af, int type, int protocol, void* user_data) { + SockToPolledFdMap* map = static_cast(user_data); + SOCKET s = WSASocket(af, type, protocol, nullptr, 0, WSA_FLAG_OVERLAPPED); + if (s == INVALID_SOCKET) { + return s; + } + grpc_tcp_set_non_block(s); + GrpcPolledFdWindows* polled_fd = + New(s, map->combiner_); + map->AddNewSocket(s, polled_fd); + return s; + } + + static int Connect(ares_socket_t as, const struct sockaddr* target, + ares_socklen_t target_len, void* user_data) { + SockToPolledFdMap* map = static_cast(user_data); + GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(as); + return polled_fd->Connect(target, target_len); + } + + static ares_ssize_t SendV(ares_socket_t as, const struct iovec* iov, + int iovec_count, void* user_data) { + SockToPolledFdMap* map = static_cast(user_data); + GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(as); + return polled_fd->SendV(iov, iovec_count); + } + + static ares_ssize_t RecvFrom(ares_socket_t as, void* data, size_t data_len, + int flags, struct sockaddr* from, + ares_socklen_t* from_len, void* user_data) { + SockToPolledFdMap* map = static_cast(user_data); + GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(as); + return polled_fd->RecvFrom(data, data_len, flags, from, from_len); + } + + static int CloseSocket(SOCKET s, void* user_data) { + SockToPolledFdMap* map = static_cast(user_data); + GrpcPolledFdWindows* polled_fd = map->LookupPolledFd(s); + map->RemoveEntry(s); + // If a gRPC polled fd has not made it in to the driver's list yet, then + // the driver has not and will never see this socket. + if (!polled_fd->gotten_into_driver_list()) { + polled_fd->ShutdownLocked(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Shut down c-ares fd before without it ever having made it into the " + "driver's list")); + return 0; + } + return 0; + } + + private: + SockToPolledFdEntry* head_ = nullptr; + grpc_combiner* combiner_; +}; + +const struct ares_socket_functions custom_ares_sock_funcs = { + &SockToPolledFdMap::Socket /* socket */, + &SockToPolledFdMap::CloseSocket /* close */, + &SockToPolledFdMap::Connect /* connect */, + &SockToPolledFdMap::RecvFrom /* recvfrom */, + &SockToPolledFdMap::SendV /* sendv */, +}; + +class GrpcPolledFdFactoryWindows : public GrpcPolledFdFactory { + public: + GrpcPolledFdFactoryWindows(grpc_combiner* combiner) + : sock_to_polled_fd_map_(combiner) {} + + GrpcPolledFd* NewGrpcPolledFdLocked(ares_socket_t as, + grpc_pollset_set* driver_pollset_set, + grpc_combiner* combiner) override { + GrpcPolledFdWindows* polled_fd = sock_to_polled_fd_map_.LookupPolledFd(as); + // Set a flag so that the virtual socket "close" method knows it + // doesn't need to call ShutdownLocked, since now the driver will. + polled_fd->set_gotten_into_driver_list(); + return polled_fd; + } + + void ConfigureAresChannelLocked(ares_channel channel) override { + ares_set_socket_functions(channel, &custom_ares_sock_funcs, + &sock_to_polled_fd_map_); + } + + private: + SockToPolledFdMap sock_to_polled_fd_map_; +}; + +UniquePtr NewGrpcPolledFdFactory(grpc_combiner* combiner) { + return UniquePtr( + New(combiner)); +} + +} // namespace grpc_core + +#endif /* GRPC_ARES == 1 && defined(GPR_WINDOWS) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc new file mode 100644 index 00000000000000..1a7e5d0626876d --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc @@ -0,0 +1,702 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#if GRPC_ARES == 1 && !defined(GRPC_UV) + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/nameser.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +using grpc_core::ServerAddress; +using grpc_core::ServerAddressList; + +static gpr_once g_basic_init = GPR_ONCE_INIT; +static gpr_mu g_init_mu; + +grpc_core::TraceFlag grpc_trace_cares_address_sorting(false, + "cares_address_sorting"); + +grpc_core::TraceFlag grpc_trace_cares_resolver(false, "cares_resolver"); + +struct grpc_ares_request { + /** indicates the DNS server to use, if specified */ + struct ares_addr_port_node dns_server_addr; + /** following members are set in grpc_resolve_address_ares_impl */ + /** closure to call when the request completes */ + grpc_closure* on_done; + /** the pointer to receive the resolved addresses */ + grpc_core::UniquePtr* addresses_out; + /** the pointer to receive the service config in JSON */ + char** service_config_json_out; + /** the evernt driver used by this request */ + grpc_ares_ev_driver* ev_driver; + /** number of ongoing queries */ + size_t pending_queries; + + /** is there at least one successful query, set in on_done_cb */ + bool success; + /** the errors explaining the request failure, set in on_done_cb */ + grpc_error* error; +}; + +typedef struct grpc_ares_hostbyname_request { + /** following members are set in create_hostbyname_request_locked + */ + /** the top-level request instance */ + grpc_ares_request* parent_request; + /** host to resolve, parsed from the name to resolve */ + char* host; + /** port to fill in sockaddr_in, parsed from the name to resolve */ + uint16_t port; + /** is it a grpclb address */ + bool is_balancer; +} grpc_ares_hostbyname_request; + +static void do_basic_init(void) { gpr_mu_init(&g_init_mu); } + +static void log_address_sorting_list(const ServerAddressList& addresses, + const char* input_output_str) { + for (size_t i = 0; i < addresses.size(); i++) { + char* addr_str; + if (grpc_sockaddr_to_string(&addr_str, &addresses[i].address(), true)) { + gpr_log(GPR_INFO, "c-ares address sorting: %s[%" PRIuPTR "]=%s", + input_output_str, i, addr_str); + gpr_free(addr_str); + } else { + gpr_log(GPR_INFO, + "c-ares address sorting: %s[%" PRIuPTR "]=", + input_output_str, i); + } + } +} + +void grpc_cares_wrapper_address_sorting_sort(ServerAddressList* addresses) { + if (grpc_trace_cares_address_sorting.enabled()) { + log_address_sorting_list(*addresses, "input"); + } + address_sorting_sortable* sortables = (address_sorting_sortable*)gpr_zalloc( + sizeof(address_sorting_sortable) * addresses->size()); + for (size_t i = 0; i < addresses->size(); ++i) { + sortables[i].user_data = &(*addresses)[i]; + memcpy(&sortables[i].dest_addr.addr, &(*addresses)[i].address().addr, + (*addresses)[i].address().len); + sortables[i].dest_addr.len = (*addresses)[i].address().len; + } + address_sorting_rfc_6724_sort(sortables, addresses->size()); + ServerAddressList sorted; + sorted.reserve(addresses->size()); + for (size_t i = 0; i < addresses->size(); ++i) { + sorted.emplace_back(*static_cast(sortables[i].user_data)); + } + gpr_free(sortables); + *addresses = std::move(sorted); + if (grpc_trace_cares_address_sorting.enabled()) { + log_address_sorting_list(*addresses, "output"); + } +} + +static void grpc_ares_request_ref_locked(grpc_ares_request* r) { + r->pending_queries++; +} + +static void grpc_ares_request_unref_locked(grpc_ares_request* r) { + r->pending_queries--; + if (r->pending_queries == 0u) { + grpc_ares_ev_driver_on_queries_complete_locked(r->ev_driver); + } +} + +void grpc_ares_complete_request_locked(grpc_ares_request* r) { + /* Invoke on_done callback and destroy the + request */ + r->ev_driver = nullptr; + ServerAddressList* addresses = r->addresses_out->get(); + if (addresses != nullptr) { + grpc_cares_wrapper_address_sorting_sort(addresses); + } + GRPC_CLOSURE_SCHED(r->on_done, r->error); +} + +static grpc_ares_hostbyname_request* create_hostbyname_request_locked( + grpc_ares_request* parent_request, char* host, uint16_t port, + bool is_balancer) { + grpc_ares_hostbyname_request* hr = static_cast( + gpr_zalloc(sizeof(grpc_ares_hostbyname_request))); + hr->parent_request = parent_request; + hr->host = gpr_strdup(host); + hr->port = port; + hr->is_balancer = is_balancer; + grpc_ares_request_ref_locked(parent_request); + return hr; +} + +static void destroy_hostbyname_request_locked( + grpc_ares_hostbyname_request* hr) { + grpc_ares_request_unref_locked(hr->parent_request); + gpr_free(hr->host); + gpr_free(hr); +} + +static void on_hostbyname_done_locked(void* arg, int status, int timeouts, + struct hostent* hostent) { + grpc_ares_hostbyname_request* hr = + static_cast(arg); + grpc_ares_request* r = hr->parent_request; + if (status == ARES_SUCCESS) { + GRPC_ERROR_UNREF(r->error); + r->error = GRPC_ERROR_NONE; + r->success = true; + if (*r->addresses_out == nullptr) { + *r->addresses_out = grpc_core::MakeUnique(); + } + ServerAddressList& addresses = **r->addresses_out; + for (size_t i = 0; hostent->h_addr_list[i] != nullptr; ++i) { + grpc_core::InlinedVector args_to_add; + if (hr->is_balancer) { + args_to_add.emplace_back(grpc_channel_arg_integer_create( + const_cast(GRPC_ARG_ADDRESS_IS_BALANCER), 1)); + args_to_add.emplace_back(grpc_channel_arg_string_create( + const_cast(GRPC_ARG_ADDRESS_BALANCER_NAME), hr->host)); + } + grpc_channel_args* args = grpc_channel_args_copy_and_add( + nullptr, args_to_add.data(), args_to_add.size()); + switch (hostent->h_addrtype) { + case AF_INET6: { + size_t addr_len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 addr; + memset(&addr, 0, addr_len); + memcpy(&addr.sin6_addr, hostent->h_addr_list[i], + sizeof(struct in6_addr)); + addr.sin6_family = static_cast(hostent->h_addrtype); + addr.sin6_port = hr->port; + addresses.emplace_back(&addr, addr_len, args); + char output[INET6_ADDRSTRLEN]; + ares_inet_ntop(AF_INET6, &addr.sin6_addr, output, INET6_ADDRSTRLEN); + GRPC_CARES_TRACE_LOG( + "request:%p c-ares resolver gets a AF_INET6 result: \n" + " addr: %s\n port: %d\n sin6_scope_id: %d\n", + r, output, ntohs(hr->port), addr.sin6_scope_id); + break; + } + case AF_INET: { + size_t addr_len = sizeof(struct sockaddr_in); + struct sockaddr_in addr; + memset(&addr, 0, addr_len); + memcpy(&addr.sin_addr, hostent->h_addr_list[i], + sizeof(struct in_addr)); + addr.sin_family = static_cast(hostent->h_addrtype); + addr.sin_port = hr->port; + addresses.emplace_back(&addr, addr_len, args); + char output[INET_ADDRSTRLEN]; + ares_inet_ntop(AF_INET, &addr.sin_addr, output, INET_ADDRSTRLEN); + GRPC_CARES_TRACE_LOG( + "request:%p c-ares resolver gets a AF_INET result: \n" + " addr: %s\n port: %d\n", + r, output, ntohs(hr->port)); + break; + } + } + } + } else if (!r->success) { + char* error_msg; + gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", + ares_strerror(status)); + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); + } + } + destroy_hostbyname_request_locked(hr); +} + +static void on_srv_query_done_locked(void* arg, int status, int timeouts, + unsigned char* abuf, int alen) { + grpc_ares_request* r = static_cast(arg); + GRPC_CARES_TRACE_LOG("request:%p on_query_srv_done_locked", r); + if (status == ARES_SUCCESS) { + GRPC_CARES_TRACE_LOG("request:%p on_query_srv_done_locked ARES_SUCCESS", r); + struct ares_srv_reply* reply; + const int parse_status = ares_parse_srv_reply(abuf, alen, &reply); + if (parse_status == ARES_SUCCESS) { + ares_channel* channel = + grpc_ares_ev_driver_get_channel_locked(r->ev_driver); + for (struct ares_srv_reply* srv_it = reply; srv_it != nullptr; + srv_it = srv_it->next) { + if (grpc_ares_query_ipv6()) { + grpc_ares_hostbyname_request* hr = create_hostbyname_request_locked( + r, srv_it->host, htons(srv_it->port), true /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET6, + on_hostbyname_done_locked, hr); + } + grpc_ares_hostbyname_request* hr = create_hostbyname_request_locked( + r, srv_it->host, htons(srv_it->port), true /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET, + on_hostbyname_done_locked, hr); + grpc_ares_ev_driver_start_locked(r->ev_driver); + } + } + if (reply != nullptr) { + ares_free_data(reply); + } + } else if (!r->success) { + char* error_msg; + gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", + ares_strerror(status)); + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); + } + } + grpc_ares_request_unref_locked(r); +} + +static const char g_service_config_attribute_prefix[] = "grpc_config="; + +static void on_txt_done_locked(void* arg, int status, int timeouts, + unsigned char* buf, int len) { + char* error_msg; + grpc_ares_request* r = static_cast(arg); + GRPC_CARES_TRACE_LOG("request:%p on_txt_done_locked", r); + const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1; + struct ares_txt_ext* result = nullptr; + struct ares_txt_ext* reply = nullptr; + grpc_error* error = GRPC_ERROR_NONE; + if (status != ARES_SUCCESS) goto fail; + status = ares_parse_txt_reply_ext(buf, len, &reply); + if (status != ARES_SUCCESS) goto fail; + // Find service config in TXT record. + for (result = reply; result != nullptr; result = result->next) { + if (result->record_start && + memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) == + 0) { + break; + } + } + // Found a service config record. + if (result != nullptr) { + size_t service_config_len = result->length - prefix_len; + *r->service_config_json_out = + static_cast(gpr_malloc(service_config_len + 1)); + memcpy(*r->service_config_json_out, result->txt + prefix_len, + service_config_len); + for (result = result->next; result != nullptr && !result->record_start; + result = result->next) { + *r->service_config_json_out = static_cast( + gpr_realloc(*r->service_config_json_out, + service_config_len + result->length + 1)); + memcpy(*r->service_config_json_out + service_config_len, result->txt, + result->length); + service_config_len += result->length; + } + (*r->service_config_json_out)[service_config_len] = '\0'; + GRPC_CARES_TRACE_LOG("request:%p found service config: %s", r, + *r->service_config_json_out); + } + // Clean up. + ares_free_data(reply); + goto done; +fail: + gpr_asprintf(&error_msg, "C-ares TXT lookup status is not ARES_SUCCESS: %s", + ares_strerror(status)); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); + } +done: + grpc_ares_request_unref_locked(r); +} + +void grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked( + grpc_ares_request* r, const char* dns_server, const char* name, + const char* default_port, grpc_pollset_set* interested_parties, + bool check_grpclb, int query_timeout_ms, grpc_combiner* combiner) { + grpc_error* error = GRPC_ERROR_NONE; + grpc_ares_hostbyname_request* hr = nullptr; + ares_channel* channel = nullptr; + /* parse name, splitting it into host and port parts */ + char* host; + char* port; + gpr_split_host_port(name, &host, &port); + if (host == nullptr) { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto error_cleanup; + } else if (port == nullptr) { + if (default_port == nullptr) { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto error_cleanup; + } + port = gpr_strdup(default_port); + } + error = grpc_ares_ev_driver_create_locked(&r->ev_driver, interested_parties, + query_timeout_ms, combiner, r); + if (error != GRPC_ERROR_NONE) goto error_cleanup; + channel = grpc_ares_ev_driver_get_channel_locked(r->ev_driver); + // If dns_server is specified, use it. + if (dns_server != nullptr) { + GRPC_CARES_TRACE_LOG("request:%p Using DNS server %s", r, dns_server); + grpc_resolved_address addr; + if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) { + r->dns_server_addr.family = AF_INET; + struct sockaddr_in* in = reinterpret_cast(addr.addr); + memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr, + sizeof(struct in_addr)); + r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); + r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); + } else if (grpc_parse_ipv6_hostport(dns_server, &addr, + false /* log_errors */)) { + r->dns_server_addr.family = AF_INET6; + struct sockaddr_in6* in6 = + reinterpret_cast(addr.addr); + memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr, + sizeof(struct in6_addr)); + r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); + r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); + } else { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("cannot parse authority"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto error_cleanup; + } + int status = ares_set_servers_ports(*channel, &r->dns_server_addr); + if (status != ARES_SUCCESS) { + char* error_msg; + gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", + ares_strerror(status)); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + goto error_cleanup; + } + } + r->pending_queries = 1; + if (grpc_ares_query_ipv6()) { + hr = create_hostbyname_request_locked(r, host, grpc_strhtons(port), + false /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET6, on_hostbyname_done_locked, + hr); + } + hr = create_hostbyname_request_locked(r, host, grpc_strhtons(port), + false /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_locked, + hr); + if (check_grpclb) { + /* Query the SRV record */ + grpc_ares_request_ref_locked(r); + char* service_name; + gpr_asprintf(&service_name, "_grpclb._tcp.%s", host); + ares_query(*channel, service_name, ns_c_in, ns_t_srv, + on_srv_query_done_locked, r); + gpr_free(service_name); + } + if (r->service_config_json_out != nullptr) { + grpc_ares_request_ref_locked(r); + char* config_name; + gpr_asprintf(&config_name, "_grpc_config.%s", host); + ares_search(*channel, config_name, ns_c_in, ns_t_txt, on_txt_done_locked, + r); + gpr_free(config_name); + } + grpc_ares_ev_driver_start_locked(r->ev_driver); + grpc_ares_request_unref_locked(r); + gpr_free(host); + gpr_free(port); + return; + +error_cleanup: + GRPC_CLOSURE_SCHED(r->on_done, error); + gpr_free(host); + gpr_free(port); +} + +static bool inner_resolve_as_ip_literal_locked( + const char* name, const char* default_port, + grpc_core::UniquePtr* addrs, char** host, + char** port, char** hostport) { + gpr_split_host_port(name, host, port); + if (*host == nullptr) { + gpr_log(GPR_ERROR, + "Failed to parse %s to host:port while attempting to resolve as ip " + "literal.", + name); + return false; + } + if (*port == nullptr) { + if (default_port == nullptr) { + gpr_log(GPR_ERROR, + "No port or default port for %s while attempting to resolve as " + "ip literal.", + name); + return false; + } + *port = gpr_strdup(default_port); + } + grpc_resolved_address addr; + GPR_ASSERT(gpr_join_host_port(hostport, *host, atoi(*port))); + if (grpc_parse_ipv4_hostport(*hostport, &addr, false /* log errors */) || + grpc_parse_ipv6_hostport(*hostport, &addr, false /* log errors */)) { + GPR_ASSERT(*addrs == nullptr); + *addrs = grpc_core::MakeUnique(); + (*addrs)->emplace_back(addr.addr, addr.len, nullptr /* args */); + return true; + } + return false; +} + +static bool resolve_as_ip_literal_locked( + const char* name, const char* default_port, + grpc_core::UniquePtr* addrs) { + char* host = nullptr; + char* port = nullptr; + char* hostport = nullptr; + bool out = inner_resolve_as_ip_literal_locked(name, default_port, addrs, + &host, &port, &hostport); + gpr_free(host); + gpr_free(port); + gpr_free(hostport); + return out; +} + +static bool target_matches_localhost_inner(const char* name, char** host, + char** port) { + if (!gpr_split_host_port(name, host, port)) { + gpr_log(GPR_ERROR, "Unable to split host and port for name: %s", name); + return false; + } + if (gpr_stricmp(*host, "localhost") == 0) { + return true; + } else { + return false; + } +} + +static bool target_matches_localhost(const char* name) { + char* host = nullptr; + char* port = nullptr; + bool out = target_matches_localhost_inner(name, &host, &port); + gpr_free(host); + gpr_free(port); + return out; +} + +static grpc_ares_request* grpc_dns_lookup_ares_locked_impl( + const char* dns_server, const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_core::UniquePtr* addrs, + bool check_grpclb, char** service_config_json, int query_timeout_ms, + grpc_combiner* combiner) { + grpc_ares_request* r = + static_cast(gpr_zalloc(sizeof(grpc_ares_request))); + r->ev_driver = nullptr; + r->on_done = on_done; + r->addresses_out = addrs; + r->service_config_json_out = service_config_json; + r->success = false; + r->error = GRPC_ERROR_NONE; + r->pending_queries = 0; + GRPC_CARES_TRACE_LOG( + "request:%p c-ares grpc_dns_lookup_ares_locked_impl name=%s, " + "default_port=%s", + r, name, default_port); + // Early out if the target is an ipv4 or ipv6 literal. + if (resolve_as_ip_literal_locked(name, default_port, addrs)) { + GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE); + return r; + } + // Early out if the target is localhost and we're on Windows. + if (grpc_ares_maybe_resolve_localhost_manually_locked(name, default_port, + addrs)) { + GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_NONE); + return r; + } + // Don't query for SRV and TXT records if the target is "localhost", so + // as to cut down on lookups over the network, especially in tests: + // https://github.com/grpc/proposal/pull/79 + if (target_matches_localhost(name)) { + check_grpclb = false; + r->service_config_json_out = nullptr; + } + // Look up name using c-ares lib. + grpc_dns_lookup_ares_continue_after_check_localhost_and_ip_literals_locked( + r, dns_server, name, default_port, interested_parties, check_grpclb, + query_timeout_ms, combiner); + return r; +} + +grpc_ares_request* (*grpc_dns_lookup_ares_locked)( + const char* dns_server, const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_core::UniquePtr* addrs, + bool check_grpclb, char** service_config_json, int query_timeout_ms, + grpc_combiner* combiner) = grpc_dns_lookup_ares_locked_impl; + +static void grpc_cancel_ares_request_locked_impl(grpc_ares_request* r) { + GPR_ASSERT(r != nullptr); + if (r->ev_driver != nullptr) { + grpc_ares_ev_driver_shutdown_locked(r->ev_driver); + } +} + +void (*grpc_cancel_ares_request_locked)(grpc_ares_request* r) = + grpc_cancel_ares_request_locked_impl; + +grpc_error* grpc_ares_init(void) { + gpr_once_init(&g_basic_init, do_basic_init); + gpr_mu_lock(&g_init_mu); + int status = ares_library_init(ARES_LIB_INIT_ALL); + gpr_mu_unlock(&g_init_mu); + + if (status != ARES_SUCCESS) { + char* error_msg; + gpr_asprintf(&error_msg, "ares_library_init failed: %s", + ares_strerror(status)); + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + return error; + } + return GRPC_ERROR_NONE; +} + +void grpc_ares_cleanup(void) { + gpr_mu_lock(&g_init_mu); + ares_library_cleanup(); + gpr_mu_unlock(&g_init_mu); +} + +/* + * grpc_resolve_address_ares related structs and functions + */ + +typedef struct grpc_resolve_address_ares_request { + /* combiner that queries and related callbacks run under */ + grpc_combiner* combiner; + /** the pointer to receive the resolved addresses */ + grpc_resolved_addresses** addrs_out; + /** currently resolving addresses */ + grpc_core::UniquePtr addresses; + /** closure to call when the resolve_address_ares request completes */ + grpc_closure* on_resolve_address_done; + /** a closure wrapping on_resolve_address_done, which should be invoked when + the grpc_dns_lookup_ares_locked operation is done. */ + grpc_closure on_dns_lookup_done_locked; + /* target name */ + const char* name; + /* default port to use if none is specified */ + const char* default_port; + /* pollset_set to be driven by */ + grpc_pollset_set* interested_parties; + /* underlying ares_request that the query is performed on */ + grpc_ares_request* ares_request = nullptr; +} grpc_resolve_address_ares_request; + +static void on_dns_lookup_done_locked(void* arg, grpc_error* error) { + grpc_resolve_address_ares_request* r = + static_cast(arg); + gpr_free(r->ares_request); + grpc_resolved_addresses** resolved_addresses = r->addrs_out; + if (r->addresses == nullptr || r->addresses->empty()) { + *resolved_addresses = nullptr; + } else { + *resolved_addresses = static_cast( + gpr_zalloc(sizeof(grpc_resolved_addresses))); + (*resolved_addresses)->naddrs = r->addresses->size(); + (*resolved_addresses)->addrs = + static_cast(gpr_zalloc( + sizeof(grpc_resolved_address) * (*resolved_addresses)->naddrs)); + for (size_t i = 0; i < (*resolved_addresses)->naddrs; ++i) { + GPR_ASSERT(!(*r->addresses)[i].IsBalancer()); + memcpy(&(*resolved_addresses)->addrs[i], &(*r->addresses)[i].address(), + sizeof(grpc_resolved_address)); + } + } + GRPC_CLOSURE_SCHED(r->on_resolve_address_done, GRPC_ERROR_REF(error)); + GRPC_COMBINER_UNREF(r->combiner, "on_dns_lookup_done_cb"); + grpc_core::Delete(r); +} + +static void grpc_resolve_address_invoke_dns_lookup_ares_locked( + void* arg, grpc_error* unused_error) { + grpc_resolve_address_ares_request* r = + static_cast(arg); + r->ares_request = grpc_dns_lookup_ares_locked( + nullptr /* dns_server */, r->name, r->default_port, r->interested_parties, + &r->on_dns_lookup_done_locked, &r->addresses, false /* check_grpclb */, + nullptr /* service_config_json */, GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS, + r->combiner); +} + +static void grpc_resolve_address_ares_impl(const char* name, + const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) { + grpc_resolve_address_ares_request* r = + grpc_core::New(); + r->combiner = grpc_combiner_create(); + r->addrs_out = addrs; + r->on_resolve_address_done = on_done; + GRPC_CLOSURE_INIT(&r->on_dns_lookup_done_locked, on_dns_lookup_done_locked, r, + grpc_combiner_scheduler(r->combiner)); + r->name = name; + r->default_port = default_port; + r->interested_parties = interested_parties; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(grpc_resolve_address_invoke_dns_lookup_ares_locked, r, + grpc_combiner_scheduler(r->combiner)), + GRPC_ERROR_NONE); +} + +void (*grpc_resolve_address_ares)( + const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_resolved_addresses** addrs) = grpc_resolve_address_ares_impl; + +#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h new file mode 100644 index 00000000000000..280825045654af --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -0,0 +1,101 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H + +#include + +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/iomgr/resolve_address.h" + +#define GRPC_DNS_ARES_DEFAULT_QUERY_TIMEOUT_MS 10000 + +extern grpc_core::TraceFlag grpc_trace_cares_address_sorting; + +extern grpc_core::TraceFlag grpc_trace_cares_resolver; + +#define GRPC_CARES_TRACE_LOG(format, ...) \ + if (grpc_trace_cares_resolver.enabled()) { \ + gpr_log(GPR_DEBUG, "(c-ares resolver) " format, __VA_ARGS__); \ + } + +typedef struct grpc_ares_request grpc_ares_request; + +/* Asynchronously resolve \a name. Use \a default_port if a port isn't + designated in \a name, otherwise use the port in \a name. grpc_ares_init() + must be called at least once before this function. \a on_done may be + called directly in this function without being scheduled with \a exec_ctx, + so it must not try to acquire locks that are being held by the caller. */ +extern void (*grpc_resolve_address_ares)(const char* name, + const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses); + +/* Asynchronously resolve \a name. It will try to resolve grpclb SRV records in + addition to the normal address records. For normal address records, it uses + \a default_port if a port isn't designated in \a name, otherwise it uses the + port in \a name. grpc_ares_init() must be called at least once before this + function. \a on_done may be called directly in this function without being + scheduled with \a exec_ctx, so it must not try to acquire locks that are + being held by the caller. The returned grpc_ares_request object is owned + by the caller and it is safe to free after on_done is called back. */ +extern grpc_ares_request* (*grpc_dns_lookup_ares_locked)( + const char* dns_server, const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_core::UniquePtr* addresses, + bool check_grpclb, char** service_config_json, int query_timeout_ms, + grpc_combiner* combiner); + +/* Cancel the pending grpc_ares_request \a request */ +extern void (*grpc_cancel_ares_request_locked)(grpc_ares_request* request); + +/* Initialize gRPC ares wrapper. Must be called at least once before + grpc_resolve_address_ares(). */ +grpc_error* grpc_ares_init(void); + +/* Uninitialized gRPC ares wrapper. If there was more than one previous call to + grpc_ares_init(), this function uninitializes the gRPC ares wrapper only if + it has been called the same number of times as grpc_ares_init(). */ +void grpc_ares_cleanup(void); + +/** Schedules the desired callback for request completion + * and destroys the grpc_ares_request */ +void grpc_ares_complete_request_locked(grpc_ares_request* request); + +/* Indicates whether or not AAAA queries should be attempted. */ +/* E.g., return false if ipv6 is known to not be available. */ +bool grpc_ares_query_ipv6(); + +/* Maybe (depending on the current platform) checks if "name" matches + * "localhost" and if so fills in addrs with the correct sockaddr structures. + * Returns a bool indicating whether or not such an action was performed. + * See https://github.com/grpc/grpc/issues/15158. */ +bool grpc_ares_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, + grpc_core::UniquePtr* addrs); + +/* Sorts destinations in lb_addrs according to RFC 6724. */ +void grpc_cares_wrapper_address_sorting_sort( + grpc_core::ServerAddressList* addresses); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc new file mode 100644 index 00000000000000..1f4701c999436e --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc @@ -0,0 +1,65 @@ +/* + * + * Copyright 2016-2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#if GRPC_ARES != 1 || defined(GRPC_UV) + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" + +struct grpc_ares_request { + char val; +}; + +static grpc_ares_request* grpc_dns_lookup_ares_locked_impl( + const char* dns_server, const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_core::UniquePtr* addrs, + bool check_grpclb, char** service_config_json, int query_timeout_ms, + grpc_combiner* combiner) { + return NULL; +} + +grpc_ares_request* (*grpc_dns_lookup_ares_locked)( + const char* dns_server, const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_core::UniquePtr* addrs, + bool check_grpclb, char** service_config_json, int query_timeout_ms, + grpc_combiner* combiner) = grpc_dns_lookup_ares_locked_impl; + +static void grpc_cancel_ares_request_locked_impl(grpc_ares_request* r) {} + +void (*grpc_cancel_ares_request_locked)(grpc_ares_request* r) = + grpc_cancel_ares_request_locked_impl; + +grpc_error* grpc_ares_init(void) { return GRPC_ERROR_NONE; } + +void grpc_ares_cleanup(void) {} + +static void grpc_resolve_address_ares_impl(const char* name, + const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) {} + +void (*grpc_resolve_address_ares)( + const char* name, const char* default_port, + grpc_pollset_set* interested_parties, grpc_closure* on_done, + grpc_resolved_addresses** addrs) = grpc_resolve_address_ares_impl; + +#endif /* GRPC_ARES != 1 || defined(GRPC_UV) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc new file mode 100644 index 00000000000000..028d8442169e5a --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_posix.cc @@ -0,0 +1,35 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" +#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" + +bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); } + +bool grpc_ares_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, + grpc_core::UniquePtr* addrs) { + return false; +} + +#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET_ARES_EV_DRIVER) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc new file mode 100644 index 00000000000000..202452f1b2b715 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_windows.cc @@ -0,0 +1,97 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" +#if GRPC_ARES == 1 && defined(GPR_WINDOWS) + +#include + +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/socket_windows.h" + +bool grpc_ares_query_ipv6() { return grpc_ipv6_loopback_available(); } + +static bool inner_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, + grpc_core::UniquePtr* addrs, char** host, + char** port) { + gpr_split_host_port(name, host, port); + if (*host == nullptr) { + gpr_log(GPR_ERROR, + "Failed to parse %s into host:port during Windows localhost " + "resolution check.", + name); + return false; + } + if (*port == nullptr) { + if (default_port == nullptr) { + gpr_log(GPR_ERROR, + "No port or default port for %s during Windows localhost " + "resolution check.", + name); + return false; + } + *port = gpr_strdup(default_port); + } + if (gpr_stricmp(*host, "localhost") == 0) { + GPR_ASSERT(*addrs == nullptr); + *addrs = grpc_core::MakeUnique(); + uint16_t numeric_port = grpc_strhtons(*port); + // Append the ipv6 loopback address. + struct sockaddr_in6 ipv6_loopback_addr; + memset(&ipv6_loopback_addr, 0, sizeof(ipv6_loopback_addr)); + ((char*)&ipv6_loopback_addr.sin6_addr)[15] = 1; + ipv6_loopback_addr.sin6_family = AF_INET6; + ipv6_loopback_addr.sin6_port = numeric_port; + (*addrs)->emplace_back(&ipv6_loopback_addr, sizeof(ipv6_loopback_addr), + nullptr /* args */); + // Append the ipv4 loopback address. + struct sockaddr_in ipv4_loopback_addr; + memset(&ipv4_loopback_addr, 0, sizeof(ipv4_loopback_addr)); + ((char*)&ipv4_loopback_addr.sin_addr)[0] = 0x7f; + ((char*)&ipv4_loopback_addr.sin_addr)[3] = 0x01; + ipv4_loopback_addr.sin_family = AF_INET; + ipv4_loopback_addr.sin_port = numeric_port; + (*addrs)->emplace_back(&ipv4_loopback_addr, sizeof(ipv4_loopback_addr), + nullptr /* args */); + // Let the address sorter figure out which one should be tried first. + grpc_cares_wrapper_address_sorting_sort(addrs->get()); + return true; + } + return false; +} + +bool grpc_ares_maybe_resolve_localhost_manually_locked( + const char* name, const char* default_port, + grpc_core::UniquePtr* addrs) { + char* host = nullptr; + char* port = nullptr; + bool out = inner_maybe_resolve_localhost_manually_locked(name, default_port, + addrs, &host, &port); + gpr_free(host); + gpr_free(port); + return out; +} + +#endif /* GRPC_ARES == 1 && defined(GPR_WINDOWS) */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/README.md b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/README.md new file mode 100644 index 00000000000000..695de47b9f74f2 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/README.md @@ -0,0 +1,2 @@ +dns: scheme name resolution, using getaddrbyname +(or other OS specific implementation) diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc new file mode 100644 index 00000000000000..c365f1abfd87b6 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc @@ -0,0 +1,343 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/timer.h" + +#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_DNS_RECONNECT_JITTER 0.2 + +namespace grpc_core { + +namespace { + +const char kDefaultPort[] = "https"; + +class NativeDnsResolver : public Resolver { + public: + explicit NativeDnsResolver(const ResolverArgs& args); + + void NextLocked(grpc_channel_args** result, + grpc_closure* on_complete) override; + + void RequestReresolutionLocked() override; + + void ResetBackoffLocked() override; + + void ShutdownLocked() override; + + private: + virtual ~NativeDnsResolver(); + + void MaybeStartResolvingLocked(); + void StartResolvingLocked(); + void MaybeFinishNextLocked(); + + static void OnNextResolutionLocked(void* arg, grpc_error* error); + static void OnResolvedLocked(void* arg, grpc_error* error); + + /// name to resolve + char* name_to_resolve_ = nullptr; + /// channel args + grpc_channel_args* channel_args_ = nullptr; + /// pollset_set to drive the name resolution process + grpc_pollset_set* interested_parties_ = nullptr; + /// are we currently resolving? + bool resolving_ = false; + grpc_closure on_resolved_; + /// which version of the result have we published? + int published_version_ = 0; + /// which version of the result is current? + int resolved_version_ = 0; + /// pending next completion, or nullptr + grpc_closure* next_completion_ = nullptr; + /// target result address for next completion + grpc_channel_args** target_result_ = nullptr; + /// current (fully resolved) result + grpc_channel_args* resolved_result_ = nullptr; + /// next resolution timer + bool have_next_resolution_timer_ = false; + grpc_timer next_resolution_timer_; + grpc_closure on_next_resolution_; + /// min time between DNS requests + grpc_millis min_time_between_resolutions_; + /// timestamp of last DNS request + grpc_millis last_resolution_timestamp_ = -1; + /// retry backoff state + BackOff backoff_; + /// currently resolving addresses + grpc_resolved_addresses* addresses_ = nullptr; +}; + +NativeDnsResolver::NativeDnsResolver(const ResolverArgs& args) + : Resolver(args.combiner), + backoff_( + BackOff::Options() + .set_initial_backoff(GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS * + 1000) + .set_multiplier(GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(GRPC_DNS_RECONNECT_JITTER) + .set_max_backoff(GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000)) { + char* path = args.uri->path; + if (path[0] == '/') ++path; + name_to_resolve_ = gpr_strdup(path); + channel_args_ = grpc_channel_args_copy(args.args); + const grpc_arg* arg = grpc_channel_args_find( + args.args, GRPC_ARG_DNS_MIN_TIME_BETWEEN_RESOLUTIONS_MS); + min_time_between_resolutions_ = + grpc_channel_arg_get_integer(arg, {1000, 0, INT_MAX}); + interested_parties_ = grpc_pollset_set_create(); + if (args.pollset_set != nullptr) { + grpc_pollset_set_add_pollset_set(interested_parties_, args.pollset_set); + } + GRPC_CLOSURE_INIT(&on_next_resolution_, + NativeDnsResolver::OnNextResolutionLocked, this, + grpc_combiner_scheduler(args.combiner)); + GRPC_CLOSURE_INIT(&on_resolved_, NativeDnsResolver::OnResolvedLocked, this, + grpc_combiner_scheduler(args.combiner)); +} + +NativeDnsResolver::~NativeDnsResolver() { + if (resolved_result_ != nullptr) { + grpc_channel_args_destroy(resolved_result_); + } + grpc_pollset_set_destroy(interested_parties_); + gpr_free(name_to_resolve_); + grpc_channel_args_destroy(channel_args_); +} + +void NativeDnsResolver::NextLocked(grpc_channel_args** result, + grpc_closure* on_complete) { + GPR_ASSERT(next_completion_ == nullptr); + next_completion_ = on_complete; + target_result_ = result; + if (resolved_version_ == 0 && !resolving_) { + MaybeStartResolvingLocked(); + } else { + MaybeFinishNextLocked(); + } +} + +void NativeDnsResolver::RequestReresolutionLocked() { + if (!resolving_) { + MaybeStartResolvingLocked(); + } +} + +void NativeDnsResolver::ResetBackoffLocked() { + if (have_next_resolution_timer_) { + grpc_timer_cancel(&next_resolution_timer_); + } + backoff_.Reset(); +} + +void NativeDnsResolver::ShutdownLocked() { + if (have_next_resolution_timer_) { + grpc_timer_cancel(&next_resolution_timer_); + } + if (next_completion_ != nullptr) { + *target_result_ = nullptr; + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Resolver Shutdown")); + next_completion_ = nullptr; + } +} + +void NativeDnsResolver::OnNextResolutionLocked(void* arg, grpc_error* error) { + NativeDnsResolver* r = static_cast(arg); + r->have_next_resolution_timer_ = false; + if (error == GRPC_ERROR_NONE && !r->resolving_) { + r->StartResolvingLocked(); + } + r->Unref(DEBUG_LOCATION, "retry-timer"); +} + +void NativeDnsResolver::OnResolvedLocked(void* arg, grpc_error* error) { + NativeDnsResolver* r = static_cast(arg); + grpc_channel_args* result = nullptr; + GPR_ASSERT(r->resolving_); + r->resolving_ = false; + GRPC_ERROR_REF(error); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(r->name_to_resolve_)); + if (r->addresses_ != nullptr) { + ServerAddressList addresses; + for (size_t i = 0; i < r->addresses_->naddrs; ++i) { + addresses.emplace_back(&r->addresses_->addrs[i].addr, + r->addresses_->addrs[i].len, nullptr /* args */); + } + grpc_arg new_arg = CreateServerAddressListChannelArg(&addresses); + result = grpc_channel_args_copy_and_add(r->channel_args_, &new_arg, 1); + grpc_resolved_addresses_destroy(r->addresses_); + // Reset backoff state so that we start from the beginning when the + // next request gets triggered. + r->backoff_.Reset(); + } else { + grpc_millis next_try = r->backoff_.NextAttemptTime(); + grpc_millis timeout = next_try - ExecCtx::Get()->Now(); + gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", + grpc_error_string(error)); + GPR_ASSERT(!r->have_next_resolution_timer_); + r->have_next_resolution_timer_ = true; + // TODO(roth): We currently deal with this ref manually. Once the + // new closure API is done, find a way to track this ref with the timer + // callback as part of the type system. + RefCountedPtr self = + r->Ref(DEBUG_LOCATION, "next_resolution_timer"); + self.release(); + if (timeout > 0) { + gpr_log(GPR_DEBUG, "retrying in %" PRId64 " milliseconds", timeout); + } else { + gpr_log(GPR_DEBUG, "retrying immediately"); + } + grpc_timer_init(&r->next_resolution_timer_, next_try, + &r->on_next_resolution_); + } + if (r->resolved_result_ != nullptr) { + grpc_channel_args_destroy(r->resolved_result_); + } + r->resolved_result_ = result; + ++r->resolved_version_; + r->MaybeFinishNextLocked(); + GRPC_ERROR_UNREF(error); + r->Unref(DEBUG_LOCATION, "dns-resolving"); +} + +void NativeDnsResolver::MaybeStartResolvingLocked() { + // If there is an existing timer, the time it fires is the earliest time we + // can start the next resolution. + if (have_next_resolution_timer_) return; + if (last_resolution_timestamp_ >= 0) { + const grpc_millis earliest_next_resolution = + last_resolution_timestamp_ + min_time_between_resolutions_; + const grpc_millis ms_until_next_resolution = + earliest_next_resolution - grpc_core::ExecCtx::Get()->Now(); + if (ms_until_next_resolution > 0) { + const grpc_millis last_resolution_ago = + grpc_core::ExecCtx::Get()->Now() - last_resolution_timestamp_; + gpr_log(GPR_DEBUG, + "In cooldown from last resolution (from %" PRId64 + " ms ago). Will resolve again in %" PRId64 " ms", + last_resolution_ago, ms_until_next_resolution); + have_next_resolution_timer_ = true; + // TODO(roth): We currently deal with this ref manually. Once the + // new closure API is done, find a way to track this ref with the timer + // callback as part of the type system. + RefCountedPtr self = + Ref(DEBUG_LOCATION, "next_resolution_timer_cooldown"); + self.release(); + grpc_timer_init(&next_resolution_timer_, ms_until_next_resolution, + &on_next_resolution_); + return; + } + } + StartResolvingLocked(); +} + +void NativeDnsResolver::StartResolvingLocked() { + gpr_log(GPR_DEBUG, "Start resolving."); + // TODO(roth): We currently deal with this ref manually. Once the + // new closure API is done, find a way to track this ref with the timer + // callback as part of the type system. + RefCountedPtr self = Ref(DEBUG_LOCATION, "dns-resolving"); + self.release(); + GPR_ASSERT(!resolving_); + resolving_ = true; + addresses_ = nullptr; + grpc_resolve_address(name_to_resolve_, kDefaultPort, interested_parties_, + &on_resolved_, &addresses_); + last_resolution_timestamp_ = grpc_core::ExecCtx::Get()->Now(); +} + +void NativeDnsResolver::MaybeFinishNextLocked() { + if (next_completion_ != nullptr && resolved_version_ != published_version_) { + *target_result_ = resolved_result_ == nullptr + ? nullptr + : grpc_channel_args_copy(resolved_result_); + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE); + next_completion_ = nullptr; + published_version_ = resolved_version_; + } +} + +// +// Factory +// + +class NativeDnsResolverFactory : public ResolverFactory { + public: + OrphanablePtr CreateResolver( + const ResolverArgs& args) const override { + if (GPR_UNLIKELY(0 != strcmp(args.uri->authority, ""))) { + gpr_log(GPR_ERROR, "authority based dns uri's not supported"); + return OrphanablePtr(nullptr); + } + return OrphanablePtr(New(args)); + } + + const char* scheme() const override { return "dns"; } +}; + +} // namespace + +} // namespace grpc_core + +void grpc_resolver_dns_native_init() { + char* resolver_env = gpr_getenv("GRPC_DNS_RESOLVER"); + if (resolver_env != nullptr && gpr_stricmp(resolver_env, "native") == 0) { + gpr_log(GPR_DEBUG, "Using native dns resolver"); + grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( + grpc_core::UniquePtr( + grpc_core::New())); + } else { + grpc_core::ResolverRegistry::Builder::InitRegistry(); + grpc_core::ResolverFactory* existing_factory = + grpc_core::ResolverRegistry::LookupResolverFactory("dns"); + if (existing_factory == nullptr) { + gpr_log(GPR_DEBUG, "Using native dns resolver"); + grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( + grpc_core::UniquePtr( + grpc_core::New())); + } + } + gpr_free(resolver_env); +} + +void grpc_resolver_dns_native_shutdown() {} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc new file mode 100644 index 00000000000000..258339491c1ab8 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc @@ -0,0 +1,297 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This is similar to the sockaddr resolver, except that it supports a +// bunch of query args that are useful for dependency injection in tests. + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" + +namespace grpc_core { + +// This cannot be in an anonymous namespace, because it is a friend of +// FakeResolverResponseGenerator. +class FakeResolver : public Resolver { + public: + explicit FakeResolver(const ResolverArgs& args); + + void NextLocked(grpc_channel_args** result, + grpc_closure* on_complete) override; + + void RequestReresolutionLocked() override; + + private: + friend class FakeResolverResponseGenerator; + + virtual ~FakeResolver(); + + void MaybeFinishNextLocked(); + + void ShutdownLocked() override; + + // passed-in parameters + grpc_channel_args* channel_args_ = nullptr; + // If not NULL, the next set of resolution results to be returned to + // NextLocked()'s closure. + grpc_channel_args* next_results_ = nullptr; + // Results to use for the pretended re-resolution in + // RequestReresolutionLocked(). + grpc_channel_args* reresolution_results_ = nullptr; + // pending next completion, or NULL + grpc_closure* next_completion_ = nullptr; + // target result address for next completion + grpc_channel_args** target_result_ = nullptr; + // if true, return failure + bool return_failure_ = false; +}; + +FakeResolver::FakeResolver(const ResolverArgs& args) : Resolver(args.combiner) { + channel_args_ = grpc_channel_args_copy(args.args); + FakeResolverResponseGenerator* response_generator = + FakeResolverResponseGenerator::GetFromArgs(args.args); + if (response_generator != nullptr) response_generator->resolver_ = this; +} + +FakeResolver::~FakeResolver() { + grpc_channel_args_destroy(next_results_); + grpc_channel_args_destroy(reresolution_results_); + grpc_channel_args_destroy(channel_args_); +} + +void FakeResolver::NextLocked(grpc_channel_args** target_result, + grpc_closure* on_complete) { + GPR_ASSERT(next_completion_ == nullptr); + next_completion_ = on_complete; + target_result_ = target_result; + MaybeFinishNextLocked(); +} + +void FakeResolver::RequestReresolutionLocked() { + if (reresolution_results_ != nullptr || return_failure_) { + grpc_channel_args_destroy(next_results_); + next_results_ = grpc_channel_args_copy(reresolution_results_); + MaybeFinishNextLocked(); + } +} + +void FakeResolver::MaybeFinishNextLocked() { + if (next_completion_ != nullptr && + (next_results_ != nullptr || return_failure_)) { + *target_result_ = + return_failure_ ? nullptr + : grpc_channel_args_union(next_results_, channel_args_); + grpc_channel_args_destroy(next_results_); + next_results_ = nullptr; + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE); + next_completion_ = nullptr; + return_failure_ = false; + } +} + +void FakeResolver::ShutdownLocked() { + if (next_completion_ != nullptr) { + *target_result_ = nullptr; + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Resolver Shutdown")); + next_completion_ = nullptr; + } +} + +// +// FakeResolverResponseGenerator +// + +struct SetResponseClosureArg { + grpc_closure set_response_closure; + FakeResolverResponseGenerator* generator; + grpc_channel_args* response; + bool immediate = true; +}; + +void FakeResolverResponseGenerator::SetResponseLocked(void* arg, + grpc_error* error) { + SetResponseClosureArg* closure_arg = static_cast(arg); + FakeResolver* resolver = closure_arg->generator->resolver_; + grpc_channel_args_destroy(resolver->next_results_); + resolver->next_results_ = closure_arg->response; + resolver->MaybeFinishNextLocked(); + Delete(closure_arg); +} + +void FakeResolverResponseGenerator::SetResponse(grpc_channel_args* response) { + GPR_ASSERT(response != nullptr); + GPR_ASSERT(resolver_ != nullptr); + SetResponseClosureArg* closure_arg = New(); + closure_arg->generator = this; + closure_arg->response = grpc_channel_args_copy(response); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetResponseLocked, + closure_arg, + grpc_combiner_scheduler(resolver_->combiner())), + GRPC_ERROR_NONE); +} + +void FakeResolverResponseGenerator::SetReresolutionResponseLocked( + void* arg, grpc_error* error) { + SetResponseClosureArg* closure_arg = static_cast(arg); + FakeResolver* resolver = closure_arg->generator->resolver_; + grpc_channel_args_destroy(resolver->reresolution_results_); + resolver->reresolution_results_ = closure_arg->response; + Delete(closure_arg); +} + +void FakeResolverResponseGenerator::SetReresolutionResponse( + grpc_channel_args* response) { + GPR_ASSERT(resolver_ != nullptr); + SetResponseClosureArg* closure_arg = New(); + closure_arg->generator = this; + closure_arg->response = + response != nullptr ? grpc_channel_args_copy(response) : nullptr; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, + SetReresolutionResponseLocked, closure_arg, + grpc_combiner_scheduler(resolver_->combiner())), + GRPC_ERROR_NONE); +} + +void FakeResolverResponseGenerator::SetFailureLocked(void* arg, + grpc_error* error) { + SetResponseClosureArg* closure_arg = static_cast(arg); + FakeResolver* resolver = closure_arg->generator->resolver_; + resolver->return_failure_ = true; + if (closure_arg->immediate) resolver->MaybeFinishNextLocked(); + Delete(closure_arg); +} + +void FakeResolverResponseGenerator::SetFailure() { + GPR_ASSERT(resolver_ != nullptr); + SetResponseClosureArg* closure_arg = New(); + closure_arg->generator = this; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked, + closure_arg, + grpc_combiner_scheduler(resolver_->combiner())), + GRPC_ERROR_NONE); +} + +void FakeResolverResponseGenerator::SetFailureOnReresolution() { + GPR_ASSERT(resolver_ != nullptr); + SetResponseClosureArg* closure_arg = New(); + closure_arg->generator = this; + closure_arg->immediate = false; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked, + closure_arg, + grpc_combiner_scheduler(resolver_->combiner())), + GRPC_ERROR_NONE); +} + +namespace { + +static void* response_generator_arg_copy(void* p) { + FakeResolverResponseGenerator* generator = + static_cast(p); + // TODO(roth): We currently deal with this ref manually. Once the + // new channel args code is converted to C++, find a way to track this ref + // in a cleaner way. + RefCountedPtr copy = generator->Ref(); + copy.release(); + return p; +} + +static void response_generator_arg_destroy(void* p) { + FakeResolverResponseGenerator* generator = + static_cast(p); + generator->Unref(); +} + +static int response_generator_cmp(void* a, void* b) { return GPR_ICMP(a, b); } + +static const grpc_arg_pointer_vtable response_generator_arg_vtable = { + response_generator_arg_copy, response_generator_arg_destroy, + response_generator_cmp}; + +} // namespace + +grpc_arg FakeResolverResponseGenerator::MakeChannelArg( + FakeResolverResponseGenerator* generator) { + grpc_arg arg; + arg.type = GRPC_ARG_POINTER; + arg.key = (char*)GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR; + arg.value.pointer.p = generator; + arg.value.pointer.vtable = &response_generator_arg_vtable; + return arg; +} + +FakeResolverResponseGenerator* FakeResolverResponseGenerator::GetFromArgs( + const grpc_channel_args* args) { + const grpc_arg* arg = + grpc_channel_args_find(args, GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR); + if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return nullptr; + return static_cast(arg->value.pointer.p); +} + +// +// Factory +// + +namespace { + +class FakeResolverFactory : public ResolverFactory { + public: + OrphanablePtr CreateResolver( + const ResolverArgs& args) const override { + return OrphanablePtr(New(args)); + } + + const char* scheme() const override { return "fake"; } +}; + +} // namespace + +} // namespace grpc_core + +void grpc_resolver_fake_init() { + grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( + grpc_core::UniquePtr( + grpc_core::New())); +} + +void grpc_resolver_fake_shutdown() {} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h new file mode 100644 index 00000000000000..d86111c38290e0 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h @@ -0,0 +1,87 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/iomgr/error.h" + +#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \ + "grpc.fake_resolver.response_generator" + +namespace grpc_core { + +class FakeResolver; + +/// A mechanism for generating responses for the fake resolver. +/// An instance of this class is passed to the fake resolver via a channel +/// argument (see \a MakeChannelArg()) and used to inject and trigger custom +/// resolutions. +// TODO(roth): I would ideally like this to be InternallyRefCounted +// instead of RefCounted, but external refs are currently needed to +// encode this in channel args. Once channel_args are converted to C++, +// see if we can find a way to fix this. +class FakeResolverResponseGenerator + : public RefCounted { + public: + FakeResolverResponseGenerator() {} + + // Instructs the fake resolver associated with the response generator + // instance to trigger a new resolution with the specified response. + void SetResponse(grpc_channel_args* next_response); + + // Sets the re-resolution response, which is returned by the fake resolver + // when re-resolution is requested (via \a RequestReresolutionLocked()). + // The new re-resolution response replaces any previous re-resolution + // response that may have been set by a previous call. + // If the re-resolution response is set to NULL, then the fake + // resolver will not return anything when \a RequestReresolutionLocked() + // is called. + void SetReresolutionResponse(grpc_channel_args* response); + + // Tells the resolver to return a transient failure (signalled by + // returning a null result with no error). + void SetFailure(); + + // Same as SetFailure(), but instead of returning the error + // immediately, waits for the next call to RequestReresolutionLocked(). + void SetFailureOnReresolution(); + + // Returns a channel arg containing \a generator. + static grpc_arg MakeChannelArg(FakeResolverResponseGenerator* generator); + + // Returns the response generator in \a args, or null if not found. + static FakeResolverResponseGenerator* GetFromArgs( + const grpc_channel_args* args); + + private: + friend class FakeResolver; + + static void SetResponseLocked(void* arg, grpc_error* error); + static void SetReresolutionResponseLocked(void* arg, grpc_error* error); + static void SetFailureLocked(void* arg, grpc_error* error); + + FakeResolver* resolver_ = nullptr; // Do not own. +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/README.md b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/README.md new file mode 100644 index 00000000000000..e307ba88f57835 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/README.md @@ -0,0 +1 @@ +Support for resolving ipv4:, ipv6:, unix: schemes diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc new file mode 100644 index 00000000000000..1654747a79f892 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc @@ -0,0 +1,207 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +namespace grpc_core { + +namespace { + +class SockaddrResolver : public Resolver { + public: + /// Takes ownership of \a addresses. + SockaddrResolver(const ResolverArgs& args, + UniquePtr addresses); + + void NextLocked(grpc_channel_args** result, + grpc_closure* on_complete) override; + + void ShutdownLocked() override; + + private: + virtual ~SockaddrResolver(); + + void MaybeFinishNextLocked(); + + /// the addresses that we've "resolved" + UniquePtr addresses_; + /// channel args + grpc_channel_args* channel_args_ = nullptr; + /// have we published? + bool published_ = false; + /// pending next completion, or NULL + grpc_closure* next_completion_ = nullptr; + /// target result address for next completion + grpc_channel_args** target_result_ = nullptr; +}; + +SockaddrResolver::SockaddrResolver(const ResolverArgs& args, + UniquePtr addresses) + : Resolver(args.combiner), + addresses_(std::move(addresses)), + channel_args_(grpc_channel_args_copy(args.args)) {} + +SockaddrResolver::~SockaddrResolver() { + grpc_channel_args_destroy(channel_args_); +} + +void SockaddrResolver::NextLocked(grpc_channel_args** target_result, + grpc_closure* on_complete) { + GPR_ASSERT(!next_completion_); + next_completion_ = on_complete; + target_result_ = target_result; + MaybeFinishNextLocked(); +} + +void SockaddrResolver::ShutdownLocked() { + if (next_completion_ != nullptr) { + *target_result_ = nullptr; + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Resolver Shutdown")); + next_completion_ = nullptr; + } +} + +void SockaddrResolver::MaybeFinishNextLocked() { + if (next_completion_ != nullptr && !published_) { + published_ = true; + grpc_arg arg = CreateServerAddressListChannelArg(addresses_.get()); + *target_result_ = grpc_channel_args_copy_and_add(channel_args_, &arg, 1); + GRPC_CLOSURE_SCHED(next_completion_, GRPC_ERROR_NONE); + next_completion_ = nullptr; + } +} + +// +// Factory +// + +void DoNothing(void* ignored) {} + +OrphanablePtr CreateSockaddrResolver( + const ResolverArgs& args, + bool parse(const grpc_uri* uri, grpc_resolved_address* dst)) { + if (0 != strcmp(args.uri->authority, "")) { + gpr_log(GPR_ERROR, "authority-based URIs not supported by the %s scheme", + args.uri->scheme); + return OrphanablePtr(nullptr); + } + // Construct addresses. + grpc_slice path_slice = + grpc_slice_new(args.uri->path, strlen(args.uri->path), DoNothing); + grpc_slice_buffer path_parts; + grpc_slice_buffer_init(&path_parts); + grpc_slice_split(path_slice, ",", &path_parts); + auto addresses = MakeUnique(); + bool errors_found = false; + for (size_t i = 0; i < path_parts.count; i++) { + grpc_uri ith_uri = *args.uri; + UniquePtr part_str(grpc_slice_to_c_string(path_parts.slices[i])); + ith_uri.path = part_str.get(); + grpc_resolved_address addr; + if (!parse(&ith_uri, &addr)) { + errors_found = true; /* GPR_TRUE */ + break; + } + addresses->emplace_back(addr, nullptr /* args */); + } + grpc_slice_buffer_destroy_internal(&path_parts); + grpc_slice_unref_internal(path_slice); + if (errors_found) { + return OrphanablePtr(nullptr); + } + // Instantiate resolver. + return OrphanablePtr( + New(args, std::move(addresses))); +} + +class IPv4ResolverFactory : public ResolverFactory { + public: + OrphanablePtr CreateResolver( + const ResolverArgs& args) const override { + return CreateSockaddrResolver(args, grpc_parse_ipv4); + } + + const char* scheme() const override { return "ipv4"; } +}; + +class IPv6ResolverFactory : public ResolverFactory { + public: + OrphanablePtr CreateResolver( + const ResolverArgs& args) const override { + return CreateSockaddrResolver(args, grpc_parse_ipv6); + } + + const char* scheme() const override { return "ipv6"; } +}; + +#ifdef GRPC_HAVE_UNIX_SOCKET +class UnixResolverFactory : public ResolverFactory { + public: + OrphanablePtr CreateResolver( + const ResolverArgs& args) const override { + return CreateSockaddrResolver(args, grpc_parse_unix); + } + + UniquePtr GetDefaultAuthority(grpc_uri* uri) const override { + return UniquePtr(gpr_strdup("localhost")); + } + + const char* scheme() const override { return "unix"; } +}; +#endif // GRPC_HAVE_UNIX_SOCKET + +} // namespace + +} // namespace grpc_core + +void grpc_resolver_sockaddr_init() { + grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( + grpc_core::UniquePtr( + grpc_core::New())); + grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( + grpc_core::UniquePtr( + grpc_core::New())); +#ifdef GRPC_HAVE_UNIX_SOCKET + grpc_core::ResolverRegistry::Builder::RegisterResolverFactory( + grpc_core::UniquePtr( + grpc_core::New())); +#endif +} + +void grpc_resolver_sockaddr_shutdown() {} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_factory.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver_factory.h new file mode 100644 index 00000000000000..d891ef62e1dc44 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_factory.h @@ -0,0 +1,71 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H + +#include + +#include + +#include "src/core/ext/filters/client_channel/resolver.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/uri/uri_parser.h" + +namespace grpc_core { + +struct ResolverArgs { + /// The parsed URI to resolve. + grpc_uri* uri = nullptr; + /// Channel args to be included in resolver results. + const grpc_channel_args* args = nullptr; + /// Used to drive I/O in the name resolution process. + grpc_pollset_set* pollset_set = nullptr; + /// The combiner under which all resolver calls will be run. + grpc_combiner* combiner = nullptr; +}; + +class ResolverFactory { + public: + /// Returns a new resolver instance. + virtual OrphanablePtr CreateResolver(const ResolverArgs& args) const + GRPC_ABSTRACT; + + /// Returns a string representing the default authority to use for this + /// scheme. + virtual UniquePtr GetDefaultAuthority(grpc_uri* uri) const { + const char* path = uri->path; + if (path[0] == '/') ++path; + return UniquePtr(gpr_strdup(path)); + } + + /// Returns the URI scheme that this factory implements. + /// Caller does NOT take ownership of result. + virtual const char* scheme() const GRPC_ABSTRACT; + + virtual ~ResolverFactory() {} + + GRPC_ABSTRACT_BASE_CLASS +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.cc new file mode 100644 index 00000000000000..91c0267f95e141 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.cc @@ -0,0 +1,178 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/resolver_registry.h" + +#include + +#include +#include +#include + +namespace grpc_core { + +namespace { + +class RegistryState { + public: + RegistryState() : default_prefix_(gpr_strdup("dns:///")) {} + + void SetDefaultPrefix(const char* default_resolver_prefix) { + GPR_ASSERT(default_resolver_prefix != nullptr); + GPR_ASSERT(*default_resolver_prefix != '\0'); + default_prefix_.reset(gpr_strdup(default_resolver_prefix)); + } + + void RegisterResolverFactory(UniquePtr factory) { + for (size_t i = 0; i < factories_.size(); ++i) { + GPR_ASSERT(strcmp(factories_[i]->scheme(), factory->scheme()) != 0); + } + factories_.push_back(std::move(factory)); + } + + ResolverFactory* LookupResolverFactory(const char* scheme) const { + for (size_t i = 0; i < factories_.size(); ++i) { + if (strcmp(scheme, factories_[i]->scheme()) == 0) { + return factories_[i].get(); + } + } + return nullptr; + } + + // Returns the factory for the scheme of \a target. If \a target does + // not parse as a URI, prepends \a default_prefix_ and tries again. + // If URI parsing is successful (in either attempt), sets \a uri to + // point to the parsed URI. + // If \a default_prefix_ needs to be prepended, sets \a canonical_target + // to the canonical target string. + ResolverFactory* FindResolverFactory(const char* target, grpc_uri** uri, + char** canonical_target) const { + GPR_ASSERT(uri != nullptr); + *uri = grpc_uri_parse(target, 1); + ResolverFactory* factory = + *uri == nullptr ? nullptr : LookupResolverFactory((*uri)->scheme); + if (factory == nullptr) { + grpc_uri_destroy(*uri); + gpr_asprintf(canonical_target, "%s%s", default_prefix_.get(), target); + *uri = grpc_uri_parse(*canonical_target, 1); + factory = + *uri == nullptr ? nullptr : LookupResolverFactory((*uri)->scheme); + if (factory == nullptr) { + grpc_uri_destroy(grpc_uri_parse(target, 0)); + grpc_uri_destroy(grpc_uri_parse(*canonical_target, 0)); + gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target, + *canonical_target); + } + } + return factory; + } + + private: + // We currently support 10 factories without doing additional + // allocation. This number could be raised if there is a case where + // more factories are needed and the additional allocations are + // hurting performance (which is unlikely, since these allocations + // only occur at gRPC initialization time). + InlinedVector, 10> factories_; + UniquePtr default_prefix_; +}; + +static RegistryState* g_state = nullptr; + +} // namespace + +// +// ResolverRegistry::Builder +// + +void ResolverRegistry::Builder::InitRegistry() { + if (g_state == nullptr) g_state = New(); +} + +void ResolverRegistry::Builder::ShutdownRegistry() { + Delete(g_state); + g_state = nullptr; +} + +void ResolverRegistry::Builder::SetDefaultPrefix( + const char* default_resolver_prefix) { + InitRegistry(); + g_state->SetDefaultPrefix(default_resolver_prefix); +} + +void ResolverRegistry::Builder::RegisterResolverFactory( + UniquePtr factory) { + InitRegistry(); + g_state->RegisterResolverFactory(std::move(factory)); +} + +// +// ResolverRegistry +// + +ResolverFactory* ResolverRegistry::LookupResolverFactory(const char* scheme) { + GPR_ASSERT(g_state != nullptr); + return g_state->LookupResolverFactory(scheme); +} + +OrphanablePtr ResolverRegistry::CreateResolver( + const char* target, const grpc_channel_args* args, + grpc_pollset_set* pollset_set, grpc_combiner* combiner) { + GPR_ASSERT(g_state != nullptr); + grpc_uri* uri = nullptr; + char* canonical_target = nullptr; + ResolverFactory* factory = + g_state->FindResolverFactory(target, &uri, &canonical_target); + ResolverArgs resolver_args; + resolver_args.uri = uri; + resolver_args.args = args; + resolver_args.pollset_set = pollset_set; + resolver_args.combiner = combiner; + OrphanablePtr resolver = + factory == nullptr ? nullptr : factory->CreateResolver(resolver_args); + grpc_uri_destroy(uri); + gpr_free(canonical_target); + return resolver; +} + +UniquePtr ResolverRegistry::GetDefaultAuthority(const char* target) { + GPR_ASSERT(g_state != nullptr); + grpc_uri* uri = nullptr; + char* canonical_target = nullptr; + ResolverFactory* factory = + g_state->FindResolverFactory(target, &uri, &canonical_target); + UniquePtr authority = + factory == nullptr ? nullptr : factory->GetDefaultAuthority(uri); + grpc_uri_destroy(uri); + gpr_free(canonical_target); + return authority; +} + +UniquePtr ResolverRegistry::AddDefaultPrefixIfNeeded(const char* target) { + GPR_ASSERT(g_state != nullptr); + grpc_uri* uri = nullptr; + char* canonical_target = nullptr; + g_state->FindResolverFactory(target, &uri, &canonical_target); + grpc_uri_destroy(uri); + return UniquePtr(canonical_target == nullptr ? gpr_strdup(target) + : canonical_target); +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.h new file mode 100644 index 00000000000000..d6ec6811bd7e05 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_registry.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H + +#include + +#include "src/core/ext/filters/client_channel/resolver_factory.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/iomgr/pollset_set.h" + +namespace grpc_core { + +class ResolverRegistry { + public: + /// Methods used to create and populate the ResolverRegistry. + /// NOT THREAD SAFE -- to be used only during global gRPC + /// initialization and shutdown. + class Builder { + public: + /// Global initialization and shutdown hooks. + static void InitRegistry(); + static void ShutdownRegistry(); + + /// Sets the default URI prefix to \a default_prefix. + /// Calls InitRegistry() if it has not already been called. + static void SetDefaultPrefix(const char* default_prefix); + + /// Registers a resolver factory. The factory will be used to create a + /// resolver for any URI whose scheme matches that of the factory. + /// Calls InitRegistry() if it has not already been called. + static void RegisterResolverFactory(UniquePtr factory); + }; + + /// Creates 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 fails or there is no factory for the URI's scheme, + /// prepends default_prefix to target and tries again. + /// If a resolver factory is found, uses it to instantiate a resolver and + /// returns it; otherwise, returns nullptr. + /// \a args, \a pollset_set, and \a combiner are passed to the factory's + /// \a CreateResolver() method. + /// \a args are the channel args to be included in resolver results. + /// \a pollset_set is used to drive I/O in the name resolution process. + /// \a combiner is the combiner under which all resolver calls will be run. + static OrphanablePtr CreateResolver(const char* target, + const grpc_channel_args* args, + grpc_pollset_set* pollset_set, + grpc_combiner* combiner); + + /// Returns the default authority to pass from a client for \a target. + static UniquePtr GetDefaultAuthority(const char* target); + + /// Returns \a target with the default prefix prepended, if needed. + static UniquePtr AddDefaultPrefixIfNeeded(const char* target); + + /// Returns the resolver factory for \a scheme. + /// Caller does NOT own the return value. + static ResolverFactory* LookupResolverFactory(const char* scheme); +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.cc b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.cc new file mode 100644 index 00000000000000..9a0122e8ece9ee --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.cc @@ -0,0 +1,377 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/resolver_result_parsing.h" + +#include +#include +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/lib/channel/status_util.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/uri/uri_parser.h" + +// As per the retry design, we do not allow more than 5 retry attempts. +#define MAX_MAX_RETRY_ATTEMPTS 5 + +namespace grpc_core { +namespace internal { + +ProcessedResolverResult::ProcessedResolverResult( + const grpc_channel_args& resolver_result, bool parse_retry) { + ProcessServiceConfig(resolver_result, parse_retry); + // If no LB config was found above, just find the LB policy name then. + if (lb_policy_name_ == nullptr) ProcessLbPolicyName(resolver_result); +} + +void ProcessedResolverResult::ProcessServiceConfig( + const grpc_channel_args& resolver_result, bool parse_retry) { + const grpc_arg* channel_arg = + grpc_channel_args_find(&resolver_result, GRPC_ARG_SERVICE_CONFIG); + const char* service_config_json = grpc_channel_arg_get_string(channel_arg); + if (service_config_json != nullptr) { + service_config_json_.reset(gpr_strdup(service_config_json)); + service_config_ = grpc_core::ServiceConfig::Create(service_config_json); + if (service_config_ != nullptr) { + if (parse_retry) { + channel_arg = + grpc_channel_args_find(&resolver_result, GRPC_ARG_SERVER_URI); + const char* server_uri = grpc_channel_arg_get_string(channel_arg); + GPR_ASSERT(server_uri != nullptr); + grpc_uri* uri = grpc_uri_parse(server_uri, true); + GPR_ASSERT(uri->path[0] != '\0'); + server_name_ = uri->path[0] == '/' ? uri->path + 1 : uri->path; + service_config_->ParseGlobalParams(ParseServiceConfig, this); + grpc_uri_destroy(uri); + } else { + service_config_->ParseGlobalParams(ParseServiceConfig, this); + } + method_params_table_ = service_config_->CreateMethodConfigTable( + ClientChannelMethodParams::CreateFromJson); + } + } +} + +void ProcessedResolverResult::ProcessLbPolicyName( + const grpc_channel_args& resolver_result) { + // Prefer the LB policy name found in the service config. Note that this is + // checking the deprecated loadBalancingPolicy field, rather than the new + // loadBalancingConfig field. + if (service_config_ != nullptr) { + lb_policy_name_.reset( + gpr_strdup(service_config_->GetLoadBalancingPolicyName())); + // Convert to lower-case. + if (lb_policy_name_ != nullptr) { + char* lb_policy_name = lb_policy_name_.get(); + for (size_t i = 0; i < strlen(lb_policy_name); ++i) { + lb_policy_name[i] = tolower(lb_policy_name[i]); + } + } + } + // Otherwise, find the LB policy name set by the client API. + if (lb_policy_name_ == nullptr) { + const grpc_arg* channel_arg = + grpc_channel_args_find(&resolver_result, GRPC_ARG_LB_POLICY_NAME); + lb_policy_name_.reset(gpr_strdup(grpc_channel_arg_get_string(channel_arg))); + } + // Special case: If at least one balancer address is present, we use + // the grpclb policy, regardless of what the resolver has returned. + const ServerAddressList* addresses = + FindServerAddressListChannelArg(&resolver_result); + if (addresses != nullptr) { + bool found_balancer_address = false; + for (size_t i = 0; i < addresses->size(); ++i) { + const ServerAddress& address = (*addresses)[i]; + if (address.IsBalancer()) { + found_balancer_address = true; + break; + } + } + if (found_balancer_address) { + if (lb_policy_name_ != nullptr && + strcmp(lb_policy_name_.get(), "grpclb") != 0) { + gpr_log(GPR_INFO, + "resolver requested LB policy %s but provided at least one " + "balancer address -- forcing use of grpclb LB policy", + lb_policy_name_.get()); + } + lb_policy_name_.reset(gpr_strdup("grpclb")); + } + } + // Use pick_first if nothing was specified and we didn't select grpclb + // above. + if (lb_policy_name_ == nullptr) { + lb_policy_name_.reset(gpr_strdup("pick_first")); + } +} + +void ProcessedResolverResult::ParseServiceConfig( + const grpc_json* field, ProcessedResolverResult* parsing_state) { + parsing_state->ParseLbConfigFromServiceConfig(field); + if (parsing_state->server_name_ != nullptr) { + parsing_state->ParseRetryThrottleParamsFromServiceConfig(field); + } +} + +void ProcessedResolverResult::ParseLbConfigFromServiceConfig( + const grpc_json* field) { + if (lb_policy_config_ != nullptr) return; // Already found. + // Find the LB config global parameter. + if (field->key == nullptr || strcmp(field->key, "loadBalancingConfig") != 0 || + field->type != GRPC_JSON_ARRAY) { + return; // Not valid lb config array. + } + // Find the first LB policy that this client supports. + for (grpc_json* lb_config = field->child; lb_config != nullptr; + lb_config = lb_config->next) { + if (lb_config->type != GRPC_JSON_OBJECT) return; + // Find the policy object. + grpc_json* policy = nullptr; + for (grpc_json* field = lb_config->child; field != nullptr; + field = field->next) { + if (field->key == nullptr || strcmp(field->key, "policy") != 0 || + field->type != GRPC_JSON_OBJECT) { + return; + } + if (policy != nullptr) return; // Duplicate. + policy = field; + } + // Find the specific policy content since the policy object is of type + // "oneof". + grpc_json* policy_content = nullptr; + for (grpc_json* field = policy->child; field != nullptr; + field = field->next) { + if (field->key == nullptr || field->type != GRPC_JSON_OBJECT) return; + if (policy_content != nullptr) return; // Violate "oneof" type. + policy_content = field; + } + // If we support this policy, then select it. + if (grpc_core::LoadBalancingPolicyRegistry::LoadBalancingPolicyExists( + policy_content->key)) { + lb_policy_name_.reset(gpr_strdup(policy_content->key)); + lb_policy_config_ = policy_content->child; + return; + } + } +} + +void ProcessedResolverResult::ParseRetryThrottleParamsFromServiceConfig( + const grpc_json* field) { + if (strcmp(field->key, "retryThrottling") == 0) { + if (retry_throttle_data_ != nullptr) return; // Duplicate. + if (field->type != GRPC_JSON_OBJECT) return; + int max_milli_tokens = 0; + int milli_token_ratio = 0; + for (grpc_json* sub_field = field->child; sub_field != nullptr; + sub_field = sub_field->next) { + if (sub_field->key == nullptr) return; + if (strcmp(sub_field->key, "maxTokens") == 0) { + if (max_milli_tokens != 0) return; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return; + max_milli_tokens = gpr_parse_nonnegative_int(sub_field->value); + if (max_milli_tokens == -1) return; + max_milli_tokens *= 1000; + } else if (strcmp(sub_field->key, "tokenRatio") == 0) { + if (milli_token_ratio != 0) return; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return; + // We support up to 3 decimal digits. + size_t whole_len = strlen(sub_field->value); + uint32_t multiplier = 1; + uint32_t decimal_value = 0; + const char* decimal_point = strchr(sub_field->value, '.'); + if (decimal_point != nullptr) { + whole_len = static_cast(decimal_point - sub_field->value); + multiplier = 1000; + size_t decimal_len = strlen(decimal_point + 1); + if (decimal_len > 3) decimal_len = 3; + if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len, + &decimal_value)) { + return; + } + uint32_t decimal_multiplier = 1; + for (size_t i = 0; i < (3 - decimal_len); ++i) { + decimal_multiplier *= 10; + } + decimal_value *= decimal_multiplier; + } + uint32_t whole_value; + if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len, + &whole_value)) { + return; + } + milli_token_ratio = + static_cast((whole_value * multiplier) + decimal_value); + if (milli_token_ratio <= 0) return; + } + } + retry_throttle_data_ = + grpc_core::internal::ServerRetryThrottleMap::GetDataForServer( + server_name_, max_milli_tokens, milli_token_ratio); + } +} + +namespace { + +bool ParseWaitForReady( + grpc_json* field, ClientChannelMethodParams::WaitForReady* wait_for_ready) { + if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) { + return false; + } + *wait_for_ready = field->type == GRPC_JSON_TRUE + ? ClientChannelMethodParams::WAIT_FOR_READY_TRUE + : ClientChannelMethodParams::WAIT_FOR_READY_FALSE; + return true; +} + +// Parses a JSON field of the form generated for a google.proto.Duration +// proto message, as per: +// https://developers.google.com/protocol-buffers/docs/proto3#json +bool ParseDuration(grpc_json* field, grpc_millis* duration) { + if (field->type != GRPC_JSON_STRING) return false; + size_t len = strlen(field->value); + if (field->value[len - 1] != 's') return false; + UniquePtr buf(gpr_strdup(field->value)); + *(buf.get() + len - 1) = '\0'; // Remove trailing 's'. + char* decimal_point = strchr(buf.get(), '.'); + int nanos = 0; + if (decimal_point != nullptr) { + *decimal_point = '\0'; + nanos = gpr_parse_nonnegative_int(decimal_point + 1); + if (nanos == -1) { + return false; + } + int num_digits = static_cast(strlen(decimal_point + 1)); + if (num_digits > 9) { // We don't accept greater precision than nanos. + return false; + } + for (int i = 0; i < (9 - num_digits); ++i) { + nanos *= 10; + } + } + int seconds = + decimal_point == buf.get() ? 0 : gpr_parse_nonnegative_int(buf.get()); + if (seconds == -1) return false; + *duration = seconds * GPR_MS_PER_SEC + nanos / GPR_NS_PER_MS; + return true; +} + +UniquePtr ParseRetryPolicy( + grpc_json* field) { + auto retry_policy = MakeUnique(); + if (field->type != GRPC_JSON_OBJECT) return nullptr; + for (grpc_json* sub_field = field->child; sub_field != nullptr; + sub_field = sub_field->next) { + if (sub_field->key == nullptr) return nullptr; + if (strcmp(sub_field->key, "maxAttempts") == 0) { + if (retry_policy->max_attempts != 0) return nullptr; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return nullptr; + retry_policy->max_attempts = gpr_parse_nonnegative_int(sub_field->value); + if (retry_policy->max_attempts <= 1) return nullptr; + if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) { + gpr_log(GPR_ERROR, + "service config: clamped retryPolicy.maxAttempts at %d", + MAX_MAX_RETRY_ATTEMPTS); + retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS; + } + } else if (strcmp(sub_field->key, "initialBackoff") == 0) { + if (retry_policy->initial_backoff > 0) return nullptr; // Duplicate. + if (!ParseDuration(sub_field, &retry_policy->initial_backoff)) { + return nullptr; + } + if (retry_policy->initial_backoff == 0) return nullptr; + } else if (strcmp(sub_field->key, "maxBackoff") == 0) { + if (retry_policy->max_backoff > 0) return nullptr; // Duplicate. + if (!ParseDuration(sub_field, &retry_policy->max_backoff)) { + return nullptr; + } + if (retry_policy->max_backoff == 0) return nullptr; + } else if (strcmp(sub_field->key, "backoffMultiplier") == 0) { + if (retry_policy->backoff_multiplier != 0) return nullptr; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return nullptr; + if (sscanf(sub_field->value, "%f", &retry_policy->backoff_multiplier) != + 1) { + return nullptr; + } + if (retry_policy->backoff_multiplier <= 0) return nullptr; + } else if (strcmp(sub_field->key, "retryableStatusCodes") == 0) { + if (!retry_policy->retryable_status_codes.Empty()) { + return nullptr; // Duplicate. + } + if (sub_field->type != GRPC_JSON_ARRAY) return nullptr; + for (grpc_json* element = sub_field->child; element != nullptr; + element = element->next) { + if (element->type != GRPC_JSON_STRING) return nullptr; + grpc_status_code status; + if (!grpc_status_code_from_string(element->value, &status)) { + return nullptr; + } + retry_policy->retryable_status_codes.Add(status); + } + if (retry_policy->retryable_status_codes.Empty()) return nullptr; + } + } + // Make sure required fields are set. + if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 || + retry_policy->max_backoff == 0 || retry_policy->backoff_multiplier == 0 || + retry_policy->retryable_status_codes.Empty()) { + return nullptr; + } + return retry_policy; +} + +} // namespace + +RefCountedPtr +ClientChannelMethodParams::CreateFromJson(const grpc_json* json) { + RefCountedPtr method_params = + MakeRefCounted(); + for (grpc_json* field = json->child; field != nullptr; field = field->next) { + if (field->key == nullptr) continue; + if (strcmp(field->key, "waitForReady") == 0) { + if (method_params->wait_for_ready_ != WAIT_FOR_READY_UNSET) { + return nullptr; // Duplicate. + } + if (!ParseWaitForReady(field, &method_params->wait_for_ready_)) { + return nullptr; + } + } else if (strcmp(field->key, "timeout") == 0) { + if (method_params->timeout_ > 0) return nullptr; // Duplicate. + if (!ParseDuration(field, &method_params->timeout_)) return nullptr; + } else if (strcmp(field->key, "retryPolicy") == 0) { + if (method_params->retry_policy_ != nullptr) { + return nullptr; // Duplicate. + } + method_params->retry_policy_ = ParseRetryPolicy(field); + if (method_params->retry_policy_ == nullptr) return nullptr; + } + } + return method_params; +} + +} // namespace internal +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.h b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.h new file mode 100644 index 00000000000000..98a9d26c467b3a --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/resolver_result_parsing.h @@ -0,0 +1,142 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H + +#include + +#include "src/core/ext/filters/client_channel/retry_throttle.h" +#include "src/core/lib/channel/status_util.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/exec_ctx.h" // for grpc_millis +#include "src/core/lib/json/json.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/transport/service_config.h" + +namespace grpc_core { +namespace internal { + +class ClientChannelMethodParams; + +// A table mapping from a method name to its method parameters. +typedef SliceHashTable> + ClientChannelMethodParamsTable; + +// A container of processed fields from the resolver result. Simplifies the +// usage of resolver result. +class ProcessedResolverResult { + public: + // Processes the resolver result and populates the relative members + // for later consumption. Tries to parse retry parameters only if parse_retry + // is true. + ProcessedResolverResult(const grpc_channel_args& resolver_result, + bool parse_retry); + + // Getters. Any managed object's ownership is transferred. + UniquePtr service_config_json() { + return std::move(service_config_json_); + } + RefCountedPtr retry_throttle_data() { + return std::move(retry_throttle_data_); + } + RefCountedPtr method_params_table() { + return std::move(method_params_table_); + } + UniquePtr lb_policy_name() { return std::move(lb_policy_name_); } + grpc_json* lb_policy_config() { return lb_policy_config_; } + + private: + // Finds the service config; extracts LB config and (maybe) retry throttle + // params from it. + void ProcessServiceConfig(const grpc_channel_args& resolver_result, + bool parse_retry); + + // Finds the LB policy name (when no LB config was found). + void ProcessLbPolicyName(const grpc_channel_args& resolver_result); + + // Parses the service config. Intended to be used by + // ServiceConfig::ParseGlobalParams. + static void ParseServiceConfig(const grpc_json* field, + ProcessedResolverResult* parsing_state); + // Parses the LB config from service config. + void ParseLbConfigFromServiceConfig(const grpc_json* field); + // Parses the retry throttle parameters from service config. + void ParseRetryThrottleParamsFromServiceConfig(const grpc_json* field); + + // Service config. + UniquePtr service_config_json_; + UniquePtr service_config_; + // LB policy. + grpc_json* lb_policy_config_ = nullptr; + UniquePtr lb_policy_name_; + // Retry throttle data. + char* server_name_ = nullptr; + RefCountedPtr retry_throttle_data_; + // Method params table. + RefCountedPtr method_params_table_; +}; + +// The parameters of a method. +class ClientChannelMethodParams : public RefCounted { + public: + enum WaitForReady { + WAIT_FOR_READY_UNSET = 0, + WAIT_FOR_READY_FALSE, + WAIT_FOR_READY_TRUE + }; + + struct RetryPolicy { + int max_attempts = 0; + grpc_millis initial_backoff = 0; + grpc_millis max_backoff = 0; + float backoff_multiplier = 0; + StatusCodeSet retryable_status_codes; + }; + + /// Creates a method_parameters object from \a json. + /// Intended for use with ServiceConfig::CreateMethodConfigTable(). + static RefCountedPtr CreateFromJson( + const grpc_json* json); + + grpc_millis timeout() const { return timeout_; } + WaitForReady wait_for_ready() const { return wait_for_ready_; } + const RetryPolicy* retry_policy() const { return retry_policy_.get(); } + + private: + // So New() can call our private ctor. + template + friend T* grpc_core::New(Args&&... args); + + // So Delete() can call our private dtor. + template + friend void grpc_core::Delete(T*); + + ClientChannelMethodParams() {} + virtual ~ClientChannelMethodParams() {} + + grpc_millis timeout_ = 0; + WaitForReady wait_for_ready_ = WAIT_FOR_READY_UNSET; + UniquePtr retry_policy_; +}; + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.cc b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.cc new file mode 100644 index 00000000000000..bdeb7e4cacd949 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.cc @@ -0,0 +1,191 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/retry_throttle.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/avl/avl.h" + +namespace grpc_core { +namespace internal { + +// +// ServerRetryThrottleData +// + +ServerRetryThrottleData::ServerRetryThrottleData( + intptr_t max_milli_tokens, intptr_t milli_token_ratio, + ServerRetryThrottleData* old_throttle_data) + : max_milli_tokens_(max_milli_tokens), + milli_token_ratio_(milli_token_ratio) { + intptr_t initial_milli_tokens = max_milli_tokens; + // If there was a pre-existing entry for this server name, initialize + // the token count by scaling proportionately to the old data. This + // ensures that if we're already throttling retries on the old scale, + // we will start out doing the same thing on the new one. + if (old_throttle_data != nullptr) { + double token_fraction = + static_cast( + gpr_atm_acq_load(&old_throttle_data->milli_tokens_)) / + static_cast(old_throttle_data->max_milli_tokens_); + initial_milli_tokens = + static_cast(token_fraction * max_milli_tokens); + } + gpr_atm_rel_store(&milli_tokens_, static_cast(initial_milli_tokens)); + // If there was a pre-existing entry, mark it as stale and give it a + // pointer to the new entry, which is its replacement. + if (old_throttle_data != nullptr) { + Ref().release(); // Ref held by pre-existing entry. + gpr_atm_rel_store(&old_throttle_data->replacement_, + reinterpret_cast(this)); + } +} + +ServerRetryThrottleData::~ServerRetryThrottleData() { + ServerRetryThrottleData* replacement = + reinterpret_cast( + gpr_atm_acq_load(&replacement_)); + if (replacement != nullptr) { + replacement->Unref(); + } +} + +void ServerRetryThrottleData::GetReplacementThrottleDataIfNeeded( + ServerRetryThrottleData** throttle_data) { + while (true) { + ServerRetryThrottleData* new_throttle_data = + reinterpret_cast( + gpr_atm_acq_load(&(*throttle_data)->replacement_)); + if (new_throttle_data == nullptr) return; + *throttle_data = new_throttle_data; + } +} + +bool ServerRetryThrottleData::RecordFailure() { + // First, check if we are stale and need to be replaced. + ServerRetryThrottleData* throttle_data = this; + GetReplacementThrottleDataIfNeeded(&throttle_data); + // We decrement milli_tokens by 1000 (1 token) for each failure. + const intptr_t new_value = + static_cast(gpr_atm_no_barrier_clamped_add( + &throttle_data->milli_tokens_, static_cast(-1000), + static_cast(0), + static_cast(throttle_data->max_milli_tokens_))); + // Retries are allowed as long as the new value is above the threshold + // (max_milli_tokens / 2). + return new_value > throttle_data->max_milli_tokens_ / 2; +} + +void ServerRetryThrottleData::RecordSuccess() { + // First, check if we are stale and need to be replaced. + ServerRetryThrottleData* throttle_data = this; + GetReplacementThrottleDataIfNeeded(&throttle_data); + // We increment milli_tokens by milli_token_ratio for each success. + gpr_atm_no_barrier_clamped_add( + &throttle_data->milli_tokens_, + static_cast(throttle_data->milli_token_ratio_), + static_cast(0), + static_cast(throttle_data->max_milli_tokens_)); +} + +// +// avl vtable for string -> server_retry_throttle_data map +// + +namespace { + +void* copy_server_name(void* key, void* unused) { + return gpr_strdup(static_cast(key)); +} + +long compare_server_name(void* key1, void* key2, void* unused) { + return strcmp(static_cast(key1), static_cast(key2)); +} + +void destroy_server_retry_throttle_data(void* value, void* unused) { + ServerRetryThrottleData* throttle_data = + static_cast(value); + throttle_data->Unref(); +} + +void* copy_server_retry_throttle_data(void* value, void* unused) { + ServerRetryThrottleData* throttle_data = + static_cast(value); + return throttle_data->Ref().release(); +} + +void destroy_server_name(void* key, void* unused) { gpr_free(key); } + +const grpc_avl_vtable avl_vtable = { + destroy_server_name, copy_server_name, compare_server_name, + destroy_server_retry_throttle_data, copy_server_retry_throttle_data}; + +} // namespace + +// +// ServerRetryThrottleMap +// + +static gpr_mu g_mu; +static grpc_avl g_avl; + +void ServerRetryThrottleMap::Init() { + gpr_mu_init(&g_mu); + g_avl = grpc_avl_create(&avl_vtable); +} + +void ServerRetryThrottleMap::Shutdown() { + gpr_mu_destroy(&g_mu); + grpc_avl_unref(g_avl, nullptr); +} + +RefCountedPtr ServerRetryThrottleMap::GetDataForServer( + const char* server_name, intptr_t max_milli_tokens, + intptr_t milli_token_ratio) { + RefCountedPtr result; + gpr_mu_lock(&g_mu); + ServerRetryThrottleData* throttle_data = + static_cast( + grpc_avl_get(g_avl, const_cast(server_name), nullptr)); + if (throttle_data == nullptr || + throttle_data->max_milli_tokens() != max_milli_tokens || + throttle_data->milli_token_ratio() != milli_token_ratio) { + // Entry not found, or found with old parameters. Create a new one. + result = MakeRefCounted( + max_milli_tokens, milli_token_ratio, throttle_data); + g_avl = grpc_avl_add(g_avl, gpr_strdup(server_name), + result->Ref().release(), nullptr); + } else { + // Entry found. Return a new ref to it. + result = throttle_data->Ref(); + } + gpr_mu_unlock(&g_mu); + return result; +} + +} // namespace internal +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.h b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.h new file mode 100644 index 00000000000000..fddafcd903e5fe --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/retry_throttle.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H + +#include + +#include "src/core/lib/gprpp/ref_counted.h" + +namespace grpc_core { +namespace internal { + +/// Tracks retry throttling data for an individual server name. +class ServerRetryThrottleData : public RefCounted { + public: + ServerRetryThrottleData(intptr_t max_milli_tokens, intptr_t milli_token_ratio, + ServerRetryThrottleData* old_throttle_data); + + /// Records a failure. Returns true if it's okay to send a retry. + bool RecordFailure(); + + /// Records a success. + void RecordSuccess(); + + intptr_t max_milli_tokens() const { return max_milli_tokens_; } + intptr_t milli_token_ratio() const { return milli_token_ratio_; } + + private: + // So Delete() can call our private dtor. + template + friend void grpc_core::Delete(T*); + + ~ServerRetryThrottleData(); + + void GetReplacementThrottleDataIfNeeded( + ServerRetryThrottleData** throttle_data); + + const intptr_t max_milli_tokens_; + const intptr_t milli_token_ratio_; + gpr_atm milli_tokens_; + // A pointer to the replacement for this ServerRetryThrottleData entry. + // If non-nullptr, then this entry is stale and must not be used. + // We hold a reference to the replacement. + gpr_atm replacement_ = 0; +}; + +/// Global map of server name to retry throttle data. +class ServerRetryThrottleMap { + public: + /// Initializes global map of failure data for each server name. + static void Init(); + /// Shuts down global map of failure data for each server name. + static void Shutdown(); + + /// Returns the failure data for \a server_name, creating a new entry if + /// needed. + static RefCountedPtr GetDataForServer( + const char* server_name, intptr_t max_milli_tokens, + intptr_t milli_token_ratio); +}; + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/server_address.cc b/third_party/grpc/src/core/ext/filters/client_channel/server_address.cc new file mode 100644 index 00000000000000..ec33cbbd956a75 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/server_address.cc @@ -0,0 +1,103 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/server_address.h" + +#include + +namespace grpc_core { + +// +// ServerAddress +// + +ServerAddress::ServerAddress(const grpc_resolved_address& address, + grpc_channel_args* args) + : address_(address), args_(args) {} + +ServerAddress::ServerAddress(const void* address, size_t address_len, + grpc_channel_args* args) + : args_(args) { + memcpy(address_.addr, address, address_len); + address_.len = static_cast(address_len); +} + +int ServerAddress::Cmp(const ServerAddress& other) const { + if (address_.len > other.address_.len) return 1; + if (address_.len < other.address_.len) return -1; + int retval = memcmp(address_.addr, other.address_.addr, address_.len); + if (retval != 0) return retval; + return grpc_channel_args_compare(args_, other.args_); +} + +bool ServerAddress::IsBalancer() const { + return grpc_channel_arg_get_bool( + grpc_channel_args_find(args_, GRPC_ARG_ADDRESS_IS_BALANCER), false); +} + +// +// ServerAddressList +// + +namespace { + +void* ServerAddressListCopy(void* addresses) { + ServerAddressList* a = static_cast(addresses); + return New(*a); +} + +void ServerAddressListDestroy(void* addresses) { + ServerAddressList* a = static_cast(addresses); + Delete(a); +} + +int ServerAddressListCompare(void* addresses1, void* addresses2) { + ServerAddressList* a1 = static_cast(addresses1); + ServerAddressList* a2 = static_cast(addresses2); + if (a1->size() > a2->size()) return 1; + if (a1->size() < a2->size()) return -1; + for (size_t i = 0; i < a1->size(); ++i) { + int retval = (*a1)[i].Cmp((*a2)[i]); + if (retval != 0) return retval; + } + return 0; +} + +const grpc_arg_pointer_vtable server_addresses_arg_vtable = { + ServerAddressListCopy, ServerAddressListDestroy, ServerAddressListCompare}; + +} // namespace + +grpc_arg CreateServerAddressListChannelArg(const ServerAddressList* addresses) { + return grpc_channel_arg_pointer_create( + const_cast(GRPC_ARG_SERVER_ADDRESS_LIST), + const_cast(addresses), &server_addresses_arg_vtable); +} + +ServerAddressList* FindServerAddressListChannelArg( + const grpc_channel_args* channel_args) { + const grpc_arg* lb_addresses_arg = + grpc_channel_args_find(channel_args, GRPC_ARG_SERVER_ADDRESS_LIST); + if (lb_addresses_arg == nullptr || lb_addresses_arg->type != GRPC_ARG_POINTER) + return nullptr; + return static_cast(lb_addresses_arg->value.pointer.p); +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/server_address.h b/third_party/grpc/src/core/ext/filters/client_channel/server_address.h new file mode 100644 index 00000000000000..3a1bf1df67d7ba --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/server_address.h @@ -0,0 +1,108 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SERVER_ADDRESS_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SERVER_ADDRESS_H + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/uri/uri_parser.h" + +// Channel arg key for ServerAddressList. +#define GRPC_ARG_SERVER_ADDRESS_LIST "grpc.server_address_list" + +// Channel arg key for a bool indicating whether an address is a grpclb +// load balancer (as opposed to a backend). +#define GRPC_ARG_ADDRESS_IS_BALANCER "grpc.address_is_balancer" + +// Channel arg key for a string indicating an address's balancer name. +#define GRPC_ARG_ADDRESS_BALANCER_NAME "grpc.address_balancer_name" + +namespace grpc_core { + +// +// ServerAddress +// + +// A server address is a grpc_resolved_address with an associated set of +// channel args. Any args present here will be merged into the channel +// args when a subchannel is created for this address. +class ServerAddress { + public: + // Takes ownership of args. + ServerAddress(const grpc_resolved_address& address, grpc_channel_args* args); + ServerAddress(const void* address, size_t address_len, + grpc_channel_args* args); + + ~ServerAddress() { grpc_channel_args_destroy(args_); } + + // Copyable. + ServerAddress(const ServerAddress& other) + : address_(other.address_), args_(grpc_channel_args_copy(other.args_)) {} + ServerAddress& operator=(const ServerAddress& other) { + address_ = other.address_; + grpc_channel_args_destroy(args_); + args_ = grpc_channel_args_copy(other.args_); + return *this; + } + + // Movable. + ServerAddress(ServerAddress&& other) + : address_(other.address_), args_(other.args_) { + other.args_ = nullptr; + } + ServerAddress& operator=(ServerAddress&& other) { + address_ = other.address_; + args_ = other.args_; + other.args_ = nullptr; + return *this; + } + + bool operator==(const ServerAddress& other) const { return Cmp(other) == 0; } + + int Cmp(const ServerAddress& other) const; + + const grpc_resolved_address& address() const { return address_; } + const grpc_channel_args* args() const { return args_; } + + bool IsBalancer() const; + + private: + grpc_resolved_address address_; + grpc_channel_args* args_; +}; + +// +// ServerAddressList +// + +typedef InlinedVector ServerAddressList; + +// Returns a channel arg containing \a addresses. +grpc_arg CreateServerAddressListChannelArg(const ServerAddressList* addresses); + +// Returns the ServerListAddress instance in channel_args or NULL. +ServerAddressList* FindServerAddressListChannelArg( + const grpc_channel_args* channel_args); + +} // namespace grpc_core + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SERVER_ADDRESS_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel.cc b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.cc new file mode 100644 index 00000000000000..9077aa9753919b --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.cc @@ -0,0 +1,1156 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/client_channel/subchannel.h" + +#include +#include + +#include +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/health/health_check_client.h" +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/backoff/backoff.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/alloc.h" +#include "src/core/lib/gprpp/debug_location.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/service_config.h" +#include "src/core/lib/transport/status_metadata.h" +#include "src/core/lib/uri/uri_parser.h" + +#define INTERNAL_REF_BITS 16 +#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1)) + +#define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_SUBCHANNEL_RECONNECT_MIN_TIMEOUT_SECONDS 20 +#define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2 + +namespace { +struct state_watcher { + grpc_closure closure; + grpc_subchannel* subchannel; + grpc_connectivity_state connectivity_state; + grpc_connectivity_state last_connectivity_state; + grpc_core::OrphanablePtr health_check_client; + grpc_closure health_check_closure; + grpc_connectivity_state health_state; +}; +} // namespace + +typedef struct external_state_watcher { + grpc_subchannel* subchannel; + grpc_pollset_set* pollset_set; + grpc_closure* notify; + grpc_closure closure; + struct external_state_watcher* next; + struct external_state_watcher* prev; +} external_state_watcher; + +namespace grpc_core { + +class ConnectedSubchannelStateWatcher; + +} // namespace grpc_core + +struct grpc_subchannel { + grpc_connector* connector; + + /** refcount + - lower INTERNAL_REF_BITS bits are for internal references: + these do not keep the subchannel open. + - upper remaining bits are for public references: these do + keep the subchannel open */ + gpr_atm ref_pair; + + /** non-transport related channel filters */ + const grpc_channel_filter** filters; + size_t num_filters; + /** channel arguments */ + grpc_channel_args* args; + + grpc_subchannel_key* key; + + /** set during connection */ + grpc_connect_out_args connecting_result; + + /** callback for connection finishing */ + grpc_closure on_connected; + + /** callback for our alarm */ + grpc_closure on_alarm; + + /** pollset_set tracking who's interested in a connection + being setup */ + grpc_pollset_set* pollset_set; + + grpc_core::UniquePtr health_check_service_name; + + /** mutex protecting remaining elements */ + gpr_mu mu; + + /** active connection, or null */ + grpc_core::RefCountedPtr connected_subchannel; + grpc_core::OrphanablePtr + connected_subchannel_watcher; + + /** have we seen a disconnection? */ + bool disconnected; + /** are we connecting */ + bool connecting; + + /** connectivity state tracking */ + grpc_connectivity_state_tracker state_tracker; + grpc_connectivity_state_tracker state_and_health_tracker; + + external_state_watcher root_external_state_watcher; + + /** backoff state */ + grpc_core::ManualConstructor backoff; + grpc_millis next_attempt_deadline; + grpc_millis min_connect_timeout_ms; + + /** do we have an active alarm? */ + bool have_alarm; + /** have we started the backoff loop */ + bool backoff_begun; + // reset_backoff() was called while alarm was pending + bool retry_immediately; + /** our alarm */ + grpc_timer alarm; + + grpc_core::RefCountedPtr + channelz_subchannel; +}; + +struct grpc_subchannel_call { + grpc_subchannel_call(grpc_core::ConnectedSubchannel* connection, + const grpc_core::ConnectedSubchannel::CallArgs& args) + : connection(connection), deadline(args.deadline) {} + + grpc_core::ConnectedSubchannel* connection; + grpc_closure* schedule_closure_after_destroy = nullptr; + // state needed to support channelz interception of recv trailing metadata. + grpc_closure recv_trailing_metadata_ready; + grpc_closure* original_recv_trailing_metadata; + grpc_metadata_batch* recv_trailing_metadata = nullptr; + grpc_millis deadline; +}; + +static void maybe_start_connecting_locked(grpc_subchannel* c); + +static const char* subchannel_connectivity_state_change_string( + grpc_connectivity_state state) { + switch (state) { + case GRPC_CHANNEL_IDLE: + return "Subchannel state change to IDLE"; + case GRPC_CHANNEL_CONNECTING: + return "Subchannel state change to CONNECTING"; + case GRPC_CHANNEL_READY: + return "Subchannel state change to READY"; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + return "Subchannel state change to TRANSIENT_FAILURE"; + case GRPC_CHANNEL_SHUTDOWN: + return "Subchannel state change to SHUTDOWN"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +static void set_subchannel_connectivity_state_locked( + grpc_subchannel* c, grpc_connectivity_state state, grpc_error* error, + const char* reason) { + if (c->channelz_subchannel != nullptr) { + c->channelz_subchannel->AddTraceEvent( + grpc_core::channelz::ChannelTrace::Severity::Info, + grpc_slice_from_static_string( + subchannel_connectivity_state_change_string(state))); + } + grpc_connectivity_state_set(&c->state_tracker, state, error, reason); +} + +namespace grpc_core { + +class ConnectedSubchannelStateWatcher + : public InternallyRefCounted { + public: + // Must be instantiated while holding c->mu. + explicit ConnectedSubchannelStateWatcher(grpc_subchannel* c) + : subchannel_(c) { + // Steal subchannel ref for connecting. + GRPC_SUBCHANNEL_WEAK_REF(subchannel_, "state_watcher"); + GRPC_SUBCHANNEL_WEAK_UNREF(subchannel_, "connecting"); + // Start watching for connectivity state changes. + // Callback uses initial ref to this. + GRPC_CLOSURE_INIT(&on_connectivity_changed_, OnConnectivityChanged, this, + grpc_schedule_on_exec_ctx); + c->connected_subchannel->NotifyOnStateChange(c->pollset_set, + &pending_connectivity_state_, + &on_connectivity_changed_); + // Start health check if needed. + grpc_connectivity_state health_state = GRPC_CHANNEL_READY; + if (c->health_check_service_name != nullptr) { + health_check_client_ = grpc_core::MakeOrphanable( + c->health_check_service_name.get(), c->connected_subchannel, + c->pollset_set, c->channelz_subchannel); + GRPC_CLOSURE_INIT(&on_health_changed_, OnHealthChanged, this, + grpc_schedule_on_exec_ctx); + Ref().release(); // Ref for health callback tracked manually. + health_check_client_->NotifyOnHealthChange(&health_state_, + &on_health_changed_); + health_state = GRPC_CHANNEL_CONNECTING; + } + // Report initial state. + set_subchannel_connectivity_state_locked( + c, GRPC_CHANNEL_READY, GRPC_ERROR_NONE, "subchannel_connected"); + grpc_connectivity_state_set(&c->state_and_health_tracker, health_state, + GRPC_ERROR_NONE, "subchannel_connected"); + } + + ~ConnectedSubchannelStateWatcher() { + GRPC_SUBCHANNEL_WEAK_UNREF(subchannel_, "state_watcher"); + } + + void Orphan() override { health_check_client_.reset(); } + + private: + static void OnConnectivityChanged(void* arg, grpc_error* error) { + auto* self = static_cast(arg); + grpc_subchannel* c = self->subchannel_; + { + MutexLock lock(&c->mu); + switch (self->pending_connectivity_state_) { + case GRPC_CHANNEL_TRANSIENT_FAILURE: + case GRPC_CHANNEL_SHUTDOWN: { + if (!c->disconnected && c->connected_subchannel != nullptr) { + if (grpc_trace_stream_refcount.enabled()) { + gpr_log(GPR_INFO, + "Connected subchannel %p of subchannel %p has gone into " + "%s. Attempting to reconnect.", + c->connected_subchannel.get(), c, + grpc_connectivity_state_name( + self->pending_connectivity_state_)); + } + c->connected_subchannel.reset(); + c->connected_subchannel_watcher.reset(); + self->last_connectivity_state_ = GRPC_CHANNEL_TRANSIENT_FAILURE; + set_subchannel_connectivity_state_locked( + c, GRPC_CHANNEL_TRANSIENT_FAILURE, GRPC_ERROR_REF(error), + "reflect_child"); + grpc_connectivity_state_set(&c->state_and_health_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_REF(error), "reflect_child"); + c->backoff_begun = false; + c->backoff->Reset(); + maybe_start_connecting_locked(c); + } else { + self->last_connectivity_state_ = GRPC_CHANNEL_SHUTDOWN; + } + self->health_check_client_.reset(); + break; + } + default: { + // In principle, this should never happen. We should not get + // a callback for READY, because that was the state we started + // this watch from. And a connected subchannel should never go + // from READY to CONNECTING or IDLE. + self->last_connectivity_state_ = self->pending_connectivity_state_; + set_subchannel_connectivity_state_locked( + c, self->pending_connectivity_state_, GRPC_ERROR_REF(error), + "reflect_child"); + if (self->pending_connectivity_state_ != GRPC_CHANNEL_READY) { + grpc_connectivity_state_set(&c->state_and_health_tracker, + self->pending_connectivity_state_, + GRPC_ERROR_REF(error), "reflect_child"); + } + c->connected_subchannel->NotifyOnStateChange( + nullptr, &self->pending_connectivity_state_, + &self->on_connectivity_changed_); + self = nullptr; // So we don't unref below. + } + } + } + // Don't unref until we've released the lock, because this might + // cause the subchannel (which contains the lock) to be destroyed. + if (self != nullptr) self->Unref(); + } + + static void OnHealthChanged(void* arg, grpc_error* error) { + auto* self = static_cast(arg); + if (self->health_state_ == GRPC_CHANNEL_SHUTDOWN) { + self->Unref(); + return; + } + grpc_subchannel* c = self->subchannel_; + MutexLock lock(&c->mu); + if (self->last_connectivity_state_ == GRPC_CHANNEL_READY) { + grpc_connectivity_state_set(&c->state_and_health_tracker, + self->health_state_, GRPC_ERROR_REF(error), + "health_changed"); + } + self->health_check_client_->NotifyOnHealthChange(&self->health_state_, + &self->on_health_changed_); + } + + grpc_subchannel* subchannel_; + grpc_closure on_connectivity_changed_; + grpc_connectivity_state pending_connectivity_state_ = GRPC_CHANNEL_READY; + grpc_connectivity_state last_connectivity_state_ = GRPC_CHANNEL_READY; + grpc_core::OrphanablePtr health_check_client_; + grpc_closure on_health_changed_; + grpc_connectivity_state health_state_ = GRPC_CHANNEL_CONNECTING; +}; + +} // namespace grpc_core + +#define SUBCHANNEL_CALL_TO_CALL_STACK(call) \ + (grpc_call_stack*)((char*)(call) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \ + sizeof(grpc_subchannel_call))) +#define CALLSTACK_TO_SUBCHANNEL_CALL(callstack) \ + (grpc_subchannel_call*)(((char*)(call_stack)) - \ + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \ + sizeof(grpc_subchannel_call))) + +static void on_subchannel_connected(void* subchannel, grpc_error* error); + +#ifndef NDEBUG +#define REF_REASON reason +#define REF_MUTATE_EXTRA_ARGS \ + GRPC_SUBCHANNEL_REF_EXTRA_ARGS, const char* purpose +#define REF_MUTATE_PURPOSE(x) , file, line, reason, x +#else +#define REF_REASON "" +#define REF_MUTATE_EXTRA_ARGS +#define REF_MUTATE_PURPOSE(x) +#endif + +/* + * connection implementation + */ + +static void connection_destroy(void* arg, grpc_error* error) { + grpc_channel_stack* stk = static_cast(arg); + grpc_channel_stack_destroy(stk); + gpr_free(stk); +} + +/* + * grpc_subchannel implementation + */ + +static void subchannel_destroy(void* arg, grpc_error* error) { + grpc_subchannel* c = static_cast(arg); + if (c->channelz_subchannel != nullptr) { + c->channelz_subchannel->AddTraceEvent( + grpc_core::channelz::ChannelTrace::Severity::Info, + grpc_slice_from_static_string("Subchannel destroyed")); + c->channelz_subchannel->MarkSubchannelDestroyed(); + c->channelz_subchannel.reset(); + } + gpr_free((void*)c->filters); + c->health_check_service_name.reset(); + grpc_channel_args_destroy(c->args); + grpc_connectivity_state_destroy(&c->state_tracker); + grpc_connectivity_state_destroy(&c->state_and_health_tracker); + grpc_connector_unref(c->connector); + grpc_pollset_set_destroy(c->pollset_set); + grpc_subchannel_key_destroy(c->key); + gpr_mu_destroy(&c->mu); + gpr_free(c); +} + +static gpr_atm ref_mutate(grpc_subchannel* c, gpr_atm delta, + int barrier REF_MUTATE_EXTRA_ARGS) { + gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) + : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); +#ifndef NDEBUG + if (grpc_trace_stream_refcount.enabled()) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SUBCHANNEL: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c, + purpose, old_val, old_val + delta, reason); + } +#endif + return old_val; +} + +grpc_subchannel* grpc_subchannel_ref( + grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, (1 << INTERNAL_REF_BITS), + 0 REF_MUTATE_PURPOSE("STRONG_REF")); + GPR_ASSERT((old_refs & STRONG_REF_MASK) != 0); + return c; +} + +grpc_subchannel* grpc_subchannel_weak_ref( + grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, 1, 0 REF_MUTATE_PURPOSE("WEAK_REF")); + GPR_ASSERT(old_refs != 0); + return c; +} + +grpc_subchannel* grpc_subchannel_ref_from_weak_ref( + grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + if (!c) return nullptr; + for (;;) { + gpr_atm old_refs = gpr_atm_acq_load(&c->ref_pair); + if (old_refs >= (1 << INTERNAL_REF_BITS)) { + gpr_atm new_refs = old_refs + (1 << INTERNAL_REF_BITS); + if (gpr_atm_rel_cas(&c->ref_pair, old_refs, new_refs)) { + return c; + } + } else { + return nullptr; + } + } +} + +static void disconnect(grpc_subchannel* c) { + grpc_subchannel_index_unregister(c->key, c); + gpr_mu_lock(&c->mu); + GPR_ASSERT(!c->disconnected); + c->disconnected = true; + grpc_connector_shutdown(c->connector, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Subchannel disconnected")); + c->connected_subchannel.reset(); + c->connected_subchannel_watcher.reset(); + gpr_mu_unlock(&c->mu); +} + +void grpc_subchannel_unref(grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + // add a weak ref and subtract a strong ref (atomically) + old_refs = ref_mutate( + c, static_cast(1) - static_cast(1 << INTERNAL_REF_BITS), + 1 REF_MUTATE_PURPOSE("STRONG_UNREF")); + if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) { + disconnect(c); + } + GRPC_SUBCHANNEL_WEAK_UNREF(c, "strong-unref"); +} + +void grpc_subchannel_weak_unref( + grpc_subchannel* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, -static_cast(1), + 1 REF_MUTATE_PURPOSE("WEAK_UNREF")); + if (old_refs == 1) { + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(subchannel_destroy, c, grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + } +} + +static void parse_args_for_backoff_values( + const grpc_channel_args* args, grpc_core::BackOff::Options* backoff_options, + grpc_millis* min_connect_timeout_ms) { + grpc_millis initial_backoff_ms = + GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000; + *min_connect_timeout_ms = + GRPC_SUBCHANNEL_RECONNECT_MIN_TIMEOUT_SECONDS * 1000; + grpc_millis max_backoff_ms = + GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; + bool fixed_reconnect_backoff = false; + if (args != nullptr) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, + "grpc.testing.fixed_reconnect_backoff_ms")) { + fixed_reconnect_backoff = true; + initial_backoff_ms = *min_connect_timeout_ms = max_backoff_ms = + grpc_channel_arg_get_integer( + &args->args[i], + {static_cast(initial_backoff_ms), 100, INT_MAX}); + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_MIN_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + *min_connect_timeout_ms = grpc_channel_arg_get_integer( + &args->args[i], + {static_cast(*min_connect_timeout_ms), 100, INT_MAX}); + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + max_backoff_ms = grpc_channel_arg_get_integer( + &args->args[i], {static_cast(max_backoff_ms), 100, INT_MAX}); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + initial_backoff_ms = grpc_channel_arg_get_integer( + &args->args[i], + {static_cast(initial_backoff_ms), 100, INT_MAX}); + } + } + } + backoff_options->set_initial_backoff(initial_backoff_ms) + .set_multiplier(fixed_reconnect_backoff + ? 1.0 + : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER) + .set_jitter(fixed_reconnect_backoff ? 0.0 + : GRPC_SUBCHANNEL_RECONNECT_JITTER) + .set_max_backoff(max_backoff_ms); +} + +namespace grpc_core { +namespace { + +struct HealthCheckParams { + UniquePtr service_name; + + static void Parse(const grpc_json* field, HealthCheckParams* params) { + if (strcmp(field->key, "healthCheckConfig") == 0) { + if (field->type != GRPC_JSON_OBJECT) return; + for (grpc_json* sub_field = field->child; sub_field != nullptr; + sub_field = sub_field->next) { + if (sub_field->key == nullptr) return; + if (strcmp(sub_field->key, "serviceName") == 0) { + if (params->service_name != nullptr) return; // Duplicate. + if (sub_field->type != GRPC_JSON_STRING) return; + params->service_name.reset(gpr_strdup(sub_field->value)); + } + } + } + } +}; + +} // namespace +} // namespace grpc_core + +grpc_subchannel* grpc_subchannel_create(grpc_connector* connector, + const grpc_subchannel_args* args) { + grpc_subchannel_key* key = grpc_subchannel_key_create(args); + grpc_subchannel* c = grpc_subchannel_index_find(key); + if (c) { + grpc_subchannel_key_destroy(key); + return c; + } + + GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED(); + c = static_cast(gpr_zalloc(sizeof(*c))); + c->key = key; + gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS); + c->connector = connector; + grpc_connector_ref(c->connector); + c->num_filters = args->filter_count; + if (c->num_filters > 0) { + c->filters = static_cast( + gpr_malloc(sizeof(grpc_channel_filter*) * c->num_filters)); + memcpy((void*)c->filters, args->filters, + sizeof(grpc_channel_filter*) * c->num_filters); + } else { + c->filters = nullptr; + } + c->pollset_set = grpc_pollset_set_create(); + grpc_resolved_address* addr = + static_cast(gpr_malloc(sizeof(*addr))); + grpc_get_subchannel_address_arg(args->args, addr); + grpc_resolved_address* new_address = nullptr; + grpc_channel_args* new_args = nullptr; + if (grpc_proxy_mappers_map_address(addr, args->args, &new_address, + &new_args)) { + GPR_ASSERT(new_address != nullptr); + gpr_free(addr); + addr = new_address; + } + static const char* keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS}; + grpc_arg new_arg = grpc_create_subchannel_address_arg(addr); + gpr_free(addr); + c->args = grpc_channel_args_copy_and_add_and_remove( + new_args != nullptr ? new_args : args->args, keys_to_remove, + GPR_ARRAY_SIZE(keys_to_remove), &new_arg, 1); + gpr_free(new_arg.value.string); + if (new_args != nullptr) grpc_channel_args_destroy(new_args); + c->root_external_state_watcher.next = c->root_external_state_watcher.prev = + &c->root_external_state_watcher; + GRPC_CLOSURE_INIT(&c->on_connected, on_subchannel_connected, c, + grpc_schedule_on_exec_ctx); + grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, + "subchannel"); + grpc_connectivity_state_init(&c->state_and_health_tracker, GRPC_CHANNEL_IDLE, + "subchannel"); + grpc_core::BackOff::Options backoff_options; + parse_args_for_backoff_values(args->args, &backoff_options, + &c->min_connect_timeout_ms); + c->backoff.Init(backoff_options); + gpr_mu_init(&c->mu); + + // Check whether we should enable health checking. + const char* service_config_json = grpc_channel_arg_get_string( + grpc_channel_args_find(c->args, GRPC_ARG_SERVICE_CONFIG)); + if (service_config_json != nullptr) { + grpc_core::UniquePtr service_config = + grpc_core::ServiceConfig::Create(service_config_json); + if (service_config != nullptr) { + grpc_core::HealthCheckParams params; + service_config->ParseGlobalParams(grpc_core::HealthCheckParams::Parse, + ¶ms); + c->health_check_service_name = std::move(params.service_name); + } + } + + const grpc_arg* arg = + grpc_channel_args_find(c->args, GRPC_ARG_ENABLE_CHANNELZ); + bool channelz_enabled = + grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT); + arg = grpc_channel_args_find( + c->args, GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE); + const grpc_integer_options options = { + GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX}; + size_t channel_tracer_max_memory = + (size_t)grpc_channel_arg_get_integer(arg, options); + if (channelz_enabled) { + c->channelz_subchannel = + grpc_core::MakeRefCounted( + c, channel_tracer_max_memory); + c->channelz_subchannel->AddTraceEvent( + grpc_core::channelz::ChannelTrace::Severity::Info, + grpc_slice_from_static_string("Subchannel created")); + } + + return grpc_subchannel_index_register(key, c); +} + +grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node( + grpc_subchannel* subchannel) { + return subchannel->channelz_subchannel.get(); +} + +intptr_t grpc_subchannel_get_child_socket_uuid(grpc_subchannel* subchannel) { + if (subchannel->connected_subchannel != nullptr) { + return subchannel->connected_subchannel->socket_uuid(); + } else { + return 0; + } +} + +static void continue_connect_locked(grpc_subchannel* c) { + grpc_connect_in_args args; + args.interested_parties = c->pollset_set; + const grpc_millis min_deadline = + c->min_connect_timeout_ms + grpc_core::ExecCtx::Get()->Now(); + c->next_attempt_deadline = c->backoff->NextAttemptTime(); + args.deadline = std::max(c->next_attempt_deadline, min_deadline); + args.channel_args = c->args; + set_subchannel_connectivity_state_locked(c, GRPC_CHANNEL_CONNECTING, + GRPC_ERROR_NONE, "connecting"); + grpc_connectivity_state_set(&c->state_and_health_tracker, + GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE, + "connecting"); + grpc_connector_connect(c->connector, &args, &c->connecting_result, + &c->on_connected); +} + +grpc_connectivity_state grpc_subchannel_check_connectivity( + grpc_subchannel* c, grpc_error** error, bool inhibit_health_checks) { + gpr_mu_lock(&c->mu); + grpc_connectivity_state_tracker* tracker = + inhibit_health_checks ? &c->state_tracker : &c->state_and_health_tracker; + grpc_connectivity_state state = grpc_connectivity_state_get(tracker, error); + gpr_mu_unlock(&c->mu); + return state; +} + +static void on_external_state_watcher_done(void* arg, grpc_error* error) { + external_state_watcher* w = static_cast(arg); + grpc_closure* follow_up = w->notify; + if (w->pollset_set != nullptr) { + grpc_pollset_set_del_pollset_set(w->subchannel->pollset_set, + w->pollset_set); + } + gpr_mu_lock(&w->subchannel->mu); + w->next->prev = w->prev; + w->prev->next = w->next; + gpr_mu_unlock(&w->subchannel->mu); + GRPC_SUBCHANNEL_WEAK_UNREF(w->subchannel, "external_state_watcher"); + gpr_free(w); + GRPC_CLOSURE_SCHED(follow_up, GRPC_ERROR_REF(error)); +} + +static void on_alarm(void* arg, grpc_error* error) { + grpc_subchannel* c = static_cast(arg); + gpr_mu_lock(&c->mu); + c->have_alarm = false; + if (c->disconnected) { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Disconnected", + &error, 1); + } else if (c->retry_immediately) { + c->retry_immediately = false; + error = GRPC_ERROR_NONE; + } else { + GRPC_ERROR_REF(error); + } + if (error == GRPC_ERROR_NONE) { + gpr_log(GPR_INFO, "Failed to connect to channel, retrying"); + continue_connect_locked(c); + gpr_mu_unlock(&c->mu); + } else { + gpr_mu_unlock(&c->mu); + GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting"); + } + GRPC_ERROR_UNREF(error); +} + +static void maybe_start_connecting_locked(grpc_subchannel* c) { + if (c->disconnected) { + /* Don't try to connect if we're already disconnected */ + return; + } + if (c->connecting) { + /* Already connecting: don't restart */ + return; + } + if (c->connected_subchannel != nullptr) { + /* Already connected: don't restart */ + return; + } + if (!grpc_connectivity_state_has_watchers(&c->state_tracker) && + !grpc_connectivity_state_has_watchers(&c->state_and_health_tracker)) { + /* Nobody is interested in connecting: so don't just yet */ + return; + } + c->connecting = true; + GRPC_SUBCHANNEL_WEAK_REF(c, "connecting"); + if (!c->backoff_begun) { + c->backoff_begun = true; + continue_connect_locked(c); + } else { + GPR_ASSERT(!c->have_alarm); + c->have_alarm = true; + const grpc_millis time_til_next = + c->next_attempt_deadline - grpc_core::ExecCtx::Get()->Now(); + if (time_til_next <= 0) { + gpr_log(GPR_INFO, "Subchannel %p: Retry immediately", c); + } else { + gpr_log(GPR_INFO, "Subchannel %p: Retry in %" PRId64 " milliseconds", c, + time_til_next); + } + GRPC_CLOSURE_INIT(&c->on_alarm, on_alarm, c, grpc_schedule_on_exec_ctx); + grpc_timer_init(&c->alarm, c->next_attempt_deadline, &c->on_alarm); + } +} + +void grpc_subchannel_notify_on_state_change( + grpc_subchannel* c, grpc_pollset_set* interested_parties, + grpc_connectivity_state* state, grpc_closure* notify, + bool inhibit_health_checks) { + grpc_connectivity_state_tracker* tracker = + inhibit_health_checks ? &c->state_tracker : &c->state_and_health_tracker; + external_state_watcher* w; + if (state == nullptr) { + gpr_mu_lock(&c->mu); + for (w = c->root_external_state_watcher.next; + w != &c->root_external_state_watcher; w = w->next) { + if (w->notify == notify) { + grpc_connectivity_state_notify_on_state_change(tracker, nullptr, + &w->closure); + } + } + gpr_mu_unlock(&c->mu); + } else { + w = static_cast(gpr_malloc(sizeof(*w))); + w->subchannel = c; + w->pollset_set = interested_parties; + w->notify = notify; + GRPC_CLOSURE_INIT(&w->closure, on_external_state_watcher_done, w, + grpc_schedule_on_exec_ctx); + if (interested_parties != nullptr) { + grpc_pollset_set_add_pollset_set(c->pollset_set, interested_parties); + } + GRPC_SUBCHANNEL_WEAK_REF(c, "external_state_watcher"); + gpr_mu_lock(&c->mu); + w->next = &c->root_external_state_watcher; + w->prev = w->next->prev; + w->next->prev = w->prev->next = w; + grpc_connectivity_state_notify_on_state_change(tracker, state, &w->closure); + maybe_start_connecting_locked(c); + gpr_mu_unlock(&c->mu); + } +} + +static bool publish_transport_locked(grpc_subchannel* c) { + /* construct channel stack */ + grpc_channel_stack_builder* builder = grpc_channel_stack_builder_create(); + grpc_channel_stack_builder_set_channel_arguments( + builder, c->connecting_result.channel_args); + grpc_channel_stack_builder_set_transport(builder, + c->connecting_result.transport); + + if (!grpc_channel_init_create_stack(builder, GRPC_CLIENT_SUBCHANNEL)) { + grpc_channel_stack_builder_destroy(builder); + return false; + } + grpc_channel_stack* stk; + grpc_error* error = grpc_channel_stack_builder_finish( + builder, 0, 1, connection_destroy, nullptr, + reinterpret_cast(&stk)); + if (error != GRPC_ERROR_NONE) { + grpc_transport_destroy(c->connecting_result.transport); + gpr_log(GPR_ERROR, "error initializing subchannel stack: %s", + grpc_error_string(error)); + GRPC_ERROR_UNREF(error); + return false; + } + intptr_t socket_uuid = c->connecting_result.socket_uuid; + memset(&c->connecting_result, 0, sizeof(c->connecting_result)); + + if (c->disconnected) { + grpc_channel_stack_destroy(stk); + gpr_free(stk); + return false; + } + + /* publish */ + c->connected_subchannel.reset(grpc_core::New( + stk, c->args, c->channelz_subchannel, socket_uuid)); + gpr_log(GPR_INFO, "New connected subchannel at %p for subchannel %p", + c->connected_subchannel.get(), c); + + // Instantiate state watcher. Will clean itself up. + c->connected_subchannel_watcher = + grpc_core::MakeOrphanable(c); + + return true; +} + +static void on_subchannel_connected(void* arg, grpc_error* error) { + grpc_subchannel* c = static_cast(arg); + grpc_channel_args* delete_channel_args = c->connecting_result.channel_args; + + GRPC_SUBCHANNEL_WEAK_REF(c, "on_subchannel_connected"); + gpr_mu_lock(&c->mu); + c->connecting = false; + if (c->connecting_result.transport != nullptr && + publish_transport_locked(c)) { + /* do nothing, transport was published */ + } else if (c->disconnected) { + GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting"); + } else { + set_subchannel_connectivity_state_locked( + c, GRPC_CHANNEL_TRANSIENT_FAILURE, + grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Connect Failed", &error, 1), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), + "connect_failed"); + grpc_connectivity_state_set( + &c->state_and_health_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Connect Failed", &error, 1), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), + "connect_failed"); + + const char* errmsg = grpc_error_string(error); + gpr_log(GPR_INFO, "Connect failed: %s", errmsg); + + maybe_start_connecting_locked(c); + GRPC_SUBCHANNEL_WEAK_UNREF(c, "connecting"); + } + gpr_mu_unlock(&c->mu); + GRPC_SUBCHANNEL_WEAK_UNREF(c, "connected"); + grpc_channel_args_destroy(delete_channel_args); +} + +void grpc_subchannel_reset_backoff(grpc_subchannel* subchannel) { + gpr_mu_lock(&subchannel->mu); + subchannel->backoff->Reset(); + if (subchannel->have_alarm) { + subchannel->retry_immediately = true; + grpc_timer_cancel(&subchannel->alarm); + } else { + subchannel->backoff_begun = false; + maybe_start_connecting_locked(subchannel); + } + gpr_mu_unlock(&subchannel->mu); +} + +/* + * grpc_subchannel_call implementation + */ + +static void subchannel_call_destroy(void* call, grpc_error* error) { + GPR_TIMER_SCOPE("grpc_subchannel_call_unref.destroy", 0); + grpc_subchannel_call* c = static_cast(call); + grpc_core::ConnectedSubchannel* connection = c->connection; + grpc_call_stack_destroy(SUBCHANNEL_CALL_TO_CALL_STACK(c), nullptr, + c->schedule_closure_after_destroy); + connection->Unref(DEBUG_LOCATION, "subchannel_call"); + c->~grpc_subchannel_call(); +} + +void grpc_subchannel_call_set_cleanup_closure(grpc_subchannel_call* call, + grpc_closure* closure) { + GPR_ASSERT(call->schedule_closure_after_destroy == nullptr); + GPR_ASSERT(closure != nullptr); + call->schedule_closure_after_destroy = closure; +} + +grpc_subchannel_call* grpc_subchannel_call_ref( + grpc_subchannel_call* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); + return c; +} + +void grpc_subchannel_call_unref( + grpc_subchannel_call* c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CALL_STACK_UNREF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); +} + +// Sets *status based on md_batch and error. +static void get_call_status(grpc_subchannel_call* call, + grpc_metadata_batch* md_batch, grpc_error* error, + grpc_status_code* status) { + if (error != GRPC_ERROR_NONE) { + grpc_error_get_status(error, call->deadline, status, nullptr, nullptr, + nullptr); + } else { + if (md_batch->idx.named.grpc_status != nullptr) { + *status = grpc_get_status_code_from_metadata( + md_batch->idx.named.grpc_status->md); + } else { + *status = GRPC_STATUS_UNKNOWN; + } + } + GRPC_ERROR_UNREF(error); +} + +static void recv_trailing_metadata_ready(void* arg, grpc_error* error) { + grpc_subchannel_call* call = static_cast(arg); + GPR_ASSERT(call->recv_trailing_metadata != nullptr); + grpc_status_code status = GRPC_STATUS_OK; + grpc_metadata_batch* md_batch = call->recv_trailing_metadata; + get_call_status(call, md_batch, GRPC_ERROR_REF(error), &status); + grpc_core::channelz::SubchannelNode* channelz_subchannel = + call->connection->channelz_subchannel(); + GPR_ASSERT(channelz_subchannel != nullptr); + if (status == GRPC_STATUS_OK) { + channelz_subchannel->RecordCallSucceeded(); + } else { + channelz_subchannel->RecordCallFailed(); + } + GRPC_CLOSURE_RUN(call->original_recv_trailing_metadata, + GRPC_ERROR_REF(error)); +} + +// If channelz is enabled, intercept recv_trailing so that we may check the +// status and associate it to a subchannel. +static void maybe_intercept_recv_trailing_metadata( + grpc_subchannel_call* call, grpc_transport_stream_op_batch* batch) { + // only intercept payloads with recv trailing. + if (!batch->recv_trailing_metadata) { + return; + } + // only add interceptor is channelz is enabled. + if (call->connection->channelz_subchannel() == nullptr) { + return; + } + GRPC_CLOSURE_INIT(&call->recv_trailing_metadata_ready, + recv_trailing_metadata_ready, call, + grpc_schedule_on_exec_ctx); + // save some state needed for the interception callback. + GPR_ASSERT(call->recv_trailing_metadata == nullptr); + call->recv_trailing_metadata = + batch->payload->recv_trailing_metadata.recv_trailing_metadata; + call->original_recv_trailing_metadata = + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &call->recv_trailing_metadata_ready; +} + +void grpc_subchannel_call_process_op(grpc_subchannel_call* call, + grpc_transport_stream_op_batch* batch) { + GPR_TIMER_SCOPE("grpc_subchannel_call_process_op", 0); + maybe_intercept_recv_trailing_metadata(call, batch); + grpc_call_stack* call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); + grpc_call_element* top_elem = grpc_call_stack_element(call_stack, 0); + GRPC_CALL_LOG_OP(GPR_INFO, top_elem, batch); + top_elem->filter->start_transport_stream_op_batch(top_elem, batch); +} + +grpc_core::RefCountedPtr +grpc_subchannel_get_connected_subchannel(grpc_subchannel* c) { + gpr_mu_lock(&c->mu); + auto copy = c->connected_subchannel; + gpr_mu_unlock(&c->mu); + return copy; +} + +const grpc_subchannel_key* grpc_subchannel_get_key( + const grpc_subchannel* subchannel) { + return subchannel->key; +} + +void* grpc_connected_subchannel_call_get_parent_data( + grpc_subchannel_call* subchannel_call) { + grpc_channel_stack* chanstk = subchannel_call->connection->channel_stack(); + return (char*)subchannel_call + sizeof(grpc_subchannel_call) + + chanstk->call_stack_size; +} + +grpc_call_stack* grpc_subchannel_call_get_call_stack( + grpc_subchannel_call* subchannel_call) { + return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call); +} + +static void grpc_uri_to_sockaddr(const char* uri_str, + grpc_resolved_address* addr) { + grpc_uri* uri = grpc_uri_parse(uri_str, 0 /* suppress_errors */); + GPR_ASSERT(uri != nullptr); + if (!grpc_parse_uri(uri, addr)) memset(addr, 0, sizeof(*addr)); + grpc_uri_destroy(uri); +} + +void grpc_get_subchannel_address_arg(const grpc_channel_args* args, + grpc_resolved_address* addr) { + const char* addr_uri_str = grpc_get_subchannel_address_uri_arg(args); + memset(addr, 0, sizeof(*addr)); + if (*addr_uri_str != '\0') { + grpc_uri_to_sockaddr(addr_uri_str, addr); + } +} + +const char* grpc_subchannel_get_target(grpc_subchannel* subchannel) { + const grpc_arg* addr_arg = + grpc_channel_args_find(subchannel->args, GRPC_ARG_SUBCHANNEL_ADDRESS); + const char* addr_str = grpc_channel_arg_get_string(addr_arg); + GPR_ASSERT(addr_str != nullptr); // Should have been set by LB policy. + return addr_str; +} + +const char* grpc_get_subchannel_address_uri_arg(const grpc_channel_args* args) { + const grpc_arg* addr_arg = + grpc_channel_args_find(args, GRPC_ARG_SUBCHANNEL_ADDRESS); + const char* addr_str = grpc_channel_arg_get_string(addr_arg); + GPR_ASSERT(addr_str != nullptr); // Should have been set by LB policy. + return addr_str; +} + +grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address* addr) { + return grpc_channel_arg_string_create( + (char*)GRPC_ARG_SUBCHANNEL_ADDRESS, + addr->len > 0 ? grpc_sockaddr_to_uri(addr) : gpr_strdup("")); +} + +namespace grpc_core { + +ConnectedSubchannel::ConnectedSubchannel( + grpc_channel_stack* channel_stack, const grpc_channel_args* args, + grpc_core::RefCountedPtr + channelz_subchannel, + intptr_t socket_uuid) + : RefCounted(&grpc_trace_stream_refcount), + channel_stack_(channel_stack), + args_(grpc_channel_args_copy(args)), + channelz_subchannel_(std::move(channelz_subchannel)), + socket_uuid_(socket_uuid) {} + +ConnectedSubchannel::~ConnectedSubchannel() { + grpc_channel_args_destroy(args_); + GRPC_CHANNEL_STACK_UNREF(channel_stack_, "connected_subchannel_dtor"); +} + +void ConnectedSubchannel::NotifyOnStateChange( + grpc_pollset_set* interested_parties, grpc_connectivity_state* state, + grpc_closure* closure) { + grpc_transport_op* op = grpc_make_transport_op(nullptr); + grpc_channel_element* elem; + op->connectivity_state = state; + op->on_connectivity_state_change = closure; + op->bind_pollset_set = interested_parties; + elem = grpc_channel_stack_element(channel_stack_, 0); + elem->filter->start_transport_op(elem, op); +} + +void ConnectedSubchannel::Ping(grpc_closure* on_initiate, + grpc_closure* on_ack) { + grpc_transport_op* op = grpc_make_transport_op(nullptr); + grpc_channel_element* elem; + op->send_ping.on_initiate = on_initiate; + op->send_ping.on_ack = on_ack; + elem = grpc_channel_stack_element(channel_stack_, 0); + elem->filter->start_transport_op(elem, op); +} + +grpc_error* ConnectedSubchannel::CreateCall(const CallArgs& args, + grpc_subchannel_call** call) { + const size_t allocation_size = + GetInitialCallSizeEstimate(args.parent_data_size); + *call = new (gpr_arena_alloc(args.arena, allocation_size)) + grpc_subchannel_call(this, args); + grpc_call_stack* callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call); + RefCountedPtr connection = + Ref(DEBUG_LOCATION, "subchannel_call"); + connection.release(); // Ref is passed to the grpc_subchannel_call object. + const grpc_call_element_args call_args = { + callstk, /* call_stack */ + nullptr, /* server_transport_data */ + args.context, /* context */ + args.path, /* path */ + args.start_time, /* start_time */ + args.deadline, /* deadline */ + args.arena, /* arena */ + args.call_combiner /* call_combiner */ + }; + grpc_error* error = grpc_call_stack_init( + channel_stack_, 1, subchannel_call_destroy, *call, &call_args); + if (GPR_UNLIKELY(error != GRPC_ERROR_NONE)) { + const char* error_string = grpc_error_string(error); + gpr_log(GPR_ERROR, "error: %s", error_string); + return error; + } + grpc_call_stack_set_pollset_or_pollset_set(callstk, args.pollent); + if (channelz_subchannel_ != nullptr) { + channelz_subchannel_->RecordCallStarted(); + } + return GRPC_ERROR_NONE; +} + +size_t ConnectedSubchannel::GetInitialCallSizeEstimate( + size_t parent_data_size) const { + size_t allocation_size = + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_subchannel_call)); + if (parent_data_size > 0) { + allocation_size += + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(channel_stack_->call_stack_size) + + parent_data_size; + } else { + allocation_size += channel_stack_->call_stack_size; + } + return allocation_size; +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel.h b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.h new file mode 100644 index 00000000000000..14f87f2c68e77a --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel.h @@ -0,0 +1,218 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H + +#include + +#include "src/core/ext/filters/client_channel/client_channel_channelz.h" +#include "src/core/ext/filters/client_channel/connector.h" +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/gpr/arena.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/metadata.h" + +// Channel arg containing a grpc_resolved_address to connect to. +#define GRPC_ARG_SUBCHANNEL_ADDRESS "grpc.subchannel_address" + +/** 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; +typedef struct grpc_subchannel_key grpc_subchannel_key; + +#ifndef NDEBUG +#define GRPC_SUBCHANNEL_REF(p, r) \ + grpc_subchannel_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(p, r) \ + grpc_subchannel_ref_from_weak_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_UNREF(p, r) \ + grpc_subchannel_unref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_WEAK_REF(p, r) \ + grpc_subchannel_weak_ref((p), __FILE__, __LINE__, (r)) +#define GRPC_SUBCHANNEL_WEAK_UNREF(p, r) \ + grpc_subchannel_weak_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_REF_FROM_WEAK_REF(p, r) \ + grpc_subchannel_ref_from_weak_ref((p)) +#define GRPC_SUBCHANNEL_UNREF(p, r) grpc_subchannel_unref((p)) +#define GRPC_SUBCHANNEL_WEAK_REF(p, r) grpc_subchannel_weak_ref((p)) +#define GRPC_SUBCHANNEL_WEAK_UNREF(p, r) grpc_subchannel_weak_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 + +namespace grpc_core { + +class ConnectedSubchannel : public RefCounted { + public: + struct CallArgs { + grpc_polling_entity* pollent; + grpc_slice path; + gpr_timespec start_time; + grpc_millis deadline; + gpr_arena* arena; + grpc_call_context_element* context; + grpc_call_combiner* call_combiner; + size_t parent_data_size; + }; + + ConnectedSubchannel( + grpc_channel_stack* channel_stack, const grpc_channel_args* args, + grpc_core::RefCountedPtr + channelz_subchannel, + intptr_t socket_uuid); + ~ConnectedSubchannel(); + + void NotifyOnStateChange(grpc_pollset_set* interested_parties, + grpc_connectivity_state* state, + grpc_closure* closure); + void Ping(grpc_closure* on_initiate, grpc_closure* on_ack); + grpc_error* CreateCall(const CallArgs& args, grpc_subchannel_call** call); + + grpc_channel_stack* channel_stack() const { return channel_stack_; } + const grpc_channel_args* args() const { return args_; } + channelz::SubchannelNode* channelz_subchannel() const { + return channelz_subchannel_.get(); + } + intptr_t socket_uuid() const { return socket_uuid_; } + + size_t GetInitialCallSizeEstimate(size_t parent_data_size) const; + + private: + grpc_channel_stack* channel_stack_; + grpc_channel_args* args_; + // ref counted pointer to the channelz node in this connected subchannel's + // owning subchannel. + grpc_core::RefCountedPtr + channelz_subchannel_; + // uuid of this subchannel's socket. 0 if this subchannel is not connected. + const intptr_t socket_uuid_; +}; + +} // namespace grpc_core + +grpc_subchannel* grpc_subchannel_ref( + grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +grpc_subchannel* grpc_subchannel_ref_from_weak_ref( + grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_unref( + grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +grpc_subchannel* grpc_subchannel_weak_ref( + grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +void grpc_subchannel_weak_unref( + grpc_subchannel* channel GRPC_SUBCHANNEL_REF_EXTRA_ARGS); +grpc_subchannel_call* 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); + +grpc_core::channelz::SubchannelNode* grpc_subchannel_get_channelz_node( + grpc_subchannel* subchannel); + +intptr_t grpc_subchannel_get_child_socket_uuid(grpc_subchannel* subchannel); + +/** Returns a pointer to the parent data associated with \a subchannel_call. + The data will be of the size specified in \a parent_data_size + field of the args passed to \a grpc_connected_subchannel_create_call(). */ +void* grpc_connected_subchannel_call_get_parent_data( + grpc_subchannel_call* subchannel_call); + +/** poll the current connectivity state of a channel */ +grpc_connectivity_state grpc_subchannel_check_connectivity( + grpc_subchannel* channel, grpc_error** error, bool inhibit_health_checking); + +/** Calls notify when the connectivity state of a channel becomes different + from *state. Updates *state with the new state of the channel. */ +void grpc_subchannel_notify_on_state_change( + grpc_subchannel* channel, grpc_pollset_set* interested_parties, + grpc_connectivity_state* state, grpc_closure* notify, + bool inhibit_health_checks); + +/** retrieve the grpc_core::ConnectedSubchannel - or nullptr if not connected + * (which may happen before it initially connects or during transient failures) + * */ +grpc_core::RefCountedPtr +grpc_subchannel_get_connected_subchannel(grpc_subchannel* c); + +/** return the subchannel index key for \a subchannel */ +const grpc_subchannel_key* grpc_subchannel_get_key( + const grpc_subchannel* subchannel); + +// Resets the connection backoff of the subchannel. +// TODO(roth): Move connection backoff out of subchannels and up into LB +// policy code (probably by adding a SubchannelGroup between +// SubchannelList and SubchannelData), at which point this method can +// go away. +void grpc_subchannel_reset_backoff(grpc_subchannel* subchannel); + +/** continue processing a transport op */ +void grpc_subchannel_call_process_op(grpc_subchannel_call* subchannel_call, + grpc_transport_stream_op_batch* op); + +/** Must be called once per call. Sets the 'then_schedule_closure' argument for + call stack destruction. */ +void grpc_subchannel_call_set_cleanup_closure( + grpc_subchannel_call* subchannel_call, grpc_closure* closure); + +grpc_call_stack* grpc_subchannel_call_get_call_stack( + grpc_subchannel_call* subchannel_call); + +struct grpc_subchannel_args { + /* When updating this struct, also update subchannel_index.c */ + + /** 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; +}; + +/** create a subchannel given a connector */ +grpc_subchannel* grpc_subchannel_create(grpc_connector* connector, + const grpc_subchannel_args* args); + +/// Sets \a addr from \a args. +void grpc_get_subchannel_address_arg(const grpc_channel_args* args, + grpc_resolved_address* addr); + +const char* grpc_subchannel_get_target(grpc_subchannel* subchannel); + +/// Returns the URI string for the address to connect to. +const char* grpc_get_subchannel_address_uri_arg(const grpc_channel_args* args); + +/// Returns a new channel arg encoding the subchannel address as a string. +/// Caller is responsible for freeing the string. +grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address* addr); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H */ diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.cc b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.cc new file mode 100644 index 00000000000000..aa8441f17b099f --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.cc @@ -0,0 +1,248 @@ +// +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include + +#include "src/core/ext/filters/client_channel/subchannel_index.h" + +#include +#include + +#include +#include + +#include "src/core/lib/avl/avl.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/tls.h" + +// a map of subchannel_key --> subchannel, used for detecting connections +// to the same destination in order to share them +static grpc_avl g_subchannel_index; + +static gpr_mu g_mu; + +static gpr_refcount g_refcount; + +struct grpc_subchannel_key { + grpc_subchannel_args args; +}; + +static bool g_force_creation = false; + +static grpc_subchannel_key* create_key( + const grpc_subchannel_args* args, + grpc_channel_args* (*copy_channel_args)(const grpc_channel_args* args)) { + grpc_subchannel_key* k = + static_cast(gpr_malloc(sizeof(*k))); + k->args.filter_count = args->filter_count; + if (k->args.filter_count > 0) { + k->args.filters = static_cast( + gpr_malloc(sizeof(*k->args.filters) * k->args.filter_count)); + memcpy(reinterpret_cast(k->args.filters), + args->filters, sizeof(*k->args.filters) * k->args.filter_count); + } else { + k->args.filters = nullptr; + } + k->args.args = copy_channel_args(args->args); + return k; +} + +grpc_subchannel_key* grpc_subchannel_key_create( + const grpc_subchannel_args* args) { + return create_key(args, grpc_channel_args_normalize); +} + +static grpc_subchannel_key* subchannel_key_copy(grpc_subchannel_key* k) { + return create_key(&k->args, grpc_channel_args_copy); +} + +int grpc_subchannel_key_compare(const grpc_subchannel_key* a, + const grpc_subchannel_key* b) { + // To pretend the keys are different, return a non-zero value. + if (GPR_UNLIKELY(g_force_creation)) return 1; + int c = GPR_ICMP(a->args.filter_count, b->args.filter_count); + if (c != 0) return c; + if (a->args.filter_count > 0) { + c = memcmp(a->args.filters, b->args.filters, + a->args.filter_count * sizeof(*a->args.filters)); + if (c != 0) return c; + } + return grpc_channel_args_compare(a->args.args, b->args.args); +} + +void grpc_subchannel_key_destroy(grpc_subchannel_key* k) { + gpr_free(reinterpret_cast(k->args.filters)); + grpc_channel_args_destroy(const_cast(k->args.args)); + gpr_free(k); +} + +static void sck_avl_destroy(void* p, void* unused) { + grpc_subchannel_key_destroy(static_cast(p)); +} + +static void* sck_avl_copy(void* p, void* unused) { + return subchannel_key_copy(static_cast(p)); +} + +static long sck_avl_compare(void* a, void* b, void* unused) { + return grpc_subchannel_key_compare(static_cast(a), + static_cast(b)); +} + +static void scv_avl_destroy(void* p, void* unused) { + GRPC_SUBCHANNEL_WEAK_UNREF((grpc_subchannel*)p, "subchannel_index"); +} + +static void* scv_avl_copy(void* p, void* unused) { + GRPC_SUBCHANNEL_WEAK_REF((grpc_subchannel*)p, "subchannel_index"); + return p; +} + +static const grpc_avl_vtable subchannel_avl_vtable = { + sck_avl_destroy, // destroy_key + sck_avl_copy, // copy_key + sck_avl_compare, // compare_keys + scv_avl_destroy, // destroy_value + scv_avl_copy // copy_value +}; + +void grpc_subchannel_index_init(void) { + g_subchannel_index = grpc_avl_create(&subchannel_avl_vtable); + gpr_mu_init(&g_mu); + gpr_ref_init(&g_refcount, 1); +} + +void grpc_subchannel_index_shutdown(void) { + // TODO(juanlishen): This refcounting mechanism may lead to memory leackage. + // To solve that, we should force polling to flush any pending callbacks, then + // shutdown safely. + grpc_subchannel_index_unref(); +} + +void grpc_subchannel_index_unref(void) { + if (gpr_unref(&g_refcount)) { + gpr_mu_destroy(&g_mu); + grpc_avl_unref(g_subchannel_index, nullptr); + } +} + +void grpc_subchannel_index_ref(void) { gpr_ref_non_zero(&g_refcount); } + +grpc_subchannel* grpc_subchannel_index_find(grpc_subchannel_key* key) { + // Lock, and take a reference to the subchannel index. + // We don't need to do the search under a lock as avl's are immutable. + gpr_mu_lock(&g_mu); + grpc_avl index = grpc_avl_ref(g_subchannel_index, nullptr); + gpr_mu_unlock(&g_mu); + + grpc_subchannel* c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF( + (grpc_subchannel*)grpc_avl_get(index, key, nullptr), "index_find"); + grpc_avl_unref(index, nullptr); + + return c; +} + +grpc_subchannel* grpc_subchannel_index_register(grpc_subchannel_key* key, + grpc_subchannel* constructed) { + grpc_subchannel* c = nullptr; + bool need_to_unref_constructed = false; + + while (c == nullptr) { + need_to_unref_constructed = false; + + // Compare and swap loop: + // - take a reference to the current index + gpr_mu_lock(&g_mu); + grpc_avl index = grpc_avl_ref(g_subchannel_index, nullptr); + gpr_mu_unlock(&g_mu); + + // - Check to see if a subchannel already exists + c = static_cast(grpc_avl_get(index, key, nullptr)); + if (c != nullptr) { + c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(c, "index_register"); + } + if (c != nullptr) { + // yes -> we're done + need_to_unref_constructed = true; + } else { + // no -> update the avl and compare/swap + grpc_avl updated = grpc_avl_add( + grpc_avl_ref(index, nullptr), subchannel_key_copy(key), + GRPC_SUBCHANNEL_WEAK_REF(constructed, "index_register"), nullptr); + + // it may happen (but it's expected to be unlikely) + // that some other thread has changed the index: + // compare/swap here to check that, and retry as necessary + gpr_mu_lock(&g_mu); + if (index.root == g_subchannel_index.root) { + GPR_SWAP(grpc_avl, updated, g_subchannel_index); + c = constructed; + } + gpr_mu_unlock(&g_mu); + + grpc_avl_unref(updated, nullptr); + } + grpc_avl_unref(index, nullptr); + } + + if (need_to_unref_constructed) { + GRPC_SUBCHANNEL_UNREF(constructed, "index_register"); + } + + return c; +} + +void grpc_subchannel_index_unregister(grpc_subchannel_key* key, + grpc_subchannel* constructed) { + bool done = false; + while (!done) { + // Compare and swap loop: + // - take a reference to the current index + gpr_mu_lock(&g_mu); + grpc_avl index = grpc_avl_ref(g_subchannel_index, nullptr); + gpr_mu_unlock(&g_mu); + + // Check to see if this key still refers to the previously + // registered subchannel + grpc_subchannel* c = + static_cast(grpc_avl_get(index, key, nullptr)); + if (c != constructed) { + grpc_avl_unref(index, nullptr); + break; + } + + // compare and swap the update (some other thread may have + // mutated the index behind us) + grpc_avl updated = + grpc_avl_remove(grpc_avl_ref(index, nullptr), key, nullptr); + + gpr_mu_lock(&g_mu); + if (index.root == g_subchannel_index.root) { + GPR_SWAP(grpc_avl, updated, g_subchannel_index); + done = true; + } + gpr_mu_unlock(&g_mu); + + grpc_avl_unref(updated, nullptr); + grpc_avl_unref(index, nullptr); + } +} + +void grpc_subchannel_index_test_only_set_force_creation(bool force_creation) { + g_force_creation = force_creation; +} diff --git a/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.h b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.h new file mode 100644 index 00000000000000..c135613d26bd72 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/client_channel/subchannel_index.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H +#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H + +#include + +#include "src/core/ext/filters/client_channel/subchannel.h" + +/** \file Provides an index of active subchannels so that they can be + shared amongst channels */ + +/** Create a key that can be used to uniquely identify a subchannel */ +grpc_subchannel_key* grpc_subchannel_key_create( + const grpc_subchannel_args* args); + +/** Destroy a subchannel key */ +void grpc_subchannel_key_destroy(grpc_subchannel_key* key); + +/** Given a subchannel key, find the subchannel registered for it. + Returns NULL if no such channel exists. + Thread-safe. */ +grpc_subchannel* grpc_subchannel_index_find(grpc_subchannel_key* key); + +/** Register a subchannel against a key. + Takes ownership of \a constructed. + Returns the registered subchannel. This may be different from + \a constructed in the case of a registration race. */ +grpc_subchannel* grpc_subchannel_index_register(grpc_subchannel_key* key, + grpc_subchannel* constructed); + +/** Remove \a constructed as the registered subchannel for \a key. */ +void grpc_subchannel_index_unregister(grpc_subchannel_key* key, + grpc_subchannel* constructed); + +int grpc_subchannel_key_compare(const grpc_subchannel_key* a, + const grpc_subchannel_key* b); + +/** Initialize the subchannel index (global) */ +void grpc_subchannel_index_init(void); +/** Shutdown the subchannel index (global) */ +void grpc_subchannel_index_shutdown(void); + +/** Increment the refcount (non-zero) of subchannel index (global). */ +void grpc_subchannel_index_ref(void); + +/** Decrement the refcount of subchannel index (global). If the refcount drops + to zero, unref the subchannel index and destroy its mutex. */ +void grpc_subchannel_index_unref(void); + +/** \em TEST ONLY. + * If \a force_creation is true, all keys are regarded different, resulting in + * new subchannels always being created. Otherwise, the keys will be compared as + * usual. + * + * Tests using this function \em MUST run tests with and without \a + * force_creation set. */ +void grpc_subchannel_index_test_only_set_force_creation(bool force_creation); + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H */ diff --git a/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.cc b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.cc new file mode 100644 index 00000000000000..b4cb07f0f92f97 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.cc @@ -0,0 +1,385 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/filters/deadline/deadline_filter.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/channel_init.h" + +// +// grpc_deadline_state +// + +// The on_complete callback used when sending a cancel_error batch down the +// filter stack. Yields the call combiner when the batch returns. +static void yield_call_combiner(void* arg, grpc_error* ignored) { + grpc_deadline_state* deadline_state = static_cast(arg); + GRPC_CALL_COMBINER_STOP(deadline_state->call_combiner, + "got on_complete from cancel_stream batch"); + GRPC_CALL_STACK_UNREF(deadline_state->call_stack, "deadline_timer"); +} + +// This is called via the call combiner, so access to deadline_state is +// synchronized. +static void send_cancel_op_in_call_combiner(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + grpc_deadline_state* deadline_state = + static_cast(elem->call_data); + grpc_transport_stream_op_batch* batch = grpc_make_transport_stream_op( + GRPC_CLOSURE_INIT(&deadline_state->timer_callback, yield_call_combiner, + deadline_state, grpc_schedule_on_exec_ctx)); + batch->cancel_stream = true; + batch->payload->cancel_stream.cancel_error = GRPC_ERROR_REF(error); + elem->filter->start_transport_stream_op_batch(elem, batch); +} + +// Timer callback. +static void timer_callback(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + grpc_deadline_state* deadline_state = + static_cast(elem->call_data); + if (error != GRPC_ERROR_CANCELLED) { + error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Deadline Exceeded"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_DEADLINE_EXCEEDED); + grpc_call_combiner_cancel(deadline_state->call_combiner, + GRPC_ERROR_REF(error)); + GRPC_CLOSURE_INIT(&deadline_state->timer_callback, + send_cancel_op_in_call_combiner, elem, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(deadline_state->call_combiner, + &deadline_state->timer_callback, error, + "deadline exceeded -- sending cancel_stream op"); + } else { + GRPC_CALL_STACK_UNREF(deadline_state->call_stack, "deadline_timer"); + } +} + +// Starts the deadline timer. +// This is called via the call combiner, so access to deadline_state is +// synchronized. +static void start_timer_if_needed(grpc_call_element* elem, + grpc_millis deadline) { + if (deadline == GRPC_MILLIS_INF_FUTURE) { + return; + } + grpc_deadline_state* deadline_state = + static_cast(elem->call_data); + grpc_closure* closure = nullptr; + switch (deadline_state->timer_state) { + case GRPC_DEADLINE_STATE_PENDING: + // Note: We do not start the timer if there is already a timer + return; + case GRPC_DEADLINE_STATE_FINISHED: + deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING; + // If we've already created and destroyed a timer, we always create a + // new closure: we have no other guarantee that the inlined closure is + // not in use (it may hold a pending call to timer_callback) + closure = + GRPC_CLOSURE_CREATE(timer_callback, elem, grpc_schedule_on_exec_ctx); + break; + case GRPC_DEADLINE_STATE_INITIAL: + deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING; + closure = + GRPC_CLOSURE_INIT(&deadline_state->timer_callback, timer_callback, + elem, grpc_schedule_on_exec_ctx); + break; + } + GPR_ASSERT(closure != nullptr); + GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer"); + grpc_timer_init(&deadline_state->timer, deadline, closure); +} + +// Cancels the deadline timer. +// This is called via the call combiner, so access to deadline_state is +// synchronized. +static void cancel_timer_if_needed(grpc_deadline_state* deadline_state) { + if (deadline_state->timer_state == GRPC_DEADLINE_STATE_PENDING) { + deadline_state->timer_state = GRPC_DEADLINE_STATE_FINISHED; + grpc_timer_cancel(&deadline_state->timer); + } else { + // timer was either in STATE_INITAL (nothing to cancel) + // OR in STATE_FINISHED (again nothing to cancel) + } +} + +// Callback run when we receive trailing metadata. +static void recv_trailing_metadata_ready(void* arg, grpc_error* error) { + grpc_deadline_state* deadline_state = static_cast(arg); + cancel_timer_if_needed(deadline_state); + // Invoke the original callback. + GRPC_CLOSURE_RUN(deadline_state->original_recv_trailing_metadata_ready, + GRPC_ERROR_REF(error)); +} + +// Inject our own recv_trailing_metadata_ready callback into op. +static void inject_recv_trailing_metadata_ready( + grpc_deadline_state* deadline_state, grpc_transport_stream_op_batch* op) { + deadline_state->original_recv_trailing_metadata_ready = + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + GRPC_CLOSURE_INIT(&deadline_state->recv_trailing_metadata_ready, + recv_trailing_metadata_ready, deadline_state, + grpc_schedule_on_exec_ctx); + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &deadline_state->recv_trailing_metadata_ready; +} + +// Callback and associated state for starting the timer after call stack +// initialization has been completed. +struct start_timer_after_init_state { + start_timer_after_init_state(grpc_call_element* elem, grpc_millis deadline) + : elem(elem), deadline(deadline) {} + ~start_timer_after_init_state() { start_timer_if_needed(elem, deadline); } + + bool in_call_combiner = false; + grpc_call_element* elem; + grpc_millis deadline; + grpc_closure closure; +}; +static void start_timer_after_init(void* arg, grpc_error* error) { + struct start_timer_after_init_state* state = + static_cast(arg); + grpc_deadline_state* deadline_state = + static_cast(state->elem->call_data); + if (!state->in_call_combiner) { + // We are initially called without holding the call combiner, so we + // need to bounce ourselves into it. + state->in_call_combiner = true; + GRPC_CALL_COMBINER_START(deadline_state->call_combiner, &state->closure, + GRPC_ERROR_REF(error), + "scheduling deadline timer"); + return; + } + grpc_core::Delete(state); + GRPC_CALL_COMBINER_STOP(deadline_state->call_combiner, + "done scheduling deadline timer"); +} + +grpc_deadline_state::grpc_deadline_state(grpc_call_element* elem, + grpc_call_stack* call_stack, + grpc_call_combiner* call_combiner, + grpc_millis deadline) + : call_stack(call_stack), call_combiner(call_combiner) { + // Deadline will always be infinite on servers, so the timer will only be + // set on clients with a finite deadline. + if (deadline != GRPC_MILLIS_INF_FUTURE) { + // When the deadline passes, we indicate the failure by sending down + // an op with cancel_error set. However, we can't send down any ops + // until after the call stack is fully initialized. If we start the + // timer here, we have no guarantee that the timer won't pop before + // call stack initialization is finished. To avoid that problem, we + // create a closure to start the timer, and we schedule that closure + // to be run after call stack initialization is done. + struct start_timer_after_init_state* state = + grpc_core::New(elem, deadline); + GRPC_CLOSURE_INIT(&state->closure, start_timer_after_init, state, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&state->closure, GRPC_ERROR_NONE); + } +} + +grpc_deadline_state::~grpc_deadline_state() { cancel_timer_if_needed(this); } + +void grpc_deadline_state_reset(grpc_call_element* elem, + grpc_millis new_deadline) { + grpc_deadline_state* deadline_state = + static_cast(elem->call_data); + cancel_timer_if_needed(deadline_state); + start_timer_if_needed(elem, new_deadline); +} + +void grpc_deadline_state_client_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + grpc_deadline_state* deadline_state = + static_cast(elem->call_data); + if (op->cancel_stream) { + cancel_timer_if_needed(deadline_state); + } else { + // Make sure we know when the call is complete, so that we can cancel + // the timer. + if (op->recv_trailing_metadata) { + inject_recv_trailing_metadata_ready(deadline_state, op); + } + } +} + +// +// filter code +// + +// Constructor for channel_data. Used for both client and server filters. +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + GPR_ASSERT(!args->is_last); + return GRPC_ERROR_NONE; +} + +// Destructor for channel_data. Used for both client and server filters. +static void destroy_channel_elem(grpc_channel_element* elem) {} + +// Call data used for both client and server filter. +typedef struct base_call_data { + grpc_deadline_state deadline_state; +} base_call_data; + +// Additional call data used only for the server filter. +typedef struct server_call_data { + base_call_data base; // Must be first. + // The closure for receiving initial metadata. + grpc_closure recv_initial_metadata_ready; + // Received initial metadata batch. + grpc_metadata_batch* recv_initial_metadata; + // The original recv_initial_metadata_ready closure, which we chain to + // after our own closure is invoked. + grpc_closure* next_recv_initial_metadata_ready; +} server_call_data; + +// Constructor for call_data. Used for both client and server filters. +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + new (elem->call_data) grpc_deadline_state( + elem, args->call_stack, args->call_combiner, args->deadline); + return GRPC_ERROR_NONE; +} + +// Destructor for call_data. Used for both client and server filters. +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + grpc_deadline_state* deadline_state = + static_cast(elem->call_data); + deadline_state->~grpc_deadline_state(); +} + +// Method for starting a call op for client filter. +static void client_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + grpc_deadline_state_client_start_transport_stream_op_batch(elem, op); + // Chain to next filter. + grpc_call_next_op(elem, op); +} + +// Callback for receiving initial metadata on the server. +static void recv_initial_metadata_ready(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + server_call_data* calld = static_cast(elem->call_data); + start_timer_if_needed(elem, calld->recv_initial_metadata->deadline); + // Invoke the next callback. + GRPC_CLOSURE_RUN(calld->next_recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); +} + +// Method for starting a call op for server filter. +static void server_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + server_call_data* calld = static_cast(elem->call_data); + if (op->cancel_stream) { + cancel_timer_if_needed(&calld->base.deadline_state); + } else { + // If we're receiving initial metadata, we need to get the deadline + // from the recv_initial_metadata_ready callback. So we inject our + // own callback into that hook. + if (op->recv_initial_metadata) { + calld->next_recv_initial_metadata_ready = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + // Make sure we know when the call is complete, so that we can cancel + // the timer. + // Note that we trigger this on recv_trailing_metadata, even though + // the client never sends trailing metadata, because this is the + // hook that tells us when the call is complete on the server side. + if (op->recv_trailing_metadata) { + inject_recv_trailing_metadata_ready(&calld->base.deadline_state, op); + } + } + // Chain to next filter. + grpc_call_next_op(elem, op); +} + +const grpc_channel_filter grpc_client_deadline_filter = { + client_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(base_call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, // sizeof(channel_data) + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "deadline", +}; + +const grpc_channel_filter grpc_server_deadline_filter = { + server_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(server_call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, // sizeof(channel_data) + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "deadline", +}; + +bool grpc_deadline_checking_enabled(const grpc_channel_args* channel_args) { + return grpc_channel_arg_get_bool( + grpc_channel_args_find(channel_args, GRPC_ARG_ENABLE_DEADLINE_CHECKS), + !grpc_channel_args_want_minimal_stack(channel_args)); +} + +static bool maybe_add_deadline_filter(grpc_channel_stack_builder* builder, + void* arg) { + return grpc_deadline_checking_enabled( + grpc_channel_stack_builder_get_channel_arguments(builder)) + ? grpc_channel_stack_builder_prepend_filter( + builder, static_cast(arg), + nullptr, nullptr) + : true; +} + +void grpc_deadline_filter_init(void) { + grpc_channel_init_register_stage( + GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_deadline_filter, (void*)&grpc_client_deadline_filter); + grpc_channel_init_register_stage( + GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_deadline_filter, (void*)&grpc_server_deadline_filter); +} + +void grpc_deadline_filter_shutdown(void) {} diff --git a/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.h b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.h new file mode 100644 index 00000000000000..e37032999c63b9 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/deadline/deadline_filter.h @@ -0,0 +1,89 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H +#define GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/iomgr/timer.h" + +enum grpc_deadline_timer_state { + GRPC_DEADLINE_STATE_INITIAL, + GRPC_DEADLINE_STATE_PENDING, + GRPC_DEADLINE_STATE_FINISHED +}; + +// State used for filters that enforce call deadlines. +// Must be the first field in the filter's call_data. +struct grpc_deadline_state { + grpc_deadline_state(grpc_call_element* elem, grpc_call_stack* call_stack, + grpc_call_combiner* call_combiner, grpc_millis deadline); + ~grpc_deadline_state(); + + // We take a reference to the call stack for the timer callback. + grpc_call_stack* call_stack; + grpc_call_combiner* call_combiner; + grpc_deadline_timer_state timer_state = GRPC_DEADLINE_STATE_INITIAL; + grpc_timer timer; + grpc_closure timer_callback; + // Closure to invoke when we receive trailing metadata. + // We use this to cancel the timer. + grpc_closure recv_trailing_metadata_ready; + // The original recv_trailing_metadata_ready closure, which we chain to + // after our own closure is invoked. + grpc_closure* original_recv_trailing_metadata_ready; +}; + +// +// NOTE: All of these functions require that the first field in +// elem->call_data is a grpc_deadline_state. +// + +// Cancels the existing timer and starts a new one with new_deadline. +// +// Note: It is generally safe to call this with an earlier deadline +// value than the current one, but not the reverse. No checks are done +// to ensure that the timer callback is not invoked while it is in the +// process of being reset, which means that attempting to increase the +// deadline may result in the timer being called twice. +// +// Note: Must be called while holding the call combiner. +void grpc_deadline_state_reset(grpc_call_element* elem, + grpc_millis new_deadline); + +// To be called from the client-side filter's start_transport_stream_op_batch() +// method. Ensures that the deadline timer is cancelled when the call +// is completed. +// +// Note: It is the caller's responsibility to chain to the next filter if +// necessary after this function returns. +// +// Note: Must be called while holding the call combiner. +void grpc_deadline_state_client_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op); + +// Should deadline checking be performed (according to channel args) +bool grpc_deadline_checking_enabled(const grpc_channel_args* args); + +// Deadline filters for direct client channels and server channels. +// Note: Deadlines for non-direct client channels are handled by the +// client_channel filter. +extern const grpc_channel_filter grpc_client_deadline_filter; +extern const grpc_channel_filter grpc_server_deadline_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H */ diff --git a/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.cc b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.cc new file mode 100644 index 00000000000000..bf9a01f659b87f --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.cc @@ -0,0 +1,598 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include +#include "src/core/ext/filters/http/client/http_client_filter.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/slice/percent_encoding.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/transport_impl.h" + +#define EXPECTED_CONTENT_TYPE "application/grpc" +#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1 + +/* default maximum size of payload eligable for GET request */ +static constexpr size_t kMaxPayloadSizeForGet = 2048; + +static void recv_initial_metadata_ready(void* user_data, grpc_error* error); +static void recv_trailing_metadata_ready(void* user_data, grpc_error* error); +static void on_send_message_next_done(void* arg, grpc_error* error); +static void send_message_on_complete(void* arg, grpc_error* error); + +namespace { +struct call_data { + call_data(grpc_call_element* elem, const grpc_call_element_args& args) + : call_combiner(args.call_combiner) { + GRPC_CLOSURE_INIT(&recv_initial_metadata_ready, + ::recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready, + ::recv_trailing_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_send_message_next_done, ::on_send_message_next_done, + elem, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&send_message_on_complete, ::send_message_on_complete, + elem, grpc_schedule_on_exec_ctx); + } + + ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); } + + grpc_call_combiner* call_combiner; + // State for handling send_initial_metadata ops. + 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; + // State for handling recv_initial_metadata ops. + grpc_metadata_batch* recv_initial_metadata; + grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE; + grpc_closure* original_recv_initial_metadata_ready = nullptr; + grpc_closure recv_initial_metadata_ready; + // State for handling recv_trailing_metadata ops. + grpc_metadata_batch* recv_trailing_metadata; + grpc_closure* original_recv_trailing_metadata_ready; + grpc_closure recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE; + bool seen_recv_trailing_metadata_ready = false; + // State for handling send_message ops. + grpc_transport_stream_op_batch* send_message_batch; + size_t send_message_bytes_read = 0; + grpc_core::ManualConstructor send_message_cache; + grpc_core::ManualConstructor + send_message_caching_stream; + grpc_closure on_send_message_next_done; + grpc_closure* original_send_message_on_complete; + grpc_closure send_message_on_complete; +}; + +struct channel_data { + grpc_mdelem static_scheme; + grpc_mdelem user_agent; + size_t max_payload_size_for_get; +}; +} // namespace + +static grpc_error* client_filter_incoming_metadata(grpc_call_element* elem, + grpc_metadata_batch* b) { + if (b->idx.named.status != nullptr) { + /* If both gRPC status and HTTP status are provided in the response, we + * should prefer the gRPC status code, as mentioned in + * https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md. + */ + if (b->idx.named.grpc_status != nullptr || + grpc_mdelem_eq(b->idx.named.status->md, GRPC_MDELEM_STATUS_200)) { + grpc_metadata_batch_remove(b, b->idx.named.status); + } else { + char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.status->md), + GPR_DUMP_ASCII); + char* msg; + gpr_asprintf(&msg, "Received http2 header with status: %s", val); + grpc_error* e = grpc_error_set_str( + grpc_error_set_int( + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Received http2 :status header with non-200 OK status"), + GRPC_ERROR_STR_VALUE, grpc_slice_from_copied_string(val)), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED), + GRPC_ERROR_STR_GRPC_MESSAGE, grpc_slice_from_copied_string(msg)); + gpr_free(val); + gpr_free(msg); + return e; + } + } + + if (b->idx.named.grpc_message != nullptr) { + grpc_slice pct_decoded_msg = grpc_permissive_percent_decode_slice( + GRPC_MDVALUE(b->idx.named.grpc_message->md)); + if (grpc_slice_is_equivalent(pct_decoded_msg, + GRPC_MDVALUE(b->idx.named.grpc_message->md))) { + grpc_slice_unref_internal(pct_decoded_msg); + } else { + grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_decoded_msg); + } + } + + if (b->idx.named.content_type != nullptr) { + if (!grpc_mdelem_eq(b->idx.named.content_type->md, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) { + if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md), + EXPECTED_CONTENT_TYPE, + EXPECTED_CONTENT_TYPE_LENGTH) && + (GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + '+' || + GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + ';')) { + /* 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. */ + char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md), + GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "Unexpected content-type '%s'", val); + gpr_free(val); + } + } + grpc_metadata_batch_remove(b, b->idx.named.content_type); + } + + return GRPC_ERROR_NONE; +} + +static void recv_initial_metadata_ready(void* user_data, grpc_error* error) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + if (error == GRPC_ERROR_NONE) { + error = client_filter_incoming_metadata(elem, calld->recv_initial_metadata); + calld->recv_initial_metadata_error = GRPC_ERROR_REF(error); + } else { + GRPC_ERROR_REF(error); + } + grpc_closure* closure = calld->original_recv_initial_metadata_ready; + calld->original_recv_initial_metadata_ready = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START( + calld->call_combiner, &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, "continue recv_trailing_metadata"); + } + GRPC_CLOSURE_RUN(closure, error); +} + +static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + if (calld->original_recv_initial_metadata_ready != nullptr) { + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring recv_trailing_metadata_ready until " + "after recv_initial_metadata_ready"); + return; + } + if (error == GRPC_ERROR_NONE) { + error = + client_filter_incoming_metadata(elem, calld->recv_trailing_metadata); + } else { + GRPC_ERROR_REF(error); + } + error = grpc_error_add_child( + error, GRPC_ERROR_REF(calld->recv_initial_metadata_error)); + GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error); +} + +static void send_message_on_complete(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + calld->send_message_cache.Destroy(); + GRPC_CLOSURE_RUN(calld->original_send_message_on_complete, + GRPC_ERROR_REF(error)); +} + +// Pulls a slice from the send_message byte stream, updating +// calld->send_message_bytes_read. +static grpc_error* pull_slice_from_send_message(call_data* calld) { + grpc_slice incoming_slice; + grpc_error* error = calld->send_message_caching_stream->Pull(&incoming_slice); + if (error == GRPC_ERROR_NONE) { + calld->send_message_bytes_read += GRPC_SLICE_LENGTH(incoming_slice); + grpc_slice_unref_internal(incoming_slice); + } + return error; +} + +// Reads as many slices as possible from the send_message byte stream. +// Upon successful return, if calld->send_message_bytes_read == +// calld->send_message_caching_stream->length(), then we have completed +// reading from the byte stream; otherwise, an async read has been dispatched +// and on_send_message_next_done() will be invoked when it is complete. +static grpc_error* read_all_available_send_message_data(call_data* calld) { + while (calld->send_message_caching_stream->Next( + SIZE_MAX, &calld->on_send_message_next_done)) { + grpc_error* error = pull_slice_from_send_message(calld); + if (error != GRPC_ERROR_NONE) return error; + if (calld->send_message_bytes_read == + calld->send_message_caching_stream->length()) { + break; + } + } + return GRPC_ERROR_NONE; +} + +// Async callback for ByteStream::Next(). +static void on_send_message_next_done(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + calld->send_message_batch, error, calld->call_combiner); + return; + } + error = pull_slice_from_send_message(calld); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + calld->send_message_batch, error, calld->call_combiner); + return; + } + // There may or may not be more to read, but we don't care. If we got + // here, then we know that all of the data was not available + // synchronously, so we were not able to do a cached call. Instead, + // we just reset the byte stream and then send down the batch as-is. + calld->send_message_caching_stream->Reset(); + grpc_call_next_op(elem, calld->send_message_batch); +} + +static char* slice_buffer_to_string(grpc_slice_buffer* slice_buffer) { + char* payload_bytes = + static_cast(gpr_malloc(slice_buffer->length + 1)); + size_t offset = 0; + for (size_t i = 0; i < slice_buffer->count; ++i) { + memcpy(payload_bytes + offset, + GRPC_SLICE_START_PTR(slice_buffer->slices[i]), + GRPC_SLICE_LENGTH(slice_buffer->slices[i])); + offset += GRPC_SLICE_LENGTH(slice_buffer->slices[i]); + } + *(payload_bytes + offset) = '\0'; + return payload_bytes; +} + +// Modifies the path entry in the batch's send_initial_metadata to +// append the base64-encoded query for a GET request. +static grpc_error* update_path_for_get(grpc_call_element* elem, + grpc_transport_stream_op_batch* batch) { + call_data* calld = static_cast(elem->call_data); + grpc_slice path_slice = + GRPC_MDVALUE(batch->payload->send_initial_metadata.send_initial_metadata + ->idx.named.path->md); + /* sum up individual component's lengths and allocate enough memory to + * hold combined path+query */ + size_t estimated_len = GRPC_SLICE_LENGTH(path_slice); + estimated_len++; /* for the '?' */ + estimated_len += grpc_base64_estimate_encoded_size( + batch->payload->send_message.send_message->length(), true /* url_safe */, + false /* multi_line */); + grpc_slice path_with_query_slice = GRPC_SLICE_MALLOC(estimated_len); + /* memcopy individual pieces into this slice */ + char* write_ptr = + reinterpret_cast GRPC_SLICE_START_PTR(path_with_query_slice); + char* original_path = + reinterpret_cast GRPC_SLICE_START_PTR(path_slice); + memcpy(write_ptr, original_path, GRPC_SLICE_LENGTH(path_slice)); + write_ptr += GRPC_SLICE_LENGTH(path_slice); + *write_ptr++ = '?'; + char* payload_bytes = + slice_buffer_to_string(calld->send_message_cache->cache_buffer()); + grpc_base64_encode_core(write_ptr, payload_bytes, + batch->payload->send_message.send_message->length(), + true /* url_safe */, false /* multi_line */); + gpr_free(payload_bytes); + /* remove trailing unused memory and add trailing 0 to terminate string */ + char* t = reinterpret_cast GRPC_SLICE_START_PTR(path_with_query_slice); + /* safe to use strlen since base64_encode will always add '\0' */ + path_with_query_slice = + grpc_slice_sub_no_ref(path_with_query_slice, 0, strlen(t)); + /* substitute previous path with the new path+query */ + grpc_mdelem mdelem_path_and_query = + grpc_mdelem_from_slices(GRPC_MDSTR_PATH, path_with_query_slice); + grpc_metadata_batch* b = + batch->payload->send_initial_metadata.send_initial_metadata; + return grpc_metadata_batch_substitute(b, b->idx.named.path, + mdelem_path_and_query); +} + +static void remove_if_present(grpc_metadata_batch* batch, + grpc_metadata_batch_callouts_index idx) { + if (batch->idx.array[idx] != nullptr) { + grpc_metadata_batch_remove(batch, batch->idx.array[idx]); + } +} + +static void hc_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + call_data* calld = static_cast(elem->call_data); + channel_data* channeld = static_cast(elem->channel_data); + GPR_TIMER_SCOPE("hc_start_transport_stream_op_batch", 0); + + if (batch->recv_initial_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_initial_metadata = + batch->payload->recv_initial_metadata.recv_initial_metadata; + calld->original_recv_initial_metadata_ready = + batch->payload->recv_initial_metadata.recv_initial_metadata_ready; + batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + + if (batch->recv_trailing_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_trailing_metadata = + batch->payload->recv_trailing_metadata.recv_trailing_metadata; + calld->original_recv_trailing_metadata_ready = + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &calld->recv_trailing_metadata_ready; + } + + grpc_error* error = GRPC_ERROR_NONE; + bool batch_will_be_handled_asynchronously = false; + if (batch->send_initial_metadata) { + // Decide which HTTP VERB to use. We use GET if the request is marked + // cacheable, and the operation contains both initial metadata and send + // message, and the payload is below the size threshold, and all the data + // for this request is immediately available. + grpc_mdelem method = GRPC_MDELEM_METHOD_POST; + if (batch->send_message && + (batch->payload->send_initial_metadata.send_initial_metadata_flags & + GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) && + batch->payload->send_message.send_message->length() < + channeld->max_payload_size_for_get) { + calld->send_message_bytes_read = 0; + calld->send_message_cache.Init( + std::move(batch->payload->send_message.send_message)); + calld->send_message_caching_stream.Init(calld->send_message_cache.get()); + batch->payload->send_message.send_message.reset( + calld->send_message_caching_stream.get()); + calld->original_send_message_on_complete = batch->on_complete; + batch->on_complete = &calld->send_message_on_complete; + calld->send_message_batch = batch; + error = read_all_available_send_message_data(calld); + if (error != GRPC_ERROR_NONE) goto done; + // If all the data has been read, then we can use GET. + if (calld->send_message_bytes_read == + calld->send_message_caching_stream->length()) { + method = GRPC_MDELEM_METHOD_GET; + error = update_path_for_get(elem, batch); + if (error != GRPC_ERROR_NONE) goto done; + batch->send_message = false; + calld->send_message_caching_stream->Orphan(); + } else { + // Not all data is available. The batch will be sent down + // asynchronously in on_send_message_next_done(). + batch_will_be_handled_asynchronously = true; + // Fall back to POST. + gpr_log(GPR_DEBUG, + "Request is marked Cacheable but not all data is available. " + "Falling back to POST"); + } + } else if (batch->payload->send_initial_metadata + .send_initial_metadata_flags & + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) { + method = GRPC_MDELEM_METHOD_PUT; + } + + remove_if_present( + batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_METHOD); + remove_if_present( + batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_SCHEME); + remove_if_present( + batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_TE); + remove_if_present( + batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_CONTENT_TYPE); + remove_if_present( + batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_USER_AGENT); + + /* Send : prefixed headers, which have to be before any application + layer headers. */ + error = grpc_metadata_batch_add_head( + batch->payload->send_initial_metadata.send_initial_metadata, + &calld->method, method); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_head( + batch->payload->send_initial_metadata.send_initial_metadata, + &calld->scheme, channeld->static_scheme); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_tail( + batch->payload->send_initial_metadata.send_initial_metadata, + &calld->te_trailers, GRPC_MDELEM_TE_TRAILERS); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_tail( + batch->payload->send_initial_metadata.send_initial_metadata, + &calld->content_type, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_tail( + batch->payload->send_initial_metadata.send_initial_metadata, + &calld->user_agent, GRPC_MDELEM_REF(channeld->user_agent)); + if (error != GRPC_ERROR_NONE) goto done; + } + +done: + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + calld->send_message_batch, error, calld->call_combiner); + } else if (!batch_will_be_handled_asynchronously) { + grpc_call_next_op(elem, batch); + } +} + +/* Constructor for call_data */ +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + new (elem->call_data) call_data(elem, *args); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = static_cast(elem->call_data); + calld->~call_data(); +} + +static grpc_mdelem scheme_from_args(const grpc_channel_args* args) { + unsigned i; + size_t j; + grpc_mdelem valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP, + GRPC_MDELEM_SCHEME_HTTPS}; + if (args != nullptr) { + 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) { + for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) { + if (0 == grpc_slice_str_cmp(GRPC_MDVALUE(valid_schemes[j]), + args->args[i].value.string)) { + return valid_schemes[j]; + } + } + } + } + } + return GRPC_MDELEM_SCHEME_HTTP; +} + +static size_t max_payload_size_from_args(const grpc_channel_args* args) { + if (args != nullptr) { + for (size_t i = 0; i < args->num_args; ++i) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) { + if (args->args[i].type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s: must be an integer", + GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET); + } else { + return static_cast(args->args[i].value.integer); + } + } + } + } + return kMaxPayloadSizeForGet; +} + +static grpc_slice user_agent_from_args(const grpc_channel_args* args, + const char* transport_name) { + gpr_strvec v; + size_t i; + int is_first = 1; + char* tmp; + grpc_slice 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; %s; %s)", is_first ? "" : " ", + grpc_version_string(), GPR_PLATFORM_STRING, transport_name, + grpc_g_stands_for()); + 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, nullptr); + gpr_strvec_destroy(&v); + result = grpc_slice_intern(grpc_slice_from_static_string(tmp)); + gpr_free(tmp); + + return result; +} + +/* Constructor for channel_data */ +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + GPR_ASSERT(!args->is_last); + GPR_ASSERT(args->optional_transport != nullptr); + chand->static_scheme = scheme_from_args(args->channel_args); + chand->max_payload_size_for_get = + max_payload_size_from_args(args->channel_args); + chand->user_agent = grpc_mdelem_from_slices( + GRPC_MDSTR_USER_AGENT, + user_agent_from_args(args->channel_args, + args->optional_transport->vtable->name)); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + GRPC_MDELEM_UNREF(chand->user_agent); +} + +const grpc_channel_filter grpc_http_client_filter = { + hc_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "http-client"}; diff --git a/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.h b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.h new file mode 100644 index 00000000000000..b7cef33f5c7428 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/client/http_client_filter.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H +#define GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" + +/* Processes metadata on the client side for HTTP2 transports */ +extern const grpc_channel_filter grpc_http_client_filter; + +/* Channel arg to determine maximum size of payload eligable for GET request */ +#define GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET "grpc.max_payload_size_for_get" + +#endif /* GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_HTTP_CLIENT_FILTER_H */ diff --git a/third_party/grpc/src/core/ext/filters/http/client_authority_filter.cc b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.cc new file mode 100644 index 00000000000000..6383f125944ea7 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.cc @@ -0,0 +1,156 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include "src/core/ext/filters/http/client_authority_filter.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/surface/channel_stack_type.h" +#include "src/core/lib/transport/static_metadata.h" + +namespace { + +struct call_data { + grpc_linked_mdelem authority_storage; + grpc_call_combiner* call_combiner; +}; + +struct channel_data { + grpc_slice default_authority; +}; + +void authority_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + // Handle send_initial_metadata. + auto* initial_metadata = + batch->payload->send_initial_metadata.send_initial_metadata; + // If the initial metadata doesn't already contain :authority, add it. + if (batch->send_initial_metadata && + initial_metadata->idx.named.authority == nullptr) { + grpc_error* error = grpc_metadata_batch_add_head( + initial_metadata, &calld->authority_storage, + grpc_mdelem_create(GRPC_MDSTR_AUTHORITY, chand->default_authority, + nullptr)); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure(batch, error, + calld->call_combiner); + return; + } + } + // Pass control down the stack. + grpc_call_next_op(elem, batch); +} + +/* Constructor for call_data */ +grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + call_data* calld = static_cast(elem->call_data); + calld->call_combiner = args->call_combiner; + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) {} + +/* Constructor for channel_data */ +grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + const grpc_arg* default_authority_arg = + grpc_channel_args_find(args->channel_args, GRPC_ARG_DEFAULT_AUTHORITY); + if (default_authority_arg == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "GRPC_ARG_DEFAULT_AUTHORITY channel arg. not found. Note that direct " + "channels must explicitly specify a value for this argument."); + } + const char* default_authority_str = + grpc_channel_arg_get_string(default_authority_arg); + if (default_authority_str == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "GRPC_ARG_DEFAULT_AUTHORITY channel arg. must be a string"); + } + chand->default_authority = + grpc_slice_intern(grpc_slice_from_static_string(default_authority_str)); + GPR_ASSERT(!args->is_last); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +void destroy_channel_elem(grpc_channel_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + grpc_slice_unref_internal(chand->default_authority); +} +} // namespace + +const grpc_channel_filter grpc_client_authority_filter = { + authority_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "authority"}; + +static bool add_client_authority_filter(grpc_channel_stack_builder* builder, + void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + const grpc_arg* disable_client_authority_filter_arg = grpc_channel_args_find( + channel_args, GRPC_ARG_DISABLE_CLIENT_AUTHORITY_FILTER); + if (disable_client_authority_filter_arg != nullptr) { + const bool is_client_authority_filter_disabled = + grpc_channel_arg_get_bool(disable_client_authority_filter_arg, false); + if (is_client_authority_filter_disabled) { + return true; + } + } + return grpc_channel_stack_builder_prepend_filter( + builder, static_cast(arg), nullptr, nullptr); +} + +void grpc_client_authority_filter_init(void) { + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX, + add_client_authority_filter, + (void*)&grpc_client_authority_filter); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX, + add_client_authority_filter, + (void*)&grpc_client_authority_filter); +} + +void grpc_client_authority_filter_shutdown(void) {} diff --git a/third_party/grpc/src/core/ext/filters/http/client_authority_filter.h b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.h new file mode 100644 index 00000000000000..5824e91ff213c5 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/client_authority_filter.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_AUTHORITY_FILTER_H +#define GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_AUTHORITY_FILTER_H + +#include + +#include + +#include "src/core/lib/channel/channel_stack.h" + +/// Filter responsible for setting the authority header, if not already set. It +/// uses the value of the GRPC_ARG_DEFAULT_AUTHORITY channel arg if the initial +/// metadata doesn't already contain an authority value. + +extern const grpc_channel_filter grpc_client_authority_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_HTTP_CLIENT_AUTHORITY_FILTER_H */ diff --git a/third_party/grpc/src/core/ext/filters/http/http_filters_plugin.cc b/third_party/grpc/src/core/ext/filters/http/http_filters_plugin.cc new file mode 100644 index 00000000000000..f03fa0141dff23 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/http_filters_plugin.cc @@ -0,0 +1,89 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/ext/filters/http/client/http_client_filter.h" +#include "src/core/ext/filters/http/message_compress/message_compress_filter.h" +#include "src/core/ext/filters/http/server/http_server_filter.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/transport_impl.h" + +typedef struct { + const grpc_channel_filter* filter; + const char* control_channel_arg; +} optional_filter; + +static optional_filter compress_filter = { + &grpc_message_compress_filter, GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION}; + +static bool is_building_http_like_transport( + grpc_channel_stack_builder* builder) { + grpc_transport* t = grpc_channel_stack_builder_get_transport(builder); + return t != nullptr && strstr(t->vtable->name, "http"); +} + +static bool maybe_add_optional_filter(grpc_channel_stack_builder* builder, + void* arg) { + if (!is_building_http_like_transport(builder)) return true; + optional_filter* filtarg = static_cast(arg); + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + bool enable = grpc_channel_arg_get_bool( + grpc_channel_args_find(channel_args, filtarg->control_channel_arg), + !grpc_channel_args_want_minimal_stack(channel_args)); + return enable ? grpc_channel_stack_builder_prepend_filter( + builder, filtarg->filter, nullptr, nullptr) + : true; +} + +static bool maybe_add_required_filter(grpc_channel_stack_builder* builder, + void* arg) { + return is_building_http_like_transport(builder) + ? grpc_channel_stack_builder_prepend_filter( + builder, static_cast(arg), + nullptr, nullptr) + : true; +} + +void grpc_http_filters_init(void) { + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_optional_filter, &compress_filter); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_optional_filter, &compress_filter); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_optional_filter, &compress_filter); + grpc_channel_init_register_stage( + GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_required_filter, (void*)&grpc_http_client_filter); + grpc_channel_init_register_stage( + GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_required_filter, (void*)&grpc_http_client_filter); + grpc_channel_init_register_stage( + GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_required_filter, (void*)&grpc_http_server_filter); +} + +void grpc_http_filters_shutdown(void) {} diff --git a/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.cc new file mode 100644 index 00000000000000..9c8c8d9e1885c9 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.cc @@ -0,0 +1,511 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/http/message_compress/message_compress_filter.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/compression/algorithm_metadata.h" +#include "src/core/lib/compression/compression_internal.h" +#include "src/core/lib/compression/message_compress.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/transport/static_metadata.h" + +static void start_send_message_batch(void* arg, grpc_error* unused); +static void send_message_on_complete(void* arg, grpc_error* error); +static void on_send_message_next_done(void* arg, grpc_error* error); + +namespace { +enum initial_metadata_state { + // Initial metadata not yet seen. + INITIAL_METADATA_UNSEEN = 0, + // Initial metadata seen; compression algorithm set. + HAS_COMPRESSION_ALGORITHM, + // Initial metadata seen; no compression algorithm set. + NO_COMPRESSION_ALGORITHM, +}; + +struct call_data { + call_data(grpc_call_element* elem, const grpc_call_element_args& args) + : call_combiner(args.call_combiner) { + GRPC_CLOSURE_INIT(&start_send_message_batch_in_call_combiner, + start_send_message_batch, elem, + grpc_schedule_on_exec_ctx); + grpc_slice_buffer_init(&slices); + GRPC_CLOSURE_INIT(&send_message_on_complete, ::send_message_on_complete, + elem, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_send_message_next_done, ::on_send_message_next_done, + elem, grpc_schedule_on_exec_ctx); + } + + ~call_data() { + grpc_slice_buffer_destroy_internal(&slices); + GRPC_ERROR_UNREF(cancel_error); + } + + grpc_call_combiner* call_combiner; + grpc_linked_mdelem compression_algorithm_storage; + grpc_linked_mdelem stream_compression_algorithm_storage; + grpc_linked_mdelem accept_encoding_storage; + grpc_linked_mdelem accept_stream_encoding_storage; + /** Compression algorithm we'll try to use. It may be given by incoming + * metadata, or by the channel's default compression settings. */ + grpc_message_compression_algorithm message_compression_algorithm = + GRPC_MESSAGE_COMPRESS_NONE; + initial_metadata_state send_initial_metadata_state = INITIAL_METADATA_UNSEEN; + grpc_error* cancel_error = GRPC_ERROR_NONE; + grpc_closure start_send_message_batch_in_call_combiner; + grpc_transport_stream_op_batch* send_message_batch = nullptr; + grpc_slice_buffer slices; /**< Buffers up input slices to be compressed */ + grpc_core::ManualConstructor + replacement_stream; + grpc_closure* original_send_message_on_complete; + grpc_closure send_message_on_complete; + grpc_closure on_send_message_next_done; +}; + +struct channel_data { + /** The default, channel-level, compression algorithm */ + grpc_compression_algorithm default_compression_algorithm; + /** Bitset of enabled compression algorithms */ + uint32_t enabled_algorithms_bitset; + /** Supported compression algorithms */ + uint32_t supported_message_compression_algorithms; + /** Supported stream compression algorithms */ + uint32_t supported_stream_compression_algorithms; +}; +} // namespace + +static bool skip_compression(grpc_call_element* elem, uint32_t flags, + bool has_compression_algorithm) { + call_data* calld = static_cast(elem->call_data); + channel_data* channeld = static_cast(elem->channel_data); + + if (flags & (GRPC_WRITE_NO_COMPRESS | GRPC_WRITE_INTERNAL_COMPRESS)) { + return true; + } + if (has_compression_algorithm) { + if (calld->message_compression_algorithm == GRPC_MESSAGE_COMPRESS_NONE) { + return true; + } + return false; /* we have an actual call-specific algorithm */ + } + /* no per-call compression override */ + return channeld->default_compression_algorithm == GRPC_COMPRESS_NONE; +} + +/** Filter initial metadata */ +static grpc_error* process_send_initial_metadata( + grpc_call_element* elem, grpc_metadata_batch* initial_metadata, + bool* has_compression_algorithm) GRPC_MUST_USE_RESULT; +static grpc_error* process_send_initial_metadata( + grpc_call_element* elem, grpc_metadata_batch* initial_metadata, + bool* has_compression_algorithm) { + call_data* calld = static_cast(elem->call_data); + channel_data* channeld = static_cast(elem->channel_data); + *has_compression_algorithm = false; + grpc_compression_algorithm compression_algorithm; + grpc_stream_compression_algorithm stream_compression_algorithm = + GRPC_STREAM_COMPRESS_NONE; + if (initial_metadata->idx.named.grpc_internal_encoding_request != nullptr) { + grpc_mdelem md = + initial_metadata->idx.named.grpc_internal_encoding_request->md; + if (GPR_UNLIKELY(!grpc_compression_algorithm_parse( + GRPC_MDVALUE(md), &compression_algorithm))) { + char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid compression algorithm: '%s' (unknown). Ignoring.", val); + gpr_free(val); + calld->message_compression_algorithm = GRPC_MESSAGE_COMPRESS_NONE; + stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; + } + if (GPR_UNLIKELY(!GPR_BITGET(channeld->enabled_algorithms_bitset, + compression_algorithm))) { + char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid compression algorithm: '%s' (previously disabled). " + "Ignoring.", + val); + gpr_free(val); + calld->message_compression_algorithm = GRPC_MESSAGE_COMPRESS_NONE; + stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; + } + *has_compression_algorithm = true; + grpc_metadata_batch_remove( + initial_metadata, + initial_metadata->idx.named.grpc_internal_encoding_request); + calld->message_compression_algorithm = + grpc_compression_algorithm_to_message_compression_algorithm( + compression_algorithm); + stream_compression_algorithm = + grpc_compression_algorithm_to_stream_compression_algorithm( + compression_algorithm); + } else { + /* If no algorithm was found in the metadata and we aren't + * exceptionally skipping compression, fall back to the channel + * default */ + if (channeld->default_compression_algorithm != GRPC_COMPRESS_NONE) { + calld->message_compression_algorithm = + grpc_compression_algorithm_to_message_compression_algorithm( + channeld->default_compression_algorithm); + stream_compression_algorithm = + grpc_compression_algorithm_to_stream_compression_algorithm( + channeld->default_compression_algorithm); + } + *has_compression_algorithm = true; + } + + grpc_error* error = GRPC_ERROR_NONE; + /* hint compression algorithm */ + if (stream_compression_algorithm != GRPC_STREAM_COMPRESS_NONE) { + error = grpc_metadata_batch_add_tail( + initial_metadata, &calld->stream_compression_algorithm_storage, + grpc_stream_compression_encoding_mdelem(stream_compression_algorithm)); + } else if (calld->message_compression_algorithm != + GRPC_MESSAGE_COMPRESS_NONE) { + error = grpc_metadata_batch_add_tail( + initial_metadata, &calld->compression_algorithm_storage, + grpc_message_compression_encoding_mdelem( + calld->message_compression_algorithm)); + } + + if (error != GRPC_ERROR_NONE) return error; + + /* convey supported compression algorithms */ + error = grpc_metadata_batch_add_tail( + initial_metadata, &calld->accept_encoding_storage, + GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS( + channeld->supported_message_compression_algorithms)); + + if (error != GRPC_ERROR_NONE) return error; + + /* Do not overwrite accept-encoding header if it already presents (e.g. added + * by some proxy). */ + if (!initial_metadata->idx.named.accept_encoding) { + error = grpc_metadata_batch_add_tail( + initial_metadata, &calld->accept_stream_encoding_storage, + GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS( + channeld->supported_stream_compression_algorithms)); + } + + return error; +} + +static void send_message_on_complete(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + grpc_slice_buffer_reset_and_unref_internal(&calld->slices); + GRPC_CLOSURE_RUN(calld->original_send_message_on_complete, + GRPC_ERROR_REF(error)); +} + +static void send_message_batch_continue(grpc_call_element* elem) { + call_data* calld = static_cast(elem->call_data); + // Note: The call to grpc_call_next_op() results in yielding the + // call combiner, so we need to clear calld->send_message_batch + // before we do that. + grpc_transport_stream_op_batch* send_message_batch = + calld->send_message_batch; + calld->send_message_batch = nullptr; + grpc_call_next_op(elem, send_message_batch); +} + +static void finish_send_message(grpc_call_element* elem) { + call_data* calld = static_cast(elem->call_data); + // Compress the data if appropriate. + grpc_slice_buffer tmp; + grpc_slice_buffer_init(&tmp); + uint32_t send_flags = + calld->send_message_batch->payload->send_message.send_message->flags(); + bool did_compress = grpc_msg_compress(calld->message_compression_algorithm, + &calld->slices, &tmp); + if (did_compress) { + if (grpc_compression_trace.enabled()) { + const char* algo_name; + const size_t before_size = calld->slices.length; + const size_t after_size = tmp.length; + const float savings_ratio = 1.0f - static_cast(after_size) / + static_cast(before_size); + GPR_ASSERT(grpc_message_compression_algorithm_name( + calld->message_compression_algorithm, &algo_name)); + gpr_log(GPR_INFO, + "Compressed[%s] %" PRIuPTR " bytes vs. %" PRIuPTR + " bytes (%.2f%% savings)", + algo_name, before_size, after_size, 100 * savings_ratio); + } + grpc_slice_buffer_swap(&calld->slices, &tmp); + send_flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } else { + if (grpc_compression_trace.enabled()) { + const char* algo_name; + GPR_ASSERT(grpc_message_compression_algorithm_name( + calld->message_compression_algorithm, &algo_name)); + gpr_log(GPR_INFO, + "Algorithm '%s' enabled but decided not to compress. Input size: " + "%" PRIuPTR, + algo_name, calld->slices.length); + } + } + grpc_slice_buffer_destroy_internal(&tmp); + // Swap out the original byte stream with our new one and send the + // batch down. + calld->replacement_stream.Init(&calld->slices, send_flags); + calld->send_message_batch->payload->send_message.send_message.reset( + calld->replacement_stream.get()); + calld->original_send_message_on_complete = + calld->send_message_batch->on_complete; + calld->send_message_batch->on_complete = &calld->send_message_on_complete; + send_message_batch_continue(elem); +} + +static void fail_send_message_batch_in_call_combiner(void* arg, + grpc_error* error) { + call_data* calld = static_cast(arg); + if (calld->send_message_batch != nullptr) { + grpc_transport_stream_op_batch_finish_with_failure( + calld->send_message_batch, GRPC_ERROR_REF(error), calld->call_combiner); + calld->send_message_batch = nullptr; + } +} + +// Pulls a slice from the send_message byte stream and adds it to calld->slices. +static grpc_error* pull_slice_from_send_message(call_data* calld) { + grpc_slice incoming_slice; + grpc_error* error = + calld->send_message_batch->payload->send_message.send_message->Pull( + &incoming_slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&calld->slices, incoming_slice); + } + return error; +} + +// Reads as many slices as possible from the send_message byte stream. +// If all data has been read, invokes finish_send_message(). Otherwise, +// an async call to ByteStream::Next() has been started, which will +// eventually result in calling on_send_message_next_done(). +static void continue_reading_send_message(grpc_call_element* elem) { + call_data* calld = static_cast(elem->call_data); + while (calld->send_message_batch->payload->send_message.send_message->Next( + ~static_cast(0), &calld->on_send_message_next_done)) { + grpc_error* error = pull_slice_from_send_message(calld); + if (error != GRPC_ERROR_NONE) { + // Closure callback; does not take ownership of error. + fail_send_message_batch_in_call_combiner(calld, error); + GRPC_ERROR_UNREF(error); + return; + } + if (calld->slices.length == calld->send_message_batch->payload->send_message + .send_message->length()) { + finish_send_message(elem); + break; + } + } +} + +// Async callback for ByteStream::Next(). +static void on_send_message_next_done(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + if (error != GRPC_ERROR_NONE) { + // Closure callback; does not take ownership of error. + fail_send_message_batch_in_call_combiner(calld, error); + return; + } + error = pull_slice_from_send_message(calld); + if (error != GRPC_ERROR_NONE) { + // Closure callback; does not take ownership of error. + fail_send_message_batch_in_call_combiner(calld, error); + GRPC_ERROR_UNREF(error); + return; + } + if (calld->slices.length == + calld->send_message_batch->payload->send_message.send_message->length()) { + finish_send_message(elem); + } else { + continue_reading_send_message(elem); + } +} + +static void start_send_message_batch(void* arg, grpc_error* unused) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + if (skip_compression( + elem, + calld->send_message_batch->payload->send_message.send_message + ->flags(), + calld->send_initial_metadata_state == HAS_COMPRESSION_ALGORITHM)) { + send_message_batch_continue(elem); + } else { + continue_reading_send_message(elem); + } +} + +static void compress_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + GPR_TIMER_SCOPE("compress_start_transport_stream_op_batch", 0); + call_data* calld = static_cast(elem->call_data); + // Handle cancel_stream. + if (batch->cancel_stream) { + GRPC_ERROR_UNREF(calld->cancel_error); + calld->cancel_error = + GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); + if (calld->send_message_batch != nullptr) { + if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) { + GRPC_CALL_COMBINER_START( + calld->call_combiner, + GRPC_CLOSURE_CREATE(fail_send_message_batch_in_call_combiner, calld, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_REF(calld->cancel_error), "failing send_message op"); + } else { + calld->send_message_batch->payload->send_message.send_message->Shutdown( + GRPC_ERROR_REF(calld->cancel_error)); + } + } + } else if (calld->cancel_error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + batch, GRPC_ERROR_REF(calld->cancel_error), calld->call_combiner); + return; + } + // Handle send_initial_metadata. + if (batch->send_initial_metadata) { + GPR_ASSERT(calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN); + bool has_compression_algorithm; + grpc_error* error = process_send_initial_metadata( + elem, batch->payload->send_initial_metadata.send_initial_metadata, + &has_compression_algorithm); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure(batch, error, + calld->call_combiner); + return; + } + calld->send_initial_metadata_state = has_compression_algorithm + ? HAS_COMPRESSION_ALGORITHM + : NO_COMPRESSION_ALGORITHM; + // If we had previously received a batch containing a send_message op, + // handle it now. Note that we need to re-enter the call combiner + // for this, since we can't send two batches down while holding the + // call combiner, since the connected_channel filter (at the bottom of + // the call stack) will release the call combiner for each batch it sees. + if (calld->send_message_batch != nullptr) { + GRPC_CALL_COMBINER_START( + calld->call_combiner, + &calld->start_send_message_batch_in_call_combiner, GRPC_ERROR_NONE, + "starting send_message after send_initial_metadata"); + } + } + // Handle send_message. + if (batch->send_message) { + GPR_ASSERT(calld->send_message_batch == nullptr); + calld->send_message_batch = batch; + // If we have not yet seen send_initial_metadata, then we have to + // wait. We save the batch in calld and then drop the call + // combiner, which we'll have to pick up again later when we get + // send_initial_metadata. + if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) { + GRPC_CALL_COMBINER_STOP( + calld->call_combiner, + "send_message batch pending send_initial_metadata"); + return; + } + start_send_message_batch(elem, GRPC_ERROR_NONE); + } else { + // Pass control down the stack. + grpc_call_next_op(elem, batch); + } +} + +/* Constructor for call_data */ +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + new (elem->call_data) call_data(elem, *args); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = static_cast(elem->call_data); + calld->~call_data(); +} + +/* Constructor for channel_data */ +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* channeld = static_cast(elem->channel_data); + + channeld->enabled_algorithms_bitset = + grpc_channel_args_compression_algorithm_get_states(args->channel_args); + channeld->default_compression_algorithm = + grpc_channel_args_get_compression_algorithm(args->channel_args); + + /* Make sure the default isn't disabled. */ + if (!GPR_BITGET(channeld->enabled_algorithms_bitset, + channeld->default_compression_algorithm)) { + gpr_log(GPR_DEBUG, + "compression algorithm %d not enabled: switching to none", + channeld->default_compression_algorithm); + channeld->default_compression_algorithm = GRPC_COMPRESS_NONE; + } + + uint32_t supported_compression_algorithms = + (((1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1) & + channeld->enabled_algorithms_bitset) | + 1u; + + channeld->supported_message_compression_algorithms = + grpc_compression_bitset_to_message_bitset( + supported_compression_algorithms); + + channeld->supported_stream_compression_algorithms = + grpc_compression_bitset_to_stream_bitset( + supported_compression_algorithms); + + GPR_ASSERT(!args->is_last); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element* elem) {} + +const grpc_channel_filter grpc_message_compress_filter = { + compress_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "message_compress"}; diff --git a/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.h b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.h new file mode 100644 index 00000000000000..e163e3cf98f47f --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/message_compress/message_compress_filter.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H +#define GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H + +#include + +#include + +#include "src/core/lib/channel/channel_stack.h" + +/** 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_COMPRESSION_REQUEST_ALGORITHM_MD_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_message_compress_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_HTTP_MESSAGE_COMPRESS_MESSAGE_COMPRESS_FILTER_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.cc b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.cc new file mode 100644 index 00000000000000..ce1be8370c662b --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.cc @@ -0,0 +1,495 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/http/server/http_server_filter.h" + +#include +#include +#include +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/slice/percent_encoding.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" + +#define EXPECTED_CONTENT_TYPE "application/grpc" +#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1 + +static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err); +static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err); +static void hs_recv_message_ready(void* user_data, grpc_error* err); + +namespace { + +struct call_data { + call_data(grpc_call_element* elem, const grpc_call_element_args& args) + : call_combiner(args.call_combiner) { + GRPC_CLOSURE_INIT(&recv_initial_metadata_ready, + hs_recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&recv_message_ready, hs_recv_message_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready, + hs_recv_trailing_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + } + + ~call_data() { + GRPC_ERROR_UNREF(recv_initial_metadata_ready_error); + if (have_read_stream) { + read_stream->Orphan(); + } + } + + grpc_call_combiner* call_combiner; + + // Outgoing headers to add to send_initial_metadata. + grpc_linked_mdelem status; + grpc_linked_mdelem content_type; + + // If we see the recv_message contents in the GET query string, we + // store it here. + grpc_core::ManualConstructor read_stream; + bool have_read_stream = false; + + // State for intercepting recv_initial_metadata. + grpc_closure recv_initial_metadata_ready; + grpc_error* recv_initial_metadata_ready_error = GRPC_ERROR_NONE; + grpc_closure* original_recv_initial_metadata_ready; + grpc_metadata_batch* recv_initial_metadata = nullptr; + uint32_t* recv_initial_metadata_flags; + bool seen_recv_initial_metadata_ready = false; + + // State for intercepting recv_message. + grpc_closure* original_recv_message_ready; + grpc_closure recv_message_ready; + grpc_core::OrphanablePtr* recv_message; + bool seen_recv_message_ready = false; + + // State for intercepting recv_trailing_metadata + grpc_closure recv_trailing_metadata_ready; + grpc_closure* original_recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_ready_error; + bool seen_recv_trailing_metadata_ready = false; +}; + +struct channel_data { + bool surface_user_agent; +}; + +} // namespace + +static grpc_error* hs_filter_outgoing_metadata(grpc_call_element* elem, + grpc_metadata_batch* b) { + if (b->idx.named.grpc_message != nullptr) { + grpc_slice pct_encoded_msg = grpc_percent_encode_slice( + GRPC_MDVALUE(b->idx.named.grpc_message->md), + grpc_compatible_percent_encoding_unreserved_bytes); + if (grpc_slice_is_equivalent(pct_encoded_msg, + GRPC_MDVALUE(b->idx.named.grpc_message->md))) { + grpc_slice_unref_internal(pct_encoded_msg); + } else { + grpc_metadata_batch_set_value(b->idx.named.grpc_message, pct_encoded_msg); + } + } + return GRPC_ERROR_NONE; +} + +static void hs_add_error(const char* error_name, grpc_error** cumulative, + grpc_error* new_err) { + if (new_err == GRPC_ERROR_NONE) return; + if (*cumulative == GRPC_ERROR_NONE) { + *cumulative = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_name); + } + *cumulative = grpc_error_add_child(*cumulative, new_err); +} + +static grpc_error* hs_filter_incoming_metadata(grpc_call_element* elem, + grpc_metadata_batch* b) { + call_data* calld = static_cast(elem->call_data); + grpc_error* error = GRPC_ERROR_NONE; + static const char* error_name = "Failed processing incoming headers"; + + if (b->idx.named.method != nullptr) { + if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) { + *calld->recv_initial_metadata_flags &= + ~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST | + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST); + } else if (grpc_mdelem_eq(b->idx.named.method->md, + GRPC_MDELEM_METHOD_PUT)) { + *calld->recv_initial_metadata_flags &= + ~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; + *calld->recv_initial_metadata_flags |= + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + } else if (grpc_mdelem_eq(b->idx.named.method->md, + GRPC_MDELEM_METHOD_GET)) { + *calld->recv_initial_metadata_flags |= + GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; + *calld->recv_initial_metadata_flags &= + ~GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + } else { + hs_add_error(error_name, &error, + grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), + b->idx.named.method->md)); + } + grpc_metadata_batch_remove(b, b->idx.named.method); + } else { + hs_add_error( + error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":method"))); + } + + if (b->idx.named.te != nullptr) { + if (!grpc_mdelem_eq(b->idx.named.te->md, GRPC_MDELEM_TE_TRAILERS)) { + hs_add_error(error_name, &error, + grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), + b->idx.named.te->md)); + } + grpc_metadata_batch_remove(b, b->idx.named.te); + } else { + hs_add_error(error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string("te"))); + } + + if (b->idx.named.scheme != nullptr) { + if (!grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTP) && + !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTPS) && + !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_GRPC)) { + hs_add_error(error_name, &error, + grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), + b->idx.named.scheme->md)); + } + grpc_metadata_batch_remove(b, b->idx.named.scheme); + } else { + hs_add_error( + error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":scheme"))); + } + + if (b->idx.named.content_type != nullptr) { + if (!grpc_mdelem_eq(b->idx.named.content_type->md, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) { + if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md), + EXPECTED_CONTENT_TYPE, + EXPECTED_CONTENT_TYPE_LENGTH) && + (GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + '+' || + GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + ';')) { + /* 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. */ + char* val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md), + GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "Unexpected content-type '%s'", val); + gpr_free(val); + } + } + grpc_metadata_batch_remove(b, b->idx.named.content_type); + } + + if (b->idx.named.path == nullptr) { + hs_add_error( + error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":path"))); + } else if (*calld->recv_initial_metadata_flags & + GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) { + /* We have a cacheable request made with GET verb. The path contains the + * query parameter which is base64 encoded request payload. */ + const char k_query_separator = '?'; + grpc_slice path_slice = GRPC_MDVALUE(b->idx.named.path->md); + uint8_t* path_ptr = GRPC_SLICE_START_PTR(path_slice); + size_t path_length = GRPC_SLICE_LENGTH(path_slice); + /* offset of the character '?' */ + size_t offset = 0; + for (offset = 0; offset < path_length && *path_ptr != k_query_separator; + path_ptr++, offset++) + ; + if (offset < path_length) { + grpc_slice query_slice = + grpc_slice_sub(path_slice, offset + 1, path_length); + + /* substitute path metadata with just the path (not query) */ + grpc_mdelem mdelem_path_without_query = grpc_mdelem_from_slices( + GRPC_MDSTR_PATH, grpc_slice_sub(path_slice, 0, offset)); + + grpc_metadata_batch_substitute(b, b->idx.named.path, + mdelem_path_without_query); + + /* decode payload from query and add to the slice buffer to be returned */ + const int k_url_safe = 1; + grpc_slice_buffer read_slice_buffer; + grpc_slice_buffer_init(&read_slice_buffer); + grpc_slice_buffer_add( + &read_slice_buffer, + grpc_base64_decode_with_len( + reinterpret_cast GRPC_SLICE_START_PTR(query_slice), + GRPC_SLICE_LENGTH(query_slice), k_url_safe)); + calld->read_stream.Init(&read_slice_buffer, 0); + grpc_slice_buffer_destroy_internal(&read_slice_buffer); + calld->have_read_stream = true; + grpc_slice_unref_internal(query_slice); + } else { + gpr_log(GPR_ERROR, "GET request without QUERY"); + } + } + + if (b->idx.named.host != nullptr && b->idx.named.authority == nullptr) { + grpc_linked_mdelem* el = b->idx.named.host; + grpc_mdelem md = GRPC_MDELEM_REF(el->md); + grpc_metadata_batch_remove(b, el); + hs_add_error(error_name, &error, + grpc_metadata_batch_add_head( + b, el, + grpc_mdelem_from_slices( + GRPC_MDSTR_AUTHORITY, + grpc_slice_ref_internal(GRPC_MDVALUE(md))))); + GRPC_MDELEM_UNREF(md); + } + + if (b->idx.named.authority == nullptr) { + hs_add_error( + error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":authority"))); + } + + channel_data* chand = static_cast(elem->channel_data); + if (!chand->surface_user_agent && b->idx.named.user_agent != nullptr) { + grpc_metadata_batch_remove(b, b->idx.named.user_agent); + } + + return error; +} + +static void hs_recv_initial_metadata_ready(void* user_data, grpc_error* err) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + calld->seen_recv_initial_metadata_ready = true; + if (err == GRPC_ERROR_NONE) { + err = hs_filter_incoming_metadata(elem, calld->recv_initial_metadata); + calld->recv_initial_metadata_ready_error = GRPC_ERROR_REF(err); + if (calld->seen_recv_message_ready) { + // We've already seen the recv_message callback, but we previously + // deferred it, so we need to return it here. + // Replace the recv_message byte stream if needed. + if (calld->have_read_stream) { + calld->recv_message->reset(calld->read_stream.get()); + calld->have_read_stream = false; + } + // Re-enter call combiner for original_recv_message_ready, since the + // surface code will release the call combiner for each callback it + // receives. + GRPC_CALL_COMBINER_START( + calld->call_combiner, calld->original_recv_message_ready, + GRPC_ERROR_REF(err), + "resuming recv_message_ready from recv_initial_metadata_ready"); + } + } else { + GRPC_ERROR_REF(err); + } + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_ready_error, + "resuming hs_recv_trailing_metadata_ready from " + "hs_recv_initial_metadata_ready"); + } + GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready, err); +} + +static void hs_recv_message_ready(void* user_data, grpc_error* err) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + calld->seen_recv_message_ready = true; + if (calld->seen_recv_initial_metadata_ready) { + // We've already seen the recv_initial_metadata callback, so + // replace the recv_message byte stream if needed and invoke the + // original recv_message callback immediately. + if (calld->have_read_stream) { + calld->recv_message->reset(calld->read_stream.get()); + calld->have_read_stream = false; + } + GRPC_CLOSURE_RUN(calld->original_recv_message_ready, GRPC_ERROR_REF(err)); + } else { + // We have not yet seen the recv_initial_metadata callback, so we + // need to wait to see if this is a GET request. + // Note that we release the call combiner here, so that other + // callbacks can run. + GRPC_CALL_COMBINER_STOP( + calld->call_combiner, + "pausing recv_message_ready until recv_initial_metadata_ready"); + } +} + +static void hs_recv_trailing_metadata_ready(void* user_data, grpc_error* err) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + if (!calld->seen_recv_initial_metadata_ready) { + calld->recv_trailing_metadata_ready_error = GRPC_ERROR_REF(err); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring hs_recv_trailing_metadata_ready until " + "ater hs_recv_initial_metadata_ready"); + return; + } + err = grpc_error_add_child( + GRPC_ERROR_REF(err), + GRPC_ERROR_REF(calld->recv_initial_metadata_ready_error)); + GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err); +} + +static grpc_error* hs_mutate_op(grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + /* grab pointers to our data from the call element */ + call_data* calld = static_cast(elem->call_data); + + if (op->send_initial_metadata) { + grpc_error* error = GRPC_ERROR_NONE; + static const char* error_name = "Failed sending initial metadata"; + hs_add_error(error_name, &error, + grpc_metadata_batch_add_head( + op->payload->send_initial_metadata.send_initial_metadata, + &calld->status, GRPC_MDELEM_STATUS_200)); + hs_add_error(error_name, &error, + grpc_metadata_batch_add_tail( + op->payload->send_initial_metadata.send_initial_metadata, + &calld->content_type, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)); + hs_add_error( + error_name, &error, + hs_filter_outgoing_metadata( + elem, op->payload->send_initial_metadata.send_initial_metadata)); + if (error != GRPC_ERROR_NONE) return error; + } + + if (op->recv_initial_metadata) { + /* substitute our callback for the higher callback */ + GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags != nullptr); + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + calld->recv_initial_metadata_flags = + op->payload->recv_initial_metadata.recv_flags; + calld->original_recv_initial_metadata_ready = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + + if (op->recv_message) { + calld->recv_message = op->payload->recv_message.recv_message; + calld->original_recv_message_ready = + op->payload->recv_message.recv_message_ready; + op->payload->recv_message.recv_message_ready = &calld->recv_message_ready; + } + + if (op->recv_trailing_metadata) { + calld->original_recv_trailing_metadata_ready = + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &calld->recv_trailing_metadata_ready; + } + + if (op->send_trailing_metadata) { + grpc_error* error = hs_filter_outgoing_metadata( + elem, op->payload->send_trailing_metadata.send_trailing_metadata); + if (error != GRPC_ERROR_NONE) return error; + } + + return GRPC_ERROR_NONE; +} + +static void hs_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + GPR_TIMER_SCOPE("hs_start_transport_stream_op_batch", 0); + call_data* calld = static_cast(elem->call_data); + grpc_error* error = hs_mutate_op(elem, op); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure(op, error, + calld->call_combiner); + } else { + grpc_call_next_op(elem, op); + } +} + +/* Constructor for call_data */ +static grpc_error* hs_init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + new (elem->call_data) call_data(elem, *args); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void hs_destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = static_cast(elem->call_data); + calld->~call_data(); +} + +/* Constructor for channel_data */ +static grpc_error* hs_init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + GPR_ASSERT(!args->is_last); + chand->surface_user_agent = grpc_channel_arg_get_bool( + grpc_channel_args_find(args->channel_args, + const_cast(GRPC_ARG_SURFACE_USER_AGENT)), + true); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void hs_destroy_channel_elem(grpc_channel_element* elem) {} + +const grpc_channel_filter grpc_http_server_filter = { + hs_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + hs_init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + hs_destroy_call_elem, + sizeof(channel_data), + hs_init_channel_elem, + hs_destroy_channel_elem, + grpc_channel_next_get_info, + "http-server"}; diff --git a/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.h b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.h new file mode 100644 index 00000000000000..e4e0546f6dbc4c --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/http/server/http_server_filter.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H +#define GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" + +/* Processes metadata on the server side for HTTP2 transports */ +extern const grpc_channel_filter grpc_http_server_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_HTTP_SERVER_HTTP_SERVER_FILTER_H */ diff --git a/third_party/grpc/src/core/ext/filters/load_reporting/registered_opencensus_objects.h b/third_party/grpc/src/core/ext/filters/load_reporting/registered_opencensus_objects.h new file mode 100644 index 00000000000000..4eacda7c02a383 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/load_reporting/registered_opencensus_objects.h @@ -0,0 +1,117 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_REGISTERED_OPENCENSUS_OBJECTS_H +#define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_REGISTERED_OPENCENSUS_OBJECTS_H + +#include + +#include "opencensus/stats/stats.h" + +#include "src/cpp/server/load_reporter/constants.h" + +namespace grpc { +namespace load_reporter { + +// Note that the functions here are specified as inline to share the static +// objects across all the translation units including this header. See more +// details on https://en.cppreference.com/w/cpp/language/inline. + +// Measures. + +inline ::opencensus::stats::MeasureInt64 MeasureStartCount() { + static const ::opencensus::stats::MeasureInt64 measure = + ::opencensus::stats::MeasureInt64::Register( + kMeasureStartCount, kMeasureStartCount, kMeasureStartCount); + return measure; +} + +inline ::opencensus::stats::MeasureInt64 MeasureEndCount() { + static const ::opencensus::stats::MeasureInt64 measure = + ::opencensus::stats::MeasureInt64::Register( + kMeasureEndCount, kMeasureEndCount, kMeasureEndCount); + return measure; +} + +inline ::opencensus::stats::MeasureInt64 MeasureEndBytesSent() { + static const ::opencensus::stats::MeasureInt64 measure = + ::opencensus::stats::MeasureInt64::Register( + kMeasureEndBytesSent, kMeasureEndBytesSent, kMeasureEndBytesSent); + return measure; +} + +inline ::opencensus::stats::MeasureInt64 MeasureEndBytesReceived() { + static const ::opencensus::stats::MeasureInt64 measure = + ::opencensus::stats::MeasureInt64::Register(kMeasureEndBytesReceived, + kMeasureEndBytesReceived, + kMeasureEndBytesReceived); + return measure; +} + +inline ::opencensus::stats::MeasureInt64 MeasureEndLatencyMs() { + static const ::opencensus::stats::MeasureInt64 measure = + ::opencensus::stats::MeasureInt64::Register( + kMeasureEndLatencyMs, kMeasureEndLatencyMs, kMeasureEndLatencyMs); + return measure; +} + +inline ::opencensus::stats::MeasureDouble MeasureOtherCallMetric() { + static const ::opencensus::stats::MeasureDouble measure = + ::opencensus::stats::MeasureDouble::Register(kMeasureOtherCallMetric, + kMeasureOtherCallMetric, + kMeasureOtherCallMetric); + return measure; +} + +// Tags. + +inline ::opencensus::stats::TagKey TagKeyToken() { + static const ::opencensus::stats::TagKey token = + opencensus::stats::TagKey::Register(kTagKeyToken); + return token; +} + +inline ::opencensus::stats::TagKey TagKeyHost() { + static const ::opencensus::stats::TagKey token = + opencensus::stats::TagKey::Register(kTagKeyHost); + return token; +} + +inline ::opencensus::stats::TagKey TagKeyUserId() { + static const ::opencensus::stats::TagKey token = + opencensus::stats::TagKey::Register(kTagKeyUserId); + return token; +} + +inline ::opencensus::stats::TagKey TagKeyStatus() { + static const ::opencensus::stats::TagKey token = + opencensus::stats::TagKey::Register(kTagKeyStatus); + return token; +} + +inline ::opencensus::stats::TagKey TagKeyMetricName() { + static const ::opencensus::stats::TagKey token = + opencensus::stats::TagKey::Register(kTagKeyMetricName); + return token; +} + +} // namespace load_reporter +} // namespace grpc + +#endif /* GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_REGISTERED_OPENCENSUS_OBJECTS_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc new file mode 100644 index 00000000000000..6a7231ff7dbc9e --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc @@ -0,0 +1,363 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/load_reporting/registered_opencensus_objects.h" +#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/context.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_posix.h" +#include "src/core/lib/iomgr/socket_utils.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/uri/uri_parser.h" + +namespace grpc { + +constexpr char kEncodedIpv4AddressLengthString[] = "08"; +constexpr char kEncodedIpv6AddressLengthString[] = "32"; +constexpr char kEmptyAddressLengthString[] = "00"; +constexpr size_t kLengthPrefixSize = 2; + +grpc_error* ServerLoadReportingChannelData::Init( + grpc_channel_element* /* elem */, grpc_channel_element_args* args) { + GPR_ASSERT(!args->is_last); + // Find and record the peer_identity. + const grpc_auth_context* auth_context = + grpc_find_auth_context_in_args(args->channel_args); + if (auth_context != nullptr && + grpc_auth_context_peer_is_authenticated(auth_context)) { + grpc_auth_property_iterator auth_it = + grpc_auth_context_peer_identity(auth_context); + const grpc_auth_property* auth_property = + grpc_auth_property_iterator_next(&auth_it); + if (auth_property != nullptr) { + peer_identity_ = auth_property->value; + peer_identity_len_ = auth_property->value_length; + } + } + return GRPC_ERROR_NONE; +} + +void ServerLoadReportingCallData::Destroy( + grpc_call_element* elem, const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) { + ServerLoadReportingChannelData* chand = + reinterpret_cast(elem->channel_data); + // Only record an end if we've recorded its corresponding start, which is + // indicated by a non-null client_ip_and_lr_token_. Note that it's possible + // that we attempt to record the call end before we have recorded the call + // start, because the data needed for recording the start comes from the + // initial metadata, which may not be ready before the call finishes. + if (client_ip_and_lr_token_ != nullptr) { + opencensus::stats::Record( + {{::grpc::load_reporter::MeasureEndCount(), 1}, + {::grpc::load_reporter::MeasureEndBytesSent(), + final_info->stats.transport_stream_stats.outgoing.data_bytes}, + {::grpc::load_reporter::MeasureEndBytesReceived(), + final_info->stats.transport_stream_stats.incoming.data_bytes}, + {::grpc::load_reporter::MeasureEndLatencyMs(), + gpr_time_to_millis(final_info->stats.latency)}}, + {{::grpc::load_reporter::TagKeyToken(), + {client_ip_and_lr_token_, client_ip_and_lr_token_len_}}, + {::grpc::load_reporter::TagKeyHost(), + {target_host_, target_host_len_}}, + {::grpc::load_reporter::TagKeyUserId(), + {chand->peer_identity(), chand->peer_identity_len()}}, + {::grpc::load_reporter::TagKeyStatus(), + GetStatusTagForStatus(final_info->final_status)}}); + gpr_free(client_ip_and_lr_token_); + } + gpr_free(target_host_); + grpc_slice_unref_internal(service_method_); +} + +void ServerLoadReportingCallData::StartTransportStreamOpBatch( + grpc_call_element* elem, TransportStreamOpBatch* op) { + GPR_TIMER_SCOPE("lr_start_transport_stream_op", 0); + if (op->recv_initial_metadata() != nullptr) { + // Save some fields to use when initial metadata is ready. + peer_string_ = op->get_peer_string(); + recv_initial_metadata_ = + op->op()->payload->recv_initial_metadata.recv_initial_metadata; + original_recv_initial_metadata_ready_ = op->recv_initial_metadata_ready(); + // Substitute the original closure for the wrapper closure. + op->set_recv_initial_metadata_ready(&recv_initial_metadata_ready_); + } else if (op->send_trailing_metadata() != nullptr) { + GRPC_LOG_IF_ERROR( + "server_load_reporting_filter", + grpc_metadata_batch_filter(op->send_trailing_metadata()->batch(), + SendTrailingMetadataFilter, elem, + "send_trailing_metadata filtering error")); + } + grpc_call_next_op(elem, op->op()); +} + +void ServerLoadReportingCallData::GetCensusSafeClientIpString( + char** client_ip_string, size_t* size) { + // Find the client URI string. + const char* client_uri_str = + reinterpret_cast(gpr_atm_acq_load(peer_string_)); + if (client_uri_str == nullptr) { + gpr_log(GPR_ERROR, + "Unable to extract client URI string (peer string) from gRPC " + "metadata."); + *client_ip_string = nullptr; + *size = 0; + return; + } + // Parse the client URI string into grpc_uri. + grpc_uri* client_uri = grpc_uri_parse(client_uri_str, true); + if (client_uri == nullptr) { + gpr_log(GPR_ERROR, + "Unable to parse the client URI string (peer string) to a client " + "URI."); + *client_ip_string = nullptr; + *size = 0; + return; + } + // Parse the client URI into grpc_resolved_address. + grpc_resolved_address resolved_address; + bool success = grpc_parse_uri(client_uri, &resolved_address); + grpc_uri_destroy(client_uri); + if (!success) { + gpr_log(GPR_ERROR, + "Unable to parse client URI into a grpc_resolved_address."); + *client_ip_string = nullptr; + *size = 0; + return; + } + // Convert the socket address in the grpc_resolved_address into a hex string + // according to the address family. + grpc_sockaddr* addr = reinterpret_cast(resolved_address.addr); + if (addr->sa_family == GRPC_AF_INET) { + grpc_sockaddr_in* addr4 = reinterpret_cast(addr); + gpr_asprintf(client_ip_string, "%08x", grpc_ntohl(addr4->sin_addr.s_addr)); + *size = 8; + } else if (addr->sa_family == GRPC_AF_INET6) { + grpc_sockaddr_in6* addr6 = reinterpret_cast(addr); + *client_ip_string = static_cast(gpr_malloc(32 + 1)); + uint32_t* addr6_next_long = reinterpret_cast(&addr6->sin6_addr); + for (size_t i = 0; i < 4; ++i) { + snprintf(*client_ip_string + 8 * i, 8 + 1, "%08x", + grpc_ntohl(*addr6_next_long++)); + } + *size = 32; + } else { + GPR_UNREACHABLE_CODE(); + } +} + +void ServerLoadReportingCallData::StoreClientIpAndLrToken(const char* lr_token, + size_t lr_token_len) { + char* client_ip; + size_t client_ip_len; + GetCensusSafeClientIpString(&client_ip, &client_ip_len); + client_ip_and_lr_token_len_ = + kLengthPrefixSize + client_ip_len + lr_token_len; + client_ip_and_lr_token_ = static_cast( + gpr_zalloc(client_ip_and_lr_token_len_ * sizeof(char))); + char* cur_pos = client_ip_and_lr_token_; + // Store the IP length prefix. + if (client_ip_len == 0) { + strncpy(cur_pos, kEmptyAddressLengthString, kLengthPrefixSize); + } else if (client_ip_len == 8) { + strncpy(cur_pos, kEncodedIpv4AddressLengthString, kLengthPrefixSize); + } else if (client_ip_len == 32) { + strncpy(cur_pos, kEncodedIpv6AddressLengthString, kLengthPrefixSize); + } else { + GPR_UNREACHABLE_CODE(); + } + cur_pos += kLengthPrefixSize; + // Store the IP. + if (client_ip_len != 0) { + strncpy(cur_pos, client_ip, client_ip_len); + } + gpr_free(client_ip); + cur_pos += client_ip_len; + // Store the LR token. + if (lr_token_len != 0) { + strncpy(cur_pos, lr_token, lr_token_len); + } + GPR_ASSERT(cur_pos + lr_token_len - client_ip_and_lr_token_ == + client_ip_and_lr_token_len_); +} + +grpc_filtered_mdelem ServerLoadReportingCallData::RecvInitialMetadataFilter( + void* user_data, grpc_mdelem md) { + grpc_call_element* elem = reinterpret_cast(user_data); + ServerLoadReportingCallData* calld = + reinterpret_cast(elem->call_data); + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) { + calld->service_method_ = grpc_slice_ref_internal(GRPC_MDVALUE(md)); + } else if (calld->target_host_ == nullptr && + grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_AUTHORITY)) { + grpc_slice target_host_slice = GRPC_MDVALUE(md); + calld->target_host_len_ = GRPC_SLICE_LENGTH(target_host_slice); + calld->target_host_ = + reinterpret_cast(gpr_zalloc(calld->target_host_len_)); + for (size_t i = 0; i < calld->target_host_len_; ++i) { + calld->target_host_[i] = static_cast( + tolower(GRPC_SLICE_START_PTR(target_host_slice)[i])); + } + } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_LB_TOKEN)) { + if (calld->client_ip_and_lr_token_ == nullptr) { + calld->StoreClientIpAndLrToken( + reinterpret_cast GRPC_SLICE_START_PTR(GRPC_MDVALUE(md)), + GRPC_SLICE_LENGTH(GRPC_MDVALUE(md))); + } + return GRPC_FILTERED_REMOVE(); + } + return GRPC_FILTERED_MDELEM(md); +} + +void ServerLoadReportingCallData::RecvInitialMetadataReady(void* arg, + grpc_error* err) { + grpc_call_element* elem = reinterpret_cast(arg); + ServerLoadReportingCallData* calld = + reinterpret_cast(elem->call_data); + ServerLoadReportingChannelData* chand = + reinterpret_cast(elem->channel_data); + if (err == GRPC_ERROR_NONE) { + GRPC_LOG_IF_ERROR( + "server_load_reporting_filter", + grpc_metadata_batch_filter(calld->recv_initial_metadata_, + RecvInitialMetadataFilter, elem, + "recv_initial_metadata filtering error")); + // If the LB token was not found in the recv_initial_metadata, only the + // client IP part will be recorded (with an empty LB token). + if (calld->client_ip_and_lr_token_ == nullptr) { + calld->StoreClientIpAndLrToken(nullptr, 0); + } + opencensus::stats::Record( + {{::grpc::load_reporter::MeasureStartCount(), 1}}, + {{::grpc::load_reporter::TagKeyToken(), + {calld->client_ip_and_lr_token_, calld->client_ip_and_lr_token_len_}}, + {::grpc::load_reporter::TagKeyHost(), + {calld->target_host_, calld->target_host_len_}}, + {::grpc::load_reporter::TagKeyUserId(), + {chand->peer_identity(), chand->peer_identity_len()}}}); + } + GRPC_CLOSURE_RUN(calld->original_recv_initial_metadata_ready_, + GRPC_ERROR_REF(err)); +} + +grpc_error* ServerLoadReportingCallData::Init( + grpc_call_element* elem, const grpc_call_element_args* args) { + service_method_ = grpc_empty_slice(); + GRPC_CLOSURE_INIT(&recv_initial_metadata_ready_, RecvInitialMetadataReady, + elem, grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +grpc_filtered_mdelem ServerLoadReportingCallData::SendTrailingMetadataFilter( + void* user_data, grpc_mdelem md) { + grpc_call_element* elem = reinterpret_cast(user_data); + ServerLoadReportingCallData* calld = + reinterpret_cast(elem->call_data); + ServerLoadReportingChannelData* chand = + reinterpret_cast(elem->channel_data); + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_LB_COST_BIN)) { + const grpc_slice value = GRPC_MDVALUE(md); + const size_t cost_entry_size = GRPC_SLICE_LENGTH(value); + if (cost_entry_size < sizeof(double)) { + gpr_log(GPR_ERROR, + "Cost metadata value too small (%zu bytes) to hold valid data. " + "Ignoring.", + cost_entry_size); + return GRPC_FILTERED_REMOVE(); + } + const double* cost_entry_ptr = + reinterpret_cast(GRPC_SLICE_START_PTR(value)); + double cost_value = *cost_entry_ptr++; + const char* cost_name = reinterpret_cast(cost_entry_ptr); + const size_t cost_name_len = cost_entry_size - sizeof(double); + opencensus::stats::Record( + {{::grpc::load_reporter::MeasureOtherCallMetric(), cost_value}}, + {{::grpc::load_reporter::TagKeyToken(), + {calld->client_ip_and_lr_token_, calld->client_ip_and_lr_token_len_}}, + {::grpc::load_reporter::TagKeyHost(), + {calld->target_host_, calld->target_host_len_}}, + {::grpc::load_reporter::TagKeyUserId(), + {chand->peer_identity(), chand->peer_identity_len()}}, + {::grpc::load_reporter::TagKeyMetricName(), + {cost_name, cost_name_len}}}); + return GRPC_FILTERED_REMOVE(); + } + return GRPC_FILTERED_MDELEM(md); +} + +const char* ServerLoadReportingCallData::GetStatusTagForStatus( + grpc_status_code status) { + switch (status) { + case GRPC_STATUS_OK: + return ::grpc::load_reporter::kCallStatusOk; + case GRPC_STATUS_UNKNOWN: + case GRPC_STATUS_DEADLINE_EXCEEDED: + case GRPC_STATUS_UNIMPLEMENTED: + case GRPC_STATUS_INTERNAL: + case GRPC_STATUS_UNAVAILABLE: + case GRPC_STATUS_DATA_LOSS: + return ::grpc::load_reporter::kCallStatusServerError; + default: + return ::grpc::load_reporter::kCallStatusClientError; + } +} + +namespace { +bool MaybeAddServerLoadReportingFilter(const grpc_channel_args& args) { + return grpc_channel_arg_get_bool( + grpc_channel_args_find(&args, GRPC_ARG_ENABLE_LOAD_REPORTING), false); +} +} // namespace + +// TODO(juanlishen): We should register the filter during grpc initialization +// time once OpenCensus is compatible with our build system. For now, we force +// registration of the server load reporting filter at static initialization +// time if we build with the filter target. +struct ServerLoadReportingFilterStaticRegistrar { + ServerLoadReportingFilterStaticRegistrar() { + static std::atomic_bool registered{false}; + if (registered) return; + RegisterChannelFilter( + "server_load_reporting", GRPC_SERVER_CHANNEL, INT_MAX, + MaybeAddServerLoadReportingFilter); + // Access measures to ensure they are initialized. Otherwise, we can't + // create any valid view before the first RPC. + ::grpc::load_reporter::MeasureStartCount(); + ::grpc::load_reporter::MeasureEndCount(); + ::grpc::load_reporter::MeasureEndBytesSent(); + ::grpc::load_reporter::MeasureEndBytesReceived(); + ::grpc::load_reporter::MeasureEndLatencyMs(); + ::grpc::load_reporter::MeasureOtherCallMetric(); + registered = true; + } +} server_load_reporting_filter_static_registrar; + +} // namespace grpc diff --git a/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.h b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.h new file mode 100644 index 00000000000000..10baf1f8332083 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/load_reporting/server_load_reporting_filter.h @@ -0,0 +1,120 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H +#define GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/cpp/common/channel_filter.h" + +namespace grpc { + +class ServerLoadReportingChannelData : public ChannelData { + public: + grpc_error* Init(grpc_channel_element* elem, + grpc_channel_element_args* args) override; + + // Getters. + const char* peer_identity() { return peer_identity_; } + size_t peer_identity_len() { return peer_identity_len_; } + + private: + // The peer's authenticated identity. + char* peer_identity_ = nullptr; + size_t peer_identity_len_ = 0; +}; + +class ServerLoadReportingCallData : public CallData { + public: + grpc_error* Init(grpc_call_element* elem, + const grpc_call_element_args* args) override; + + void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) override; + + void StartTransportStreamOpBatch(grpc_call_element* elem, + TransportStreamOpBatch* op) override; + + private: + // From the peer_string_ in calld, extracts the client IP string (owned by + // caller), e.g., "01020a0b". Upon failure, set the output pointer to null and + // size to zero. + void GetCensusSafeClientIpString(char** client_ip_string, size_t* size); + + // Concatenates the client IP address and the load reporting token, then + // stores the result into the call data. + void StoreClientIpAndLrToken(const char* lr_token, size_t lr_token_len); + + // This matches the classification of the status codes in + // googleapis/google/rpc/code.proto. + static const char* GetStatusTagForStatus(grpc_status_code status); + + // Records the call start. + static void RecvInitialMetadataReady(void* arg, grpc_error* err); + + // From the initial metadata, extracts the service_method_, target_host_, and + // client_ip_and_lr_token_. + static grpc_filtered_mdelem RecvInitialMetadataFilter(void* user_data, + grpc_mdelem md); + + // Records the other call metrics. + static grpc_filtered_mdelem SendTrailingMetadataFilter(void* user_data, + grpc_mdelem md); + + // The peer string (a member of the recv_initial_metadata op). Note that + // gpr_atm itself is a pointer type here, making "peer_string_" effectively a + // double pointer. + const gpr_atm* peer_string_; + + // The received initial metadata (a member of the recv_initial_metadata op). + // When it is ready, we will extract some data from it via + // recv_initial_metadata_ready_ closure, before the original + // recv_initial_metadata_ready closure. + grpc_metadata_batch* recv_initial_metadata_; + + // The original recv_initial_metadata closure, which is wrapped by our own + // closure (recv_initial_metadata_ready_) to capture the incoming initial + // metadata. + grpc_closure* original_recv_initial_metadata_ready_; + + // The closure that wraps the original closure. Scheduled when + // recv_initial_metadata_ is ready. + grpc_closure recv_initial_metadata_ready_; + + // Corresponds to the :path header. + grpc_slice service_method_; + + // The backend host that the client thinks it's talking to. This may be + // different from the actual backend in the case of, for example, + // load-balanced targets. We store a copy of the metadata slice in order to + // lowercase it. */ + char* target_host_; + size_t target_host_len_; + + // The client IP address (including a length prefix) and the load reporting + // token. + char* client_ip_and_lr_token_; + size_t client_ip_and_lr_token_len_; +}; + +} // namespace grpc + +#endif /* GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_FILTER_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.cc b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.cc new file mode 100644 index 00000000000000..431472609eb378 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.cc @@ -0,0 +1,542 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/max_age/max_age_filter.h" + +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/http2_errors.h" + +#define DEFAULT_MAX_CONNECTION_AGE_MS INT_MAX +#define DEFAULT_MAX_CONNECTION_AGE_GRACE_MS INT_MAX +#define DEFAULT_MAX_CONNECTION_IDLE_MS INT_MAX +#define MAX_CONNECTION_AGE_JITTER 0.1 + +#define MAX_CONNECTION_AGE_INTEGER_OPTIONS \ + { DEFAULT_MAX_CONNECTION_AGE_MS, 1, INT_MAX } +#define MAX_CONNECTION_IDLE_INTEGER_OPTIONS \ + { DEFAULT_MAX_CONNECTION_IDLE_MS, 1, INT_MAX } + +/* States for idle_state in channel_data */ +#define MAX_IDLE_STATE_INIT ((gpr_atm)0) +#define MAX_IDLE_STATE_SEEN_EXIT_IDLE ((gpr_atm)1) +#define MAX_IDLE_STATE_SEEN_ENTER_IDLE ((gpr_atm)2) +#define MAX_IDLE_STATE_TIMER_SET ((gpr_atm)3) + +namespace { +struct channel_data { + /* We take a reference to the channel stack for the timer callback */ + grpc_channel_stack* channel_stack; + /* Guards access to max_age_timer, max_age_timer_pending, max_age_grace_timer + and max_age_grace_timer_pending */ + gpr_mu max_age_timer_mu; + /* True if the max_age timer callback is currently pending */ + bool max_age_timer_pending; + /* True if the max_age_grace timer callback is currently pending */ + bool max_age_grace_timer_pending; + /* The timer for checking if the channel has reached its max age */ + grpc_timer max_age_timer; + /* The timer for checking if the max-aged channel has uesed up the grace + period */ + grpc_timer max_age_grace_timer; + /* The timer for checking if the channel's idle duration reaches + max_connection_idle */ + grpc_timer max_idle_timer; + /* Allowed max time a channel may have no outstanding rpcs */ + grpc_millis max_connection_idle; + /* Allowed max time a channel may exist */ + grpc_millis max_connection_age; + /* Allowed grace period after the channel reaches its max age */ + grpc_millis max_connection_age_grace; + /* Closure to run when the channel's idle duration reaches max_connection_idle + and should be closed gracefully */ + grpc_closure max_idle_timer_cb; + /* Closure to run when the channel reaches its max age and should be closed + gracefully */ + grpc_closure close_max_age_channel; + /* Closure to run the channel uses up its max age grace time and should be + closed forcibly */ + grpc_closure force_close_max_age_channel; + /* Closure to run when the init fo channel stack is done and the max_idle + timer should be started */ + grpc_closure start_max_idle_timer_after_init; + /* Closure to run when the init fo channel stack is done and the max_age timer + should be started */ + grpc_closure start_max_age_timer_after_init; + /* Closure to run when the goaway op is finished and the max_age_timer */ + grpc_closure start_max_age_grace_timer_after_goaway_op; + /* Closure to run when the channel connectivity state changes */ + grpc_closure channel_connectivity_changed; + /* Records the current connectivity state */ + grpc_connectivity_state connectivity_state; + /* Number of active calls */ + gpr_atm call_count; + /* TODO(zyc): C++lize this state machine */ + /* 'idle_state' holds the states of max_idle_timer and channel idleness. + It can contain one of the following values: + +--------------------------------+----------------+---------+ + | idle_state | max_idle_timer | channel | + +--------------------------------+----------------+---------+ + | MAX_IDLE_STATE_INIT | unset | busy | + | MAX_IDLE_STATE_TIMER_SET | set, valid | idle | + | MAX_IDLE_STATE_SEEN_EXIT_IDLE | set, invalid | busy | + | MAX_IDLE_STATE_SEEN_ENTER_IDLE | set, invalid | idle | + +--------------------------------+----------------+---------+ + + MAX_IDLE_STATE_INIT: The initial and final state of 'idle_state'. The + channel has 1 or 1+ active calls, and the the timer is not set. Note that + we may put a virtual call to hold this state at channel initialization or + shutdown, so that the channel won't enter other states. + + MAX_IDLE_STATE_TIMER_SET: The state after the timer is set and no calls + have arrived after the timer is set. The channel must have 0 active call in + this state. If the timer is fired in this state, we will close the channel + due to idleness. + + MAX_IDLE_STATE_SEEN_EXIT_IDLE: The state after the timer is set and at + least one call has arrived after the timer is set. The channel must have 1 + or 1+ active calls in this state. If the timer is fired in this state, we + won't reschudle it. + + MAX_IDLE_STATE_SEEN_ENTER_IDLE: The state after the timer is set and the at + least one call has arrived after the timer is set, BUT the channel + currently has 1 or 1+ active calls. If the timer is fired in this state, we + will reschudle it. + + max_idle_timer will not be cancelled (unless the channel is shutting down). + If the timer callback is called when the max_idle_timer is valid (i.e. + idle_state is MAX_IDLE_STATE_TIMER_SET), the channel will be closed due to + idleness, otherwise the channel won't be changed. + + State transitions: + MAX_IDLE_STATE_INIT <-------3------ MAX_IDLE_STATE_SEEN_EXIT_IDLE + ^ | ^ ^ | + | | | | | + 1 2 +-----------4------------+ 6 7 + | | | | | + | v | | v + MAX_IDLE_STATE_TIMER_SET <----5------ MAX_IDLE_STATE_SEEN_ENTER_IDLE + + For 1, 3, 5 : See max_idle_timer_cb() function + For 2, 7 : See decrease_call_count() function + For 4, 6 : See increase_call_count() function */ + gpr_atm idle_state; + /* Time when the channel finished its last outstanding call, in grpc_millis */ + gpr_atm last_enter_idle_time_millis; +}; +} // namespace + +/* Increase the nubmer of active calls. Before the increasement, if there are no + calls, the max_idle_timer should be cancelled. */ +static void increase_call_count(channel_data* chand) { + /* Exit idle */ + if (gpr_atm_full_fetch_add(&chand->call_count, 1) == 0) { + while (true) { + gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state); + switch (idle_state) { + case MAX_IDLE_STATE_TIMER_SET: + /* max_idle_timer_cb may have already set idle_state to + MAX_IDLE_STATE_INIT, in this case, we don't need to set it to + MAX_IDLE_STATE_SEEN_EXIT_IDLE */ + gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_TIMER_SET, + MAX_IDLE_STATE_SEEN_EXIT_IDLE); + return; + case MAX_IDLE_STATE_SEEN_ENTER_IDLE: + gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE); + return; + default: + /* try again */ + break; + } + } + } +} + +/* Decrease the nubmer of active calls. After the decrement, if there are no + calls, the max_idle_timer should be started. */ +static void decrease_call_count(channel_data* chand) { + /* Enter idle */ + if (gpr_atm_full_fetch_add(&chand->call_count, -1) == 1) { + gpr_atm_no_barrier_store(&chand->last_enter_idle_time_millis, + (gpr_atm)grpc_core::ExecCtx::Get()->Now()); + while (true) { + gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state); + switch (idle_state) { + case MAX_IDLE_STATE_INIT: + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age max_idle_timer"); + grpc_timer_init( + &chand->max_idle_timer, + grpc_core::ExecCtx::Get()->Now() + chand->max_connection_idle, + &chand->max_idle_timer_cb); + gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_TIMER_SET); + return; + case MAX_IDLE_STATE_SEEN_EXIT_IDLE: + if (gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE, + MAX_IDLE_STATE_SEEN_ENTER_IDLE)) { + return; + } + break; + default: + /* try again */ + break; + } + } + } +} + +static void start_max_idle_timer_after_init(void* arg, grpc_error* error) { + channel_data* chand = static_cast(arg); + /* Decrease call_count. If there are no active calls at this time, + max_idle_timer will start here. If the number of active calls is not 0, + max_idle_timer will start after all the active calls end. */ + decrease_call_count(chand); + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, + "max_age start_max_idle_timer_after_init"); +} + +static void start_max_age_timer_after_init(void* arg, grpc_error* error) { + channel_data* chand = static_cast(arg); + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_timer_pending = true; + GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_timer"); + grpc_timer_init(&chand->max_age_timer, + grpc_core::ExecCtx::Get()->Now() + chand->max_connection_age, + &chand->close_max_age_channel); + gpr_mu_unlock(&chand->max_age_timer_mu); + grpc_transport_op* op = grpc_make_transport_op(nullptr); + op->on_connectivity_state_change = &chand->channel_connectivity_changed; + op->connectivity_state = &chand->connectivity_state; + grpc_channel_next_op(grpc_channel_stack_element(chand->channel_stack, 0), op); + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, + "max_age start_max_age_timer_after_init"); +} + +static void start_max_age_grace_timer_after_goaway_op(void* arg, + grpc_error* error) { + channel_data* chand = static_cast(arg); + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_grace_timer_pending = true; + GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_grace_timer"); + grpc_timer_init( + &chand->max_age_grace_timer, + chand->max_connection_age_grace == GRPC_MILLIS_INF_FUTURE + ? GRPC_MILLIS_INF_FUTURE + : grpc_core::ExecCtx::Get()->Now() + chand->max_connection_age_grace, + &chand->force_close_max_age_channel); + gpr_mu_unlock(&chand->max_age_timer_mu); + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, + "max_age start_max_age_grace_timer_after_goaway_op"); +} + +static void close_max_idle_channel(channel_data* chand) { + /* Prevent the max idle timer from being set again */ + gpr_atm_no_barrier_fetch_add(&chand->call_count, 1); + grpc_transport_op* op = grpc_make_transport_op(nullptr); + op->goaway_error = + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_idle"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(elem, op); +} + +static void max_idle_timer_cb(void* arg, grpc_error* error) { + channel_data* chand = static_cast(arg); + if (error == GRPC_ERROR_NONE) { + bool try_again = true; + while (try_again) { + gpr_atm idle_state = gpr_atm_acq_load(&chand->idle_state); + switch (idle_state) { + case MAX_IDLE_STATE_TIMER_SET: + close_max_idle_channel(chand); + /* This MAX_IDLE_STATE_INIT is a final state, we don't have to check + * if idle_state has been changed */ + gpr_atm_rel_store(&chand->idle_state, MAX_IDLE_STATE_INIT); + try_again = false; + break; + case MAX_IDLE_STATE_SEEN_EXIT_IDLE: + if (gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_EXIT_IDLE, + MAX_IDLE_STATE_INIT)) { + try_again = false; + } + break; + case MAX_IDLE_STATE_SEEN_ENTER_IDLE: + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age max_idle_timer"); + grpc_timer_init(&chand->max_idle_timer, + static_cast(gpr_atm_no_barrier_load( + &chand->last_enter_idle_time_millis)) + + chand->max_connection_idle, + &chand->max_idle_timer_cb); + /* idle_state may have already been set to + MAX_IDLE_STATE_SEEN_EXIT_IDLE by increase_call_count(), in this + case, we don't need to set it to MAX_IDLE_STATE_TIMER_SET */ + gpr_atm_rel_cas(&chand->idle_state, MAX_IDLE_STATE_SEEN_ENTER_IDLE, + MAX_IDLE_STATE_TIMER_SET); + try_again = false; + break; + default: + /* try again */ + break; + } + } + } + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_idle_timer"); +} + +static void close_max_age_channel(void* arg, grpc_error* error) { + channel_data* chand = static_cast(arg); + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_timer_pending = false; + gpr_mu_unlock(&chand->max_age_timer_mu); + if (error == GRPC_ERROR_NONE) { + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_age_grace_timer_after_goaway_op"); + grpc_transport_op* op = grpc_make_transport_op( + &chand->start_max_age_grace_timer_after_goaway_op); + op->goaway_error = + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_age"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(elem, op); + } else if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("close_max_age_channel", error); + } + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_age_timer"); +} + +static void force_close_max_age_channel(void* arg, grpc_error* error) { + channel_data* chand = static_cast(arg); + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_grace_timer_pending = false; + gpr_mu_unlock(&chand->max_age_timer_mu); + if (error == GRPC_ERROR_NONE) { + grpc_transport_op* op = grpc_make_transport_op(nullptr); + op->disconnect_with_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel reaches max age"); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(elem, op); + } else if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("force_close_max_age_channel", error); + } + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack, "max_age max_age_grace_timer"); +} + +static void channel_connectivity_changed(void* arg, grpc_error* error) { + channel_data* chand = static_cast(arg); + if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { + grpc_transport_op* op = grpc_make_transport_op(nullptr); + op->on_connectivity_state_change = &chand->channel_connectivity_changed; + op->connectivity_state = &chand->connectivity_state; + grpc_channel_next_op(grpc_channel_stack_element(chand->channel_stack, 0), + op); + } else { + gpr_mu_lock(&chand->max_age_timer_mu); + if (chand->max_age_timer_pending) { + grpc_timer_cancel(&chand->max_age_timer); + chand->max_age_timer_pending = false; + } + if (chand->max_age_grace_timer_pending) { + grpc_timer_cancel(&chand->max_age_grace_timer); + chand->max_age_grace_timer_pending = false; + } + gpr_mu_unlock(&chand->max_age_timer_mu); + /* If there are no active calls, this increasement will cancel + max_idle_timer, and prevent max_idle_timer from being started in the + future. */ + increase_call_count(chand); + if (gpr_atm_acq_load(&chand->idle_state) == MAX_IDLE_STATE_SEEN_EXIT_IDLE) { + grpc_timer_cancel(&chand->max_idle_timer); + } + } +} + +/* A random jitter of +/-10% will be added to MAX_CONNECTION_AGE to spread out + connection storms. Note that the MAX_CONNECTION_AGE option without jitter + would not create connection storms by itself, but if there happened to be a + connection storm it could cause it to repeat at a fixed period. */ +static grpc_millis +add_random_max_connection_age_jitter_and_convert_to_grpc_millis(int value) { + /* generate a random number between 1 - MAX_CONNECTION_AGE_JITTER and + 1 + MAX_CONNECTION_AGE_JITTER */ + double multiplier = rand() * MAX_CONNECTION_AGE_JITTER * 2.0 / RAND_MAX + + 1.0 - MAX_CONNECTION_AGE_JITTER; + double result = multiplier * value; + /* INT_MAX - 0.5 converts the value to float, so that result will not be + cast to int implicitly before the comparison. */ + return result > (static_cast(GRPC_MILLIS_INF_FUTURE)) - 0.5 + ? GRPC_MILLIS_INF_FUTURE + : static_cast(result); +} + +/* Constructor for call_data. */ +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + increase_call_count(chand); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data. */ +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + channel_data* chand = static_cast(elem->channel_data); + decrease_call_count(chand); +} + +/* Constructor for channel_data. */ +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + gpr_mu_init(&chand->max_age_timer_mu); + chand->max_age_timer_pending = false; + chand->max_age_grace_timer_pending = false; + chand->channel_stack = args->channel_stack; + chand->max_connection_age = + add_random_max_connection_age_jitter_and_convert_to_grpc_millis( + DEFAULT_MAX_CONNECTION_AGE_MS); + chand->max_connection_age_grace = + DEFAULT_MAX_CONNECTION_AGE_GRACE_MS == INT_MAX + ? GRPC_MILLIS_INF_FUTURE + : DEFAULT_MAX_CONNECTION_AGE_GRACE_MS; + chand->max_connection_idle = DEFAULT_MAX_CONNECTION_IDLE_MS == INT_MAX + ? GRPC_MILLIS_INF_FUTURE + : DEFAULT_MAX_CONNECTION_IDLE_MS; + chand->idle_state = MAX_IDLE_STATE_INIT; + gpr_atm_no_barrier_store(&chand->last_enter_idle_time_millis, GPR_ATM_MIN); + for (size_t i = 0; i < args->channel_args->num_args; ++i) { + if (0 == strcmp(args->channel_args->args[i].key, + GRPC_ARG_MAX_CONNECTION_AGE_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], MAX_CONNECTION_AGE_INTEGER_OPTIONS); + chand->max_connection_age = + add_random_max_connection_age_jitter_and_convert_to_grpc_millis( + value); + } else if (0 == strcmp(args->channel_args->args[i].key, + GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], + {DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0, INT_MAX}); + chand->max_connection_age_grace = + value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value; + } else if (0 == strcmp(args->channel_args->args[i].key, + GRPC_ARG_MAX_CONNECTION_IDLE_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], MAX_CONNECTION_IDLE_INTEGER_OPTIONS); + chand->max_connection_idle = + value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value; + } + } + GRPC_CLOSURE_INIT(&chand->max_idle_timer_cb, max_idle_timer_cb, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->close_max_age_channel, close_max_age_channel, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->force_close_max_age_channel, + force_close_max_age_channel, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->start_max_idle_timer_after_init, + start_max_idle_timer_after_init, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->start_max_age_timer_after_init, + start_max_age_timer_after_init, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->start_max_age_grace_timer_after_goaway_op, + start_max_age_grace_timer_after_goaway_op, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed, + channel_connectivity_changed, chand, + grpc_schedule_on_exec_ctx); + + if (chand->max_connection_age != GRPC_MILLIS_INF_FUTURE) { + /* When the channel reaches its max age, we send down an op with + goaway_error set. However, we can't send down any ops until after the + channel stack is fully initialized. If we start the timer here, we have + no guarantee that the timer won't pop before channel stack initialization + is finished. To avoid that problem, we create a closure to start the + timer, and we schedule that closure to be run after call stack + initialization is done. */ + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_age_timer_after_init"); + GRPC_CLOSURE_SCHED(&chand->start_max_age_timer_after_init, GRPC_ERROR_NONE); + } + + /* Initialize the number of calls as 1, so that the max_idle_timer will not + start until start_max_idle_timer_after_init is invoked. */ + gpr_atm_rel_store(&chand->call_count, 1); + if (chand->max_connection_idle != GRPC_MILLIS_INF_FUTURE) { + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_idle_timer_after_init"); + GRPC_CLOSURE_SCHED(&chand->start_max_idle_timer_after_init, + GRPC_ERROR_NONE); + } + return GRPC_ERROR_NONE; +} + +/* Destructor for channel_data. */ +static void destroy_channel_elem(grpc_channel_element* elem) {} + +const grpc_channel_filter grpc_max_age_filter = { + grpc_call_next_op, + grpc_channel_next_op, + 0, /* sizeof_call_data */ + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "max_age"}; + +static bool maybe_add_max_age_filter(grpc_channel_stack_builder* builder, + void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + bool enable = + grpc_channel_arg_get_integer( + grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_AGE_MS), + MAX_CONNECTION_AGE_INTEGER_OPTIONS) != INT_MAX || + grpc_channel_arg_get_integer( + grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_IDLE_MS), + MAX_CONNECTION_IDLE_INTEGER_OPTIONS) != INT_MAX; + if (enable) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_max_age_filter, nullptr, nullptr); + } else { + return true; + } +} + +void grpc_max_age_filter_init(void) { + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_max_age_filter, nullptr); +} + +void grpc_max_age_filter_shutdown(void) {} diff --git a/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.h b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.h new file mode 100644 index 00000000000000..989322244f78e7 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/max_age/max_age_filter.h @@ -0,0 +1,26 @@ +// +// Copyright 2017 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H +#define GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_max_age_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_MAX_AGE_MAX_AGE_FILTER_H */ diff --git a/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.cc b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.cc new file mode 100644 index 00000000000000..94d6942aa4f44b --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.cc @@ -0,0 +1,385 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/filters/message_size/message_size_filter.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/service_config.h" + +typedef struct { + int max_send_size; + int max_recv_size; +} message_size_limits; + +namespace grpc_core { +namespace { + +class MessageSizeLimits : public RefCounted { + public: + static RefCountedPtr CreateFromJson(const grpc_json* json); + + const message_size_limits& limits() const { return limits_; } + + private: + // So New() can call our private ctor. + template + friend T* grpc_core::New(Args&&... args); + + MessageSizeLimits(int max_send_size, int max_recv_size) { + limits_.max_send_size = max_send_size; + limits_.max_recv_size = max_recv_size; + } + + message_size_limits limits_; +}; + +RefCountedPtr MessageSizeLimits::CreateFromJson( + const grpc_json* json) { + int max_request_message_bytes = -1; + int max_response_message_bytes = -1; + for (grpc_json* field = json->child; field != nullptr; field = field->next) { + if (field->key == nullptr) continue; + if (strcmp(field->key, "maxRequestMessageBytes") == 0) { + if (max_request_message_bytes >= 0) return nullptr; // Duplicate. + if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) { + return nullptr; + } + max_request_message_bytes = gpr_parse_nonnegative_int(field->value); + if (max_request_message_bytes == -1) return nullptr; + } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) { + if (max_response_message_bytes >= 0) return nullptr; // Duplicate. + if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) { + return nullptr; + } + max_response_message_bytes = gpr_parse_nonnegative_int(field->value); + if (max_response_message_bytes == -1) return nullptr; + } + } + return MakeRefCounted(max_request_message_bytes, + max_response_message_bytes); +} + +} // namespace +} // namespace grpc_core + +static void recv_message_ready(void* user_data, grpc_error* error); +static void recv_trailing_metadata_ready(void* user_data, grpc_error* error); + +namespace { + +struct channel_data { + message_size_limits limits; + // Maps path names to refcounted_message_size_limits structs. + grpc_core::RefCountedPtr>> + method_limit_table; +}; + +struct call_data { + call_data(grpc_call_element* elem, const channel_data& chand, + const grpc_call_element_args& args) + : call_combiner(args.call_combiner), limits(chand.limits) { + GRPC_CLOSURE_INIT(&recv_message_ready, ::recv_message_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready, + ::recv_trailing_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + // Get max sizes from channel data, then merge in per-method config values. + // Note: Per-method config is only available on the client, so we + // apply the max request size to the send limit and the max response + // size to the receive limit. + if (chand.method_limit_table != nullptr) { + grpc_core::RefCountedPtr limits = + grpc_core::ServiceConfig::MethodConfigTableLookup( + *chand.method_limit_table, args.path); + if (limits != nullptr) { + if (limits->limits().max_send_size >= 0 && + (limits->limits().max_send_size < this->limits.max_send_size || + this->limits.max_send_size < 0)) { + this->limits.max_send_size = limits->limits().max_send_size; + } + if (limits->limits().max_recv_size >= 0 && + (limits->limits().max_recv_size < this->limits.max_recv_size || + this->limits.max_recv_size < 0)) { + this->limits.max_recv_size = limits->limits().max_recv_size; + } + } + } + } + + ~call_data() { GRPC_ERROR_UNREF(error); } + + grpc_call_combiner* call_combiner; + message_size_limits limits; + // Receive closures are chained: we inject this closure as the + // recv_message_ready up-call on transport_stream_op, and remember to + // call our next_recv_message_ready member after handling it. + grpc_closure recv_message_ready; + grpc_closure recv_trailing_metadata_ready; + // The error caused by a message that is too large, or GRPC_ERROR_NONE + grpc_error* error = GRPC_ERROR_NONE; + // Used by recv_message_ready. + grpc_core::OrphanablePtr* recv_message = nullptr; + // Original recv_message_ready callback, invoked after our own. + grpc_closure* next_recv_message_ready = nullptr; + // Original recv_trailing_metadata callback, invoked after our own. + grpc_closure* original_recv_trailing_metadata_ready; + bool seen_recv_trailing_metadata = false; + grpc_error* recv_trailing_metadata_error; +}; + +} // namespace + +// Callback invoked when we receive a message. Here we check the max +// receive message size. +static void recv_message_ready(void* user_data, grpc_error* error) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + if (*calld->recv_message != nullptr && calld->limits.max_recv_size >= 0 && + (*calld->recv_message)->length() > + static_cast(calld->limits.max_recv_size)) { + char* message_string; + gpr_asprintf(&message_string, + "Received message larger than max (%u vs. %d)", + (*calld->recv_message)->length(), calld->limits.max_recv_size); + grpc_error* new_error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED); + GRPC_ERROR_UNREF(calld->error); + if (error == GRPC_ERROR_NONE) { + error = new_error; + } else { + error = grpc_error_add_child(error, new_error); + } + calld->error = GRPC_ERROR_REF(error); + gpr_free(message_string); + } else { + GRPC_ERROR_REF(error); + } + // Invoke the next callback. + grpc_closure* closure = calld->next_recv_message_ready; + calld->next_recv_message_ready = nullptr; + if (calld->seen_recv_trailing_metadata) { + /* We might potentially see another RECV_MESSAGE op. In that case, we do not + * want to run the recv_trailing_metadata_ready closure again. The newer + * RECV_MESSAGE op cannot cause any errors since the transport has already + * invoked the recv_trailing_metadata_ready closure and all further + * RECV_MESSAGE ops will get null payloads. */ + calld->seen_recv_trailing_metadata = false; + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_RUN(closure, error); +} + +// Callback invoked on completion of recv_trailing_metadata +// Notifies the recv_trailing_metadata batch of any message size failures +static void recv_trailing_metadata_ready(void* user_data, grpc_error* error) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + if (calld->next_recv_message_ready != nullptr) { + calld->seen_recv_trailing_metadata = true; + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error); + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring recv_trailing_metadata_ready until " + "after recv_message_ready"); + return; + } + error = + grpc_error_add_child(GRPC_ERROR_REF(error), GRPC_ERROR_REF(calld->error)); + // Invoke the next callback. + GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error); +} + +// Start transport stream op. +static void start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + call_data* calld = static_cast(elem->call_data); + // Check max send message size. + if (op->send_message && calld->limits.max_send_size >= 0 && + op->payload->send_message.send_message->length() > + static_cast(calld->limits.max_send_size)) { + char* message_string; + gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)", + op->payload->send_message.send_message->length(), + calld->limits.max_send_size); + grpc_transport_stream_op_batch_finish_with_failure( + op, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_RESOURCE_EXHAUSTED), + calld->call_combiner); + gpr_free(message_string); + return; + } + // Inject callback for receiving a message. + if (op->recv_message) { + calld->next_recv_message_ready = + op->payload->recv_message.recv_message_ready; + calld->recv_message = op->payload->recv_message.recv_message; + op->payload->recv_message.recv_message_ready = &calld->recv_message_ready; + } + // Inject callback for receiving trailing metadata. + if (op->recv_trailing_metadata) { + calld->original_recv_trailing_metadata_ready = + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &calld->recv_trailing_metadata_ready; + } + // Chain to the next filter. + grpc_call_next_op(elem, op); +} + +// Constructor for call_data. +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + new (elem->call_data) call_data(elem, *chand, *args); + return GRPC_ERROR_NONE; +} + +// Destructor for call_data. +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = (call_data*)elem->call_data; + calld->~call_data(); +} + +static int default_size(const grpc_channel_args* args, + int without_minimal_stack) { + if (grpc_channel_args_want_minimal_stack(args)) { + return -1; + } + return without_minimal_stack; +} + +message_size_limits get_message_size_limits( + const grpc_channel_args* channel_args) { + message_size_limits lim; + lim.max_send_size = + default_size(channel_args, GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH); + lim.max_recv_size = + default_size(channel_args, GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH); + for (size_t i = 0; i < channel_args->num_args; ++i) { + if (strcmp(channel_args->args[i].key, GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) == + 0) { + const grpc_integer_options options = {lim.max_send_size, -1, INT_MAX}; + lim.max_send_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } + if (strcmp(channel_args->args[i].key, + GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) { + const grpc_integer_options options = {lim.max_recv_size, -1, INT_MAX}; + lim.max_recv_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } + } + return lim; +} + +// Constructor for channel_data. +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + GPR_ASSERT(!args->is_last); + channel_data* chand = static_cast(elem->channel_data); + chand->limits = get_message_size_limits(args->channel_args); + // Get method config table from channel args. + const grpc_arg* channel_arg = + grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG); + const char* service_config_str = grpc_channel_arg_get_string(channel_arg); + if (service_config_str != nullptr) { + grpc_core::UniquePtr service_config = + grpc_core::ServiceConfig::Create(service_config_str); + if (service_config != nullptr) { + chand->method_limit_table = service_config->CreateMethodConfigTable( + grpc_core::MessageSizeLimits::CreateFromJson); + } + } + return GRPC_ERROR_NONE; +} + +// Destructor for channel_data. +static void destroy_channel_elem(grpc_channel_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + chand->method_limit_table.reset(); +} + +const grpc_channel_filter grpc_message_size_filter = { + start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "message_size"}; + +static bool maybe_add_message_size_filter(grpc_channel_stack_builder* builder, + void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + bool enable = false; + message_size_limits lim = get_message_size_limits(channel_args); + if (lim.max_send_size != -1 || lim.max_recv_size != -1) { + enable = true; + } + const grpc_arg* a = + grpc_channel_args_find(channel_args, GRPC_ARG_SERVICE_CONFIG); + if (a != nullptr) { + enable = true; + } + if (enable) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_message_size_filter, nullptr, nullptr); + } else { + return true; + } +} + +void grpc_message_size_filter_init(void) { + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_message_size_filter, nullptr); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_message_size_filter, nullptr); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_message_size_filter, nullptr); +} + +void grpc_message_size_filter_shutdown(void) {} diff --git a/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.h b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.h new file mode 100644 index 00000000000000..f66636e5832fea --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/message_size/message_size_filter.h @@ -0,0 +1,26 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H +#define GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_message_size_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_MESSAGE_SIZE_MESSAGE_SIZE_FILTER_H */ diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc new file mode 100644 index 00000000000000..c7070d4d9ba041 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc @@ -0,0 +1,208 @@ +// +// Copyright 2017 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" + +#include + +#include + +#include "src/core/ext/filters/workarounds/workaround_utils.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/metadata.h" + +namespace { +struct call_data { + // Receive closures are chained: we inject this closure as the + // recv_initial_metadata_ready up-call on transport_stream_op, and remember to + // call our next_recv_initial_metadata_ready member after handling it. + grpc_closure recv_initial_metadata_ready; + // Used by recv_initial_metadata_ready. + grpc_metadata_batch* recv_initial_metadata; + // Original recv_initial_metadata_ready callback, invoked after our own. + grpc_closure* next_recv_initial_metadata_ready; + + // Marks whether the workaround is active + bool workaround_active; +}; +} // namespace + +// Find the user agent metadata element in the batch +static bool get_user_agent_mdelem(const grpc_metadata_batch* batch, + grpc_mdelem* md) { + if (batch->idx.named.user_agent != nullptr) { + *md = batch->idx.named.user_agent->md; + return true; + } + return false; +} + +// Callback invoked when we receive an initial metadata. +static void recv_initial_metadata_ready(void* user_data, grpc_error* error) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + + if (GRPC_ERROR_NONE == error) { + grpc_mdelem md; + if (get_user_agent_mdelem(calld->recv_initial_metadata, &md)) { + grpc_workaround_user_agent_md* user_agent_md = grpc_parse_user_agent(md); + if (user_agent_md + ->workaround_active[GRPC_WORKAROUND_ID_CRONET_COMPRESSION]) { + calld->workaround_active = true; + } + } + } + + // Invoke the next callback. + GRPC_CLOSURE_RUN(calld->next_recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); +} + +// Start transport stream op. +static void start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + call_data* calld = static_cast(elem->call_data); + + // Inject callback for receiving initial metadata + if (op->recv_initial_metadata) { + calld->next_recv_initial_metadata_ready = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + } + + if (op->send_message) { + /* Send message happens after client's user-agent (initial metadata) is + * received, so workaround_active must be set already */ + if (calld->workaround_active) { + op->payload->send_message.send_message->set_flags( + op->payload->send_message.send_message->flags() | + GRPC_WRITE_NO_COMPRESS); + } + } + + // Chain to the next filter. + grpc_call_next_op(elem, op); +} + +// Constructor for call_data. +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + call_data* calld = static_cast(elem->call_data); + calld->next_recv_initial_metadata_ready = nullptr; + calld->workaround_active = false; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +// Destructor for call_data. +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) {} + +// Constructor for channel_data. +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + return GRPC_ERROR_NONE; +} + +// Destructor for channel_data. +static void destroy_channel_elem(grpc_channel_element* elem) {} + +// Parse the user agent +static bool parse_user_agent(grpc_mdelem md) { + const char grpc_objc_specifier[] = "grpc-objc/"; + const size_t grpc_objc_specifier_len = sizeof(grpc_objc_specifier) - 1; + const char cronet_specifier[] = "cronet_http"; + const size_t cronet_specifier_len = sizeof(cronet_specifier) - 1; + + char* user_agent_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + bool grpc_objc_specifier_seen = false; + bool cronet_specifier_seen = false; + char *major_version_str = user_agent_str, *minor_version_str; + long major_version = 0, minor_version = 0; + + char* head = strtok(user_agent_str, " "); + while (head != nullptr) { + if (!grpc_objc_specifier_seen && + 0 == strncmp(head, grpc_objc_specifier, grpc_objc_specifier_len)) { + major_version_str = head + grpc_objc_specifier_len; + grpc_objc_specifier_seen = true; + } else if (grpc_objc_specifier_seen && + 0 == strncmp(head, cronet_specifier, cronet_specifier_len)) { + cronet_specifier_seen = true; + break; + } + + head = strtok(nullptr, " "); + } + if (grpc_objc_specifier_seen) { + major_version_str = strtok(major_version_str, "."); + minor_version_str = strtok(nullptr, "."); + major_version = atol(major_version_str); + minor_version = atol(minor_version_str); + } + + gpr_free(user_agent_str); + return (grpc_objc_specifier_seen && cronet_specifier_seen && + (major_version < 1 || (major_version == 1 && minor_version <= 3))); +} + +const grpc_channel_filter grpc_workaround_cronet_compression_filter = { + start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "workaround_cronet_compression"}; + +static bool register_workaround_cronet_compression( + grpc_channel_stack_builder* builder, void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + const grpc_arg* a = grpc_channel_args_find( + channel_args, GRPC_ARG_WORKAROUND_CRONET_COMPRESSION); + if (a == nullptr) { + return true; + } + if (grpc_channel_arg_get_bool(a, false) == false) { + return true; + } + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_workaround_cronet_compression_filter, nullptr, nullptr); +} + +void grpc_workaround_cronet_compression_filter_init(void) { + grpc_channel_init_register_stage( + GRPC_SERVER_CHANNEL, GRPC_WORKAROUND_PRIORITY_HIGH, + register_workaround_cronet_compression, nullptr); + grpc_register_workaround(GRPC_WORKAROUND_ID_CRONET_COMPRESSION, + parse_user_agent); +} + +void grpc_workaround_cronet_compression_filter_shutdown(void) {} diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h new file mode 100644 index 00000000000000..94d20f0c4ac643 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h @@ -0,0 +1,27 @@ +// +// Copyright 2017 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H +#define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H + +#include + +#include "src/core/lib/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_workaround_cronet_compression_filter; + +#endif /* GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_CRONET_COMPRESSION_FILTER_H \ + */ diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.cc b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.cc new file mode 100644 index 00000000000000..4dabe896d3b8b2 --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.cc @@ -0,0 +1,53 @@ +// +// Copyright 2017 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/filters/workarounds/workaround_utils.h" + +#include +#include + +user_agent_parser ua_parser[GRPC_MAX_WORKAROUND_ID]; + +static void destroy_user_agent_md(void* user_agent_md) { + gpr_free(user_agent_md); +} + +grpc_workaround_user_agent_md* grpc_parse_user_agent(grpc_mdelem md) { + grpc_workaround_user_agent_md* user_agent_md = + static_cast( + grpc_mdelem_get_user_data(md, destroy_user_agent_md)); + + if (nullptr != user_agent_md) { + return user_agent_md; + } + user_agent_md = static_cast( + gpr_malloc(sizeof(grpc_workaround_user_agent_md))); + for (int i = 0; i < GRPC_MAX_WORKAROUND_ID; i++) { + if (ua_parser[i]) { + user_agent_md->workaround_active[i] = ua_parser[i](md); + } + } + grpc_mdelem_set_user_data(md, destroy_user_agent_md, (void*)user_agent_md); + + return user_agent_md; +} + +void grpc_register_workaround(uint32_t id, user_agent_parser parser) { + GPR_ASSERT(id < GRPC_MAX_WORKAROUND_ID); + ua_parser[id] = parser; +} diff --git a/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.h b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.h new file mode 100644 index 00000000000000..f172ccc078836f --- /dev/null +++ b/third_party/grpc/src/core/ext/filters/workarounds/workaround_utils.h @@ -0,0 +1,39 @@ +// +// Copyright 2017 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H +#define GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H + +#include + +#include + +#include "src/core/lib/transport/metadata.h" + +#define GRPC_WORKAROUND_PRIORITY_HIGH 10001 +#define GRPC_WORKAROUND_PROIRITY_LOW 9999 + +typedef struct grpc_workaround_user_agent_md { + bool workaround_active[GRPC_MAX_WORKAROUND_ID]; +} grpc_workaround_user_agent_md; + +grpc_workaround_user_agent_md* grpc_parse_user_agent(grpc_mdelem md); + +typedef bool (*user_agent_parser)(grpc_mdelem); + +void grpc_register_workaround(uint32_t id, user_agent_parser parser); + +#endif /* GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H */ diff --git a/third_party/grpc/src/core/ext/transport/README.md b/third_party/grpc/src/core/ext/transport/README.md new file mode 100644 index 00000000000000..2290568784d5e4 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/README.md @@ -0,0 +1 @@ +Transports for gRPC diff --git a/third_party/grpc/src/core/ext/transport/chttp2/README.md b/third_party/grpc/src/core/ext/transport/chttp2/README.md new file mode 100644 index 00000000000000..8880a47460a13a --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/README.md @@ -0,0 +1 @@ +CHTTP2 - gRPC's implementation of a HTTP2 based transport diff --git a/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.cc b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.cc new file mode 100644 index 00000000000000..1fdab76dbf035f --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.cc @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include "src/core/ext/transport/chttp2/alpn/alpn.h" + +#include "src/core/lib/gpr/useful.h" + +/* in order of preference */ +static const char* const supported_versions[] = {"grpc-exp", "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/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.h b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.h new file mode 100644 index 00000000000000..0042eafd95a750 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/alpn/alpn.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H + +#include + +#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_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/authority.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.cc new file mode 100644 index 00000000000000..bad3153b013c94 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/client/authority.h" + +grpc_channel_args* grpc_default_authority_add_if_not_present( + const grpc_channel_args* args) { + const bool has_default_authority = + grpc_channel_args_find(args, GRPC_ARG_DEFAULT_AUTHORITY) != nullptr; + grpc_arg new_args[1]; + size_t num_new_args = 0; + grpc_core::UniquePtr default_authority; + if (!has_default_authority) { + const grpc_arg* server_uri_arg = + grpc_channel_args_find(args, GRPC_ARG_SERVER_URI); + const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg); + GPR_ASSERT(server_uri_str != nullptr); + default_authority = + grpc_core::ResolverRegistry::GetDefaultAuthority(server_uri_str); + GPR_ASSERT(default_authority != nullptr); + new_args[num_new_args++] = grpc_channel_arg_string_create( + const_cast(GRPC_ARG_DEFAULT_AUTHORITY), default_authority.get()); + } + return grpc_channel_args_copy_and_add(args, new_args, num_new_args); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/authority.h b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.h new file mode 100644 index 00000000000000..642584ef5660b9 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/authority.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_AUTHORITY_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_AUTHORITY_H + +#include + +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/memory.h" + +/// Returns a copy of \a args with the default authority channel arg set if it +/// wasn't already present. +grpc_channel_args* grpc_default_authority_add_if_not_present( + const grpc_channel_args* args); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_AUTHORITY_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.cc new file mode 100644 index 00000000000000..42a2e2e896c08c --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.cc @@ -0,0 +1,239 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" + +#include + +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/connector.h" +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/slice/slice_internal.h" + +typedef struct { + grpc_connector base; + + gpr_mu mu; + gpr_refcount refs; + + bool shutdown; + bool connecting; + + grpc_closure* notify; + grpc_connect_in_args args; + grpc_connect_out_args* result; + + grpc_endpoint* endpoint; // Non-NULL until handshaking starts. + + grpc_closure connected; + + grpc_handshake_manager* handshake_mgr; +} chttp2_connector; + +static void chttp2_connector_ref(grpc_connector* con) { + chttp2_connector* c = reinterpret_cast(con); + gpr_ref(&c->refs); +} + +static void chttp2_connector_unref(grpc_connector* con) { + chttp2_connector* c = reinterpret_cast(con); + if (gpr_unref(&c->refs)) { + gpr_mu_destroy(&c->mu); + // If handshaking is not yet in progress, destroy the endpoint. + // Otherwise, the handshaker will do this for us. + if (c->endpoint != nullptr) grpc_endpoint_destroy(c->endpoint); + gpr_free(c); + } +} + +static void chttp2_connector_shutdown(grpc_connector* con, grpc_error* why) { + chttp2_connector* c = reinterpret_cast(con); + gpr_mu_lock(&c->mu); + c->shutdown = true; + if (c->handshake_mgr != nullptr) { + grpc_handshake_manager_shutdown(c->handshake_mgr, GRPC_ERROR_REF(why)); + } + // If handshaking is not yet in progress, shutdown the endpoint. + // Otherwise, the handshaker will do this for us. + if (!c->connecting && c->endpoint != nullptr) { + grpc_endpoint_shutdown(c->endpoint, GRPC_ERROR_REF(why)); + } + gpr_mu_unlock(&c->mu); + GRPC_ERROR_UNREF(why); +} + +static void on_handshake_done(void* arg, grpc_error* error) { + grpc_handshaker_args* args = static_cast(arg); + chttp2_connector* c = static_cast(args->user_data); + gpr_mu_lock(&c->mu); + if (error != GRPC_ERROR_NONE || c->shutdown) { + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown"); + // We were shut down after handshaking completed successfully, so + // destroy the endpoint here. + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(args->endpoint, GRPC_ERROR_REF(error)); + grpc_endpoint_destroy(args->endpoint); + grpc_channel_args_destroy(args->args); + grpc_slice_buffer_destroy_internal(args->read_buffer); + gpr_free(args->read_buffer); + } else { + error = GRPC_ERROR_REF(error); + } + memset(c->result, 0, sizeof(*c->result)); + } else { + grpc_endpoint_delete_from_pollset_set(args->endpoint, + c->args.interested_parties); + c->result->transport = + grpc_create_chttp2_transport(args->args, args->endpoint, true); + grpc_core::RefCountedPtr socket_node = + grpc_chttp2_transport_get_socket_node(c->result->transport); + c->result->socket_uuid = socket_node == nullptr ? 0 : socket_node->uuid(); + GPR_ASSERT(c->result->transport); + // TODO(roth): We ideally want to wait until we receive HTTP/2 + // settings from the server before we consider the connection + // established. If that doesn't happen before the connection + // timeout expires, then we should consider the connection attempt a + // failure and feed that information back into the backoff code. + // We could pass a notify_on_receive_settings callback to + // grpc_chttp2_transport_start_reading() to let us know when + // settings are received, but we would need to figure out how to use + // that information here. + // + // Unfortunately, we don't currently have a way to split apart the two + // effects of scheduling c->notify: we start sending RPCs immediately + // (which we want to do) and we consider the connection attempt successful + // (which we don't want to do until we get the notify_on_receive_settings + // callback from the transport). If we could split those things + // apart, then we could start sending RPCs but then wait for our + // timeout before deciding if the connection attempt is successful. + // If the attempt is not successful, then we would tear down the + // transport and feed the failure back into the backoff code. + // + // In addition, even if we did that, we would probably not want to do + // so until after transparent retries is implemented. Otherwise, any + // RPC that we attempt to send on the connection before the timeout + // would fail instead of being retried on a subsequent attempt. + grpc_chttp2_transport_start_reading(c->result->transport, args->read_buffer, + nullptr); + c->result->channel_args = args->args; + } + grpc_closure* notify = c->notify; + c->notify = nullptr; + GRPC_CLOSURE_SCHED(notify, error); + grpc_handshake_manager_destroy(c->handshake_mgr); + c->handshake_mgr = nullptr; + gpr_mu_unlock(&c->mu); + chttp2_connector_unref(reinterpret_cast(c)); +} + +static void start_handshake_locked(chttp2_connector* c) { + c->handshake_mgr = grpc_handshake_manager_create(); + grpc_handshakers_add(HANDSHAKER_CLIENT, c->args.channel_args, + c->args.interested_parties, c->handshake_mgr); + grpc_endpoint_add_to_pollset_set(c->endpoint, c->args.interested_parties); + grpc_handshake_manager_do_handshake( + c->handshake_mgr, c->endpoint, c->args.channel_args, c->args.deadline, + nullptr /* acceptor */, on_handshake_done, c); + c->endpoint = nullptr; // Endpoint handed off to handshake manager. +} + +static void connected(void* arg, grpc_error* error) { + chttp2_connector* c = static_cast(arg); + gpr_mu_lock(&c->mu); + GPR_ASSERT(c->connecting); + c->connecting = false; + if (error != GRPC_ERROR_NONE || c->shutdown) { + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown"); + } else { + error = GRPC_ERROR_REF(error); + } + memset(c->result, 0, sizeof(*c->result)); + grpc_closure* notify = c->notify; + c->notify = nullptr; + GRPC_CLOSURE_SCHED(notify, error); + if (c->endpoint != nullptr) { + grpc_endpoint_shutdown(c->endpoint, GRPC_ERROR_REF(error)); + } + gpr_mu_unlock(&c->mu); + chttp2_connector_unref(static_cast(arg)); + } else { + GPR_ASSERT(c->endpoint != nullptr); + start_handshake_locked(c); + gpr_mu_unlock(&c->mu); + } +} + +static void chttp2_connector_connect(grpc_connector* con, + const grpc_connect_in_args* args, + grpc_connect_out_args* result, + grpc_closure* notify) { + chttp2_connector* c = reinterpret_cast(con); + grpc_resolved_address addr; + grpc_get_subchannel_address_arg(args->channel_args, &addr); + gpr_mu_lock(&c->mu); + GPR_ASSERT(c->notify == nullptr); + c->notify = notify; + c->args = *args; + c->result = result; + GPR_ASSERT(c->endpoint == nullptr); + chttp2_connector_ref(con); // Ref taken for callback. + GRPC_CLOSURE_INIT(&c->connected, connected, c, grpc_schedule_on_exec_ctx); + GPR_ASSERT(!c->connecting); + c->connecting = true; + grpc_closure* closure = &c->connected; + grpc_endpoint** ep = &c->endpoint; + gpr_mu_unlock(&c->mu); + // In some implementations, the closure can be flushed before + // grpc_tcp_client_connect and since the closure requires access to c->mu, + // this can result in a deadlock. Refer + // https://github.com/grpc/grpc/issues/16427 + // grpc_tcp_client_connect would fill c->endpoint with proper contents and we + // make sure that we would still exist at that point by taking a ref. + grpc_tcp_client_connect(closure, ep, args->interested_parties, + args->channel_args, &addr, args->deadline); +} + +static const grpc_connector_vtable chttp2_connector_vtable = { + chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown, + chttp2_connector_connect}; + +grpc_connector* grpc_chttp2_connector_create() { + chttp2_connector* c = static_cast(gpr_zalloc(sizeof(*c))); + c->base.vtable = &chttp2_connector_vtable; + gpr_mu_init(&c->mu); + gpr_ref_init(&c->refs, 1); + return &c->base; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.h b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.h new file mode 100644 index 00000000000000..04da4413095bc1 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/chttp2_connector.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H + +#include + +#include "src/core/ext/filters/client_channel/connector.h" + +grpc_connector* grpc_chttp2_connector_create(); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_CLIENT_CHTTP2_CONNECTOR_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/README.md new file mode 100644 index 00000000000000..fa114633889c8d --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/README.md @@ -0,0 +1 @@ +Plugin for creating insecure channels using chttp2 diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create.cc new file mode 100644 index 00000000000000..e6c8c3826077c0 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create.cc @@ -0,0 +1,110 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/transport/chttp2/client/authority.h" +#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" + +static void client_channel_factory_ref( + grpc_client_channel_factory* cc_factory) {} + +static void client_channel_factory_unref( + grpc_client_channel_factory* cc_factory) {} + +static grpc_subchannel* client_channel_factory_create_subchannel( + grpc_client_channel_factory* cc_factory, const grpc_subchannel_args* args) { + grpc_subchannel_args final_sc_args; + memcpy(&final_sc_args, args, sizeof(*args)); + final_sc_args.args = grpc_default_authority_add_if_not_present(args->args); + grpc_connector* connector = grpc_chttp2_connector_create(); + grpc_subchannel* s = grpc_subchannel_create(connector, &final_sc_args); + grpc_connector_unref(connector); + grpc_channel_args_destroy(const_cast(final_sc_args.args)); + return s; +} + +static grpc_channel* client_channel_factory_create_channel( + grpc_client_channel_factory* cc_factory, const char* target, + grpc_client_channel_type type, const grpc_channel_args* args) { + if (target == nullptr) { + gpr_log(GPR_ERROR, "cannot create channel with NULL target name"); + return nullptr; + } + // Add channel arg containing the server URI. + grpc_core::UniquePtr canonical_target = + grpc_core::ResolverRegistry::AddDefaultPrefixIfNeeded(target); + grpc_arg arg = grpc_channel_arg_string_create( + const_cast(GRPC_ARG_SERVER_URI), canonical_target.get()); + const char* to_remove[] = {GRPC_ARG_SERVER_URI}; + grpc_channel_args* new_args = + grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1); + grpc_channel* channel = + grpc_channel_create(target, new_args, GRPC_CLIENT_CHANNEL, nullptr); + grpc_channel_args_destroy(new_args); + return channel; +} + +static const grpc_client_channel_factory_vtable client_channel_factory_vtable = + {client_channel_factory_ref, client_channel_factory_unref, + client_channel_factory_create_subchannel, + client_channel_factory_create_channel}; + +static grpc_client_channel_factory client_channel_factory = { + &client_channel_factory_vtable}; + +/* 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_core::ExecCtx exec_ctx; + GRPC_API_TRACE( + "grpc_insecure_channel_create(target=%s, args=%p, reserved=%p)", 3, + (target, args, reserved)); + GPR_ASSERT(reserved == nullptr); + // Add channel arg containing the client channel factory. + grpc_arg arg = + grpc_client_channel_factory_create_channel_arg(&client_channel_factory); + grpc_channel_args* new_args = grpc_channel_args_copy_and_add(args, &arg, 1); + // Create channel. + grpc_channel* channel = client_channel_factory_create_channel( + &client_channel_factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, + new_args); + // Clean up. + grpc_channel_args_destroy(new_args); + + return channel != nullptr ? channel + : grpc_lame_client_channel_create( + target, GRPC_STATUS_INTERNAL, + "Failed to create client channel"); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc new file mode 100644 index 00000000000000..5bdcb387c9b5ad --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc @@ -0,0 +1,79 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/tcp_client_posix.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/transport.h" + +grpc_channel* grpc_insecure_channel_create_from_fd( + const char* target, int fd, const grpc_channel_args* args) { + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE("grpc_insecure_channel_create(target=%p, fd=%d, args=%p)", 3, + (target, fd, args)); + + grpc_arg default_authority_arg = grpc_channel_arg_string_create( + (char*)GRPC_ARG_DEFAULT_AUTHORITY, (char*)"test.authority"); + grpc_channel_args* final_args = + grpc_channel_args_copy_and_add(args, &default_authority_arg, 1); + + int flags = fcntl(fd, F_GETFL, 0); + GPR_ASSERT(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0); + + grpc_endpoint* client = grpc_tcp_client_create_from_fd( + grpc_fd_create(fd, "client", true), args, "fd-client"); + + grpc_transport* transport = + grpc_create_chttp2_transport(final_args, client, true); + GPR_ASSERT(transport); + grpc_channel* channel = grpc_channel_create( + target, final_args, GRPC_CLIENT_DIRECT_CHANNEL, transport); + grpc_channel_args_destroy(final_args); + grpc_chttp2_transport_start_reading(transport, nullptr, nullptr); + + grpc_core::ExecCtx::Get()->Flush(); + + return channel != nullptr ? channel + : grpc_lame_client_channel_create( + target, GRPC_STATUS_INTERNAL, + "Failed to create client channel"); +} + +#else // !GPR_SUPPORT_CHANNELS_FROM_FD + +grpc_channel* grpc_insecure_channel_create_from_fd( + const char* target, int fd, const grpc_channel_args* args) { + GPR_ASSERT(0); + return nullptr; +} + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/secure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/README.md new file mode 100644 index 00000000000000..405a86e5db1ed5 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/README.md @@ -0,0 +1 @@ +Plugin for creating secure channels using chttp2 diff --git a/third_party/grpc/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc new file mode 100644 index 00000000000000..9612698e967523 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc @@ -0,0 +1,229 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/security_connector/security_connector.h" +#include "src/core/lib/security/transport/target_authority_table.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/uri/uri_parser.h" + +static void client_channel_factory_ref( + grpc_client_channel_factory* cc_factory) {} + +static void client_channel_factory_unref( + grpc_client_channel_factory* cc_factory) {} + +static grpc_subchannel_args* get_secure_naming_subchannel_args( + const grpc_subchannel_args* args) { + grpc_channel_credentials* channel_credentials = + grpc_channel_credentials_find_in_args(args->args); + if (channel_credentials == nullptr) { + gpr_log(GPR_ERROR, + "Can't create subchannel: channel credentials missing for secure " + "channel."); + return nullptr; + } + // Make sure security connector does not already exist in args. + if (grpc_security_connector_find_in_args(args->args) != nullptr) { + gpr_log(GPR_ERROR, + "Can't create subchannel: security connector already present in " + "channel args."); + return nullptr; + } + // To which address are we connecting? By default, use the server URI. + const grpc_arg* server_uri_arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI); + const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg); + GPR_ASSERT(server_uri_str != nullptr); + grpc_uri* server_uri = + grpc_uri_parse(server_uri_str, true /* supress errors */); + GPR_ASSERT(server_uri != nullptr); + const grpc_core::TargetAuthorityTable* target_authority_table = + grpc_core::FindTargetAuthorityTableInArgs(args->args); + grpc_core::UniquePtr authority; + if (target_authority_table != nullptr) { + // Find the authority for the target. + const char* target_uri_str = + grpc_get_subchannel_address_uri_arg(args->args); + grpc_uri* target_uri = + grpc_uri_parse(target_uri_str, false /* suppress errors */); + GPR_ASSERT(target_uri != nullptr); + if (target_uri->path[0] != '\0') { // "path" may be empty + const grpc_slice key = grpc_slice_from_static_string( + target_uri->path[0] == '/' ? target_uri->path + 1 : target_uri->path); + const grpc_core::UniquePtr* value = + target_authority_table->Get(key); + if (value != nullptr) authority.reset(gpr_strdup(value->get())); + grpc_slice_unref_internal(key); + } + grpc_uri_destroy(target_uri); + } + // If the authority hasn't already been set (either because no target + // authority table was present or because the target was not present + // in the table), fall back to using the original server URI. + if (authority == nullptr) { + authority = + grpc_core::ResolverRegistry::GetDefaultAuthority(server_uri_str); + } + grpc_arg args_to_add[2]; + size_t num_args_to_add = 0; + if (grpc_channel_args_find(args->args, GRPC_ARG_DEFAULT_AUTHORITY) == + nullptr) { + // If the channel args don't already contain GRPC_ARG_DEFAULT_AUTHORITY, add + // the arg, setting it to the value just obtained. + args_to_add[num_args_to_add++] = grpc_channel_arg_string_create( + const_cast(GRPC_ARG_DEFAULT_AUTHORITY), authority.get()); + } + grpc_channel_args* args_with_authority = + grpc_channel_args_copy_and_add(args->args, args_to_add, num_args_to_add); + grpc_uri_destroy(server_uri); + // Create the security connector using the credentials and target name. + grpc_channel_args* new_args_from_connector = nullptr; + grpc_core::RefCountedPtr + subchannel_security_connector = + channel_credentials->create_security_connector( + /*call_creds=*/nullptr, authority.get(), args_with_authority, + &new_args_from_connector); + if (subchannel_security_connector == nullptr) { + gpr_log(GPR_ERROR, + "Failed to create secure subchannel for secure name '%s'", + authority.get()); + grpc_channel_args_destroy(args_with_authority); + return nullptr; + } + grpc_arg new_security_connector_arg = + grpc_security_connector_to_arg(subchannel_security_connector.get()); + + grpc_channel_args* new_args = grpc_channel_args_copy_and_add( + new_args_from_connector != nullptr ? new_args_from_connector + : args_with_authority, + &new_security_connector_arg, 1); + + subchannel_security_connector.reset(DEBUG_LOCATION, "lb_channel_create"); + if (new_args_from_connector != nullptr) { + grpc_channel_args_destroy(new_args_from_connector); + } + grpc_channel_args_destroy(args_with_authority); + grpc_subchannel_args* final_sc_args = + static_cast(gpr_malloc(sizeof(*final_sc_args))); + memcpy(final_sc_args, args, sizeof(*args)); + final_sc_args->args = new_args; + return final_sc_args; +} + +static grpc_subchannel* client_channel_factory_create_subchannel( + grpc_client_channel_factory* cc_factory, const grpc_subchannel_args* args) { + grpc_subchannel_args* subchannel_args = + get_secure_naming_subchannel_args(args); + if (subchannel_args == nullptr) { + gpr_log( + GPR_ERROR, + "Failed to create subchannel arguments during subchannel creation."); + return nullptr; + } + grpc_connector* connector = grpc_chttp2_connector_create(); + grpc_subchannel* s = grpc_subchannel_create(connector, subchannel_args); + grpc_connector_unref(connector); + grpc_channel_args_destroy( + const_cast(subchannel_args->args)); + gpr_free(subchannel_args); + return s; +} + +static grpc_channel* client_channel_factory_create_channel( + grpc_client_channel_factory* cc_factory, const char* target, + grpc_client_channel_type type, const grpc_channel_args* args) { + if (target == nullptr) { + gpr_log(GPR_ERROR, "cannot create channel with NULL target name"); + return nullptr; + } + // Add channel arg containing the server URI. + grpc_core::UniquePtr canonical_target = + grpc_core::ResolverRegistry::AddDefaultPrefixIfNeeded(target); + grpc_arg arg = grpc_channel_arg_string_create((char*)GRPC_ARG_SERVER_URI, + canonical_target.get()); + const char* to_remove[] = {GRPC_ARG_SERVER_URI}; + grpc_channel_args* new_args = + grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1); + grpc_channel* channel = + grpc_channel_create(target, new_args, GRPC_CLIENT_CHANNEL, nullptr); + grpc_channel_args_destroy(new_args); + return channel; +} + +static const grpc_client_channel_factory_vtable client_channel_factory_vtable = + {client_channel_factory_ref, client_channel_factory_unref, + client_channel_factory_create_subchannel, + client_channel_factory_create_channel}; + +static grpc_client_channel_factory client_channel_factory = { + &client_channel_factory_vtable}; + +// Create a secure client channel: +// Asynchronously: - resolve target +// - connect to it (trying alternatives as presented) +// - perform handshakes +grpc_channel* grpc_secure_channel_create(grpc_channel_credentials* creds, + const char* target, + const grpc_channel_args* args, + void* reserved) { + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE( + "grpc_secure_channel_create(creds=%p, target=%s, args=%p, " + "reserved=%p)", + 4, ((void*)creds, target, (void*)args, (void*)reserved)); + GPR_ASSERT(reserved == nullptr); + grpc_channel* channel = nullptr; + if (creds != nullptr) { + // Add channel args containing the client channel factory and channel + // credentials. + grpc_arg args_to_add[] = { + grpc_client_channel_factory_create_channel_arg(&client_channel_factory), + grpc_channel_credentials_to_arg(creds)}; + grpc_channel_args* new_args = grpc_channel_args_copy_and_add( + args, args_to_add, GPR_ARRAY_SIZE(args_to_add)); + // Create channel. + channel = client_channel_factory_create_channel( + &client_channel_factory, target, GRPC_CLIENT_CHANNEL_TYPE_REGULAR, + new_args); + // Clean up. + grpc_channel_args_destroy(new_args); + } + return channel != nullptr ? channel + : grpc_lame_client_channel_create( + target, GRPC_STATUS_INTERNAL, + "Failed to create secure client channel"); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.cc new file mode 100644 index 00000000000000..3d09187b9ba022 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.cc @@ -0,0 +1,403 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/server/chttp2_server.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/http/server/http_server_filter.h" +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/server.h" + +typedef struct { + grpc_server* server; + grpc_tcp_server* tcp_server; + grpc_channel_args* args; + gpr_mu mu; + bool shutdown; + grpc_closure tcp_server_shutdown_complete; + grpc_closure* server_destroy_listener_done; + grpc_handshake_manager* pending_handshake_mgrs; + grpc_core::RefCountedPtr + channelz_listen_socket; +} server_state; + +typedef struct { + gpr_refcount refs; + server_state* svr_state; + grpc_pollset* accepting_pollset; + grpc_tcp_server_acceptor* acceptor; + grpc_handshake_manager* handshake_mgr; + // State for enforcing handshake timeout on receiving HTTP/2 settings. + grpc_chttp2_transport* transport; + grpc_millis deadline; + grpc_timer timer; + grpc_closure on_timeout; + grpc_closure on_receive_settings; + grpc_pollset_set* interested_parties; +} server_connection_state; + +static void server_connection_state_unref( + server_connection_state* connection_state) { + if (gpr_unref(&connection_state->refs)) { + if (connection_state->transport != nullptr) { + GRPC_CHTTP2_UNREF_TRANSPORT(connection_state->transport, + "receive settings timeout"); + } + grpc_pollset_set_del_pollset(connection_state->interested_parties, + connection_state->accepting_pollset); + grpc_pollset_set_destroy(connection_state->interested_parties); + gpr_free(connection_state); + } +} + +static void on_timeout(void* arg, grpc_error* error) { + server_connection_state* connection_state = + static_cast(arg); + // Note that we may be called with GRPC_ERROR_NONE when the timer fires + // or with an error indicating that the timer system is being shut down. + if (error != GRPC_ERROR_CANCELLED) { + grpc_transport_op* op = grpc_make_transport_op(nullptr); + op->disconnect_with_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Did not receive HTTP/2 settings before handshake timeout"); + grpc_transport_perform_op(&connection_state->transport->base, op); + } + server_connection_state_unref(connection_state); +} + +static void on_receive_settings(void* arg, grpc_error* error) { + server_connection_state* connection_state = + static_cast(arg); + if (error == GRPC_ERROR_NONE) { + grpc_timer_cancel(&connection_state->timer); + } + server_connection_state_unref(connection_state); +} + +static void on_handshake_done(void* arg, grpc_error* error) { + grpc_handshaker_args* args = static_cast(arg); + server_connection_state* connection_state = + static_cast(args->user_data); + gpr_mu_lock(&connection_state->svr_state->mu); + grpc_resource_user* resource_user = grpc_server_get_default_resource_user( + connection_state->svr_state->server); + if (error != GRPC_ERROR_NONE || connection_state->svr_state->shutdown) { + const char* error_str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str); + grpc_resource_user* resource_user = grpc_server_get_default_resource_user( + connection_state->svr_state->server); + if (resource_user != nullptr) { + grpc_resource_user_free(resource_user, GRPC_RESOURCE_QUOTA_CHANNEL_SIZE); + } + if (error == GRPC_ERROR_NONE && args->endpoint != nullptr) { + // We were shut down after handshaking completed successfully, so + // destroy the endpoint here. + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(args->endpoint, GRPC_ERROR_NONE); + grpc_endpoint_destroy(args->endpoint); + grpc_channel_args_destroy(args->args); + grpc_slice_buffer_destroy_internal(args->read_buffer); + gpr_free(args->read_buffer); + } + } else { + // If the handshaking succeeded but there is no endpoint, then the + // handshaker may have handed off the connection to some external + // code, so we can just clean up here without creating a transport. + if (args->endpoint != nullptr) { + grpc_transport* transport = grpc_create_chttp2_transport( + args->args, args->endpoint, false, resource_user); + grpc_server_setup_transport( + connection_state->svr_state->server, transport, + connection_state->accepting_pollset, args->args, + grpc_chttp2_transport_get_socket_node(transport), resource_user); + // Use notify_on_receive_settings callback to enforce the + // handshake deadline. + connection_state->transport = + reinterpret_cast(transport); + gpr_ref(&connection_state->refs); + GRPC_CLOSURE_INIT(&connection_state->on_receive_settings, + on_receive_settings, connection_state, + grpc_schedule_on_exec_ctx); + grpc_chttp2_transport_start_reading( + transport, args->read_buffer, &connection_state->on_receive_settings); + grpc_channel_args_destroy(args->args); + gpr_ref(&connection_state->refs); + GRPC_CHTTP2_REF_TRANSPORT((grpc_chttp2_transport*)transport, + "receive settings timeout"); + GRPC_CLOSURE_INIT(&connection_state->on_timeout, on_timeout, + connection_state, grpc_schedule_on_exec_ctx); + grpc_timer_init(&connection_state->timer, connection_state->deadline, + &connection_state->on_timeout); + } else { + if (resource_user != nullptr) { + grpc_resource_user_free(resource_user, + GRPC_RESOURCE_QUOTA_CHANNEL_SIZE); + } + } + } + grpc_handshake_manager_pending_list_remove( + &connection_state->svr_state->pending_handshake_mgrs, + connection_state->handshake_mgr); + gpr_mu_unlock(&connection_state->svr_state->mu); + grpc_handshake_manager_destroy(connection_state->handshake_mgr); + gpr_free(connection_state->acceptor); + grpc_tcp_server_unref(connection_state->svr_state->tcp_server); + server_connection_state_unref(connection_state); +} + +static void on_accept(void* arg, grpc_endpoint* tcp, + grpc_pollset* accepting_pollset, + grpc_tcp_server_acceptor* acceptor) { + server_state* state = static_cast(arg); + gpr_mu_lock(&state->mu); + if (state->shutdown) { + gpr_mu_unlock(&state->mu); + grpc_endpoint_shutdown(tcp, GRPC_ERROR_NONE); + grpc_endpoint_destroy(tcp); + gpr_free(acceptor); + return; + } + grpc_resource_user* resource_user = + grpc_server_get_default_resource_user(state->server); + if (resource_user != nullptr && + !grpc_resource_user_safe_alloc(resource_user, + GRPC_RESOURCE_QUOTA_CHANNEL_SIZE)) { + gpr_log( + GPR_ERROR, + "Memory quota exhausted, rejecting the connection, no handshaking."); + gpr_mu_unlock(&state->mu); + grpc_endpoint_shutdown(tcp, GRPC_ERROR_NONE); + grpc_endpoint_destroy(tcp); + gpr_free(acceptor); + return; + } + grpc_handshake_manager* handshake_mgr = grpc_handshake_manager_create(); + grpc_handshake_manager_pending_list_add(&state->pending_handshake_mgrs, + handshake_mgr); + grpc_tcp_server_ref(state->tcp_server); + gpr_mu_unlock(&state->mu); + server_connection_state* connection_state = + static_cast( + gpr_zalloc(sizeof(*connection_state))); + gpr_ref_init(&connection_state->refs, 1); + connection_state->svr_state = state; + connection_state->accepting_pollset = accepting_pollset; + connection_state->acceptor = acceptor; + connection_state->handshake_mgr = handshake_mgr; + connection_state->interested_parties = grpc_pollset_set_create(); + grpc_pollset_set_add_pollset(connection_state->interested_parties, + connection_state->accepting_pollset); + grpc_handshakers_add(HANDSHAKER_SERVER, state->args, + connection_state->interested_parties, + connection_state->handshake_mgr); + const grpc_arg* timeout_arg = + grpc_channel_args_find(state->args, GRPC_ARG_SERVER_HANDSHAKE_TIMEOUT_MS); + connection_state->deadline = + grpc_core::ExecCtx::Get()->Now() + + grpc_channel_arg_get_integer(timeout_arg, + {120 * GPR_MS_PER_SEC, 1, INT_MAX}); + grpc_handshake_manager_do_handshake(connection_state->handshake_mgr, tcp, + state->args, connection_state->deadline, + acceptor, on_handshake_done, + connection_state); +} + +/* Server callback: start listening on our ports */ +static void server_start_listener(grpc_server* server, void* arg, + grpc_pollset** pollsets, + size_t pollset_count) { + server_state* state = static_cast(arg); + gpr_mu_lock(&state->mu); + state->shutdown = false; + gpr_mu_unlock(&state->mu); + grpc_tcp_server_start(state->tcp_server, pollsets, pollset_count, on_accept, + state); +} + +static void tcp_server_shutdown_complete(void* arg, grpc_error* error) { + server_state* state = static_cast(arg); + /* ensure all threads have unlocked */ + gpr_mu_lock(&state->mu); + grpc_closure* destroy_done = state->server_destroy_listener_done; + GPR_ASSERT(state->shutdown); + grpc_handshake_manager_pending_list_shutdown_all( + state->pending_handshake_mgrs, GRPC_ERROR_REF(error)); + state->channelz_listen_socket.reset(); + gpr_mu_unlock(&state->mu); + // Flush queued work before destroying handshaker factory, since that + // may do a synchronous unref. + grpc_core::ExecCtx::Get()->Flush(); + if (destroy_done != nullptr) { + destroy_done->cb(destroy_done->cb_arg, GRPC_ERROR_REF(error)); + grpc_core::ExecCtx::Get()->Flush(); + } + grpc_channel_args_destroy(state->args); + gpr_mu_destroy(&state->mu); + gpr_free(state); +} + +/* Server callback: destroy the tcp listener (so we don't generate further + callbacks) */ +static void server_destroy_listener(grpc_server* server, void* arg, + grpc_closure* destroy_done) { + server_state* state = static_cast(arg); + gpr_mu_lock(&state->mu); + state->shutdown = true; + state->server_destroy_listener_done = destroy_done; + grpc_tcp_server* tcp_server = state->tcp_server; + gpr_mu_unlock(&state->mu); + grpc_tcp_server_shutdown_listeners(tcp_server); + grpc_tcp_server_unref(tcp_server); +} + +grpc_error* grpc_chttp2_server_add_port(grpc_server* server, const char* addr, + grpc_channel_args* args, + int* port_num) { + grpc_resolved_addresses* resolved = nullptr; + grpc_tcp_server* tcp_server = nullptr; + size_t i; + size_t count = 0; + int port_temp; + grpc_error* err = GRPC_ERROR_NONE; + server_state* state = nullptr; + grpc_error** errors = nullptr; + size_t naddrs = 0; + const grpc_arg* arg = nullptr; + intptr_t socket_uuid = 0; + + *port_num = -1; + + /* resolve address */ + err = grpc_blocking_resolve_address(addr, "https", &resolved); + if (err != GRPC_ERROR_NONE) { + goto error; + } + state = static_cast(gpr_zalloc(sizeof(*state))); + GRPC_CLOSURE_INIT(&state->tcp_server_shutdown_complete, + tcp_server_shutdown_complete, state, + grpc_schedule_on_exec_ctx); + err = grpc_tcp_server_create(&state->tcp_server_shutdown_complete, args, + &tcp_server); + if (err != GRPC_ERROR_NONE) { + goto error; + } + + state->server = server; + state->tcp_server = tcp_server; + state->args = args; + state->shutdown = true; + gpr_mu_init(&state->mu); + + naddrs = resolved->naddrs; + errors = static_cast(gpr_malloc(sizeof(*errors) * naddrs)); + for (i = 0; i < naddrs; i++) { + errors[i] = + grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp); + if (errors[i] == GRPC_ERROR_NONE) { + if (*port_num == -1) { + *port_num = port_temp; + } else { + GPR_ASSERT(*port_num == port_temp); + } + count++; + } + } + if (count == 0) { + char* msg; + gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved", + naddrs); + err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs); + gpr_free(msg); + goto error; + } else if (count != naddrs) { + char* msg; + gpr_asprintf(&msg, + "Only %" PRIuPTR " addresses added out of total %" PRIuPTR + " resolved", + count, naddrs); + err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs); + gpr_free(msg); + + const char* warning_message = grpc_error_string(err); + gpr_log(GPR_INFO, "WARNING: %s", warning_message); + + /* we managed to bind some addresses: continue */ + } + grpc_resolved_addresses_destroy(resolved); + + arg = grpc_channel_args_find(args, GRPC_ARG_ENABLE_CHANNELZ); + if (grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT)) { + state->channelz_listen_socket = + grpc_core::MakeRefCounted( + grpc_core::UniquePtr(gpr_strdup(addr))); + socket_uuid = state->channelz_listen_socket->uuid(); + } + + /* Register with the server only upon success */ + grpc_server_add_listener(server, state, server_start_listener, + server_destroy_listener, socket_uuid); + goto done; + +/* Error path: cleanup and return */ +error: + GPR_ASSERT(err != GRPC_ERROR_NONE); + if (resolved) { + grpc_resolved_addresses_destroy(resolved); + } + if (tcp_server) { + grpc_tcp_server_unref(tcp_server); + } else { + grpc_channel_args_destroy(args); + gpr_free(state); + } + *port_num = 0; + +done: + if (errors != nullptr) { + for (i = 0; i < naddrs; i++) { + GRPC_ERROR_UNREF(errors[i]); + } + gpr_free(errors); + } + return err; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.h b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.h new file mode 100644 index 00000000000000..6e51001b53b0b1 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/server/chttp2_server.h @@ -0,0 +1,33 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H + +#include + +#include + +#include "src/core/lib/iomgr/error.h" + +/// Adds a port to \a server. Sets \a port_num to the port number. +/// Takes ownership of \a args. +grpc_error* grpc_chttp2_server_add_port(grpc_server* server, const char* addr, + grpc_channel_args* args, int* port_num); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/README.md new file mode 100644 index 00000000000000..fc0bc14ed7ac0b --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/README.md @@ -0,0 +1 @@ +Plugin for creating insecure servers using chttp2 diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc new file mode 100644 index 00000000000000..99f18cdf39f0d8 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include "src/core/ext/transport/chttp2/server/chttp2_server.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/server.h" + +int grpc_server_add_insecure_http2_port(grpc_server* server, const char* addr) { + grpc_core::ExecCtx exec_ctx; + int port_num = 0; + GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2, + (server, addr)); + grpc_error* err = grpc_chttp2_server_add_port( + server, addr, + grpc_channel_args_copy(grpc_server_get_channel_args(server)), &port_num); + if (err != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "%s", msg); + + GRPC_ERROR_UNREF(err); + } + return port_num; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc new file mode 100644 index 00000000000000..c29c1e58cd978b --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc @@ -0,0 +1,75 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +#include +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/server.h" + +void grpc_server_add_insecure_channel_from_fd(grpc_server* server, + void* reserved, int fd) { + GPR_ASSERT(reserved == nullptr); + + grpc_core::ExecCtx exec_ctx; + char* name; + gpr_asprintf(&name, "fd:%d", fd); + + grpc_endpoint* server_endpoint = + grpc_tcp_create(grpc_fd_create(fd, name, true), + grpc_server_get_channel_args(server), name); + + gpr_free(name); + + const grpc_channel_args* server_args = grpc_server_get_channel_args(server); + grpc_transport* transport = grpc_create_chttp2_transport( + server_args, server_endpoint, false /* is_client */); + + grpc_pollset** pollsets; + size_t num_pollsets = 0; + grpc_server_get_pollsets(server, &pollsets, &num_pollsets); + + for (size_t i = 0; i < num_pollsets; i++) { + grpc_endpoint_add_to_pollset(server_endpoint, pollsets[i]); + } + + grpc_server_setup_transport(server, transport, nullptr, server_args, nullptr); + grpc_chttp2_transport_start_reading(transport, nullptr, nullptr); +} + +#else // !GPR_SUPPORT_CHANNELS_FROM_FD + +void grpc_server_add_insecure_channel_from_fd(grpc_server* server, + void* reserved, int fd) { + GPR_ASSERT(0); +} + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/secure/README.md b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/README.md new file mode 100644 index 00000000000000..6bda696a9a415f --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/README.md @@ -0,0 +1 @@ +Plugin for creating secure servers using chttp2 diff --git a/third_party/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc new file mode 100644 index 00000000000000..98fdb620704850 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc @@ -0,0 +1,86 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/server/chttp2_server.h" + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/server.h" + +int grpc_server_add_secure_http2_port(grpc_server* server, const char* addr, + grpc_server_credentials* creds) { + grpc_core::ExecCtx exec_ctx; + grpc_error* err = GRPC_ERROR_NONE; + grpc_core::RefCountedPtr sc; + int port_num = 0; + grpc_channel_args* args = nullptr; + GRPC_API_TRACE( + "grpc_server_add_secure_http2_port(" + "server=%p, addr=%s, creds=%p)", + 3, (server, addr, creds)); + // Create security context. + if (creds == nullptr) { + err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No credentials specified for secure server port (creds==NULL)"); + goto done; + } + sc = creds->create_security_connector(); + if (sc == nullptr) { + char* msg; + gpr_asprintf(&msg, + "Unable to create secure server with credentials of type %s.", + creds->type()); + err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + goto done; + } + // Create channel args. + grpc_arg args_to_add[2]; + args_to_add[0] = grpc_server_credentials_to_arg(creds); + args_to_add[1] = grpc_security_connector_to_arg(sc.get()); + args = + grpc_channel_args_copy_and_add(grpc_server_get_channel_args(server), + args_to_add, GPR_ARRAY_SIZE(args_to_add)); + // Add server port. + err = grpc_chttp2_server_add_port(server, addr, args, &port_num); +done: + sc.reset(DEBUG_LOCATION, "server"); + + if (err != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "%s", msg); + + GRPC_ERROR_UNREF(err); + } + return port_num; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/README.md b/third_party/grpc/src/core/ext/transport/chttp2/transport/README.md new file mode 100644 index 00000000000000..4684e587593e97 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/README.md @@ -0,0 +1,4 @@ +chttp2 transport plugin - implements grpc over http2 + +Used by chttp2/{client,server}/{insecure,secure} plugins to implement most of +their functionality diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.cc new file mode 100644 index 00000000000000..b660a456521d72 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.cc @@ -0,0 +1,250 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include "src/core/ext/transport/chttp2/transport/bin_decoder.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +static uint8_t decode_table[] = { + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 62, 0x40, 0x40, 0x40, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 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, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 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, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40}; + +static const uint8_t tail_xtra[4] = {0, 0, 1, 2}; + +static bool input_is_valid(uint8_t* input_ptr, size_t length) { + size_t i; + + for (i = 0; i < length; ++i) { + if (GPR_UNLIKELY((decode_table[input_ptr[i]] & 0xC0) != 0)) { + gpr_log(GPR_ERROR, + "Base64 decoding failed, invalid character '%c' in base64 " + "input.\n", + static_cast(*input_ptr)); + return false; + } + } + return true; +} + +#define COMPOSE_OUTPUT_BYTE_0(input_ptr) \ + (uint8_t)((decode_table[input_ptr[0]] << 2) | \ + (decode_table[input_ptr[1]] >> 4)) + +#define COMPOSE_OUTPUT_BYTE_1(input_ptr) \ + (uint8_t)((decode_table[input_ptr[1]] << 4) | \ + (decode_table[input_ptr[2]] >> 2)) + +#define COMPOSE_OUTPUT_BYTE_2(input_ptr) \ + (uint8_t)((decode_table[input_ptr[2]] << 6) | decode_table[input_ptr[3]]) + +// By RFC 4648, if the length of the encoded string without padding is 4n+r, +// the length of decoded string is: 1) 3n if r = 0, 2) 3n + 1 if r = 2, 3, or +// 3) invalid if r = 1. +size_t grpc_chttp2_base64_infer_length_after_decode(const grpc_slice& slice) { + size_t len = GRPC_SLICE_LENGTH(slice); + const uint8_t* bytes = GRPC_SLICE_START_PTR(slice); + while (len > 0 && bytes[len - 1] == '=') { + len--; + } + if (GPR_UNLIKELY(GRPC_SLICE_LENGTH(slice) - len > 2)) { + gpr_log(GPR_ERROR, + "Base64 decoding failed. Input has more than 2 paddings."); + return 0; + } + size_t tuples = len / 4; + size_t tail_case = len % 4; + if (GPR_UNLIKELY(tail_case == 1)) { + gpr_log(GPR_ERROR, + "Base64 decoding failed. Input has a length of %zu (without" + " padding), which is invalid.\n", + len); + return 0; + } + return tuples * 3 + tail_xtra[tail_case]; +} + +bool grpc_base64_decode_partial(struct grpc_base64_decode_context* ctx) { + size_t input_tail; + + if (ctx->input_cur > ctx->input_end || ctx->output_cur > ctx->output_end) { + return false; + } + + // Process a block of 4 input characters and 3 output bytes + while (ctx->input_end >= ctx->input_cur + 4 && + ctx->output_end >= ctx->output_cur + 3) { + if (!input_is_valid(ctx->input_cur, 4)) return false; + ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); + ctx->output_cur[2] = COMPOSE_OUTPUT_BYTE_2(ctx->input_cur); + ctx->output_cur += 3; + ctx->input_cur += 4; + } + + // Process the tail of input data + input_tail = static_cast(ctx->input_end - ctx->input_cur); + if (input_tail == 4) { + // Process the input data with pad chars + if (ctx->input_cur[3] == '=') { + if (ctx->input_cur[2] == '=' && ctx->output_end >= ctx->output_cur + 1) { + if (!input_is_valid(ctx->input_cur, 2)) return false; + *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + ctx->input_cur += 4; + } else if (ctx->output_end >= ctx->output_cur + 2) { + if (!input_is_valid(ctx->input_cur, 3)) return false; + *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); + ; + ctx->input_cur += 4; + } + } + + } else if (ctx->contains_tail && input_tail > 1) { + // Process the input data without pad chars, but constains_tail is set + if (ctx->output_end >= ctx->output_cur + tail_xtra[input_tail]) { + if (!input_is_valid(ctx->input_cur, input_tail)) return false; + switch (input_tail) { + case 3: + ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); + /* fallthrough */ + case 2: + ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + } + ctx->output_cur += tail_xtra[input_tail]; + ctx->input_cur += input_tail; + } + } + + return true; +} + +grpc_slice grpc_chttp2_base64_decode(grpc_slice input) { + size_t input_length = GRPC_SLICE_LENGTH(input); + size_t output_length = input_length / 4 * 3; + struct grpc_base64_decode_context ctx; + grpc_slice output; + + if (GPR_UNLIKELY(input_length % 4 != 0)) { + gpr_log(GPR_ERROR, + "Base64 decoding failed, input of " + "grpc_chttp2_base64_decode has a length of %d, which is not a " + "multiple of 4.\n", + static_cast(input_length)); + return grpc_empty_slice(); + } + + if (input_length > 0) { + uint8_t* input_end = GRPC_SLICE_END_PTR(input); + if (*(--input_end) == '=') { + output_length--; + if (*(--input_end) == '=') { + output_length--; + } + } + } + output = GRPC_SLICE_MALLOC(output_length); + + ctx.input_cur = GRPC_SLICE_START_PTR(input); + ctx.input_end = GRPC_SLICE_END_PTR(input); + ctx.output_cur = GRPC_SLICE_START_PTR(output); + ctx.output_end = GRPC_SLICE_END_PTR(output); + ctx.contains_tail = false; + + if (GPR_UNLIKELY(!grpc_base64_decode_partial(&ctx))) { + char* s = grpc_slice_to_c_string(input); + gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s); + gpr_free(s); + grpc_slice_unref_internal(output); + return grpc_empty_slice(); + } + GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output)); + GPR_ASSERT(ctx.input_cur == GRPC_SLICE_END_PTR(input)); + return output; +} + +grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input, + size_t output_length) { + size_t input_length = GRPC_SLICE_LENGTH(input); + grpc_slice output = GRPC_SLICE_MALLOC(output_length); + struct grpc_base64_decode_context ctx; + + // The length of a base64 string cannot be 4 * n + 1 + if (GPR_UNLIKELY(input_length % 4 == 1)) { + gpr_log(GPR_ERROR, + "Base64 decoding failed, input of " + "grpc_chttp2_base64_decode_with_length has a length of %d, which " + "has a tail of 1 byte.\n", + static_cast(input_length)); + grpc_slice_unref_internal(output); + return grpc_empty_slice(); + } + + if (GPR_UNLIKELY(output_length > + input_length / 4 * 3 + tail_xtra[input_length % 4])) { + gpr_log( + GPR_ERROR, + "Base64 decoding failed, output_length %d is longer " + "than the max possible output length %d.\n", + static_cast(output_length), + static_cast(input_length / 4 * 3 + tail_xtra[input_length % 4])); + grpc_slice_unref_internal(output); + return grpc_empty_slice(); + } + + ctx.input_cur = GRPC_SLICE_START_PTR(input); + ctx.input_end = GRPC_SLICE_END_PTR(input); + ctx.output_cur = GRPC_SLICE_START_PTR(output); + ctx.output_end = GRPC_SLICE_END_PTR(output); + ctx.contains_tail = true; + + if (GPR_UNLIKELY(!grpc_base64_decode_partial(&ctx))) { + char* s = grpc_slice_to_c_string(input); + gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s); + gpr_free(s); + grpc_slice_unref_internal(output); + return grpc_empty_slice(); + } + GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output)); + GPR_ASSERT(ctx.input_cur <= GRPC_SLICE_END_PTR(input)); + return output; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.h new file mode 100644 index 00000000000000..8a4d4a71790cc4 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_decoder.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H + +#include + +#include +#include + +struct grpc_base64_decode_context { + /* input/output: */ + uint8_t* input_cur; + uint8_t* input_end; + uint8_t* output_cur; + uint8_t* output_end; + /* Indicate if the decoder should handle the tail of input data*/ + bool contains_tail; +}; + +/* base64 decode a grpc_base64_decode_context util either input_end is reached + or output_end is reached. When input_end is reached, (input_end - input_cur) + is less than 4. When output_end is reached, (output_end - output_cur) is less + than 3. Returns false if decoding is failed. */ +bool grpc_base64_decode_partial(struct grpc_base64_decode_context* ctx); + +/* base64 decode a slice with pad chars. Returns a new slice, does not take + ownership of the input. Returns an empty slice if decoding is failed. */ +grpc_slice grpc_chttp2_base64_decode(grpc_slice input); + +/* base64 decode a slice without pad chars, data length is needed. Returns a new + slice, does not take ownership of the input. Returns an empty slice if + decoding is failed. */ +grpc_slice grpc_chttp2_base64_decode_with_length(grpc_slice input, + size_t output_length); + +/* Infer the length of decoded data from encoded data. */ +size_t grpc_chttp2_base64_infer_length_after_decode(const grpc_slice& slice); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.cc new file mode 100644 index 00000000000000..bad29e3421ca12 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.cc @@ -0,0 +1,231 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" + +#include + +#include +#include "src/core/ext/transport/chttp2/transport/huffsyms.h" + +static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +typedef struct { + uint16_t bits; + uint8_t 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 uint8_t tail_xtra[3] = {0, 2, 3}; + +grpc_slice grpc_chttp2_base64_encode(grpc_slice input) { + size_t input_length = GRPC_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]; + grpc_slice output = GRPC_SLICE_MALLOC(output_length); + uint8_t* in = GRPC_SLICE_START_PTR(input); + char* out = reinterpret_cast GRPC_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 == (char*)GRPC_SLICE_END_PTR(output)); + GPR_ASSERT(in == GRPC_SLICE_END_PTR(input)); + return output; +} + +grpc_slice grpc_chttp2_huffman_compress(grpc_slice input) { + size_t nbits; + uint8_t* in; + uint8_t* out; + grpc_slice output; + uint32_t temp = 0; + uint32_t temp_length = 0; + + nbits = 0; + for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input); + ++in) { + nbits += grpc_chttp2_huffsyms[*in].length; + } + + output = GRPC_SLICE_MALLOC(nbits / 8 + (nbits % 8 != 0)); + out = GRPC_SLICE_START_PTR(output); + for (in = GRPC_SLICE_START_PTR(input); in != GRPC_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++ = static_cast(temp >> temp_length); + } + } + + if (temp_length) { + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + *out++ = + static_cast(static_cast(temp << (8u - temp_length)) | + static_cast(0xffu >> temp_length)); + } + + GPR_ASSERT(out == GRPC_SLICE_END_PTR(output)); + + return output; +} + +typedef struct { + uint32_t temp; + uint32_t temp_length; + uint8_t* out; +} huff_out; + +static void enc_flush_some(huff_out* out) { + while (out->temp_length > 8) { + out->temp_length -= 8; + *out->out++ = static_cast(out->temp >> out->temp_length); + } +} + +static void enc_add2(huff_out* out, uint8_t a, uint8_t b) { + b64_huff_sym sa = huff_alphabet[a]; + b64_huff_sym sb = huff_alphabet[b]; + out->temp = (out->temp << (sa.length + sb.length)) | + (static_cast(sa.bits) << sb.length) | sb.bits; + out->temp_length += + static_cast(sa.length) + static_cast(sb.length); + enc_flush_some(out); +} + +static void enc_add1(huff_out* out, uint8_t 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); +} + +grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(grpc_slice input) { + size_t input_length = GRPC_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); + grpc_slice output = GRPC_SLICE_MALLOC(max_output_length); + uint8_t* in = GRPC_SLICE_START_PTR(input); + uint8_t* start_out = GRPC_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++) { + const uint8_t low_to_high = static_cast((in[0] & 0x3) << 4); + const uint8_t high_to_low = in[1] >> 4; + enc_add2(&out, in[0] >> 2, low_to_high | high_to_low); + + const uint8_t a = static_cast((in[1] & 0xf) << 2); + const uint8_t b = (in[2] >> 6); + enc_add2(&out, a | b, in[2] & 0x3f); + in += 3; + } + + /* encode the remaining bytes */ + switch (tail_case) { + case 0: + break; + case 1: + enc_add2(&out, in[0] >> 2, static_cast((in[0] & 0x3) << 4)); + in += 1; + break; + case 2: { + const uint8_t low_to_high = static_cast((in[0] & 0x3) << 4); + const uint8_t high_to_low = in[1] >> 4; + enc_add2(&out, in[0] >> 2, low_to_high | high_to_low); + enc_add1(&out, static_cast((in[1] & 0xf) << 2)); + in += 2; + break; + } + } + + if (out.temp_length) { + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + *out.out++ = static_cast( + static_cast(out.temp << (8u - out.temp_length)) | + static_cast(0xffu >> out.temp_length)); + } + + GPR_ASSERT(out.out <= GRPC_SLICE_END_PTR(output)); + GRPC_SLICE_SET_LENGTH(output, out.out - start_out); + + GPR_ASSERT(in == GRPC_SLICE_END_PTR(input)); + return output; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.h new file mode 100644 index 00000000000000..1b7bb1574af5c1 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/bin_encoder.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H + +#include + +#include + +/* base64 encode a slice. Returns a new slice, does not take ownership of the + input */ +grpc_slice grpc_chttp2_base64_encode(grpc_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 */ +grpc_slice grpc_chttp2_huffman_compress(grpc_slice input); + +/* equivalent to: + grpc_slice x = grpc_chttp2_base64_encode(input); + grpc_slice y = grpc_chttp2_huffman_compress(x); + grpc_slice_unref_internal( x); + return y; */ +grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(grpc_slice input); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc new file mode 100644 index 00000000000000..531ea73e9e6392 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc @@ -0,0 +1,35 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/transport/metadata.h" + +void grpc_chttp2_plugin_init(void) { + g_flow_control_enabled = true; + char* env_variable = gpr_getenv("GRPC_EXPERIMENTAL_DISABLE_FLOW_CONTROL"); + if (env_variable != nullptr) { + g_flow_control_enabled = false; + gpr_free(env_variable); + } +} + +void grpc_chttp2_plugin_shutdown(void) {} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc new file mode 100644 index 00000000000000..7f4627fa773c3a --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -0,0 +1,3181 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/context_list.h" +#include "src/core/ext/transport/chttp2/transport/frame_data.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/ext/transport/chttp2/transport/varint.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/compression/stream_compression.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/http2_errors.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_conversion.h" +#include "src/core/lib/transport/timeout_encoding.h" +#include "src/core/lib/transport/transport.h" +#include "src/core/lib/transport/transport_impl.h" +#include "src/core/lib/uri/uri_parser.h" + +#define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024) +#define MAX_WINDOW 0x7fffffffu +#define MAX_WRITE_BUFFER_SIZE (64 * 1024 * 1024) +#define DEFAULT_MAX_HEADER_LIST_SIZE (8 * 1024) + +#define DEFAULT_CLIENT_KEEPALIVE_TIME_MS INT_MAX +#define DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */ +#define DEFAULT_SERVER_KEEPALIVE_TIME_MS 7200000 /* 2 hours */ +#define DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */ +#define DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS false +#define KEEPALIVE_TIME_BACKOFF_MULTIPLIER 2 + +#define DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */ +#define DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */ +#define DEFAULT_MAX_PINGS_BETWEEN_DATA 2 +#define DEFAULT_MAX_PING_STRIKES 2 + +static int g_default_client_keepalive_time_ms = + DEFAULT_CLIENT_KEEPALIVE_TIME_MS; +static int g_default_client_keepalive_timeout_ms = + DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS; +static int g_default_server_keepalive_time_ms = + DEFAULT_SERVER_KEEPALIVE_TIME_MS; +static int g_default_server_keepalive_timeout_ms = + DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS; +static bool g_default_client_keepalive_permit_without_calls = + DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS; +static bool g_default_server_keepalive_permit_without_calls = + DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS; + +static int g_default_min_sent_ping_interval_without_data_ms = + DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS; +static int g_default_min_recv_ping_interval_without_data_ms = + DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS; +static int g_default_max_pings_without_data = DEFAULT_MAX_PINGS_BETWEEN_DATA; +static int g_default_max_ping_strikes = DEFAULT_MAX_PING_STRIKES; + +#define MAX_CLIENT_STREAM_ID 0x7fffffffu +grpc_core::TraceFlag grpc_http_trace(false, "http"); +grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount(false, + "chttp2_refcount"); + +/* forward declarations of various callbacks that we'll build closures around */ +static void write_action_begin_locked(void* t, grpc_error* error); +static void write_action(void* t, grpc_error* error); +static void write_action_end_locked(void* t, grpc_error* error); + +static void read_action_locked(void* t, grpc_error* error); + +static void complete_fetch_locked(void* gs, grpc_error* error); +/** Set a transport level setting, and push it to our peer */ +static void queue_setting_update(grpc_chttp2_transport* t, + grpc_chttp2_setting_id id, uint32_t value); + +static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_error* error); + +/** Start new streams that have been created if we can */ +static void maybe_start_some_streams(grpc_chttp2_transport* t); + +static void connectivity_state_set(grpc_chttp2_transport* t, + grpc_connectivity_state state, + grpc_error* error, const char* reason); + +static void benign_reclaimer_locked(void* t, grpc_error* error); +static void destructive_reclaimer_locked(void* t, grpc_error* error); + +static void post_benign_reclaimer(grpc_chttp2_transport* t); +static void post_destructive_reclaimer(grpc_chttp2_transport* t); + +static void close_transport_locked(grpc_chttp2_transport* t, grpc_error* error); +static void end_all_the_calls(grpc_chttp2_transport* t, grpc_error* error); + +static void schedule_bdp_ping_locked(grpc_chttp2_transport* t); +static void start_bdp_ping_locked(void* tp, grpc_error* error); +static void finish_bdp_ping_locked(void* tp, grpc_error* error); +static void next_bdp_ping_timer_expired_locked(void* tp, grpc_error* error); + +static void cancel_pings(grpc_chttp2_transport* t, grpc_error* error); +static void send_ping_locked(grpc_chttp2_transport* t, + grpc_closure* on_initiate, + grpc_closure* on_complete); +static void retry_initiate_ping_locked(void* tp, grpc_error* error); + +/** keepalive-relevant functions */ +static void init_keepalive_ping_locked(void* arg, grpc_error* error); +static void start_keepalive_ping_locked(void* arg, grpc_error* error); +static void finish_keepalive_ping_locked(void* arg, grpc_error* error); +static void keepalive_watchdog_fired_locked(void* arg, grpc_error* error); + +static void reset_byte_stream(void* arg, grpc_error* error); + +// Flow control default enabled. Can be disabled by setting +// GRPC_EXPERIMENTAL_DISABLE_FLOW_CONTROL +bool g_flow_control_enabled = true; + +/******************************************************************************* + * CONSTRUCTION/DESTRUCTION/REFCOUNTING + */ + +grpc_chttp2_transport::~grpc_chttp2_transport() { + size_t i; + + if (channelz_socket != nullptr) { + channelz_socket.reset(); + } + + grpc_endpoint_destroy(ep); + + grpc_slice_buffer_destroy_internal(&qbuf); + + grpc_slice_buffer_destroy_internal(&outbuf); + grpc_chttp2_hpack_compressor_destroy(&hpack_compressor); + + grpc_error* error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"); + // ContextList::Execute follows semantics of a callback function and does not + // take a ref on error + grpc_core::ContextList::Execute(cl, nullptr, error); + GRPC_ERROR_UNREF(error); + cl = nullptr; + + grpc_slice_buffer_destroy_internal(&read_buffer); + grpc_chttp2_hpack_parser_destroy(&hpack_parser); + grpc_chttp2_goaway_parser_destroy(&goaway_parser); + + for (i = 0; i < STREAM_LIST_COUNT; i++) { + GPR_ASSERT(lists[i].head == nullptr); + GPR_ASSERT(lists[i].tail == nullptr); + } + + GRPC_ERROR_UNREF(goaway_error); + + GPR_ASSERT(grpc_chttp2_stream_map_size(&stream_map) == 0); + + grpc_chttp2_stream_map_destroy(&stream_map); + grpc_connectivity_state_destroy(&channel_callback.state_tracker); + + GRPC_COMBINER_UNREF(combiner, "chttp2_transport"); + + cancel_pings(this, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed")); + + while (write_cb_pool) { + grpc_chttp2_write_cb* next = write_cb_pool->next; + gpr_free(write_cb_pool); + write_cb_pool = next; + } + + flow_control.Destroy(); + + GRPC_ERROR_UNREF(closed_with_error); + gpr_free(ping_acks); + gpr_free(peer_string); +} + +static const grpc_transport_vtable* get_vtable(void); + +/* Returns whether bdp is enabled */ +static bool read_channel_args(grpc_chttp2_transport* t, + const grpc_channel_args* channel_args, + bool is_client) { + bool enable_bdp = true; + bool channelz_enabled = GRPC_ENABLE_CHANNELZ_DEFAULT; + size_t i; + int j; + + for (i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) { + const grpc_integer_options options = {-1, 0, INT_MAX}; + const int value = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + if (value >= 0) { + if ((t->next_stream_id & 1) != (value & 1)) { + gpr_log(GPR_ERROR, "%s: low bit must be %d on %s", + GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER, t->next_stream_id & 1, + is_client ? "client" : "server"); + } else { + t->next_stream_id = static_cast(value); + } + } + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) { + const grpc_integer_options options = {-1, 0, INT_MAX}; + const int value = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + if (value >= 0) { + grpc_chttp2_hpack_compressor_set_max_usable_size( + &t->hpack_compressor, static_cast(value)); + } + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { + t->ping_policy.max_pings_without_data = grpc_channel_arg_get_integer( + &channel_args->args[i], + {g_default_max_pings_without_data, 0, INT_MAX}); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { + t->ping_policy.max_ping_strikes = grpc_channel_arg_get_integer( + &channel_args->args[i], {g_default_max_ping_strikes, 0, INT_MAX}); + } else if (0 == + strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) { + t->ping_policy.min_sent_ping_interval_without_data = + grpc_channel_arg_get_integer( + &channel_args->args[i], + grpc_integer_options{ + g_default_min_sent_ping_interval_without_data_ms, 0, + INT_MAX}); + } else if (0 == + strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) { + t->ping_policy.min_recv_ping_interval_without_data = + grpc_channel_arg_get_integer( + &channel_args->args[i], + grpc_integer_options{ + g_default_min_recv_ping_interval_without_data_ms, 0, + INT_MAX}); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE)) { + t->write_buffer_size = static_cast(grpc_channel_arg_get_integer( + &channel_args->args[i], {0, 0, MAX_WRITE_BUFFER_SIZE})); + } else if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_BDP_PROBE)) { + enable_bdp = grpc_channel_arg_get_bool(&channel_args->args[i], true); + } else if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], + grpc_integer_options{t->is_client + ? g_default_client_keepalive_time_ms + : g_default_server_keepalive_time_ms, + 1, INT_MAX}); + t->keepalive_time = value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value; + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], + grpc_integer_options{t->is_client + ? g_default_client_keepalive_timeout_ms + : g_default_server_keepalive_timeout_ms, + 0, INT_MAX}); + t->keepalive_timeout = value == INT_MAX ? GRPC_MILLIS_INF_FUTURE : value; + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { + t->keepalive_permit_without_calls = static_cast( + grpc_channel_arg_get_integer(&channel_args->args[i], {0, 0, 1})); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_OPTIMIZATION_TARGET)) { + if (channel_args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "%s should be a string", + GRPC_ARG_OPTIMIZATION_TARGET); + } else if (0 == strcmp(channel_args->args[i].value.string, "blend")) { + t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; + } else if (0 == strcmp(channel_args->args[i].value.string, "latency")) { + t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; + } else if (0 == + strcmp(channel_args->args[i].value.string, "throughput")) { + t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT; + } else { + gpr_log(GPR_ERROR, "%s value '%s' unknown, assuming 'blend'", + GRPC_ARG_OPTIMIZATION_TARGET, + channel_args->args[i].value.string); + } + } else if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_ENABLE_CHANNELZ)) { + channelz_enabled = grpc_channel_arg_get_bool( + &channel_args->args[i], GRPC_ENABLE_CHANNELZ_DEFAULT); + } else { + static const struct { + const char* channel_arg_name; + grpc_chttp2_setting_id setting_id; + grpc_integer_options integer_options; + bool availability[2] /* server, client */; + } settings_map[] = {{GRPC_ARG_MAX_CONCURRENT_STREAMS, + GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, + {-1, 0, INT32_MAX}, + {true, false}}, + {GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER, + GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE, + {-1, 0, INT32_MAX}, + {true, true}}, + {GRPC_ARG_MAX_METADATA_SIZE, + GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, + {-1, 0, INT32_MAX}, + {true, true}}, + {GRPC_ARG_HTTP2_MAX_FRAME_SIZE, + GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, + {-1, 16384, 16777215}, + {true, true}}, + {GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY, + GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, + {1, 0, 1}, + {true, true}}, + {GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, + {-1, 5, INT32_MAX}, + {true, true}}}; + for (j = 0; j < static_cast GPR_ARRAY_SIZE(settings_map); j++) { + if (0 == strcmp(channel_args->args[i].key, + settings_map[j].channel_arg_name)) { + if (!settings_map[j].availability[is_client]) { + gpr_log(GPR_DEBUG, "%s is not available on %s", + settings_map[j].channel_arg_name, + is_client ? "clients" : "servers"); + } else { + int value = grpc_channel_arg_get_integer( + &channel_args->args[i], settings_map[j].integer_options); + if (value >= 0) { + queue_setting_update(t, settings_map[j].setting_id, + static_cast(value)); + } + } + break; + } + } + } + } + if (channelz_enabled) { + // TODO(ncteisen): add an API to endpoint to query for local addr, and pass + // it in here, so SocketNode knows its own address. + t->channelz_socket = + grpc_core::MakeRefCounted( + grpc_core::UniquePtr(), + grpc_core::UniquePtr(gpr_strdup(t->peer_string))); + } + return enable_bdp; +} + +static void init_transport_closures(grpc_chttp2_transport* t) { + GRPC_CLOSURE_INIT(&t->read_action_locked, read_action_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->benign_reclaimer_locked, benign_reclaimer_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->destructive_reclaimer_locked, + destructive_reclaimer_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->retry_initiate_ping_locked, retry_initiate_ping_locked, + t, grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->start_bdp_ping_locked, start_bdp_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->finish_bdp_ping_locked, finish_bdp_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->next_bdp_ping_timer_expired_locked, + next_bdp_ping_timer_expired_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->init_keepalive_ping_locked, init_keepalive_ping_locked, + t, grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->start_keepalive_ping_locked, + start_keepalive_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->finish_keepalive_ping_locked, + finish_keepalive_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->keepalive_watchdog_fired_locked, + keepalive_watchdog_fired_locked, t, + grpc_combiner_scheduler(t->combiner)); +} + +static void init_transport_keepalive_settings(grpc_chttp2_transport* t) { + if (t->is_client) { + t->keepalive_time = g_default_client_keepalive_time_ms == INT_MAX + ? GRPC_MILLIS_INF_FUTURE + : g_default_client_keepalive_time_ms; + t->keepalive_timeout = g_default_client_keepalive_timeout_ms == INT_MAX + ? GRPC_MILLIS_INF_FUTURE + : g_default_client_keepalive_timeout_ms; + t->keepalive_permit_without_calls = + g_default_client_keepalive_permit_without_calls; + } else { + t->keepalive_time = g_default_server_keepalive_time_ms == INT_MAX + ? GRPC_MILLIS_INF_FUTURE + : g_default_server_keepalive_time_ms; + t->keepalive_timeout = g_default_server_keepalive_timeout_ms == INT_MAX + ? GRPC_MILLIS_INF_FUTURE + : g_default_server_keepalive_timeout_ms; + t->keepalive_permit_without_calls = + g_default_server_keepalive_permit_without_calls; + } +} + +static void configure_transport_ping_policy(grpc_chttp2_transport* t) { + t->ping_policy.max_pings_without_data = g_default_max_pings_without_data; + t->ping_policy.min_sent_ping_interval_without_data = + g_default_min_sent_ping_interval_without_data_ms; + t->ping_policy.max_ping_strikes = g_default_max_ping_strikes; + t->ping_policy.min_recv_ping_interval_without_data = + g_default_min_recv_ping_interval_without_data_ms; +} + +static void init_keepalive_pings_if_enabled(grpc_chttp2_transport* t) { + if (t->keepalive_time != GRPC_MILLIS_INF_FUTURE) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING; + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init(&t->keepalive_ping_timer, + grpc_core::ExecCtx::Get()->Now() + t->keepalive_time, + &t->init_keepalive_ping_locked); + } else { + /* Use GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED to indicate there are no + inflight keeaplive timers */ + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED; + } +} + +grpc_chttp2_transport::grpc_chttp2_transport( + const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client, + grpc_resource_user* resource_user) + : refs(1, &grpc_trace_chttp2_refcount), + ep(ep), + peer_string(grpc_endpoint_get_peer(ep)), + resource_user(resource_user), + combiner(grpc_combiner_create()), + is_client(is_client), + next_stream_id(is_client ? 1 : 2), + deframe_state(is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0) { + GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == + GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); + base.vtable = get_vtable(); + /* 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(&stream_map, 8); + + grpc_slice_buffer_init(&read_buffer); + grpc_connectivity_state_init( + &channel_callback.state_tracker, GRPC_CHANNEL_READY, + is_client ? "client_transport" : "server_transport"); + grpc_slice_buffer_init(&outbuf); + if (is_client) { + grpc_slice_buffer_add(&outbuf, grpc_slice_from_copied_string( + GRPC_CHTTP2_CLIENT_CONNECT_STRING)); + } + grpc_chttp2_hpack_compressor_init(&hpack_compressor); + grpc_slice_buffer_init(&qbuf); + /* copy in initial settings to all setting sets */ + size_t i; + int j; + for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) { + for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) { + settings[j][i] = grpc_chttp2_settings_parameters[i].default_value; + } + } + grpc_chttp2_hpack_parser_init(&hpack_parser); + grpc_chttp2_goaway_parser_init(&goaway_parser); + + init_transport_closures(this); + + /* configure http2 the way we like it */ + if (is_client) { + queue_setting_update(this, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0); + queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0); + } + queue_setting_update(this, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, + DEFAULT_MAX_HEADER_LIST_SIZE); + queue_setting_update(this, + GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, 1); + + configure_transport_ping_policy(this); + init_transport_keepalive_settings(this); + + bool enable_bdp = true; + if (channel_args) { + enable_bdp = read_channel_args(this, channel_args, is_client); + } + + if (g_flow_control_enabled) { + flow_control.Init(this, + enable_bdp); + } else { + flow_control.Init(this); + enable_bdp = false; + } + + /* No pings allowed before receiving a header or data frame. */ + ping_state.pings_before_data_required = 0; + ping_state.is_delayed_ping_timer_set = false; + ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST; + + ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST; + ping_recv_state.ping_strikes = 0; + + init_keepalive_pings_if_enabled(this); + + if (enable_bdp) { + GRPC_CHTTP2_REF_TRANSPORT(this, "bdp_ping"); + schedule_bdp_ping_locked(this); + grpc_chttp2_act_on_flowctl_action(flow_control->PeriodicUpdate(), this, + nullptr); + } + + grpc_chttp2_initiate_write(this, GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE); + post_benign_reclaimer(this); +} + +static void destroy_transport_locked(void* tp, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(tp); + t->destroying = 1; + close_transport_locked( + t, grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"), + GRPC_ERROR_INT_OCCURRED_DURING_WRITE, t->write_state)); + // Must be the last line. + GRPC_CHTTP2_UNREF_TRANSPORT(t, "destroy"); +} + +static void destroy_transport(grpc_transport* gt) { + grpc_chttp2_transport* t = reinterpret_cast(gt); + GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(destroy_transport_locked, t, + grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); +} + +static void close_transport_locked(grpc_chttp2_transport* t, + grpc_error* error) { + end_all_the_calls(t, GRPC_ERROR_REF(error)); + cancel_pings(t, GRPC_ERROR_REF(error)); + if (t->closed_with_error == GRPC_ERROR_NONE) { + if (!grpc_error_has_clear_grpc_status(error)) { + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + } + if (t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE) { + if (t->close_transport_on_writes_finished == nullptr) { + t->close_transport_on_writes_finished = + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Delayed close due to in-progress write"); + } + t->close_transport_on_writes_finished = + grpc_error_add_child(t->close_transport_on_writes_finished, error); + return; + } + GPR_ASSERT(error != GRPC_ERROR_NONE); + t->closed_with_error = GRPC_ERROR_REF(error); + connectivity_state_set(t, GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(error), + "close_transport"); + if (t->ping_state.is_delayed_ping_timer_set) { + grpc_timer_cancel(&t->ping_state.delayed_ping_timer); + } + if (t->have_next_bdp_ping_timer) { + grpc_timer_cancel(&t->next_bdp_ping_timer); + } + switch (t->keepalive_state) { + case GRPC_CHTTP2_KEEPALIVE_STATE_WAITING: + grpc_timer_cancel(&t->keepalive_ping_timer); + break; + case GRPC_CHTTP2_KEEPALIVE_STATE_PINGING: + grpc_timer_cancel(&t->keepalive_ping_timer); + grpc_timer_cancel(&t->keepalive_watchdog_timer); + break; + case GRPC_CHTTP2_KEEPALIVE_STATE_DYING: + case GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED: + /* keepalive timers are not set in these two states */ + break; + } + + /* flush writable stream list to avoid dangling references */ + grpc_chttp2_stream* s; + while (grpc_chttp2_list_pop_writable_stream(t, &s)) { + GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:close"); + } + GPR_ASSERT(t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE); + grpc_endpoint_shutdown(t->ep, GRPC_ERROR_REF(error)); + } + if (t->notify_on_receive_settings != nullptr) { + GRPC_CLOSURE_SCHED(t->notify_on_receive_settings, GRPC_ERROR_CANCELLED); + t->notify_on_receive_settings = nullptr; + } + GRPC_ERROR_UNREF(error); +} + +#ifndef NDEBUG +void grpc_chttp2_stream_ref(grpc_chttp2_stream* s, const char* reason) { + grpc_stream_ref(s->refcount, reason); +} +void grpc_chttp2_stream_unref(grpc_chttp2_stream* s, const char* reason) { + grpc_stream_unref(s->refcount, reason); +} +#else +void grpc_chttp2_stream_ref(grpc_chttp2_stream* s) { + grpc_stream_ref(s->refcount); +} +void grpc_chttp2_stream_unref(grpc_chttp2_stream* s) { + grpc_stream_unref(s->refcount); +} +#endif + +grpc_chttp2_stream::grpc_chttp2_stream(grpc_chttp2_transport* t, + grpc_stream_refcount* refcount, + const void* server_data, + gpr_arena* arena) + : t(t), refcount(refcount), metadata_buffer{{arena}, {arena}} { + /* We reserve one 'active stream' that's dropped when the stream is + read-closed. The others are for Chttp2IncomingByteStreams that are + actively reading */ + GRPC_CHTTP2_STREAM_REF(this, "chttp2"); + GRPC_CHTTP2_REF_TRANSPORT(t, "stream"); + + if (server_data) { + id = static_cast((uintptr_t)server_data); + *t->accepting_stream = this; + grpc_chttp2_stream_map_add(&t->stream_map, id, this); + post_destructive_reclaimer(t); + } + if (t->flow_control->flow_control_enabled()) { + flow_control.Init( + static_cast( + t->flow_control.get()), + this); + } else { + flow_control.Init(); + } + + grpc_slice_buffer_init(&frame_storage); + grpc_slice_buffer_init(&unprocessed_incoming_frames_buffer); + grpc_slice_buffer_init(&flow_controlled_buffer); + grpc_slice_buffer_init(&compressed_data_buffer); + grpc_slice_buffer_init(&decompressed_data_buffer); + + GRPC_CLOSURE_INIT(&complete_fetch_locked, ::complete_fetch_locked, this, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&reset_byte_stream, ::reset_byte_stream, this, + grpc_combiner_scheduler(t->combiner)); +} + +grpc_chttp2_stream::~grpc_chttp2_stream() { + if (t->channelz_socket != nullptr) { + if ((t->is_client && eos_received) || (!t->is_client && eos_sent)) { + t->channelz_socket->RecordStreamSucceeded(); + } else { + t->channelz_socket->RecordStreamFailed(); + } + } + + GPR_ASSERT((write_closed && read_closed) || id == 0); + if (id != 0) { + GPR_ASSERT(grpc_chttp2_stream_map_find(&t->stream_map, id) == nullptr); + } + + grpc_slice_buffer_destroy_internal(&unprocessed_incoming_frames_buffer); + grpc_slice_buffer_destroy_internal(&frame_storage); + grpc_slice_buffer_destroy_internal(&compressed_data_buffer); + grpc_slice_buffer_destroy_internal(&decompressed_data_buffer); + + grpc_chttp2_list_remove_stalled_by_transport(t, this); + grpc_chttp2_list_remove_stalled_by_stream(t, this); + + for (int i = 0; i < STREAM_LIST_COUNT; i++) { + if (GPR_UNLIKELY(included[i])) { + gpr_log(GPR_ERROR, "%s stream %d still included in list %d", + t->is_client ? "client" : "server", id, i); + abort(); + } + } + + GPR_ASSERT(send_initial_metadata_finished == nullptr); + GPR_ASSERT(fetching_send_message == nullptr); + GPR_ASSERT(send_trailing_metadata_finished == nullptr); + GPR_ASSERT(recv_initial_metadata_ready == nullptr); + GPR_ASSERT(recv_message_ready == nullptr); + GPR_ASSERT(recv_trailing_metadata_finished == nullptr); + grpc_slice_buffer_destroy_internal(&flow_controlled_buffer); + GRPC_ERROR_UNREF(read_closed_error); + GRPC_ERROR_UNREF(write_closed_error); + GRPC_ERROR_UNREF(byte_stream_error); + + flow_control.Destroy(); + + if (t->resource_user != nullptr) { + grpc_resource_user_free(t->resource_user, GRPC_RESOURCE_QUOTA_CALL_SIZE); + } + + GRPC_CHTTP2_UNREF_TRANSPORT(t, "stream"); + GRPC_CLOSURE_SCHED(destroy_stream_arg, GRPC_ERROR_NONE); +} + +static int init_stream(grpc_transport* gt, grpc_stream* gs, + grpc_stream_refcount* refcount, const void* server_data, + gpr_arena* arena) { + GPR_TIMER_SCOPE("init_stream", 0); + grpc_chttp2_transport* t = reinterpret_cast(gt); + new (gs) grpc_chttp2_stream(t, refcount, server_data, arena); + return 0; +} + +static void destroy_stream_locked(void* sp, grpc_error* error) { + GPR_TIMER_SCOPE("destroy_stream", 0); + grpc_chttp2_stream* s = static_cast(sp); + s->~grpc_chttp2_stream(); +} + +static void destroy_stream(grpc_transport* gt, grpc_stream* gs, + grpc_closure* then_schedule_closure) { + GPR_TIMER_SCOPE("destroy_stream", 0); + grpc_chttp2_transport* t = reinterpret_cast(gt); + grpc_chttp2_stream* s = reinterpret_cast(gs); + + if (s->stream_compression_ctx != nullptr) { + grpc_stream_compression_context_destroy(s->stream_compression_ctx); + s->stream_compression_ctx = nullptr; + } + if (s->stream_decompression_ctx != nullptr) { + grpc_stream_compression_context_destroy(s->stream_decompression_ctx); + s->stream_decompression_ctx = nullptr; + } + + s->destroy_stream_arg = then_schedule_closure; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&s->destroy_stream, destroy_stream_locked, s, + grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); +} + +grpc_chttp2_stream* grpc_chttp2_parsing_lookup_stream(grpc_chttp2_transport* t, + uint32_t id) { + return static_cast( + grpc_chttp2_stream_map_find(&t->stream_map, id)); +} + +grpc_chttp2_stream* grpc_chttp2_parsing_accept_stream(grpc_chttp2_transport* t, + uint32_t id) { + if (t->channel_callback.accept_stream == nullptr) { + return nullptr; + } + // Don't accept the stream if memory quota doesn't allow. Note that we should + // simply refuse the stream here instead of canceling the stream after it's + // accepted since the latter will create the call which costs much memory. + if (t->resource_user != nullptr && + !grpc_resource_user_safe_alloc(t->resource_user, + GRPC_RESOURCE_QUOTA_CALL_SIZE)) { + gpr_log(GPR_ERROR, "Memory exhausted, rejecting the stream."); + grpc_slice_buffer_add( + &t->qbuf, + grpc_chttp2_rst_stream_create( + id, static_cast(GRPC_HTTP2_REFUSED_STREAM), nullptr)); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM); + return nullptr; + } + grpc_chttp2_stream* accepting = nullptr; + GPR_ASSERT(t->accepting_stream == nullptr); + t->accepting_stream = &accepting; + t->channel_callback.accept_stream(t->channel_callback.accept_stream_user_data, + &t->base, + (void*)static_cast(id)); + t->accepting_stream = nullptr; + return accepting; +} + +/******************************************************************************* + * OUTPUT PROCESSING + */ + +static const char* write_state_name(grpc_chttp2_write_state st) { + switch (st) { + case GRPC_CHTTP2_WRITE_STATE_IDLE: + return "IDLE"; + case GRPC_CHTTP2_WRITE_STATE_WRITING: + return "WRITING"; + case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: + return "WRITING+MORE"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +static void set_write_state(grpc_chttp2_transport* t, + grpc_chttp2_write_state st, const char* reason) { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "W:%p %s state %s -> %s [%s]", t, + t->is_client ? "CLIENT" : "SERVER", + write_state_name(t->write_state), + write_state_name(st), reason)); + t->write_state = st; + /* If the state is being reset back to idle, it means a write was just + * finished. Make sure all the run_after_write closures are scheduled. + * + * This is also our chance to close the transport if the transport was marked + * to be closed after all writes finish (for example, if we received a go-away + * from peer while we had some pending writes) */ + if (st == GRPC_CHTTP2_WRITE_STATE_IDLE) { + GRPC_CLOSURE_LIST_SCHED(&t->run_after_write); + if (t->close_transport_on_writes_finished != nullptr) { + grpc_error* err = t->close_transport_on_writes_finished; + t->close_transport_on_writes_finished = nullptr; + close_transport_locked(t, err); + } + } +} + +static void inc_initiate_write_reason( + grpc_chttp2_initiate_write_reason reason) { + switch (reason) { + case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE(); + break; + case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM(); + break; + } +} + +void grpc_chttp2_initiate_write(grpc_chttp2_transport* t, + grpc_chttp2_initiate_write_reason reason) { + GPR_TIMER_SCOPE("grpc_chttp2_initiate_write", 0); + + switch (t->write_state) { + case GRPC_CHTTP2_WRITE_STATE_IDLE: + inc_initiate_write_reason(reason); + set_write_state(t, GRPC_CHTTP2_WRITE_STATE_WRITING, + grpc_chttp2_initiate_write_reason_string(reason)); + t->is_first_write_in_batch = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "writing"); + /* Note that the 'write_action_begin_locked' closure is being scheduled + * on the 'finally_scheduler' of t->combiner. This means that + * 'write_action_begin_locked' is called only *after* all the other + * closures (some of which are potentially initiating more writes on the + * transport) are executed on the t->combiner. + * + * The reason for scheduling on finally_scheduler is to make sure we batch + * as many writes as possible. 'write_action_begin_locked' is the function + * that gathers all the relevant bytes (which are at various places in the + * grpc_chttp2_transport structure) and append them to 'outbuf' field in + * grpc_chttp2_transport thereby batching what would have been potentially + * multiple write operations. + * + * Also, 'write_action_begin_locked' only gathers the bytes into outbuf. + * It does not call the endpoint to write the bytes. That is done by the + * 'write_action' (which is scheduled by 'write_action_begin_locked') */ + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&t->write_action_begin_locked, + write_action_begin_locked, t, + grpc_combiner_finally_scheduler(t->combiner)), + GRPC_ERROR_NONE); + break; + case GRPC_CHTTP2_WRITE_STATE_WRITING: + set_write_state(t, GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE, + grpc_chttp2_initiate_write_reason_string(reason)); + break; + case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: + break; + } +} + +void grpc_chttp2_mark_stream_writable(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + if (t->closed_with_error == GRPC_ERROR_NONE && + grpc_chttp2_list_add_writable_stream(t, s)) { + GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:become"); + } +} + +static grpc_closure_scheduler* write_scheduler(grpc_chttp2_transport* t, + bool early_results_scheduled, + bool partial_write) { + /* if it's not the first write in a batch, always offload to the executor: + we'll probably end up queuing against the kernel anyway, so we'll likely + get better latency overall if we switch writing work elsewhere and continue + with application work above */ + if (!t->is_first_write_in_batch) { + return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + } + /* equivalently, if it's a partial write, we *know* we're going to be taking a + thread jump to write it because of the above, may as well do so + immediately */ + if (partial_write) { + return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + } + switch (t->opt_target) { + case GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT: + /* executor gives us the largest probability of being able to batch a + * write with others on this transport */ + return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + case GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY: + return grpc_schedule_on_exec_ctx; + } + GPR_UNREACHABLE_CODE(return nullptr); +} + +#define WRITE_STATE_TUPLE_TO_INT(p, i) (2 * (int)(p) + (int)(i)) +static const char* begin_writing_desc(bool partial, bool inlined) { + switch (WRITE_STATE_TUPLE_TO_INT(partial, inlined)) { + case WRITE_STATE_TUPLE_TO_INT(false, false): + return "begin write in background"; + case WRITE_STATE_TUPLE_TO_INT(false, true): + return "begin write in current thread"; + case WRITE_STATE_TUPLE_TO_INT(true, false): + return "begin partial write in background"; + case WRITE_STATE_TUPLE_TO_INT(true, true): + return "begin partial write in current thread"; + } + GPR_UNREACHABLE_CODE(return "bad state tuple"); +} + +static void write_action_begin_locked(void* gt, grpc_error* error_ignored) { + GPR_TIMER_SCOPE("write_action_begin_locked", 0); + grpc_chttp2_transport* t = static_cast(gt); + GPR_ASSERT(t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE); + grpc_chttp2_begin_write_result r; + if (t->closed_with_error != GRPC_ERROR_NONE) { + r.writing = false; + } else { + r = grpc_chttp2_begin_write(t); + } + if (r.writing) { + if (r.partial) { + GRPC_STATS_INC_HTTP2_PARTIAL_WRITES(); + } + if (!t->is_first_write_in_batch) { + GRPC_STATS_INC_HTTP2_WRITES_CONTINUED(); + } + grpc_closure_scheduler* scheduler = + write_scheduler(t, r.early_results_scheduled, r.partial); + if (scheduler != grpc_schedule_on_exec_ctx) { + GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED(); + } + set_write_state( + t, + r.partial ? GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE + : GRPC_CHTTP2_WRITE_STATE_WRITING, + begin_writing_desc(r.partial, scheduler == grpc_schedule_on_exec_ctx)); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&t->write_action, write_action, t, scheduler), + GRPC_ERROR_NONE); + } else { + GRPC_STATS_INC_HTTP2_SPURIOUS_WRITES_BEGUN(); + set_write_state(t, GRPC_CHTTP2_WRITE_STATE_IDLE, "begin writing nothing"); + GRPC_CHTTP2_UNREF_TRANSPORT(t, "writing"); + } +} + +static void write_action(void* gt, grpc_error* error) { + GPR_TIMER_SCOPE("write_action", 0); + grpc_chttp2_transport* t = static_cast(gt); + void* cl = t->cl; + t->cl = nullptr; + grpc_endpoint_write( + t->ep, &t->outbuf, + GRPC_CLOSURE_INIT(&t->write_action_end_locked, write_action_end_locked, t, + grpc_combiner_scheduler(t->combiner)), + cl); +} + +/* Callback from the grpc_endpoint after bytes have been written by calling + * sendmsg */ +static void write_action_end_locked(void* tp, grpc_error* error) { + GPR_TIMER_SCOPE("terminate_writing_with_lock", 0); + grpc_chttp2_transport* t = static_cast(tp); + + if (error != GRPC_ERROR_NONE) { + close_transport_locked(t, GRPC_ERROR_REF(error)); + } + + if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED) { + t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SENT; + if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + close_transport_locked( + t, GRPC_ERROR_CREATE_FROM_STATIC_STRING("goaway sent")); + } + } + + switch (t->write_state) { + case GRPC_CHTTP2_WRITE_STATE_IDLE: + GPR_UNREACHABLE_CODE(break); + case GRPC_CHTTP2_WRITE_STATE_WRITING: + GPR_TIMER_MARK("state=writing", 0); + set_write_state(t, GRPC_CHTTP2_WRITE_STATE_IDLE, "finish writing"); + break; + case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: + GPR_TIMER_MARK("state=writing_stale_no_poller", 0); + set_write_state(t, GRPC_CHTTP2_WRITE_STATE_WRITING, "continue writing"); + t->is_first_write_in_batch = false; + GRPC_CHTTP2_REF_TRANSPORT(t, "writing"); + GRPC_CLOSURE_RUN( + GRPC_CLOSURE_INIT(&t->write_action_begin_locked, + write_action_begin_locked, t, + grpc_combiner_finally_scheduler(t->combiner)), + GRPC_ERROR_NONE); + break; + } + + grpc_chttp2_end_write(t, GRPC_ERROR_REF(error)); + + GRPC_CHTTP2_UNREF_TRANSPORT(t, "writing"); +} + +// Dirties an HTTP2 setting to be sent out next time a writing path occurs. +// If the change needs to occur immediately, manually initiate a write. +static void queue_setting_update(grpc_chttp2_transport* t, + grpc_chttp2_setting_id id, uint32_t value) { + const grpc_chttp2_setting_parameters* sp = + &grpc_chttp2_settings_parameters[id]; + uint32_t 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->settings[GRPC_LOCAL_SETTINGS][id]) { + t->settings[GRPC_LOCAL_SETTINGS][id] = use_value; + t->dirtied_local_settings = 1; + } +} + +void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t, + uint32_t goaway_error, + grpc_slice goaway_text) { + // GRPC_CHTTP2_IF_TRACING( + // gpr_log(GPR_INFO, "got goaway [%d]: %s", goaway_error, msg)); + + // Discard the error from a previous goaway frame (if any) + if (t->goaway_error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(t->goaway_error); + } + t->goaway_error = grpc_error_set_str( + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("GOAWAY received"), + GRPC_ERROR_INT_HTTP2_ERROR, static_cast(goaway_error)), + GRPC_ERROR_STR_RAW_BYTES, goaway_text); + + /* When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug + * data equal to "too_many_pings", it should log the occurrence at a log level + * that is enabled by default and double the configured KEEPALIVE_TIME used + * for new connections on that channel. */ + if (GPR_UNLIKELY(t->is_client && + goaway_error == GRPC_HTTP2_ENHANCE_YOUR_CALM && + grpc_slice_str_cmp(goaway_text, "too_many_pings") == 0)) { + gpr_log(GPR_ERROR, + "Received a GOAWAY with error code ENHANCE_YOUR_CALM and debug " + "data equal to \"too_many_pings\""); + double current_keepalive_time_ms = static_cast(t->keepalive_time); + t->keepalive_time = + current_keepalive_time_ms > INT_MAX / KEEPALIVE_TIME_BACKOFF_MULTIPLIER + ? GRPC_MILLIS_INF_FUTURE + : static_cast(current_keepalive_time_ms * + KEEPALIVE_TIME_BACKOFF_MULTIPLIER); + } + + /* lie: use transient failure from the transport to indicate goaway has been + * received */ + connectivity_state_set(t, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_REF(t->goaway_error), "got_goaway"); +} + +static void maybe_start_some_streams(grpc_chttp2_transport* t) { + grpc_chttp2_stream* s; + /* start streams where we have free grpc_chttp2_stream ids and free + * concurrency */ + while (t->next_stream_id <= MAX_CLIENT_STREAM_ID && + grpc_chttp2_stream_map_size(&t->stream_map) < + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] && + grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) { + /* safe since we can't (legally) be parsing this stream yet */ + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_INFO, "HTTP:%s: Allocating new grpc_chttp2_stream %p to id %d", + t->is_client ? "CLI" : "SVR", s, t->next_stream_id)); + + GPR_ASSERT(s->id == 0); + s->id = t->next_stream_id; + t->next_stream_id += 2; + + if (t->next_stream_id >= MAX_CLIENT_STREAM_ID) { + connectivity_state_set( + t, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"), + "no_more_stream_ids"); + } + + grpc_chttp2_stream_map_add(&t->stream_map, s->id, s); + post_destructive_reclaimer(t); + grpc_chttp2_mark_stream_writable(t, s); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM); + } + /* cancel out streams that will never be started */ + while (t->next_stream_id >= MAX_CLIENT_STREAM_ID && + grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) { + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } +} + +/* Flag that this closure barrier may be covering a write in a pollset, and so + we should not complete this closure until we can prove that the write got + scheduled */ +#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 0) +/* First bit of the reference count, stored in the high order bits (with the low + bits being used for flags defined above) */ +#define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16) + +static grpc_closure* add_closure_barrier(grpc_closure* closure) { + closure->next_data.scratch += CLOSURE_BARRIER_FIRST_REF_BIT; + return closure; +} + +static void null_then_run_closure(grpc_closure** closure, grpc_error* error) { + grpc_closure* c = *closure; + *closure = nullptr; + GRPC_CLOSURE_RUN(c, error); +} + +void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_closure** pclosure, + grpc_error* error, const char* desc) { + grpc_closure* closure = *pclosure; + *pclosure = nullptr; + if (closure == nullptr) { + GRPC_ERROR_UNREF(error); + return; + } + closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT; + if (grpc_http_trace.enabled()) { + const char* errstr = grpc_error_string(error); + gpr_log( + GPR_INFO, + "complete_closure_step: t=%p %p refs=%d flags=0x%04x desc=%s err=%s " + "write_state=%s", + t, closure, + static_cast(closure->next_data.scratch / + CLOSURE_BARRIER_FIRST_REF_BIT), + static_cast(closure->next_data.scratch % + CLOSURE_BARRIER_FIRST_REF_BIT), + desc, errstr, write_state_name(t->write_state)); + } + if (error != GRPC_ERROR_NONE) { + if (closure->error_data.error == GRPC_ERROR_NONE) { + closure->error_data.error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Error in HTTP transport completing operation"); + closure->error_data.error = grpc_error_set_str( + closure->error_data.error, GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(t->peer_string)); + } + closure->error_data.error = + grpc_error_add_child(closure->error_data.error, error); + } + if (closure->next_data.scratch < CLOSURE_BARRIER_FIRST_REF_BIT) { + if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) || + !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) { + GRPC_CLOSURE_RUN(closure, closure->error_data.error); + } else { + grpc_closure_list_append(&t->run_after_write, closure, + closure->error_data.error); + } + } +} + +static bool contains_non_ok_status(grpc_metadata_batch* batch) { + if (batch->idx.named.grpc_status != nullptr) { + return !grpc_mdelem_eq(batch->idx.named.grpc_status->md, + GRPC_MDELEM_GRPC_STATUS_0); + } + return false; +} + +static void maybe_become_writable_due_to_send_msg(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + if (s->id != 0 && (!s->write_buffering || + s->flow_controlled_buffer.length > t->write_buffer_size)) { + grpc_chttp2_mark_stream_writable(t, s); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE); + } +} + +static void add_fetched_slice_locked(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + s->fetched_send_message_length += + static_cast GRPC_SLICE_LENGTH(s->fetching_slice); + grpc_slice_buffer_add(&s->flow_controlled_buffer, s->fetching_slice); + maybe_become_writable_due_to_send_msg(t, s); +} + +static void continue_fetching_send_locked(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + for (;;) { + if (s->fetching_send_message == nullptr) { + /* Stream was cancelled before message fetch completed */ + abort(); /* TODO(ctiller): what cleanup here? */ + return; /* early out */ + } + if (s->fetched_send_message_length == s->fetching_send_message->length()) { + int64_t notify_offset = s->next_message_end_offset; + if (notify_offset <= s->flow_controlled_bytes_written) { + grpc_chttp2_complete_closure_step( + t, s, &s->fetching_send_message_finished, GRPC_ERROR_NONE, + "fetching_send_message_finished"); + } else { + grpc_chttp2_write_cb* cb = t->write_cb_pool; + if (cb == nullptr) { + cb = static_cast(gpr_malloc(sizeof(*cb))); + } else { + t->write_cb_pool = cb->next; + } + cb->call_at_byte = notify_offset; + cb->closure = s->fetching_send_message_finished; + s->fetching_send_message_finished = nullptr; + grpc_chttp2_write_cb** list = + s->fetching_send_message->flags() & GRPC_WRITE_THROUGH + ? &s->on_write_finished_cbs + : &s->on_flow_controlled_cbs; + cb->next = *list; + *list = cb; + } + s->fetching_send_message.reset(); + return; /* early out */ + } else if (s->fetching_send_message->Next(UINT32_MAX, + &s->complete_fetch_locked)) { + grpc_error* error = s->fetching_send_message->Pull(&s->fetching_slice); + if (error != GRPC_ERROR_NONE) { + s->fetching_send_message.reset(); + grpc_chttp2_cancel_stream(t, s, error); + } else { + add_fetched_slice_locked(t, s); + } + } + } +} + +static void complete_fetch_locked(void* gs, grpc_error* error) { + grpc_chttp2_stream* s = static_cast(gs); + grpc_chttp2_transport* t = s->t; + if (error == GRPC_ERROR_NONE) { + error = s->fetching_send_message->Pull(&s->fetching_slice); + if (error == GRPC_ERROR_NONE) { + add_fetched_slice_locked(t, s); + continue_fetching_send_locked(t, s); + } + } + if (error != GRPC_ERROR_NONE) { + s->fetching_send_message.reset(); + grpc_chttp2_cancel_stream(t, s, error); + } +} + +static void do_nothing(void* arg, grpc_error* error) {} + +static void log_metadata(const grpc_metadata_batch* md_batch, uint32_t id, + bool is_client, bool is_initial) { + for (grpc_linked_mdelem* md = md_batch->list.head; md != nullptr; + md = md->next) { + char* key = grpc_slice_to_c_string(GRPC_MDKEY(md->md)); + char* value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md)); + gpr_log(GPR_INFO, "HTTP:%d:%s:%s: %s: %s", id, is_initial ? "HDR" : "TRL", + is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } +} + +static void perform_stream_op_locked(void* stream_op, + grpc_error* error_ignored) { + GPR_TIMER_SCOPE("perform_stream_op_locked", 0); + + grpc_transport_stream_op_batch* op = + static_cast(stream_op); + grpc_chttp2_stream* s = + static_cast(op->handler_private.extra_arg); + grpc_transport_stream_op_batch_payload* op_payload = op->payload; + grpc_chttp2_transport* t = s->t; + + GRPC_STATS_INC_HTTP2_OP_BATCHES(); + + s->context = op->payload->context; + s->traced = op->is_traced; + if (grpc_http_trace.enabled()) { + char* str = grpc_transport_stream_op_batch_string(op); + gpr_log(GPR_INFO, "perform_stream_op_locked: %s; on_complete = %p", str, + op->on_complete); + gpr_free(str); + if (op->send_initial_metadata) { + log_metadata(op_payload->send_initial_metadata.send_initial_metadata, + s->id, t->is_client, true); + } + if (op->send_trailing_metadata) { + log_metadata(op_payload->send_trailing_metadata.send_trailing_metadata, + s->id, t->is_client, false); + } + } + + grpc_closure* on_complete = op->on_complete; + // TODO(roth): This is a hack needed because we use data inside of the + // closure itself to do the barrier calculation (i.e., to ensure that + // we don't schedule the closure until all ops in the batch have been + // completed). This can go away once we move to a new C++ closure API + // that provides the ability to create a barrier closure. + if (on_complete == nullptr) { + on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing, + nullptr, grpc_schedule_on_exec_ctx); + } + + /* use final_data as a barrier until enqueue time; the inital counter is + dropped at the end of this function */ + on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT; + on_complete->error_data.error = GRPC_ERROR_NONE; + + if (op->cancel_stream) { + GRPC_STATS_INC_HTTP2_OP_CANCEL(); + grpc_chttp2_cancel_stream(t, s, op_payload->cancel_stream.cancel_error); + } + + if (op->send_initial_metadata) { + if (t->is_client && t->channelz_socket != nullptr) { + t->channelz_socket->RecordStreamStartedFromLocal(); + } + GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA(); + GPR_ASSERT(s->send_initial_metadata_finished == nullptr); + on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; + + /* Identify stream compression */ + if (op_payload->send_initial_metadata.send_initial_metadata->idx.named + .content_encoding == nullptr || + grpc_stream_compression_method_parse( + GRPC_MDVALUE( + op_payload->send_initial_metadata.send_initial_metadata->idx + .named.content_encoding->md), + true, &s->stream_compression_method) == 0) { + s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS; + } + + s->send_initial_metadata_finished = add_closure_barrier(on_complete); + s->send_initial_metadata = + op_payload->send_initial_metadata.send_initial_metadata; + const size_t metadata_size = + grpc_metadata_batch_size(s->send_initial_metadata); + const size_t metadata_peer_limit = + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (t->is_client) { + s->deadline = GPR_MIN(s->deadline, s->send_initial_metadata->deadline); + } + if (metadata_size > metadata_peer_limit) { + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int( + grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "to-be-sent initial metadata size " + "exceeds peer limit"), + GRPC_ERROR_INT_SIZE, + static_cast(metadata_size)), + GRPC_ERROR_INT_LIMIT, + static_cast(metadata_peer_limit)), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); + } else { + if (contains_non_ok_status(s->send_initial_metadata)) { + s->seen_error = true; + } + if (!s->write_closed) { + if (t->is_client) { + if (t->closed_with_error == GRPC_ERROR_NONE) { + GPR_ASSERT(s->id == 0); + grpc_chttp2_list_add_waiting_for_concurrency(t, s); + maybe_start_some_streams(t); + } else { + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int( + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Transport closed", &t->closed_with_error, 1), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } + } else { + GPR_ASSERT(s->id != 0); + grpc_chttp2_mark_stream_writable(t, s); + if (!(op->send_message && + (op->payload->send_message.send_message->flags() & + GRPC_WRITE_BUFFER_HINT))) { + grpc_chttp2_initiate_write( + t, GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA); + } + } + } else { + s->send_initial_metadata = nullptr; + grpc_chttp2_complete_closure_step( + t, s, &s->send_initial_metadata_finished, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Attempt to send initial metadata after stream was closed", + &s->write_closed_error, 1), + "send_initial_metadata_finished"); + } + } + if (op_payload->send_initial_metadata.peer_string != nullptr) { + gpr_atm_rel_store(op_payload->send_initial_metadata.peer_string, + (gpr_atm)t->peer_string); + } + } + + if (op->send_message) { + GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE(); + t->num_messages_in_next_write++; + GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE( + op->payload->send_message.send_message->length()); + on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; + s->fetching_send_message_finished = add_closure_barrier(op->on_complete); + if (s->write_closed) { + // Return an error unless the client has already received trailing + // metadata from the server, since an application using a + // streaming call might send another message before getting a + // recv_message failure, breaking out of its loop, and then + // starting recv_trailing_metadata. + op->payload->send_message.send_message.reset(); + grpc_chttp2_complete_closure_step( + t, s, &s->fetching_send_message_finished, + t->is_client && s->received_trailing_metadata + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Attempt to send message after stream was closed", + &s->write_closed_error, 1), + "fetching_send_message_finished"); + } else { + GPR_ASSERT(s->fetching_send_message == nullptr); + uint8_t* frame_hdr = grpc_slice_buffer_tiny_add( + &s->flow_controlled_buffer, GRPC_HEADER_SIZE_IN_BYTES); + uint32_t flags = op_payload->send_message.send_message->flags(); + frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0; + size_t len = op_payload->send_message.send_message->length(); + frame_hdr[1] = static_cast(len >> 24); + frame_hdr[2] = static_cast(len >> 16); + frame_hdr[3] = static_cast(len >> 8); + frame_hdr[4] = static_cast(len); + s->fetching_send_message = + std::move(op_payload->send_message.send_message); + s->fetched_send_message_length = 0; + s->next_message_end_offset = + s->flow_controlled_bytes_written + + static_cast(s->flow_controlled_buffer.length) + + static_cast(len); + if (flags & GRPC_WRITE_BUFFER_HINT) { + s->next_message_end_offset -= t->write_buffer_size; + s->write_buffering = true; + } else { + s->write_buffering = false; + } + continue_fetching_send_locked(t, s); + maybe_become_writable_due_to_send_msg(t, s); + } + } + + if (op->send_trailing_metadata) { + GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA(); + GPR_ASSERT(s->send_trailing_metadata_finished == nullptr); + on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; + s->send_trailing_metadata_finished = add_closure_barrier(on_complete); + s->send_trailing_metadata = + op_payload->send_trailing_metadata.send_trailing_metadata; + s->write_buffering = false; + const size_t metadata_size = + grpc_metadata_batch_size(s->send_trailing_metadata); + const size_t metadata_peer_limit = + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (metadata_size > metadata_peer_limit) { + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int( + grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "to-be-sent trailing metadata size " + "exceeds peer limit"), + GRPC_ERROR_INT_SIZE, + static_cast(metadata_size)), + GRPC_ERROR_INT_LIMIT, + static_cast(metadata_peer_limit)), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); + } else { + if (contains_non_ok_status(s->send_trailing_metadata)) { + s->seen_error = true; + } + if (s->write_closed) { + s->send_trailing_metadata = nullptr; + grpc_chttp2_complete_closure_step( + t, s, &s->send_trailing_metadata_finished, + grpc_metadata_batch_is_empty( + op->payload->send_trailing_metadata.send_trailing_metadata) + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Attempt to send trailing metadata after " + "stream was closed"), + "send_trailing_metadata_finished"); + } else if (s->id != 0) { + /* TODO(ctiller): check if there's flow control for any outstanding + bytes before going writable */ + grpc_chttp2_mark_stream_writable(t, s); + grpc_chttp2_initiate_write( + t, GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA); + } + } + } + + if (op->recv_initial_metadata) { + GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA(); + GPR_ASSERT(s->recv_initial_metadata_ready == nullptr); + s->recv_initial_metadata_ready = + op_payload->recv_initial_metadata.recv_initial_metadata_ready; + s->recv_initial_metadata = + op_payload->recv_initial_metadata.recv_initial_metadata; + s->trailing_metadata_available = + op_payload->recv_initial_metadata.trailing_metadata_available; + if (op_payload->recv_initial_metadata.peer_string != nullptr) { + gpr_atm_rel_store(op_payload->recv_initial_metadata.peer_string, + (gpr_atm)t->peer_string); + } + grpc_chttp2_maybe_complete_recv_initial_metadata(t, s); + } + + if (op->recv_message) { + GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE(); + size_t before = 0; + GPR_ASSERT(s->recv_message_ready == nullptr); + GPR_ASSERT(!s->pending_byte_stream); + s->recv_message_ready = op_payload->recv_message.recv_message_ready; + s->recv_message = op_payload->recv_message.recv_message; + if (s->id != 0) { + if (!s->read_closed) { + before = s->frame_storage.length + + s->unprocessed_incoming_frames_buffer.length; + } + } + grpc_chttp2_maybe_complete_recv_message(t, s); + if (s->id != 0) { + if (!s->read_closed && s->frame_storage.length == 0) { + size_t after = s->frame_storage.length + + s->unprocessed_incoming_frames_buffer_cached_length; + s->flow_control->IncomingByteStreamUpdate(GRPC_HEADER_SIZE_IN_BYTES, + before - after); + grpc_chttp2_act_on_flowctl_action(s->flow_control->MakeAction(), t, s); + } + } + } + + if (op->recv_trailing_metadata) { + GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA(); + GPR_ASSERT(s->collecting_stats == nullptr); + s->collecting_stats = op_payload->recv_trailing_metadata.collect_stats; + GPR_ASSERT(s->recv_trailing_metadata_finished == nullptr); + s->recv_trailing_metadata_finished = + op_payload->recv_trailing_metadata.recv_trailing_metadata_ready; + s->recv_trailing_metadata = + op_payload->recv_trailing_metadata.recv_trailing_metadata; + s->final_metadata_requested = true; + grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s); + } + + grpc_chttp2_complete_closure_step(t, s, &on_complete, GRPC_ERROR_NONE, + "op->on_complete"); + + GRPC_CHTTP2_STREAM_UNREF(s, "perform_stream_op"); +} + +static void perform_stream_op(grpc_transport* gt, grpc_stream* gs, + grpc_transport_stream_op_batch* op) { + GPR_TIMER_SCOPE("perform_stream_op", 0); + grpc_chttp2_transport* t = reinterpret_cast(gt); + grpc_chttp2_stream* s = reinterpret_cast(gs); + + if (!t->is_client) { + if (op->send_initial_metadata) { + grpc_millis deadline = + op->payload->send_initial_metadata.send_initial_metadata->deadline; + GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE); + } + if (op->send_trailing_metadata) { + grpc_millis deadline = + op->payload->send_trailing_metadata.send_trailing_metadata->deadline; + GPR_ASSERT(deadline == GRPC_MILLIS_INF_FUTURE); + } + } + + if (grpc_http_trace.enabled()) { + char* str = grpc_transport_stream_op_batch_string(op); + gpr_log(GPR_INFO, "perform_stream_op[s=%p]: %s", s, str); + gpr_free(str); + } + + GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op"); + op->handler_private.extra_arg = gs; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&op->handler_private.closure, perform_stream_op_locked, + op, grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); +} + +static void cancel_pings(grpc_chttp2_transport* t, grpc_error* error) { + /* callback remaining pings: they're not allowed to call into the transpot, + and maybe they hold resources that need to be freed */ + grpc_chttp2_ping_queue* pq = &t->ping_queue; + GPR_ASSERT(error != GRPC_ERROR_NONE); + for (size_t j = 0; j < GRPC_CHTTP2_PCL_COUNT; j++) { + grpc_closure_list_fail_all(&pq->lists[j], GRPC_ERROR_REF(error)); + GRPC_CLOSURE_LIST_SCHED(&pq->lists[j]); + } + GRPC_ERROR_UNREF(error); +} + +static void send_ping_locked(grpc_chttp2_transport* t, + grpc_closure* on_initiate, grpc_closure* on_ack) { + if (t->closed_with_error != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(on_initiate, GRPC_ERROR_REF(t->closed_with_error)); + GRPC_CLOSURE_SCHED(on_ack, GRPC_ERROR_REF(t->closed_with_error)); + return; + } + grpc_chttp2_ping_queue* pq = &t->ping_queue; + grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INITIATE], on_initiate, + GRPC_ERROR_NONE); + grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_NEXT], on_ack, + GRPC_ERROR_NONE); +} + +/* + * Specialized form of send_ping_locked for keepalive ping. If there is already + * a ping in progress, the keepalive ping would piggyback onto that ping, + * instead of waiting for that ping to complete and then starting a new ping. + */ +static void send_keepalive_ping_locked(grpc_chttp2_transport* t) { + if (t->closed_with_error != GRPC_ERROR_NONE) { + GRPC_CLOSURE_RUN(&t->start_keepalive_ping_locked, + GRPC_ERROR_REF(t->closed_with_error)); + GRPC_CLOSURE_RUN(&t->finish_keepalive_ping_locked, + GRPC_ERROR_REF(t->closed_with_error)); + return; + } + grpc_chttp2_ping_queue* pq = &t->ping_queue; + if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) { + /* There is a ping in flight. Add yourself to the inflight closure list. */ + GRPC_CLOSURE_RUN(&t->start_keepalive_ping_locked, GRPC_ERROR_NONE); + grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INFLIGHT], + &t->finish_keepalive_ping_locked, GRPC_ERROR_NONE); + return; + } + grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INITIATE], + &t->start_keepalive_ping_locked, GRPC_ERROR_NONE); + grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_NEXT], + &t->finish_keepalive_ping_locked, GRPC_ERROR_NONE); +} + +static void retry_initiate_ping_locked(void* tp, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(tp); + t->ping_state.is_delayed_ping_timer_set = false; + if (error == GRPC_ERROR_NONE) { + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING); + } + GRPC_CHTTP2_UNREF_TRANSPORT(t, "retry_initiate_ping_locked"); +} + +void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id) { + grpc_chttp2_ping_queue* pq = &t->ping_queue; + if (pq->inflight_id != id) { + char* from = grpc_endpoint_get_peer(t->ep); + gpr_log(GPR_DEBUG, "Unknown ping response from %s: %" PRIx64, from, id); + gpr_free(from); + return; + } + GRPC_CLOSURE_LIST_SCHED(&pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]); + if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) { + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS); + } +} + +static void send_goaway(grpc_chttp2_transport* t, grpc_error* error) { + t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED; + grpc_http2_error_code http_error; + grpc_slice slice; + grpc_error_get_status(error, GRPC_MILLIS_INF_FUTURE, nullptr, &slice, + &http_error, nullptr); + grpc_chttp2_goaway_append(t->last_new_stream_id, + static_cast(http_error), + grpc_slice_ref_internal(slice), &t->qbuf); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT); + GRPC_ERROR_UNREF(error); +} + +void grpc_chttp2_add_ping_strike(grpc_chttp2_transport* t) { + if (++t->ping_recv_state.ping_strikes > t->ping_policy.max_ping_strikes && + t->ping_policy.max_ping_strikes != 0) { + send_goaway(t, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("too_many_pings"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM)); + /*The transport will be closed after the write is done */ + close_transport_locked( + t, grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many pings"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } +} + +static void perform_transport_op_locked(void* stream_op, + grpc_error* error_ignored) { + grpc_transport_op* op = static_cast(stream_op); + grpc_chttp2_transport* t = + static_cast(op->handler_private.extra_arg); + + if (op->goaway_error) { + send_goaway(t, op->goaway_error); + } + + if (op->set_accept_stream) { + t->channel_callback.accept_stream = op->set_accept_stream_fn; + t->channel_callback.accept_stream_user_data = + op->set_accept_stream_user_data; + } + + if (op->bind_pollset) { + grpc_endpoint_add_to_pollset(t->ep, op->bind_pollset); + } + + if (op->bind_pollset_set) { + grpc_endpoint_add_to_pollset_set(t->ep, op->bind_pollset_set); + } + + if (op->send_ping.on_initiate != nullptr || op->send_ping.on_ack != nullptr) { + send_ping_locked(t, op->send_ping.on_initiate, op->send_ping.on_ack); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING); + } + + if (op->on_connectivity_state_change != nullptr) { + grpc_connectivity_state_notify_on_state_change( + &t->channel_callback.state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + } + + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + close_transport_locked(t, op->disconnect_with_error); + } + + GRPC_CLOSURE_RUN(op->on_consumed, GRPC_ERROR_NONE); + + GRPC_CHTTP2_UNREF_TRANSPORT(t, "transport_op"); +} + +static void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) { + grpc_chttp2_transport* t = reinterpret_cast(gt); + if (grpc_http_trace.enabled()) { + char* msg = grpc_transport_op_string(op); + gpr_log(GPR_INFO, "perform_transport_op[t=%p]: %s", t, msg); + gpr_free(msg); + } + op->handler_private.extra_arg = gt; + GRPC_CHTTP2_REF_TRANSPORT(t, "transport_op"); + GRPC_CLOSURE_SCHED(GRPC_CLOSURE_INIT(&op->handler_private.closure, + perform_transport_op_locked, op, + grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); +} + +/******************************************************************************* + * INPUT PROCESSING - GENERAL + */ + +void grpc_chttp2_maybe_complete_recv_initial_metadata(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + if (s->recv_initial_metadata_ready != nullptr && + s->published_metadata[0] != GRPC_METADATA_NOT_PUBLISHED) { + if (s->seen_error) { + grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage); + if (!s->pending_byte_stream) { + grpc_slice_buffer_reset_and_unref_internal( + &s->unprocessed_incoming_frames_buffer); + } + } + grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[0], + s->recv_initial_metadata); + null_then_run_closure(&s->recv_initial_metadata_ready, GRPC_ERROR_NONE); + } +} + +void grpc_chttp2_maybe_complete_recv_message(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + grpc_error* error = GRPC_ERROR_NONE; + if (s->recv_message_ready != nullptr) { + *s->recv_message = nullptr; + if (s->final_metadata_requested && s->seen_error) { + grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage); + if (!s->pending_byte_stream) { + grpc_slice_buffer_reset_and_unref_internal( + &s->unprocessed_incoming_frames_buffer); + } + } + if (!s->pending_byte_stream) { + while (s->unprocessed_incoming_frames_buffer.length > 0 || + s->frame_storage.length > 0) { + if (s->unprocessed_incoming_frames_buffer.length == 0) { + grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer, + &s->frame_storage); + s->unprocessed_incoming_frames_decompressed = false; + } + if (!s->unprocessed_incoming_frames_decompressed && + s->stream_decompression_method != + GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS) { + GPR_ASSERT(s->decompressed_data_buffer.length == 0); + bool end_of_context; + if (!s->stream_decompression_ctx) { + s->stream_decompression_ctx = + grpc_stream_compression_context_create( + s->stream_decompression_method); + } + if (!grpc_stream_decompress( + s->stream_decompression_ctx, + &s->unprocessed_incoming_frames_buffer, + &s->decompressed_data_buffer, nullptr, + GRPC_HEADER_SIZE_IN_BYTES - s->decompressed_header_bytes, + &end_of_context)) { + grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage); + grpc_slice_buffer_reset_and_unref_internal( + &s->unprocessed_incoming_frames_buffer); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Stream decompression error."); + } else { + s->decompressed_header_bytes += s->decompressed_data_buffer.length; + if (s->decompressed_header_bytes == GRPC_HEADER_SIZE_IN_BYTES) { + s->decompressed_header_bytes = 0; + } + error = grpc_deframe_unprocessed_incoming_frames( + &s->data_parser, s, &s->decompressed_data_buffer, nullptr, + s->recv_message); + if (end_of_context) { + grpc_stream_compression_context_destroy( + s->stream_decompression_ctx); + s->stream_decompression_ctx = nullptr; + } + } + } else { + error = grpc_deframe_unprocessed_incoming_frames( + &s->data_parser, s, &s->unprocessed_incoming_frames_buffer, + nullptr, s->recv_message); + } + if (error != GRPC_ERROR_NONE) { + s->seen_error = true; + grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage); + grpc_slice_buffer_reset_and_unref_internal( + &s->unprocessed_incoming_frames_buffer); + break; + } else if (*s->recv_message != nullptr) { + break; + } + } + } + // save the length of the buffer before handing control back to application + // threads. Needed to support correct flow control bookkeeping + s->unprocessed_incoming_frames_buffer_cached_length = + s->unprocessed_incoming_frames_buffer.length; + if (error == GRPC_ERROR_NONE && *s->recv_message != nullptr) { + null_then_run_closure(&s->recv_message_ready, GRPC_ERROR_NONE); + } else if (s->published_metadata[1] != GRPC_METADATA_NOT_PUBLISHED) { + *s->recv_message = nullptr; + null_then_run_closure(&s->recv_message_ready, GRPC_ERROR_NONE); + } + GRPC_ERROR_UNREF(error); + } +} + +void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + grpc_chttp2_maybe_complete_recv_message(t, s); + if (s->recv_trailing_metadata_finished != nullptr && s->read_closed && + s->write_closed) { + if (s->seen_error || !t->is_client) { + grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage); + if (!s->pending_byte_stream) { + grpc_slice_buffer_reset_and_unref_internal( + &s->unprocessed_incoming_frames_buffer); + } + } + bool pending_data = s->pending_byte_stream || + s->unprocessed_incoming_frames_buffer.length > 0; + if (s->read_closed && s->frame_storage.length > 0 && !pending_data && + !s->seen_error && s->recv_trailing_metadata_finished != nullptr) { + /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and + * maybe decompress the next 5 bytes in the stream. */ + bool end_of_context; + if (!s->stream_decompression_ctx) { + s->stream_decompression_ctx = grpc_stream_compression_context_create( + s->stream_decompression_method); + } + if (!grpc_stream_decompress( + s->stream_decompression_ctx, &s->frame_storage, + &s->unprocessed_incoming_frames_buffer, nullptr, + GRPC_HEADER_SIZE_IN_BYTES, &end_of_context)) { + grpc_slice_buffer_reset_and_unref_internal(&s->frame_storage); + grpc_slice_buffer_reset_and_unref_internal( + &s->unprocessed_incoming_frames_buffer); + s->seen_error = true; + } else { + if (s->unprocessed_incoming_frames_buffer.length > 0) { + s->unprocessed_incoming_frames_decompressed = true; + pending_data = true; + } + if (end_of_context) { + grpc_stream_compression_context_destroy(s->stream_decompression_ctx); + s->stream_decompression_ctx = nullptr; + } + } + } + if (s->read_closed && s->frame_storage.length == 0 && !pending_data && + s->recv_trailing_metadata_finished != nullptr) { + grpc_transport_move_stats(&s->stats, s->collecting_stats); + s->collecting_stats = nullptr; + grpc_chttp2_incoming_metadata_buffer_publish(&s->metadata_buffer[1], + s->recv_trailing_metadata); + null_then_run_closure(&s->recv_trailing_metadata_finished, + GRPC_ERROR_NONE); + } + } +} + +static void remove_stream(grpc_chttp2_transport* t, uint32_t id, + grpc_error* error) { + grpc_chttp2_stream* s = static_cast( + grpc_chttp2_stream_map_delete(&t->stream_map, id)); + GPR_ASSERT(s); + if (t->incoming_stream == s) { + t->incoming_stream = nullptr; + grpc_chttp2_parsing_become_skip_parser(t); + } + if (s->pending_byte_stream) { + if (s->on_next != nullptr) { + grpc_core::Chttp2IncomingByteStream* bs = s->data_parser.parsing_frame; + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + } + bs->PublishError(error); + bs->Unref(); + s->data_parser.parsing_frame = nullptr; + } else { + GRPC_ERROR_UNREF(s->byte_stream_error); + s->byte_stream_error = GRPC_ERROR_REF(error); + } + } + + if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + post_benign_reclaimer(t); + if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SENT) { + close_transport_locked( + t, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Last stream closed after sending GOAWAY", &error, 1)); + } + } + if (grpc_chttp2_list_remove_writable_stream(t, s)) { + GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:remove_stream"); + } + + GRPC_ERROR_UNREF(error); + + maybe_start_some_streams(t); +} + +void grpc_chttp2_cancel_stream(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_error* due_to_error) { + if (!t->is_client && !s->sent_trailing_metadata && + grpc_error_has_clear_grpc_status(due_to_error)) { + close_from_api(t, s, due_to_error); + return; + } + + if (!s->read_closed || !s->write_closed) { + if (s->id != 0) { + grpc_http2_error_code http_error; + grpc_error_get_status(due_to_error, s->deadline, nullptr, nullptr, + &http_error, nullptr); + grpc_slice_buffer_add( + &t->qbuf, + grpc_chttp2_rst_stream_create( + s->id, static_cast(http_error), &s->stats.outgoing)); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM); + } + } + if (due_to_error != GRPC_ERROR_NONE && !s->seen_error) { + s->seen_error = true; + } + grpc_chttp2_mark_stream_closed(t, s, 1, 1, due_to_error); +} + +void grpc_chttp2_fake_status(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_error* error) { + grpc_status_code status; + grpc_slice slice; + grpc_error_get_status(error, s->deadline, &status, &slice, nullptr, nullptr); + if (status != GRPC_STATUS_OK) { + s->seen_error = true; + } + /* stream_global->recv_trailing_metadata_finished gives us a + last chance replacement: we've received trailing metadata, + but something more important has become available to signal + to the upper layers - drop what we've got, and then publish + what we want - which is safe because we haven't told anyone + about the metadata yet */ + if (s->published_metadata[1] == GRPC_METADATA_NOT_PUBLISHED || + s->recv_trailing_metadata_finished != nullptr) { + char status_string[GPR_LTOA_MIN_BUFSIZE]; + gpr_ltoa(status, status_string); + GRPC_LOG_IF_ERROR("add_status", + grpc_chttp2_incoming_metadata_buffer_replace_or_add( + &s->metadata_buffer[1], + grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_STATUS, + grpc_slice_from_copied_string(status_string)))); + if (!GRPC_SLICE_IS_EMPTY(slice)) { + GRPC_LOG_IF_ERROR( + "add_status_message", + grpc_chttp2_incoming_metadata_buffer_replace_or_add( + &s->metadata_buffer[1], + grpc_mdelem_create(GRPC_MDSTR_GRPC_MESSAGE, slice, nullptr))); + } + s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE; + grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s); + } + + GRPC_ERROR_UNREF(error); +} + +static void add_error(grpc_error* error, grpc_error** refs, size_t* nrefs) { + if (error == GRPC_ERROR_NONE) return; + for (size_t i = 0; i < *nrefs; i++) { + if (error == refs[i]) { + return; + } + } + refs[*nrefs] = error; + ++*nrefs; +} + +static grpc_error* removal_error(grpc_error* extra_error, grpc_chttp2_stream* s, + const char* master_error_msg) { + grpc_error* refs[3]; + size_t nrefs = 0; + add_error(s->read_closed_error, refs, &nrefs); + add_error(s->write_closed_error, refs, &nrefs); + add_error(extra_error, refs, &nrefs); + grpc_error* error = GRPC_ERROR_NONE; + if (nrefs > 0) { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(master_error_msg, + refs, nrefs); + } + GRPC_ERROR_UNREF(extra_error); + return error; +} + +static void flush_write_list(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_chttp2_write_cb** list, grpc_error* error) { + while (*list) { + grpc_chttp2_write_cb* cb = *list; + *list = cb->next; + grpc_chttp2_complete_closure_step(t, s, &cb->closure, GRPC_ERROR_REF(error), + "on_write_finished_cb"); + cb->next = t->write_cb_pool; + t->write_cb_pool = cb; + } + GRPC_ERROR_UNREF(error); +} + +void grpc_chttp2_fail_pending_writes(grpc_chttp2_transport* t, + grpc_chttp2_stream* s, grpc_error* error) { + error = + removal_error(error, s, "Pending writes failed due to stream closure"); + s->send_initial_metadata = nullptr; + grpc_chttp2_complete_closure_step(t, s, &s->send_initial_metadata_finished, + GRPC_ERROR_REF(error), + "send_initial_metadata_finished"); + + s->send_trailing_metadata = nullptr; + grpc_chttp2_complete_closure_step(t, s, &s->send_trailing_metadata_finished, + GRPC_ERROR_REF(error), + "send_trailing_metadata_finished"); + + s->fetching_send_message.reset(); + grpc_chttp2_complete_closure_step(t, s, &s->fetching_send_message_finished, + GRPC_ERROR_REF(error), + "fetching_send_message_finished"); + flush_write_list(t, s, &s->on_write_finished_cbs, GRPC_ERROR_REF(error)); + flush_write_list(t, s, &s->on_flow_controlled_cbs, error); +} + +void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t, + grpc_chttp2_stream* s, int close_reads, + int close_writes, grpc_error* error) { + if (s->read_closed && s->write_closed) { + /* already closed */ + grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s); + GRPC_ERROR_UNREF(error); + return; + } + bool closed_read = false; + bool became_closed = false; + if (close_reads && !s->read_closed) { + s->read_closed_error = GRPC_ERROR_REF(error); + s->read_closed = true; + closed_read = true; + } + if (close_writes && !s->write_closed) { + s->write_closed_error = GRPC_ERROR_REF(error); + s->write_closed = true; + grpc_chttp2_fail_pending_writes(t, s, GRPC_ERROR_REF(error)); + } + if (s->read_closed && s->write_closed) { + became_closed = true; + grpc_error* overall_error = + removal_error(GRPC_ERROR_REF(error), s, "Stream removed"); + if (s->id != 0) { + remove_stream(t, s->id, GRPC_ERROR_REF(overall_error)); + } else { + /* Purge streams waiting on concurrency still waiting for id assignment */ + grpc_chttp2_list_remove_waiting_for_concurrency(t, s); + } + if (overall_error != GRPC_ERROR_NONE) { + grpc_chttp2_fake_status(t, s, overall_error); + } + } + if (closed_read) { + for (int i = 0; i < 2; i++) { + if (s->published_metadata[i] == GRPC_METADATA_NOT_PUBLISHED) { + s->published_metadata[i] = GPRC_METADATA_PUBLISHED_AT_CLOSE; + } + } + grpc_chttp2_maybe_complete_recv_initial_metadata(t, s); + grpc_chttp2_maybe_complete_recv_message(t, s); + } + if (became_closed) { + grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s); + GRPC_CHTTP2_STREAM_UNREF(s, "chttp2"); + } + GRPC_ERROR_UNREF(error); +} + +static void close_from_api(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_error* error) { + grpc_slice hdr; + grpc_slice status_hdr; + grpc_slice http_status_hdr; + grpc_slice content_type_hdr; + grpc_slice message_pfx; + uint8_t* p; + uint32_t len = 0; + grpc_status_code grpc_status; + grpc_slice slice; + grpc_error_get_status(error, s->deadline, &grpc_status, &slice, nullptr, + nullptr); + + GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100); + + /* 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. */ + if (!s->sent_initial_metadata) { + http_status_hdr = GRPC_SLICE_MALLOC(13); + p = GRPC_SLICE_START_PTR(http_status_hdr); + *p++ = 0x00; + *p++ = 7; + *p++ = ':'; + *p++ = 's'; + *p++ = 't'; + *p++ = 'a'; + *p++ = 't'; + *p++ = 'u'; + *p++ = 's'; + *p++ = 3; + *p++ = '2'; + *p++ = '0'; + *p++ = '0'; + GPR_ASSERT(p == GRPC_SLICE_END_PTR(http_status_hdr)); + len += static_cast GRPC_SLICE_LENGTH(http_status_hdr); + + content_type_hdr = GRPC_SLICE_MALLOC(31); + p = GRPC_SLICE_START_PTR(content_type_hdr); + *p++ = 0x00; + *p++ = 12; + *p++ = 'c'; + *p++ = 'o'; + *p++ = 'n'; + *p++ = 't'; + *p++ = 'e'; + *p++ = 'n'; + *p++ = 't'; + *p++ = '-'; + *p++ = 't'; + *p++ = 'y'; + *p++ = 'p'; + *p++ = 'e'; + *p++ = 16; + *p++ = 'a'; + *p++ = 'p'; + *p++ = 'p'; + *p++ = 'l'; + *p++ = 'i'; + *p++ = 'c'; + *p++ = 'a'; + *p++ = 't'; + *p++ = 'i'; + *p++ = 'o'; + *p++ = 'n'; + *p++ = '/'; + *p++ = 'g'; + *p++ = 'r'; + *p++ = 'p'; + *p++ = 'c'; + GPR_ASSERT(p == GRPC_SLICE_END_PTR(content_type_hdr)); + len += static_cast GRPC_SLICE_LENGTH(content_type_hdr); + } + + status_hdr = GRPC_SLICE_MALLOC(15 + (grpc_status >= 10)); + p = GRPC_SLICE_START_PTR(status_hdr); + *p++ = 0x00; /* literal header, not indexed */ + *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 (grpc_status < 10) { + *p++ = 1; + *p++ = static_cast('0' + grpc_status); + } else { + *p++ = 2; + *p++ = static_cast('0' + (grpc_status / 10)); + *p++ = static_cast('0' + (grpc_status % 10)); + } + GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr)); + len += static_cast GRPC_SLICE_LENGTH(status_hdr); + + size_t msg_len = GRPC_SLICE_LENGTH(slice); + GPR_ASSERT(msg_len <= UINT32_MAX); + uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 1); + message_pfx = GRPC_SLICE_MALLOC(14 + msg_len_len); + p = GRPC_SLICE_START_PTR(message_pfx); + *p++ = 0x00; /* literal header, not indexed */ + *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'; + GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 1, 0, p, (uint32_t)msg_len_len); + p += msg_len_len; + GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx)); + len += static_cast GRPC_SLICE_LENGTH(message_pfx); + len += static_cast(msg_len); + + hdr = GRPC_SLICE_MALLOC(9); + p = GRPC_SLICE_START_PTR(hdr); + *p++ = static_cast(len >> 16); + *p++ = static_cast(len >> 8); + *p++ = static_cast(len); + *p++ = GRPC_CHTTP2_FRAME_HEADER; + *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; + *p++ = static_cast(s->id >> 24); + *p++ = static_cast(s->id >> 16); + *p++ = static_cast(s->id >> 8); + *p++ = static_cast(s->id); + GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr)); + + grpc_slice_buffer_add(&t->qbuf, hdr); + if (!s->sent_initial_metadata) { + grpc_slice_buffer_add(&t->qbuf, http_status_hdr); + grpc_slice_buffer_add(&t->qbuf, content_type_hdr); + } + grpc_slice_buffer_add(&t->qbuf, status_hdr); + grpc_slice_buffer_add(&t->qbuf, message_pfx); + grpc_slice_buffer_add(&t->qbuf, grpc_slice_ref_internal(slice)); + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR, + &s->stats.outgoing)); + + grpc_chttp2_mark_stream_closed(t, s, 1, 1, error); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API); +} + +typedef struct { + grpc_error* error; + grpc_chttp2_transport* t; +} cancel_stream_cb_args; + +static void cancel_stream_cb(void* user_data, uint32_t key, void* stream) { + cancel_stream_cb_args* args = static_cast(user_data); + grpc_chttp2_stream* s = static_cast(stream); + grpc_chttp2_cancel_stream(args->t, s, GRPC_ERROR_REF(args->error)); +} + +static void end_all_the_calls(grpc_chttp2_transport* t, grpc_error* error) { + cancel_stream_cb_args args = {error, t}; + grpc_chttp2_stream_map_for_each(&t->stream_map, cancel_stream_cb, &args); + GRPC_ERROR_UNREF(error); +} + +/******************************************************************************* + * INPUT PROCESSING - PARSING + */ + +template +static void WithUrgency(grpc_chttp2_transport* t, + grpc_core::chttp2::FlowControlAction::Urgency urgency, + grpc_chttp2_initiate_write_reason reason, F action) { + switch (urgency) { + case grpc_core::chttp2::FlowControlAction::Urgency::NO_ACTION_NEEDED: + break; + case grpc_core::chttp2::FlowControlAction::Urgency::UPDATE_IMMEDIATELY: + grpc_chttp2_initiate_write(t, reason); + // fallthrough + case grpc_core::chttp2::FlowControlAction::Urgency::QUEUE_UPDATE: + action(); + break; + } +} + +void grpc_chttp2_act_on_flowctl_action( + const grpc_core::chttp2::FlowControlAction& action, + grpc_chttp2_transport* t, grpc_chttp2_stream* s) { + WithUrgency(t, action.send_stream_update(), + GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL, + [t, s]() { grpc_chttp2_mark_stream_writable(t, s); }); + WithUrgency(t, action.send_transport_update(), + GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL, []() {}); + WithUrgency(t, action.send_initial_window_update(), + GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() { + queue_setting_update(t, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, + action.initial_window_size()); + }); + WithUrgency(t, action.send_max_frame_size_update(), + GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, [t, &action]() { + queue_setting_update(t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, + action.max_frame_size()); + }); +} + +static grpc_error* try_http_parsing(grpc_chttp2_transport* t) { + grpc_http_parser parser; + size_t i = 0; + grpc_error* error = GRPC_ERROR_NONE; + grpc_http_response response; + memset(&response, 0, sizeof(response)); + + grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response); + + grpc_error* parse_error = GRPC_ERROR_NONE; + for (; i < t->read_buffer.count && parse_error == GRPC_ERROR_NONE; i++) { + parse_error = + grpc_http_parser_parse(&parser, t->read_buffer.slices[i], nullptr); + } + if (parse_error == GRPC_ERROR_NONE && + (parse_error = grpc_http_parser_eof(&parser)) == GRPC_ERROR_NONE) { + error = grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Trying to connect an http1.x server"), + GRPC_ERROR_INT_HTTP_STATUS, response.status), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); + } + GRPC_ERROR_UNREF(parse_error); + + grpc_http_parser_destroy(&parser); + grpc_http_response_destroy(&response); + return error; +} + +static void read_action_locked(void* tp, grpc_error* error) { + GPR_TIMER_SCOPE("reading_action_locked", 0); + + grpc_chttp2_transport* t = static_cast(tp); + + GRPC_ERROR_REF(error); + + grpc_error* err = error; + if (err != GRPC_ERROR_NONE) { + err = grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Endpoint read failed", &err, 1), + GRPC_ERROR_INT_OCCURRED_DURING_WRITE, + t->write_state); + } + GPR_SWAP(grpc_error*, err, error); + GRPC_ERROR_UNREF(err); + if (t->closed_with_error == GRPC_ERROR_NONE) { + GPR_TIMER_SCOPE("reading_action.parse", 0); + size_t i = 0; + grpc_error* errors[3] = {GRPC_ERROR_REF(error), GRPC_ERROR_NONE, + GRPC_ERROR_NONE}; + for (; i < t->read_buffer.count && errors[1] == GRPC_ERROR_NONE; i++) { + grpc_core::BdpEstimator* bdp_est = t->flow_control->bdp_estimator(); + if (bdp_est) { + bdp_est->AddIncomingBytes( + static_cast GRPC_SLICE_LENGTH(t->read_buffer.slices[i])); + } + errors[1] = grpc_chttp2_perform_read(t, t->read_buffer.slices[i]); + } + if (errors[1] != GRPC_ERROR_NONE) { + errors[2] = try_http_parsing(t); + GRPC_ERROR_UNREF(error); + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed parsing HTTP/2", errors, GPR_ARRAY_SIZE(errors)); + } + for (i = 0; i < GPR_ARRAY_SIZE(errors); i++) { + GRPC_ERROR_UNREF(errors[i]); + } + + GPR_TIMER_SCOPE("post_parse_locked", 0); + if (t->initial_window_update != 0) { + if (t->initial_window_update > 0) { + grpc_chttp2_stream* s; + while (grpc_chttp2_list_pop_stalled_by_stream(t, &s)) { + grpc_chttp2_mark_stream_writable(t, s); + grpc_chttp2_initiate_write( + t, GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING); + } + } + t->initial_window_update = 0; + } + } + + GPR_TIMER_SCOPE("post_reading_action_locked", 0); + bool keep_reading = false; + if (error == GRPC_ERROR_NONE && t->closed_with_error != GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Transport closed", &t->closed_with_error, 1); + } + if (error != GRPC_ERROR_NONE) { + /* If a goaway frame was received, this might be the reason why the read + * failed. Add this info to the error */ + if (t->goaway_error != GRPC_ERROR_NONE) { + error = grpc_error_add_child(error, GRPC_ERROR_REF(t->goaway_error)); + } + + close_transport_locked(t, GRPC_ERROR_REF(error)); + t->endpoint_reading = 0; + } else if (t->closed_with_error == GRPC_ERROR_NONE) { + keep_reading = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "keep_reading"); + } + grpc_slice_buffer_reset_and_unref_internal(&t->read_buffer); + + if (keep_reading) { + grpc_endpoint_read(t->ep, &t->read_buffer, &t->read_action_locked); + grpc_chttp2_act_on_flowctl_action(t->flow_control->MakeAction(), t, + nullptr); + GRPC_CHTTP2_UNREF_TRANSPORT(t, "keep_reading"); + } else { + GRPC_CHTTP2_UNREF_TRANSPORT(t, "reading_action"); + } + + GRPC_ERROR_UNREF(error); +} + +// t is reffed prior to calling the first time, and once the callback chain +// that kicks off finishes, it's unreffed +static void schedule_bdp_ping_locked(grpc_chttp2_transport* t) { + t->flow_control->bdp_estimator()->SchedulePing(); + send_ping_locked(t, &t->start_bdp_ping_locked, &t->finish_bdp_ping_locked); +} + +static void start_bdp_ping_locked(void* tp, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(tp); + if (grpc_http_trace.enabled()) { + gpr_log(GPR_INFO, "%s: Start BDP ping err=%s", t->peer_string, + grpc_error_string(error)); + } + /* Reset the keepalive ping timer */ + if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING) { + grpc_timer_cancel(&t->keepalive_ping_timer); + } + t->flow_control->bdp_estimator()->StartPing(); +} + +static void finish_bdp_ping_locked(void* tp, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(tp); + if (grpc_http_trace.enabled()) { + gpr_log(GPR_INFO, "%s: Complete BDP ping err=%s", t->peer_string, + grpc_error_string(error)); + } + if (error != GRPC_ERROR_NONE) { + GRPC_CHTTP2_UNREF_TRANSPORT(t, "bdp_ping"); + return; + } + grpc_millis next_ping = t->flow_control->bdp_estimator()->CompletePing(); + grpc_chttp2_act_on_flowctl_action(t->flow_control->PeriodicUpdate(), t, + nullptr); + GPR_ASSERT(!t->have_next_bdp_ping_timer); + t->have_next_bdp_ping_timer = true; + grpc_timer_init(&t->next_bdp_ping_timer, next_ping, + &t->next_bdp_ping_timer_expired_locked); +} + +static void next_bdp_ping_timer_expired_locked(void* tp, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(tp); + GPR_ASSERT(t->have_next_bdp_ping_timer); + t->have_next_bdp_ping_timer = false; + if (error != GRPC_ERROR_NONE) { + GRPC_CHTTP2_UNREF_TRANSPORT(t, "bdp_ping"); + return; + } + schedule_bdp_ping_locked(t); +} + +void grpc_chttp2_config_default_keepalive_args(grpc_channel_args* args, + bool is_client) { + size_t i; + if (args) { + for (i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->args[i], {is_client ? g_default_client_keepalive_time_ms + : g_default_server_keepalive_time_ms, + 1, INT_MAX}); + if (is_client) { + g_default_client_keepalive_time_ms = value; + } else { + g_default_server_keepalive_time_ms = value; + } + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->args[i], {is_client ? g_default_client_keepalive_timeout_ms + : g_default_server_keepalive_timeout_ms, + 0, INT_MAX}); + if (is_client) { + g_default_client_keepalive_timeout_ms = value; + } else { + g_default_server_keepalive_timeout_ms = value; + } + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { + const bool value = static_cast(grpc_channel_arg_get_integer( + &args->args[i], + {is_client ? g_default_client_keepalive_permit_without_calls + : g_default_server_keepalive_timeout_ms, + 0, 1})); + if (is_client) { + g_default_client_keepalive_permit_without_calls = value; + } else { + g_default_server_keepalive_permit_without_calls = value; + } + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { + g_default_max_ping_strikes = grpc_channel_arg_get_integer( + &args->args[i], {g_default_max_ping_strikes, 0, INT_MAX}); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { + g_default_max_pings_without_data = grpc_channel_arg_get_integer( + &args->args[i], {g_default_max_pings_without_data, 0, INT_MAX}); + } else if (0 == + strcmp( + args->args[i].key, + GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) { + g_default_min_sent_ping_interval_without_data_ms = + grpc_channel_arg_get_integer( + &args->args[i], + {g_default_min_sent_ping_interval_without_data_ms, 0, INT_MAX}); + } else if (0 == + strcmp( + args->args[i].key, + GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) { + g_default_min_recv_ping_interval_without_data_ms = + grpc_channel_arg_get_integer( + &args->args[i], + {g_default_min_recv_ping_interval_without_data_ms, 0, INT_MAX}); + } + } + } +} + +static void init_keepalive_ping_locked(void* arg, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(arg); + GPR_ASSERT(t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING); + if (t->destroying || t->closed_with_error != GRPC_ERROR_NONE) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING; + } else if (error == GRPC_ERROR_NONE) { + if (t->keepalive_permit_without_calls || + grpc_chttp2_stream_map_size(&t->stream_map) > 0) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_PINGING; + GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive ping end"); + grpc_timer_init_unset(&t->keepalive_watchdog_timer); + send_keepalive_ping_locked(t); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING); + } else { + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init(&t->keepalive_ping_timer, + grpc_core::ExecCtx::Get()->Now() + t->keepalive_time, + &t->init_keepalive_ping_locked); + } + } else if (error == GRPC_ERROR_CANCELLED) { + /* The keepalive ping timer may be cancelled by bdp */ + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init(&t->keepalive_ping_timer, + grpc_core::ExecCtx::Get()->Now() + t->keepalive_time, + &t->init_keepalive_ping_locked); + } + GRPC_CHTTP2_UNREF_TRANSPORT(t, "init keepalive ping"); +} + +static void start_keepalive_ping_locked(void* arg, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(arg); + if (error != GRPC_ERROR_NONE) { + return; + } + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordKeepaliveSent(); + } + GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog"); + grpc_timer_init(&t->keepalive_watchdog_timer, + grpc_core::ExecCtx::Get()->Now() + t->keepalive_timeout, + &t->keepalive_watchdog_fired_locked); +} + +static void finish_keepalive_ping_locked(void* arg, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(arg); + if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) { + if (error == GRPC_ERROR_NONE) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING; + grpc_timer_cancel(&t->keepalive_watchdog_timer); + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init(&t->keepalive_ping_timer, + grpc_core::ExecCtx::Get()->Now() + t->keepalive_time, + &t->init_keepalive_ping_locked); + } + } + GRPC_CHTTP2_UNREF_TRANSPORT(t, "keepalive ping end"); +} + +static void keepalive_watchdog_fired_locked(void* arg, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(arg); + if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) { + if (error == GRPC_ERROR_NONE) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING; + close_transport_locked( + t, grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "keepalive watchdog timeout"), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE)); + } + } else { + /* The watchdog timer should have been cancelled by + * finish_keepalive_ping_locked. */ + if (GPR_UNLIKELY(error != GRPC_ERROR_CANCELLED)) { + gpr_log(GPR_ERROR, "keepalive_ping_end state error: %d (expect: %d)", + t->keepalive_state, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING); + } + } + GRPC_CHTTP2_UNREF_TRANSPORT(t, "keepalive watchdog"); +} + +/******************************************************************************* + * CALLBACK LOOP + */ + +static void connectivity_state_set(grpc_chttp2_transport* t, + grpc_connectivity_state state, + grpc_error* error, const char* reason) { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "set connectivity_state=%d", state)); + grpc_connectivity_state_set(&t->channel_callback.state_tracker, state, error, + reason); +} + +/******************************************************************************* + * POLLSET STUFF + */ + +static void set_pollset(grpc_transport* gt, grpc_stream* gs, + grpc_pollset* pollset) { + grpc_chttp2_transport* t = reinterpret_cast(gt); + grpc_endpoint_add_to_pollset(t->ep, pollset); +} + +static void set_pollset_set(grpc_transport* gt, grpc_stream* gs, + grpc_pollset_set* pollset_set) { + grpc_chttp2_transport* t = reinterpret_cast(gt); + grpc_endpoint_add_to_pollset_set(t->ep, pollset_set); +} + +/******************************************************************************* + * BYTE STREAM + */ + +static void reset_byte_stream(void* arg, grpc_error* error) { + grpc_chttp2_stream* s = static_cast(arg); + s->pending_byte_stream = false; + if (error == GRPC_ERROR_NONE) { + grpc_chttp2_maybe_complete_recv_message(s->t, s); + grpc_chttp2_maybe_complete_recv_trailing_metadata(s->t, s); + } else { + GPR_ASSERT(error != GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(s->on_next, GRPC_ERROR_REF(error)); + s->on_next = nullptr; + GRPC_ERROR_UNREF(s->byte_stream_error); + s->byte_stream_error = GRPC_ERROR_NONE; + grpc_chttp2_cancel_stream(s->t, s, GRPC_ERROR_REF(error)); + s->byte_stream_error = GRPC_ERROR_REF(error); + } +} + +namespace grpc_core { + +Chttp2IncomingByteStream::Chttp2IncomingByteStream( + grpc_chttp2_transport* transport, grpc_chttp2_stream* stream, + uint32_t frame_size, uint32_t flags) + : ByteStream(frame_size, flags), + transport_(transport), + stream_(stream), + refs_(2), + remaining_bytes_(frame_size) { + GRPC_ERROR_UNREF(stream->byte_stream_error); + stream->byte_stream_error = GRPC_ERROR_NONE; +} + +void Chttp2IncomingByteStream::OrphanLocked(void* arg, + grpc_error* error_ignored) { + Chttp2IncomingByteStream* bs = static_cast(arg); + grpc_chttp2_stream* s = bs->stream_; + grpc_chttp2_transport* t = s->t; + bs->Unref(); + s->pending_byte_stream = false; + grpc_chttp2_maybe_complete_recv_message(t, s); + grpc_chttp2_maybe_complete_recv_trailing_metadata(t, s); +} + +void Chttp2IncomingByteStream::Orphan() { + GPR_TIMER_SCOPE("incoming_byte_stream_destroy", 0); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&destroy_action_, + &Chttp2IncomingByteStream::OrphanLocked, this, + grpc_combiner_scheduler(transport_->combiner)), + GRPC_ERROR_NONE); +} + +void Chttp2IncomingByteStream::NextLocked(void* arg, + grpc_error* error_ignored) { + Chttp2IncomingByteStream* bs = static_cast(arg); + grpc_chttp2_transport* t = bs->transport_; + grpc_chttp2_stream* s = bs->stream_; + size_t cur_length = s->frame_storage.length; + if (!s->read_closed) { + s->flow_control->IncomingByteStreamUpdate(bs->next_action_.max_size_hint, + cur_length); + grpc_chttp2_act_on_flowctl_action(s->flow_control->MakeAction(), t, s); + } + GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0); + if (s->frame_storage.length > 0) { + grpc_slice_buffer_swap(&s->frame_storage, + &s->unprocessed_incoming_frames_buffer); + s->unprocessed_incoming_frames_decompressed = false; + GRPC_CLOSURE_SCHED(bs->next_action_.on_complete, GRPC_ERROR_NONE); + } else if (s->byte_stream_error != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(bs->next_action_.on_complete, + GRPC_ERROR_REF(s->byte_stream_error)); + if (s->data_parser.parsing_frame != nullptr) { + s->data_parser.parsing_frame->Unref(); + s->data_parser.parsing_frame = nullptr; + } + } else if (s->read_closed) { + if (bs->remaining_bytes_ != 0) { + s->byte_stream_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + GRPC_CLOSURE_SCHED(bs->next_action_.on_complete, + GRPC_ERROR_REF(s->byte_stream_error)); + if (s->data_parser.parsing_frame != nullptr) { + s->data_parser.parsing_frame->Unref(); + s->data_parser.parsing_frame = nullptr; + } + } else { + /* Should never reach here. */ + GPR_ASSERT(false); + } + } else { + s->on_next = bs->next_action_.on_complete; + } + bs->Unref(); +} + +bool Chttp2IncomingByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { + GPR_TIMER_SCOPE("incoming_byte_stream_next", 0); + if (stream_->unprocessed_incoming_frames_buffer.length > 0) { + return true; + } else { + Ref(); + next_action_.max_size_hint = max_size_hint; + next_action_.on_complete = on_complete; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&next_action_.closure, + &Chttp2IncomingByteStream::NextLocked, this, + grpc_combiner_scheduler(transport_->combiner)), + GRPC_ERROR_NONE); + return false; + } +} + +void Chttp2IncomingByteStream::MaybeCreateStreamDecompressionCtx() { + if (!stream_->stream_decompression_ctx) { + stream_->stream_decompression_ctx = grpc_stream_compression_context_create( + stream_->stream_decompression_method); + } +} + +grpc_error* Chttp2IncomingByteStream::Pull(grpc_slice* slice) { + GPR_TIMER_SCOPE("incoming_byte_stream_pull", 0); + grpc_error* error; + if (stream_->unprocessed_incoming_frames_buffer.length > 0) { + if (!stream_->unprocessed_incoming_frames_decompressed) { + bool end_of_context; + MaybeCreateStreamDecompressionCtx(); + if (!grpc_stream_decompress(stream_->stream_decompression_ctx, + &stream_->unprocessed_incoming_frames_buffer, + &stream_->decompressed_data_buffer, nullptr, + MAX_SIZE_T, &end_of_context)) { + error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream decompression error."); + return error; + } + GPR_ASSERT(stream_->unprocessed_incoming_frames_buffer.length == 0); + grpc_slice_buffer_swap(&stream_->unprocessed_incoming_frames_buffer, + &stream_->decompressed_data_buffer); + stream_->unprocessed_incoming_frames_decompressed = true; + if (end_of_context) { + grpc_stream_compression_context_destroy( + stream_->stream_decompression_ctx); + stream_->stream_decompression_ctx = nullptr; + } + if (stream_->unprocessed_incoming_frames_buffer.length == 0) { + *slice = grpc_empty_slice(); + } + } + error = grpc_deframe_unprocessed_incoming_frames( + &stream_->data_parser, stream_, + &stream_->unprocessed_incoming_frames_buffer, slice, nullptr); + if (error != GRPC_ERROR_NONE) { + return error; + } + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error)); + return error; + } + return GRPC_ERROR_NONE; +} + +void Chttp2IncomingByteStream::PublishError(grpc_error* error) { + GPR_ASSERT(error != GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(stream_->on_next, GRPC_ERROR_REF(error)); + stream_->on_next = nullptr; + GRPC_ERROR_UNREF(stream_->byte_stream_error); + stream_->byte_stream_error = GRPC_ERROR_REF(error); + grpc_chttp2_cancel_stream(transport_, stream_, GRPC_ERROR_REF(error)); +} + +grpc_error* Chttp2IncomingByteStream::Push(grpc_slice slice, + grpc_slice* slice_out) { + if (remaining_bytes_ < GRPC_SLICE_LENGTH(slice)) { + grpc_error* error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many bytes in stream"); + GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error)); + grpc_slice_unref_internal(slice); + return error; + } else { + remaining_bytes_ -= static_cast GRPC_SLICE_LENGTH(slice); + if (slice_out != nullptr) { + *slice_out = slice; + } + return GRPC_ERROR_NONE; + } +} + +grpc_error* Chttp2IncomingByteStream::Finished(grpc_error* error, + bool reset_on_error) { + if (error == GRPC_ERROR_NONE) { + if (remaining_bytes_ != 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + } + } + if (error != GRPC_ERROR_NONE && reset_on_error) { + GRPC_CLOSURE_SCHED(&stream_->reset_byte_stream, GRPC_ERROR_REF(error)); + } + Unref(); + return error; +} + +void Chttp2IncomingByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(Finished(error, true /* reset_on_error */)); +} + +} // namespace grpc_core + +/******************************************************************************* + * RESOURCE QUOTAS + */ + +static void post_benign_reclaimer(grpc_chttp2_transport* t) { + if (!t->benign_reclaimer_registered) { + t->benign_reclaimer_registered = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "benign_reclaimer"); + grpc_resource_user_post_reclaimer(grpc_endpoint_get_resource_user(t->ep), + false, &t->benign_reclaimer_locked); + } +} + +static void post_destructive_reclaimer(grpc_chttp2_transport* t) { + if (!t->destructive_reclaimer_registered) { + t->destructive_reclaimer_registered = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "destructive_reclaimer"); + grpc_resource_user_post_reclaimer(grpc_endpoint_get_resource_user(t->ep), + true, &t->destructive_reclaimer_locked); + } +} + +static void benign_reclaimer_locked(void* arg, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(arg); + if (error == GRPC_ERROR_NONE && + grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + /* Channel with no active streams: send a goaway to try and make it + * disconnect cleanly */ + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, "HTTP2: %s - send goaway to free memory", + t->peer_string); + } + send_goaway(t, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM)); + } else if (error == GRPC_ERROR_NONE && grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, + "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR + " streams", + t->peer_string, grpc_chttp2_stream_map_size(&t->stream_map)); + } + t->benign_reclaimer_registered = false; + if (error != GRPC_ERROR_CANCELLED) { + grpc_resource_user_finish_reclamation( + grpc_endpoint_get_resource_user(t->ep)); + } + GRPC_CHTTP2_UNREF_TRANSPORT(t, "benign_reclaimer"); +} + +static void destructive_reclaimer_locked(void* arg, grpc_error* error) { + grpc_chttp2_transport* t = static_cast(arg); + size_t n = grpc_chttp2_stream_map_size(&t->stream_map); + t->destructive_reclaimer_registered = false; + if (error == GRPC_ERROR_NONE && n > 0) { + grpc_chttp2_stream* s = static_cast( + grpc_chttp2_stream_map_rand(&t->stream_map)); + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, "HTTP2: %s - abandon stream id %d", t->peer_string, + s->id); + } + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"), + GRPC_ERROR_INT_HTTP2_ERROR, + GRPC_HTTP2_ENHANCE_YOUR_CALM)); + if (n > 1) { + /* Since we cancel one stream per destructive reclamation, if + there are more streams left, we can immediately post a new + reclaimer in case the resource quota needs to free more + memory */ + post_destructive_reclaimer(t); + } + } + if (error != GRPC_ERROR_CANCELLED) { + grpc_resource_user_finish_reclamation( + grpc_endpoint_get_resource_user(t->ep)); + } + GRPC_CHTTP2_UNREF_TRANSPORT(t, "destructive_reclaimer"); +} + +/******************************************************************************* + * MONITORING + */ + +const char* grpc_chttp2_initiate_write_reason_string( + grpc_chttp2_initiate_write_reason reason) { + switch (reason) { + case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE: + return "INITIAL_WRITE"; + case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM: + return "START_NEW_STREAM"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE: + return "SEND_MESSAGE"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA: + return "SEND_INITIAL_METADATA"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA: + return "SEND_TRAILING_METADATA"; + case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING: + return "RETRY_SEND_PING"; + case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS: + return "CONTINUE_PINGS"; + case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT: + return "GOAWAY_SENT"; + case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM: + return "RST_STREAM"; + case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API: + return "CLOSE_FROM_API"; + case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL: + return "STREAM_FLOW_CONTROL"; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL: + return "TRANSPORT_FLOW_CONTROL"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS: + return "SEND_SETTINGS"; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING: + return "FLOW_CONTROL_UNSTALLED_BY_SETTING"; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE: + return "FLOW_CONTROL_UNSTALLED_BY_UPDATE"; + case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING: + return "APPLICATION_PING"; + case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING: + return "KEEPALIVE_PING"; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED: + return "TRANSPORT_FLOW_CONTROL_UNSTALLED"; + case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE: + return "PING_RESPONSE"; + case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM: + return "FORCE_RST_STREAM"; + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +static grpc_endpoint* chttp2_get_endpoint(grpc_transport* t) { + return (reinterpret_cast(t))->ep; +} + +static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream), + "chttp2", + init_stream, + set_pollset, + set_pollset_set, + perform_stream_op, + perform_transport_op, + destroy_stream, + destroy_transport, + chttp2_get_endpoint}; + +static const grpc_transport_vtable* get_vtable(void) { return &vtable; } + +grpc_core::RefCountedPtr +grpc_chttp2_transport_get_socket_node(grpc_transport* transport) { + grpc_chttp2_transport* t = + reinterpret_cast(transport); + return t->channelz_socket; +} + +grpc_transport* grpc_create_chttp2_transport( + const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client, + grpc_resource_user* resource_user) { + auto t = grpc_core::New(channel_args, ep, is_client, + resource_user); + return &t->base; +} + +void grpc_chttp2_transport_start_reading( + grpc_transport* transport, grpc_slice_buffer* read_buffer, + grpc_closure* notify_on_receive_settings) { + grpc_chttp2_transport* t = + reinterpret_cast(transport); + GRPC_CHTTP2_REF_TRANSPORT( + t, "reading_action"); /* matches unref inside reading_action */ + if (read_buffer != nullptr) { + grpc_slice_buffer_move_into(read_buffer, &t->read_buffer); + gpr_free(read_buffer); + } + t->notify_on_receive_settings = notify_on_receive_settings; + GRPC_CLOSURE_SCHED(&t->read_action_locked, GRPC_ERROR_NONE); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.h new file mode 100644 index 00000000000000..c22cfb0ad7d69d --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/chttp2_transport.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H + +#include + +#include "src/core/lib/channel/channelz.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/transport/transport.h" + +extern grpc_core::TraceFlag grpc_http_trace; +extern grpc_core::TraceFlag grpc_trace_http2_stream_state; +extern grpc_core::DebugOnlyTraceFlag grpc_trace_chttp2_refcount; + +extern bool g_flow_control_enabled; + +grpc_transport* grpc_create_chttp2_transport( + const grpc_channel_args* channel_args, grpc_endpoint* ep, bool is_client, + grpc_resource_user* resource_user = nullptr); + +grpc_core::RefCountedPtr +grpc_chttp2_transport_get_socket_node(grpc_transport* transport); + +/// Takes ownership of \a read_buffer, which (if non-NULL) contains +/// leftover bytes previously read from the endpoint (e.g., by handshakers). +/// If non-null, \a notify_on_receive_settings will be scheduled when +/// HTTP/2 settings are received from the peer. +void grpc_chttp2_transport_start_reading( + grpc_transport* transport, grpc_slice_buffer* read_buffer, + grpc_closure* notify_on_receive_settings); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.cc new file mode 100644 index 00000000000000..df09809067db1f --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.cc @@ -0,0 +1,67 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/context_list.h" + +namespace { +void (*write_timestamps_callback_g)(void*, grpc_core::Timestamps*, + grpc_error* error) = nullptr; +void* (*get_copied_context_fn_g)(void*) = nullptr; +} // namespace + +namespace grpc_core { +void ContextList::Append(ContextList** head, grpc_chttp2_stream* s) { + if (get_copied_context_fn_g == nullptr || + write_timestamps_callback_g == nullptr) { + return; + } + /* Create a new element in the list and add it at the front */ + ContextList* elem = grpc_core::New(); + elem->trace_context_ = get_copied_context_fn_g(s->context); + elem->byte_offset_ = s->byte_counter; + elem->next_ = *head; + *head = elem; +} + +void ContextList::Execute(void* arg, grpc_core::Timestamps* ts, + grpc_error* error) { + ContextList* head = static_cast(arg); + ContextList* to_be_freed; + while (head != nullptr) { + if (write_timestamps_callback_g) { + ts->byte_offset = static_cast(head->byte_offset_); + write_timestamps_callback_g(head->trace_context_, ts, error); + } + to_be_freed = head; + head = head->next_; + grpc_core::Delete(to_be_freed); + } +} + +void grpc_http2_set_write_timestamps_callback(void (*fn)(void*, + grpc_core::Timestamps*, + grpc_error* error)) { + write_timestamps_callback_g = fn; +} + +void grpc_http2_set_fn_get_copied_context(void* (*fn)(void*)) { + get_copied_context_fn_g = fn; +} +} /* namespace grpc_core */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.h new file mode 100644 index 00000000000000..5b9d2ab378453b --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/context_list.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CONTEXT_LIST_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CONTEXT_LIST_H + +#include + +#include "src/core/lib/iomgr/buffer_list.h" + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +namespace grpc_core { +/** A list of RPC Contexts */ +class ContextList { + public: + /* Creates a new element with \a context as the value and appends it to the + * list. */ + static void Append(ContextList** head, grpc_chttp2_stream* s); + + /* Executes a function \a fn with each context in the list and \a ts. It also + * frees up the entire list after this operation. It is intended as a callback + * and hence does not take a ref on \a error */ + static void Execute(void* arg, grpc_core::Timestamps* ts, grpc_error* error); + + private: + void* trace_context_ = nullptr; + ContextList* next_ = nullptr; + size_t byte_offset_ = 0; +}; + +void grpc_http2_set_write_timestamps_callback(void (*fn)(void*, + grpc_core::Timestamps*, + grpc_error* error)); +void grpc_http2_set_fn_get_copied_context(void* (*fn)(void*)); +} /* namespace grpc_core */ + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CONTEXT_LIST_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.cc new file mode 100644 index 00000000000000..53932bcb7f502a --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.cc @@ -0,0 +1,408 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/flow_control.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/lib/gpr/string.h" + +grpc_core::TraceFlag grpc_flowctl_trace(false, "flowctl"); + +namespace grpc_core { +namespace chttp2 { + +namespace { + +static constexpr const int kTracePadding = 30; +static constexpr const uint32_t kMaxWindowUpdateSize = (1u << 31) - 1; + +static char* fmt_int64_diff_str(int64_t old_val, int64_t new_val) { + char* str; + if (old_val != new_val) { + gpr_asprintf(&str, "%" PRId64 " -> %" PRId64 "", old_val, new_val); + } else { + gpr_asprintf(&str, "%" PRId64 "", old_val); + } + char* str_lp = gpr_leftpad(str, ' ', kTracePadding); + gpr_free(str); + return str_lp; +} + +static char* fmt_uint32_diff_str(uint32_t old_val, uint32_t new_val) { + char* str; + if (old_val != new_val) { + gpr_asprintf(&str, "%" PRIu32 " -> %" PRIu32 "", old_val, new_val); + } else { + gpr_asprintf(&str, "%" PRIu32 "", old_val); + } + char* str_lp = gpr_leftpad(str, ' ', kTracePadding); + gpr_free(str); + return str_lp; +} +} // namespace + +void FlowControlTrace::Init(const char* reason, TransportFlowControl* tfc, + StreamFlowControl* sfc) { + tfc_ = tfc; + sfc_ = sfc; + reason_ = reason; + remote_window_ = tfc->remote_window(); + target_window_ = tfc->target_window(); + announced_window_ = tfc->announced_window(); + if (sfc != nullptr) { + remote_window_delta_ = sfc->remote_window_delta(); + local_window_delta_ = sfc->local_window_delta(); + announced_window_delta_ = sfc->announced_window_delta(); + } +} + +void FlowControlTrace::Finish() { + uint32_t acked_local_window = + tfc_->transport()->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + uint32_t remote_window = + tfc_->transport()->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + char* trw_str = fmt_int64_diff_str(remote_window_, tfc_->remote_window()); + char* tlw_str = fmt_int64_diff_str(target_window_, tfc_->target_window()); + char* taw_str = + fmt_int64_diff_str(announced_window_, tfc_->announced_window()); + char* srw_str; + char* slw_str; + char* saw_str; + if (sfc_ != nullptr) { + srw_str = fmt_int64_diff_str(remote_window_delta_ + remote_window, + sfc_->remote_window_delta() + remote_window); + slw_str = + fmt_int64_diff_str(local_window_delta_ + acked_local_window, + sfc_->local_window_delta() + acked_local_window); + saw_str = + fmt_int64_diff_str(announced_window_delta_ + acked_local_window, + sfc_->announced_window_delta() + acked_local_window); + } else { + srw_str = gpr_leftpad("", ' ', kTracePadding); + slw_str = gpr_leftpad("", ' ', kTracePadding); + saw_str = gpr_leftpad("", ' ', kTracePadding); + } + gpr_log(GPR_DEBUG, + "%p[%u][%s] | %s | trw:%s, ttw:%s, taw:%s, srw:%s, slw:%s, saw:%s", + tfc_, sfc_ != nullptr ? sfc_->stream()->id : 0, + tfc_->transport()->is_client ? "cli" : "svr", reason_, trw_str, + tlw_str, taw_str, srw_str, slw_str, saw_str); + gpr_free(trw_str); + gpr_free(tlw_str); + gpr_free(taw_str); + gpr_free(srw_str); + gpr_free(slw_str); + gpr_free(saw_str); +} + +const char* FlowControlAction::UrgencyString(Urgency u) { + switch (u) { + case Urgency::NO_ACTION_NEEDED: + return "no action"; + case Urgency::UPDATE_IMMEDIATELY: + return "update immediately"; + case Urgency::QUEUE_UPDATE: + return "queue update"; + default: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +void FlowControlAction::Trace(grpc_chttp2_transport* t) const { + char* iw_str = fmt_uint32_diff_str( + t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE], + initial_window_size_); + char* mf_str = fmt_uint32_diff_str( + t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + max_frame_size_); + gpr_log(GPR_DEBUG, "t[%s], s[%s], iw:%s:%s mf:%s:%s", + UrgencyString(send_transport_update_), + UrgencyString(send_stream_update_), + UrgencyString(send_initial_window_update_), iw_str, + UrgencyString(send_max_frame_size_update_), mf_str); + gpr_free(iw_str); + gpr_free(mf_str); +} + +TransportFlowControlDisabled::TransportFlowControlDisabled( + grpc_chttp2_transport* t) { + remote_window_ = kMaxWindow; + target_initial_window_size_ = kMaxWindow; + announced_window_ = kMaxWindow; + t->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE] = + kFrameSize; + t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE] = + kFrameSize; + t->settings[GRPC_ACKED_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE] = + kFrameSize; + t->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = + kMaxWindow; + t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = + kMaxWindow; + t->settings[GRPC_ACKED_SETTINGS][GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE] = + kMaxWindow; +} + +TransportFlowControl::TransportFlowControl(const grpc_chttp2_transport* t, + bool enable_bdp_probe) + : t_(t), + enable_bdp_probe_(enable_bdp_probe), + bdp_estimator_(t->peer_string), + pid_controller_(grpc_core::PidController::Args() + .set_gain_p(4) + .set_gain_i(8) + .set_gain_d(0) + .set_initial_control_value(TargetLogBdp()) + .set_min_control_value(-1) + .set_max_control_value(25) + .set_integral_range(10)), + last_pid_update_(grpc_core::ExecCtx::Get()->Now()) {} + +uint32_t TransportFlowControl::MaybeSendUpdate(bool writing_anyway) { + FlowControlTrace trace("t updt sent", this, nullptr); + const uint32_t target_announced_window = + static_cast(target_window()); + if ((writing_anyway || announced_window_ <= target_announced_window / 2) && + announced_window_ != target_announced_window) { + const uint32_t announce = static_cast GPR_CLAMP( + target_announced_window - announced_window_, 0, kMaxWindowUpdateSize); + announced_window_ += announce; + return announce; + } + return 0; +} + +grpc_error* TransportFlowControl::ValidateRecvData( + int64_t incoming_frame_size) { + if (incoming_frame_size > announced_window_) { + char* msg; + gpr_asprintf(&msg, + "frame of size %" PRId64 " overflows local window of %" PRId64, + incoming_frame_size, announced_window_); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + return GRPC_ERROR_NONE; +} + +StreamFlowControl::StreamFlowControl(TransportFlowControl* tfc, + const grpc_chttp2_stream* s) + : tfc_(tfc), s_(s) {} + +grpc_error* StreamFlowControl::RecvData(int64_t incoming_frame_size) { + FlowControlTrace trace(" data recv", tfc_, this); + + grpc_error* error = GRPC_ERROR_NONE; + error = tfc_->ValidateRecvData(incoming_frame_size); + if (error != GRPC_ERROR_NONE) return error; + + uint32_t sent_init_window = + tfc_->transport()->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + uint32_t acked_init_window = + tfc_->transport()->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + + int64_t acked_stream_window = announced_window_delta_ + acked_init_window; + int64_t sent_stream_window = announced_window_delta_ + sent_init_window; + if (incoming_frame_size > acked_stream_window) { + if (incoming_frame_size <= sent_stream_window) { + gpr_log(GPR_ERROR, + "Incoming frame of size %" PRId64 + " exceeds local window size of %" PRId64 + ".\n" + "The (un-acked, future) window size would be %" PRId64 + " which is not exceeded.\n" + "This would usually cause a disconnection, but allowing it due to" + "broken HTTP2 implementations in the wild.\n" + "See (for example) https://github.com/netty/netty/issues/6520.", + incoming_frame_size, acked_stream_window, sent_stream_window); + } else { + char* msg; + gpr_asprintf( + &msg, "frame of size %" PRId64 " overflows local window of %" PRId64, + incoming_frame_size, acked_stream_window); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + } + + UpdateAnnouncedWindowDelta(tfc_, -incoming_frame_size); + local_window_delta_ -= incoming_frame_size; + tfc_->CommitRecvData(incoming_frame_size); + return GRPC_ERROR_NONE; +} + +uint32_t StreamFlowControl::MaybeSendUpdate() { + FlowControlTrace trace("s updt sent", tfc_, this); + if (local_window_delta_ > announced_window_delta_) { + uint32_t announce = static_cast GPR_CLAMP( + local_window_delta_ - announced_window_delta_, 0, kMaxWindowUpdateSize); + UpdateAnnouncedWindowDelta(tfc_, announce); + return announce; + } + return 0; +} + +void StreamFlowControl::IncomingByteStreamUpdate(size_t max_size_hint, + size_t have_already) { + FlowControlTrace trace("app st recv", tfc_, this); + uint32_t max_recv_bytes; + uint32_t sent_init_window = + tfc_->transport()->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + + /* clamp max recv hint to an allowable size */ + if (max_size_hint >= UINT32_MAX - sent_init_window) { + max_recv_bytes = UINT32_MAX - sent_init_window; + } else { + max_recv_bytes = static_cast(max_size_hint); + } + + /* account for bytes already received but unknown to higher layers */ + if (max_recv_bytes >= have_already) { + max_recv_bytes -= static_cast(have_already); + } else { + max_recv_bytes = 0; + } + + /* add some small lookahead to keep pipelines flowing */ + GPR_ASSERT(max_recv_bytes <= UINT32_MAX - sent_init_window); + if (local_window_delta_ < max_recv_bytes) { + uint32_t add_max_recv_bytes = + static_cast(max_recv_bytes - local_window_delta_); + local_window_delta_ += add_max_recv_bytes; + } +} + +// Take in a target and modifies it based on the memory pressure of the system +static double AdjustForMemoryPressure(grpc_resource_quota* quota, + double target) { + // do not increase window under heavy memory pressure. + double memory_pressure = grpc_resource_quota_get_memory_pressure(quota); + static const double kLowMemPressure = 0.1; + static const double kZeroTarget = 22; + static const double kHighMemPressure = 0.8; + static const double kMaxMemPressure = 0.9; + if (memory_pressure < kLowMemPressure && target < kZeroTarget) { + target = (target - kZeroTarget) * memory_pressure / kLowMemPressure + + kZeroTarget; + } else if (memory_pressure > kHighMemPressure) { + target *= 1 - GPR_MIN(1, (memory_pressure - kHighMemPressure) / + (kMaxMemPressure - kHighMemPressure)); + } + return target; +} + +double TransportFlowControl::TargetLogBdp() { + return AdjustForMemoryPressure( + grpc_resource_user_quota(grpc_endpoint_get_resource_user(t_->ep)), + 1 + log2(bdp_estimator_.EstimateBdp())); +} + +double TransportFlowControl::SmoothLogBdp(double value) { + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + double bdp_error = value - pid_controller_.last_control_value(); + const double dt = static_cast(now - last_pid_update_) * 1e-3; + last_pid_update_ = now; + // Limit dt to 100ms + const double kMaxDt = 0.1; + return pid_controller_.Update(bdp_error, dt > kMaxDt ? kMaxDt : dt); +} + +FlowControlAction::Urgency TransportFlowControl::DeltaUrgency( + int64_t value, grpc_chttp2_setting_id setting_id) { + int64_t delta = value - static_cast( + t_->settings[GRPC_LOCAL_SETTINGS][setting_id]); + // TODO(ncteisen): tune this + if (delta != 0 && (delta <= -value / 5 || delta >= value / 5)) { + return FlowControlAction::Urgency::QUEUE_UPDATE; + } else { + return FlowControlAction::Urgency::NO_ACTION_NEEDED; + } +} + +FlowControlAction TransportFlowControl::PeriodicUpdate() { + FlowControlAction action; + if (enable_bdp_probe_) { + // get bdp estimate and update initial_window accordingly. + // target might change based on how much memory pressure we are under + // TODO(ncteisen): experiment with setting target to be huge under low + // memory pressure. + const double target = pow(2, SmoothLogBdp(TargetLogBdp())); + + // Though initial window 'could' drop to 0, we keep the floor at 128 + target_initial_window_size_ = + static_cast GPR_CLAMP(target, 128, INT32_MAX); + + action.set_send_initial_window_update( + DeltaUrgency(target_initial_window_size_, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE), + static_cast(target_initial_window_size_)); + + // get bandwidth estimate and update max_frame accordingly. + double bw_dbl = bdp_estimator_.EstimateBandwidth(); + // we target the max of BDP or bandwidth in microseconds. + int32_t frame_size = static_cast GPR_CLAMP( + GPR_MAX((int32_t)GPR_CLAMP(bw_dbl, 0, INT_MAX) / 1000, + target_initial_window_size_), + 16384, 16777215); + action.set_send_max_frame_size_update( + DeltaUrgency(static_cast(frame_size), + GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE), + frame_size); + } + return UpdateAction(action); +} + +FlowControlAction StreamFlowControl::UpdateAction(FlowControlAction action) { + // TODO(ncteisen): tune this + if (!s_->read_closed) { + uint32_t sent_init_window = + tfc_->transport()->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + if (local_window_delta_ > announced_window_delta_ && + announced_window_delta_ + sent_init_window <= sent_init_window / 2) { + action.set_send_stream_update( + FlowControlAction::Urgency::UPDATE_IMMEDIATELY); + } else if (local_window_delta_ > announced_window_delta_) { + action.set_send_stream_update(FlowControlAction::Urgency::QUEUE_UPDATE); + } + } + + return action; +} + +} // namespace chttp2 +} // namespace grpc_core diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.h new file mode 100644 index 00000000000000..120fefc8b79625 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/flow_control.h @@ -0,0 +1,482 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FLOW_CONTROL_H + +#include + +#include + +#include "src/core/ext/transport/chttp2/transport/http2_settings.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/transport/bdp_estimator.h" +#include "src/core/lib/transport/pid_controller.h" + +struct grpc_chttp2_transport; +struct grpc_chttp2_stream; + +extern grpc_core::TraceFlag grpc_flowctl_trace; + +namespace grpc { +namespace testing { +class TrickledCHTTP2; // to make this a friend +} // namespace testing +} // namespace grpc + +namespace grpc_core { +namespace chttp2 { + +static constexpr uint32_t kDefaultWindow = 65535; +static constexpr int64_t kMaxWindow = static_cast((1u << 31) - 1); +// TODO(ncteisen): Tune this +static constexpr uint32_t kFrameSize = 1024 * 1024; + +class TransportFlowControl; +class StreamFlowControl; + +// Encapsulates a collections of actions the transport needs to take with +// regard to flow control. Each action comes with urgencies that tell the +// transport how quickly the action must take place. +class FlowControlAction { + public: + enum class Urgency : uint8_t { + // Nothing to be done. + NO_ACTION_NEEDED = 0, + // Initiate a write to update the initial window immediately. + UPDATE_IMMEDIATELY, + // Push the flow control update into a send buffer, to be sent + // out the next time a write is initiated. + QUEUE_UPDATE, + }; + + Urgency send_stream_update() const { return send_stream_update_; } + Urgency send_transport_update() const { return send_transport_update_; } + Urgency send_initial_window_update() const { + return send_initial_window_update_; + } + Urgency send_max_frame_size_update() const { + return send_max_frame_size_update_; + } + uint32_t initial_window_size() const { return initial_window_size_; } + uint32_t max_frame_size() const { return max_frame_size_; } + + FlowControlAction& set_send_stream_update(Urgency u) { + send_stream_update_ = u; + return *this; + } + FlowControlAction& set_send_transport_update(Urgency u) { + send_transport_update_ = u; + return *this; + } + FlowControlAction& set_send_initial_window_update(Urgency u, + uint32_t update) { + send_initial_window_update_ = u; + initial_window_size_ = update; + return *this; + } + FlowControlAction& set_send_max_frame_size_update(Urgency u, + uint32_t update) { + send_max_frame_size_update_ = u; + max_frame_size_ = update; + return *this; + } + + static const char* UrgencyString(Urgency u); + void Trace(grpc_chttp2_transport* t) const; + + private: + Urgency send_stream_update_ = Urgency::NO_ACTION_NEEDED; + Urgency send_transport_update_ = Urgency::NO_ACTION_NEEDED; + Urgency send_initial_window_update_ = Urgency::NO_ACTION_NEEDED; + Urgency send_max_frame_size_update_ = Urgency::NO_ACTION_NEEDED; + uint32_t initial_window_size_ = 0; + uint32_t max_frame_size_ = 0; +}; + +class FlowControlTrace { + public: + FlowControlTrace(const char* reason, TransportFlowControl* tfc, + StreamFlowControl* sfc) { + if (enabled_) Init(reason, tfc, sfc); + } + + ~FlowControlTrace() { + if (enabled_) Finish(); + } + + private: + void Init(const char* reason, TransportFlowControl* tfc, + StreamFlowControl* sfc); + void Finish(); + + const bool enabled_ = grpc_flowctl_trace.enabled(); + + TransportFlowControl* tfc_; + StreamFlowControl* sfc_; + const char* reason_; + int64_t remote_window_; + int64_t target_window_; + int64_t announced_window_; + int64_t remote_window_delta_; + int64_t local_window_delta_; + int64_t announced_window_delta_; +}; + +// Fat interface with all methods a flow control implementation needs to +// support. gRPC C Core does not support pure virtual functions, so instead +// we abort in any methods which require implementation in the base class. +class TransportFlowControlBase { + public: + TransportFlowControlBase() {} + virtual ~TransportFlowControlBase() {} + + // Is flow control enabled? This is needed in other codepaths like the checks + // in parsing and in writing. + virtual bool flow_control_enabled() const { abort(); } + + // Called to check if the transport needs to send a WINDOW_UPDATE frame + virtual uint32_t MaybeSendUpdate(bool writing_anyway) { abort(); } + + // Using the protected members, returns and Action to be taken by the + // tranport. + virtual FlowControlAction MakeAction() { abort(); } + + // Using the protected members, returns and Action to be taken by the + // tranport. Also checks for updates to our BDP estimate and acts + // accordingly. + virtual FlowControlAction PeriodicUpdate() { abort(); } + + // Called to do bookkeeping when a stream owned by this transport sends + // data on the wire + virtual void StreamSentData(int64_t size) { abort(); } + + // Called to do bookkeeping when a stream owned by this transport receives + // data from the wire. Also does error checking for frame size. + virtual grpc_error* RecvData(int64_t incoming_frame_size) { abort(); } + + // Called to do bookkeeping when we receive a WINDOW_UPDATE frame. + virtual void RecvUpdate(uint32_t size) { abort(); } + + // Returns the BdpEstimator held by this object. Caller is responsible for + // checking for nullptr. TODO(ncteisen): consider fully encapsulating all + // bdp estimator actions inside TransportFlowControl + virtual BdpEstimator* bdp_estimator() { return nullptr; } + + // Getters + int64_t remote_window() const { return remote_window_; } + virtual int64_t target_window() const { return target_initial_window_size_; } + int64_t announced_window() const { return announced_window_; } + + // Used in certain benchmarks in which we don't want FlowControl to be a + // factor + virtual void TestOnlyForceHugeWindow() {} + + GRPC_ABSTRACT_BASE_CLASS + + protected: + friend class ::grpc::testing::TrickledCHTTP2; + int64_t remote_window_ = kDefaultWindow; + int64_t target_initial_window_size_ = kDefaultWindow; + int64_t announced_window_ = kDefaultWindow; +}; + +// Implementation of flow control that does NOTHING. Always returns maximum +// values, never initiates writes, and assumes that the remote peer is doing +// the same. To be used to narrow down on flow control as the cause of negative +// performance. +class TransportFlowControlDisabled final : public TransportFlowControlBase { + public: + // Maxes out all values + TransportFlowControlDisabled(grpc_chttp2_transport* t); + + bool flow_control_enabled() const override { return false; } + + // Never do anything. + uint32_t MaybeSendUpdate(bool writing_anyway) override { return 0; } + FlowControlAction MakeAction() override { return FlowControlAction(); } + FlowControlAction PeriodicUpdate() override { return FlowControlAction(); } + void StreamSentData(int64_t size) override {} + grpc_error* RecvData(int64_t incoming_frame_size) override { + return GRPC_ERROR_NONE; + } + void RecvUpdate(uint32_t size) override {} +}; + +// Implementation of flow control that abides to HTTP/2 spec and attempts +// to be as performant as possible. +class TransportFlowControl final : public TransportFlowControlBase { + public: + TransportFlowControl(const grpc_chttp2_transport* t, bool enable_bdp_probe); + ~TransportFlowControl() {} + + bool flow_control_enabled() const override { return true; } + + bool bdp_probe() const { return enable_bdp_probe_; } + + // returns an announce if we should send a transport update to our peer, + // else returns zero; writing_anyway indicates if a write would happen + // regardless of the send - if it is false and this function returns non-zero, + // this announce will cause a write to occur + uint32_t MaybeSendUpdate(bool writing_anyway) override; + + // Reads the flow control data and returns and actionable struct that will + // tell chttp2 exactly what it needs to do + FlowControlAction MakeAction() override { + return UpdateAction(FlowControlAction()); + } + + // Call periodically (at a low-ish rate, 100ms - 10s makes sense) + // to perform more complex flow control calculations and return an action + // to let chttp2 change its parameters + FlowControlAction PeriodicUpdate() override; + + void StreamSentData(int64_t size) override { remote_window_ -= size; } + + grpc_error* ValidateRecvData(int64_t incoming_frame_size); + void CommitRecvData(int64_t incoming_frame_size) { + announced_window_ -= incoming_frame_size; + } + + grpc_error* RecvData(int64_t incoming_frame_size) override { + FlowControlTrace trace(" data recv", this, nullptr); + grpc_error* error = ValidateRecvData(incoming_frame_size); + if (error != GRPC_ERROR_NONE) return error; + CommitRecvData(incoming_frame_size); + return GRPC_ERROR_NONE; + } + + // we have received a WINDOW_UPDATE frame for a transport + void RecvUpdate(uint32_t size) override { + FlowControlTrace trace("t updt recv", this, nullptr); + remote_window_ += size; + } + + // See comment above announced_stream_total_over_incoming_window_ for the + // logic behind this decision. + int64_t target_window() const override { + return static_cast GPR_MIN( + (int64_t)((1u << 31) - 1), + announced_stream_total_over_incoming_window_ + + target_initial_window_size_); + } + + const grpc_chttp2_transport* transport() const { return t_; } + + void PreUpdateAnnouncedWindowOverIncomingWindow(int64_t delta) { + if (delta > 0) { + announced_stream_total_over_incoming_window_ -= delta; + } else { + announced_stream_total_under_incoming_window_ += -delta; + } + } + + void PostUpdateAnnouncedWindowOverIncomingWindow(int64_t delta) { + if (delta > 0) { + announced_stream_total_over_incoming_window_ += delta; + } else { + announced_stream_total_under_incoming_window_ -= -delta; + } + } + + BdpEstimator* bdp_estimator() override { return &bdp_estimator_; } + + void TestOnlyForceHugeWindow() override { + announced_window_ = 1024 * 1024 * 1024; + remote_window_ = 1024 * 1024 * 1024; + } + + private: + double TargetLogBdp(); + double SmoothLogBdp(double value); + FlowControlAction::Urgency DeltaUrgency(int64_t value, + grpc_chttp2_setting_id setting_id); + + FlowControlAction UpdateAction(FlowControlAction action) { + if (announced_window_ < target_window() / 2) { + action.set_send_transport_update( + FlowControlAction::Urgency::UPDATE_IMMEDIATELY); + } + return action; + } + + const grpc_chttp2_transport* const t_; + + /** calculating what we should give for local window: + we track the total amount of flow control over initial window size + across all streams: this is data that we want to receive right now (it + has an outstanding read) + and the total amount of flow control under initial window size across all + streams: this is data we've read early + we want to adjust incoming_window such that: + incoming_window = total_over - max(bdp - total_under, 0) */ + int64_t announced_stream_total_over_incoming_window_ = 0; + int64_t announced_stream_total_under_incoming_window_ = 0; + + /** should we probe bdp? */ + const bool enable_bdp_probe_; + + /* bdp estimation */ + grpc_core::BdpEstimator bdp_estimator_; + + /* pid controller */ + grpc_core::PidController pid_controller_; + grpc_millis last_pid_update_ = 0; +}; + +// Fat interface with all methods a stream flow control implementation needs +// to support. gRPC C Core does not support pure virtual functions, so instead +// we abort in any methods which require implementation in the base class. +class StreamFlowControlBase { + public: + StreamFlowControlBase() {} + virtual ~StreamFlowControlBase() {} + + // Updates an action using the protected members. + virtual FlowControlAction UpdateAction(FlowControlAction action) { abort(); } + + // Using the protected members, returns an Action for this stream to be + // taken by the tranport. + virtual FlowControlAction MakeAction() { abort(); } + + // Bookkeeping for when data is sent on this stream. + virtual void SentData(int64_t outgoing_frame_size) { abort(); } + + // Bookkeeping and error checking for when data is received by this stream. + virtual grpc_error* RecvData(int64_t incoming_frame_size) { abort(); } + + // Called to check if this stream needs to send a WINDOW_UPDATE frame. + virtual uint32_t MaybeSendUpdate() { abort(); } + + // Bookkeeping for receiving a WINDOW_UPDATE from for this stream. + virtual void RecvUpdate(uint32_t size) { abort(); } + + // Bookkeeping for when a call pulls bytes out of the transport. At this + // point we consider the data 'used' and can thus let out peer know we are + // ready for more data. + virtual void IncomingByteStreamUpdate(size_t max_size_hint, + size_t have_already) { + abort(); + } + + // Used in certain benchmarks in which we don't want FlowControl to be a + // factor + virtual void TestOnlyForceHugeWindow() {} + + // Getters + int64_t remote_window_delta() { return remote_window_delta_; } + int64_t local_window_delta() { return local_window_delta_; } + int64_t announced_window_delta() { return announced_window_delta_; } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + friend class ::grpc::testing::TrickledCHTTP2; + int64_t remote_window_delta_ = 0; + int64_t local_window_delta_ = 0; + int64_t announced_window_delta_ = 0; +}; + +// Implementation of flow control that does NOTHING. Always returns maximum +// values, never initiates writes, and assumes that the remote peer is doing +// the same. To be used to narrow down on flow control as the cause of negative +// performance. +class StreamFlowControlDisabled : public StreamFlowControlBase { + public: + FlowControlAction UpdateAction(FlowControlAction action) override { + return action; + } + FlowControlAction MakeAction() override { return FlowControlAction(); } + void SentData(int64_t outgoing_frame_size) override {} + grpc_error* RecvData(int64_t incoming_frame_size) override { + return GRPC_ERROR_NONE; + } + uint32_t MaybeSendUpdate() override { return 0; } + void RecvUpdate(uint32_t size) override {} + void IncomingByteStreamUpdate(size_t max_size_hint, + size_t have_already) override {} +}; + +// Implementation of flow control that abides to HTTP/2 spec and attempts +// to be as performant as possible. +class StreamFlowControl final : public StreamFlowControlBase { + public: + StreamFlowControl(TransportFlowControl* tfc, const grpc_chttp2_stream* s); + ~StreamFlowControl() { + tfc_->PreUpdateAnnouncedWindowOverIncomingWindow(announced_window_delta_); + } + + FlowControlAction UpdateAction(FlowControlAction action) override; + FlowControlAction MakeAction() override { + return UpdateAction(tfc_->MakeAction()); + } + + // we have sent data on the wire, we must track this in our bookkeeping for + // the remote peer's flow control. + void SentData(int64_t outgoing_frame_size) override { + FlowControlTrace tracer(" data sent", tfc_, this); + tfc_->StreamSentData(outgoing_frame_size); + remote_window_delta_ -= outgoing_frame_size; + } + + // we have received data from the wire + grpc_error* RecvData(int64_t incoming_frame_size) override; + + // returns an announce if we should send a stream update to our peer, else + // returns zero + uint32_t MaybeSendUpdate() override; + + // we have received a WINDOW_UPDATE frame for a stream + void RecvUpdate(uint32_t size) override { + FlowControlTrace trace("s updt recv", tfc_, this); + remote_window_delta_ += size; + } + + // the application is asking for a certain amount of bytes + void IncomingByteStreamUpdate(size_t max_size_hint, + size_t have_already) override; + + int64_t remote_window_delta() const { return remote_window_delta_; } + int64_t local_window_delta() const { return local_window_delta_; } + int64_t announced_window_delta() const { return announced_window_delta_; } + + const grpc_chttp2_stream* stream() const { return s_; } + + void TestOnlyForceHugeWindow() override { + announced_window_delta_ = 1024 * 1024 * 1024; + local_window_delta_ = 1024 * 1024 * 1024; + remote_window_delta_ = 1024 * 1024 * 1024; + } + + private: + TransportFlowControl* const tfc_; + const grpc_chttp2_stream* const s_; + + void UpdateAnnouncedWindowDelta(TransportFlowControl* tfc, int64_t change) { + tfc->PreUpdateAnnouncedWindowOverIncomingWindow(announced_window_delta_); + announced_window_delta_ += change; + tfc->PostUpdateAnnouncedWindowOverIncomingWindow(announced_window_delta_); + } +}; + +} // namespace chttp2 +} // namespace grpc_core + +#endif diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame.h new file mode 100644 index 00000000000000..083c0076e71c0b --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H + +#include + +#include + +#include "src/core/lib/iomgr/error.h" + +/* defined in internal.h */ +typedef struct grpc_chttp2_stream grpc_chttp2_stream; +typedef struct grpc_chttp2_transport grpc_chttp2_transport; + +#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_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_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.cc new file mode 100644 index 00000000000000..1de00735cf342e --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.cc @@ -0,0 +1,312 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/frame_data.h" + +#include + +#include +#include +#include +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/transport.h" + +grpc_chttp2_data_parser::~grpc_chttp2_data_parser() { + if (parsing_frame != nullptr) { + GRPC_ERROR_UNREF(parsing_frame->Finished( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Parser destroyed"), false)); + } + GRPC_ERROR_UNREF(error); +} + +grpc_error* grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser* parser, + uint8_t flags, + uint32_t stream_id, + grpc_chttp2_stream* s) { + if (flags & ~GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + char* msg; + gpr_asprintf(&msg, "unsupported data flags: 0x%02x", flags); + grpc_error* err = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg), GRPC_ERROR_INT_STREAM_ID, + static_cast(stream_id)); + gpr_free(msg); + return err; + } + + if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + s->received_last_frame = true; + s->eos_received = true; + } else { + s->received_last_frame = false; + } + + return GRPC_ERROR_NONE; +} + +void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer* inbuf, + uint32_t write_bytes, int is_eof, + grpc_transport_one_way_stats* stats, + grpc_slice_buffer* outbuf) { + grpc_slice hdr; + uint8_t* p; + static const size_t header_size = 9; + + hdr = GRPC_SLICE_MALLOC(header_size); + p = GRPC_SLICE_START_PTR(hdr); + GPR_ASSERT(write_bytes < (1 << 24)); + *p++ = static_cast(write_bytes >> 16); + *p++ = static_cast(write_bytes >> 8); + *p++ = static_cast(write_bytes); + *p++ = GRPC_CHTTP2_FRAME_DATA; + *p++ = is_eof ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0; + *p++ = static_cast(id >> 24); + *p++ = static_cast(id >> 16); + *p++ = static_cast(id >> 8); + *p++ = static_cast(id); + grpc_slice_buffer_add(outbuf, hdr); + + grpc_slice_buffer_move_first_no_ref(inbuf, write_bytes, outbuf); + + stats->framing_bytes += header_size; + stats->data_bytes += write_bytes; +} + +grpc_error* grpc_deframe_unprocessed_incoming_frames( + grpc_chttp2_data_parser* p, grpc_chttp2_stream* s, + grpc_slice_buffer* slices, grpc_slice* slice_out, + grpc_core::OrphanablePtr* stream_out) { + grpc_error* error = GRPC_ERROR_NONE; + grpc_chttp2_transport* t = s->t; + + while (slices->count > 0) { + uint8_t* beg = nullptr; + uint8_t* end = nullptr; + uint8_t* cur = nullptr; + + grpc_slice slice = grpc_slice_buffer_take_first(slices); + + beg = GRPC_SLICE_START_PTR(slice); + end = GRPC_SLICE_END_PTR(slice); + cur = beg; + uint32_t message_flags; + char* msg; + + if (cur == end) { + grpc_slice_unref_internal(slice); + continue; + } + + switch (p->state) { + case GRPC_CHTTP2_DATA_ERROR: + p->state = GRPC_CHTTP2_DATA_ERROR; + grpc_slice_unref_internal(slice); + return GRPC_ERROR_REF(p->error); + case GRPC_CHTTP2_DATA_FH_0: + s->stats.incoming.framing_bytes++; + p->frame_type = *cur; + switch (p->frame_type) { + case 0: + p->is_frame_compressed = false; /* GPR_FALSE */ + break; + case 1: + p->is_frame_compressed = true; /* GPR_TRUE */ + break; + default: + gpr_asprintf(&msg, "Bad GRPC frame type 0x%02x", p->frame_type); + p->error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + p->error = grpc_error_set_int(p->error, GRPC_ERROR_INT_STREAM_ID, + static_cast(s->id)); + gpr_free(msg); + msg = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); + p->error = grpc_error_set_str(p->error, GRPC_ERROR_STR_RAW_BYTES, + grpc_slice_from_copied_string(msg)); + gpr_free(msg); + p->error = + grpc_error_set_int(p->error, GRPC_ERROR_INT_OFFSET, cur - beg); + p->state = GRPC_CHTTP2_DATA_ERROR; + grpc_slice_unref_internal(slice); + return GRPC_ERROR_REF(p->error); + } + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_1; + grpc_slice_unref_internal(slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_1: + s->stats.incoming.framing_bytes++; + p->frame_size = (static_cast(*cur)) << 24; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_2; + grpc_slice_unref_internal(slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_2: + s->stats.incoming.framing_bytes++; + p->frame_size |= (static_cast(*cur)) << 16; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_3; + grpc_slice_unref_internal(slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_3: + s->stats.incoming.framing_bytes++; + p->frame_size |= (static_cast(*cur)) << 8; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_4; + grpc_slice_unref_internal(slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_4: + s->stats.incoming.framing_bytes++; + GPR_ASSERT(stream_out != nullptr); + GPR_ASSERT(p->parsing_frame == nullptr); + p->frame_size |= (static_cast(*cur)); + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordMessageReceived(); + } + p->state = GRPC_CHTTP2_DATA_FRAME; + ++cur; + message_flags = 0; + if (p->is_frame_compressed) { + message_flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } + p->parsing_frame = grpc_core::New( + t, s, p->frame_size, message_flags); + stream_out->reset(p->parsing_frame); + if (p->parsing_frame->remaining_bytes() == 0) { + GRPC_ERROR_UNREF(p->parsing_frame->Finished(GRPC_ERROR_NONE, true)); + p->parsing_frame = nullptr; + p->state = GRPC_CHTTP2_DATA_FH_0; + } + s->pending_byte_stream = true; + + if (cur != end) { + grpc_slice_buffer_undo_take_first( + slices, grpc_slice_sub(slice, static_cast(cur - beg), + static_cast(end - beg))); + } + grpc_slice_unref_internal(slice); + return GRPC_ERROR_NONE; + case GRPC_CHTTP2_DATA_FRAME: { + GPR_ASSERT(p->parsing_frame != nullptr); + GPR_ASSERT(slice_out != nullptr); + if (cur == end) { + grpc_slice_unref_internal(slice); + continue; + } + uint32_t remaining = static_cast(end - cur); + if (remaining == p->frame_size) { + s->stats.incoming.data_bytes += remaining; + if (GRPC_ERROR_NONE != + (error = p->parsing_frame->Push( + grpc_slice_sub(slice, static_cast(cur - beg), + static_cast(end - beg)), + slice_out))) { + grpc_slice_unref_internal(slice); + return error; + } + if (GRPC_ERROR_NONE != + (error = p->parsing_frame->Finished(GRPC_ERROR_NONE, true))) { + grpc_slice_unref_internal(slice); + return error; + } + p->parsing_frame = nullptr; + p->state = GRPC_CHTTP2_DATA_FH_0; + grpc_slice_unref_internal(slice); + return GRPC_ERROR_NONE; + } else if (remaining < p->frame_size) { + s->stats.incoming.data_bytes += remaining; + if (GRPC_ERROR_NONE != + (error = p->parsing_frame->Push( + grpc_slice_sub(slice, static_cast(cur - beg), + static_cast(end - beg)), + slice_out))) { + return error; + } + p->frame_size -= remaining; + grpc_slice_unref_internal(slice); + return GRPC_ERROR_NONE; + } else { + GPR_ASSERT(remaining > p->frame_size); + s->stats.incoming.data_bytes += p->frame_size; + if (GRPC_ERROR_NONE != + p->parsing_frame->Push( + grpc_slice_sub( + slice, static_cast(cur - beg), + static_cast(cur + p->frame_size - beg)), + slice_out)) { + grpc_slice_unref_internal(slice); + return error; + } + if (GRPC_ERROR_NONE != + (error = p->parsing_frame->Finished(GRPC_ERROR_NONE, true))) { + grpc_slice_unref_internal(slice); + return error; + } + p->parsing_frame = nullptr; + p->state = GRPC_CHTTP2_DATA_FH_0; + cur += p->frame_size; + grpc_slice_buffer_undo_take_first( + slices, grpc_slice_sub(slice, static_cast(cur - beg), + static_cast(end - beg))); + grpc_slice_unref_internal(slice); + return GRPC_ERROR_NONE; + } + } + } + } + + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_chttp2_data_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last) { + if (!s->pending_byte_stream) { + grpc_slice_ref_internal(slice); + grpc_slice_buffer_add(&s->frame_storage, slice); + grpc_chttp2_maybe_complete_recv_message(t, s); + } else if (s->on_next) { + GPR_ASSERT(s->frame_storage.length == 0); + grpc_slice_ref_internal(slice); + grpc_slice_buffer_add(&s->unprocessed_incoming_frames_buffer, slice); + GRPC_CLOSURE_SCHED(s->on_next, GRPC_ERROR_NONE); + s->on_next = nullptr; + s->unprocessed_incoming_frames_decompressed = false; + } else { + grpc_slice_ref_internal(slice); + grpc_slice_buffer_add(&s->frame_storage, slice); + } + + if (is_last && s->received_last_frame) { + grpc_chttp2_mark_stream_closed(t, s, true, false, GRPC_ERROR_NONE); + } + + return GRPC_ERROR_NONE; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.h new file mode 100644 index 00000000000000..2c5da99fa68d5d --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_data.h @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H + +/* Parser for GRPC streams embedded in DATA frames */ + +#include + +#include +#include +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/transport/byte_stream.h" +#include "src/core/lib/transport/transport.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_DATA_ERROR +} grpc_chttp2_stream_state; + +namespace grpc_core { +class Chttp2IncomingByteStream; +} // namespace grpc_core + +struct grpc_chttp2_data_parser { + grpc_chttp2_data_parser() = default; + ~grpc_chttp2_data_parser(); + + grpc_chttp2_stream_state state = GRPC_CHTTP2_DATA_FH_0; + uint8_t frame_type = 0; + uint32_t frame_size = 0; + grpc_error* error = GRPC_ERROR_NONE; + + bool is_frame_compressed = false; + grpc_core::Chttp2IncomingByteStream* parsing_frame = nullptr; +}; + +/* start processing a new data frame */ +grpc_error* grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser* parser, + uint8_t flags, + uint32_t stream_id, + grpc_chttp2_stream* s); + +/* handle a slice of a data frame - is_last indicates the last slice of a + frame */ +grpc_error* grpc_chttp2_data_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last); + +void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer* inbuf, + uint32_t write_bytes, int is_eof, + grpc_transport_one_way_stats* stats, + grpc_slice_buffer* outbuf); + +grpc_error* grpc_deframe_unprocessed_incoming_frames( + grpc_chttp2_data_parser* p, grpc_chttp2_stream* s, + grpc_slice_buffer* slices, grpc_slice* slice_out, + grpc_core::OrphanablePtr* stream_out); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.cc new file mode 100644 index 00000000000000..2a1dd3c3163994 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.cc @@ -0,0 +1,186 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/frame_goaway.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include + +void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser* p) { + p->debug_data = nullptr; +} + +void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser* p) { + gpr_free(p->debug_data); +} + +grpc_error* grpc_chttp2_goaway_parser_begin_frame(grpc_chttp2_goaway_parser* p, + uint32_t length, + uint8_t flags) { + if (length < 8) { + char* msg; + gpr_asprintf(&msg, "goaway frame too short (%d bytes)", length); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + + gpr_free(p->debug_data); + p->debug_length = length - 8; + p->debug_data = static_cast(gpr_malloc(p->debug_length)); + p->debug_pos = 0; + p->state = GRPC_CHTTP2_GOAWAY_LSI0; + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_chttp2_goaway_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last) { + uint8_t* const beg = GRPC_SLICE_START_PTR(slice); + uint8_t* const end = GRPC_SLICE_END_PTR(slice); + uint8_t* cur = beg; + grpc_chttp2_goaway_parser* p = + static_cast(parser); + + switch (p->state) { + case GRPC_CHTTP2_GOAWAY_LSI0: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI0; + return GRPC_ERROR_NONE; + } + p->last_stream_id = (static_cast(*cur)) << 24; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI1: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI1; + return GRPC_ERROR_NONE; + } + p->last_stream_id |= (static_cast(*cur)) << 16; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI2: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI2; + return GRPC_ERROR_NONE; + } + p->last_stream_id |= (static_cast(*cur)) << 8; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI3: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI3; + return GRPC_ERROR_NONE; + } + p->last_stream_id |= (static_cast(*cur)); + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR0: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR0; + return GRPC_ERROR_NONE; + } + p->error_code = (static_cast(*cur)) << 24; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR1: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR1; + return GRPC_ERROR_NONE; + } + p->error_code |= (static_cast(*cur)) << 16; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR2: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR2; + return GRPC_ERROR_NONE; + } + p->error_code |= (static_cast(*cur)) << 8; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR3: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR3; + return GRPC_ERROR_NONE; + } + p->error_code |= (static_cast(*cur)); + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_DEBUG: + if (end != cur) + memcpy(p->debug_data + p->debug_pos, cur, + static_cast(end - cur)); + GPR_ASSERT((size_t)(end - cur) < UINT32_MAX - p->debug_pos); + p->debug_pos += static_cast(end - cur); + p->state = GRPC_CHTTP2_GOAWAY_DEBUG; + if (is_last) { + grpc_chttp2_add_incoming_goaway( + t, p->error_code, + grpc_slice_new(p->debug_data, p->debug_length, gpr_free)); + p->debug_data = nullptr; + } + return GRPC_ERROR_NONE; + } + GPR_UNREACHABLE_CODE( + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); +} + +void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code, + grpc_slice debug_data, + grpc_slice_buffer* slice_buffer) { + grpc_slice header = GRPC_SLICE_MALLOC(9 + 4 + 4); + uint8_t* p = GRPC_SLICE_START_PTR(header); + uint32_t frame_length; + GPR_ASSERT(GRPC_SLICE_LENGTH(debug_data) < UINT32_MAX - 4 - 4); + frame_length = 4 + 4 + static_cast GRPC_SLICE_LENGTH(debug_data); + + /* frame header: length */ + *p++ = static_cast(frame_length >> 16); + *p++ = static_cast(frame_length >> 8); + *p++ = static_cast(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++ = static_cast(last_stream_id >> 24); + *p++ = static_cast(last_stream_id >> 16); + *p++ = static_cast(last_stream_id >> 8); + *p++ = static_cast(last_stream_id); + /* payload: error code */ + *p++ = static_cast(error_code >> 24); + *p++ = static_cast(error_code >> 16); + *p++ = static_cast(error_code >> 8); + *p++ = static_cast(error_code); + GPR_ASSERT(p == GRPC_SLICE_END_PTR(header)); + grpc_slice_buffer_add(slice_buffer, header); + grpc_slice_buffer_add(slice_buffer, debug_data); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.h new file mode 100644 index 00000000000000..66c7a68befe30f --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_goaway.h @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H + +#include + +#include +#include +#include "src/core/ext/transport/chttp2/transport/frame.h" + +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; + uint32_t last_stream_id; + uint32_t error_code; + char* debug_data; + uint32_t debug_length; + uint32_t 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_error* grpc_chttp2_goaway_parser_begin_frame( + grpc_chttp2_goaway_parser* parser, uint32_t length, uint8_t flags); +grpc_error* grpc_chttp2_goaway_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last); + +void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code, + grpc_slice debug_data, + grpc_slice_buffer* slice_buffer); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.cc new file mode 100644 index 00000000000000..205826b779aa30 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.cc @@ -0,0 +1,131 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/frame_ping.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include + +static bool g_disable_ping_ack = false; + +grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes) { + grpc_slice slice = GRPC_SLICE_MALLOC(9 + 8); + uint8_t* p = GRPC_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; + *p++ = static_cast(opaque_8bytes >> 56); + *p++ = static_cast(opaque_8bytes >> 48); + *p++ = static_cast(opaque_8bytes >> 40); + *p++ = static_cast(opaque_8bytes >> 32); + *p++ = static_cast(opaque_8bytes >> 24); + *p++ = static_cast(opaque_8bytes >> 16); + *p++ = static_cast(opaque_8bytes >> 8); + *p++ = static_cast(opaque_8bytes); + + return slice; +} + +grpc_error* grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser* parser, + uint32_t length, + uint8_t flags) { + if (flags & 0xfe || length != 8) { + char* msg; + gpr_asprintf(&msg, "invalid ping: length=%d, flags=%02x", length, flags); + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + parser->byte = 0; + parser->is_ack = flags; + parser->opaque_8bytes = 0; + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_chttp2_ping_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last) { + uint8_t* const beg = GRPC_SLICE_START_PTR(slice); + uint8_t* const end = GRPC_SLICE_END_PTR(slice); + uint8_t* cur = beg; + grpc_chttp2_ping_parser* p = static_cast(parser); + + while (p->byte != 8 && cur != end) { + p->opaque_8bytes |= ((static_cast(*cur)) << (56 - 8 * p->byte)); + cur++; + p->byte++; + } + + if (p->byte == 8) { + GPR_ASSERT(is_last); + if (p->is_ack) { + grpc_chttp2_ack_ping(t, p->opaque_8bytes); + } else { + if (!t->is_client) { + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + grpc_millis next_allowed_ping = + t->ping_recv_state.last_ping_recv_time + + t->ping_policy.min_recv_ping_interval_without_data; + + if (t->keepalive_permit_without_calls == 0 && + grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + /* According to RFC1122, the interval of TCP Keep-Alive is default to + no less than two hours. When there is no outstanding streams, we + restrict the number of PINGS equivalent to TCP Keep-Alive. */ + next_allowed_ping = + t->ping_recv_state.last_ping_recv_time + 7200 * GPR_MS_PER_SEC; + } + + if (next_allowed_ping > now) { + grpc_chttp2_add_ping_strike(t); + } + + t->ping_recv_state.last_ping_recv_time = now; + } + if (!g_disable_ping_ack) { + if (t->ping_ack_count == t->ping_ack_capacity) { + t->ping_ack_capacity = GPR_MAX(t->ping_ack_capacity * 3 / 2, 3); + t->ping_acks = static_cast(gpr_realloc( + t->ping_acks, t->ping_ack_capacity * sizeof(*t->ping_acks))); + } + t->ping_acks[t->ping_ack_count++] = p->opaque_8bytes; + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE); + } + } + } + + return GRPC_ERROR_NONE; +} + +void grpc_set_disable_ping_ack(bool disable_ping_ack) { + g_disable_ping_ack = disable_ping_ack; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.h new file mode 100644 index 00000000000000..55a4499ad5980e --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_ping.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H + +#include + +#include +#include "src/core/ext/transport/chttp2/transport/frame.h" + +typedef struct { + uint8_t byte; + uint8_t is_ack; + uint64_t opaque_8bytes; +} grpc_chttp2_ping_parser; + +grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes); + +grpc_error* grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser* parser, + uint32_t length, uint8_t flags); +grpc_error* grpc_chttp2_ping_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last); + +/* Test-only function for disabling ping ack */ +void grpc_set_disable_ping_ack(bool disable_ping_ack); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc new file mode 100644 index 00000000000000..a0a753459473f9 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc @@ -0,0 +1,112 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/transport/http2_errors.h" + +grpc_slice grpc_chttp2_rst_stream_create(uint32_t id, uint32_t code, + grpc_transport_one_way_stats* stats) { + static const size_t frame_size = 13; + grpc_slice slice = GRPC_SLICE_MALLOC(frame_size); + if (stats != nullptr) stats->framing_bytes += frame_size; + uint8_t* p = GRPC_SLICE_START_PTR(slice); + + // Frame size. + *p++ = 0; + *p++ = 0; + *p++ = 4; + // Frame type. + *p++ = GRPC_CHTTP2_FRAME_RST_STREAM; + // Flags. + *p++ = 0; + // Stream ID. + *p++ = static_cast(id >> 24); + *p++ = static_cast(id >> 16); + *p++ = static_cast(id >> 8); + *p++ = static_cast(id); + // Error code. + *p++ = static_cast(code >> 24); + *p++ = static_cast(code >> 16); + *p++ = static_cast(code >> 8); + *p++ = static_cast(code); + + return slice; +} + +grpc_error* grpc_chttp2_rst_stream_parser_begin_frame( + grpc_chttp2_rst_stream_parser* parser, uint32_t length, uint8_t flags) { + if (length != 4) { + char* msg; + gpr_asprintf(&msg, "invalid rst_stream: length=%d, flags=%02x", length, + flags); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + parser->byte = 0; + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_chttp2_rst_stream_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last) { + uint8_t* const beg = GRPC_SLICE_START_PTR(slice); + uint8_t* const end = GRPC_SLICE_END_PTR(slice); + uint8_t* cur = beg; + grpc_chttp2_rst_stream_parser* p = + static_cast(parser); + + while (p->byte != 4 && cur != end) { + p->reason_bytes[p->byte] = *cur; + cur++; + p->byte++; + } + s->stats.incoming.framing_bytes += static_cast(end - cur); + + if (p->byte == 4) { + GPR_ASSERT(is_last); + uint32_t reason = ((static_cast(p->reason_bytes[0])) << 24) | + ((static_cast(p->reason_bytes[1])) << 16) | + ((static_cast(p->reason_bytes[2])) << 8) | + ((static_cast(p->reason_bytes[3]))); + grpc_error* error = GRPC_ERROR_NONE; + if (reason != GRPC_HTTP2_NO_ERROR || s->metadata_buffer[1].size == 0) { + char* message; + gpr_asprintf(&message, "Received RST_STREAM with error code %d", reason); + error = grpc_error_set_int( + grpc_error_set_str(GRPC_ERROR_CREATE_FROM_STATIC_STRING("RST_STREAM"), + GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_from_copied_string(message)), + GRPC_ERROR_INT_HTTP2_ERROR, static_cast(reason)); + gpr_free(message); + } + grpc_chttp2_mark_stream_closed(t, s, true, true, error); + } + + return GRPC_ERROR_NONE; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.h new file mode 100644 index 00000000000000..6bcf9c44797bd2 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_rst_stream.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H + +#include + +#include +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/transport/transport.h" + +typedef struct { + uint8_t byte; + uint8_t reason_bytes[4]; +} grpc_chttp2_rst_stream_parser; + +grpc_slice grpc_chttp2_rst_stream_create(uint32_t stream_id, uint32_t code, + grpc_transport_one_way_stats* stats); + +grpc_error* grpc_chttp2_rst_stream_parser_begin_frame( + grpc_chttp2_rst_stream_parser* parser, uint32_t length, uint8_t flags); +grpc_error* grpc_chttp2_rst_stream_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.cc new file mode 100644 index 00000000000000..987ac0e79d019a --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.cc @@ -0,0 +1,238 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/frame_settings.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/transport/http2_errors.h" + +static uint8_t* fill_header(uint8_t* out, uint32_t length, uint8_t flags) { + *out++ = static_cast(length >> 16); + *out++ = static_cast(length >> 8); + *out++ = static_cast(length); + *out++ = GRPC_CHTTP2_FRAME_SETTINGS; + *out++ = flags; + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = 0; + return out; +} + +grpc_slice grpc_chttp2_settings_create(uint32_t* old_settings, + const uint32_t* new_settings, + uint32_t force_mask, size_t count) { + size_t i; + uint32_t n = 0; + grpc_slice output; + uint8_t* p; + + for (i = 0; i < count; i++) { + n += (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0); + } + + output = GRPC_SLICE_MALLOC(9 + 6 * n); + p = fill_header(GRPC_SLICE_START_PTR(output), 6 * n, 0); + + for (i = 0; i < count; i++) { + if (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0) { + *p++ = static_cast(grpc_setting_id_to_wire_id[i] >> 8); + *p++ = static_cast(grpc_setting_id_to_wire_id[i]); + *p++ = static_cast(new_settings[i] >> 24); + *p++ = static_cast(new_settings[i] >> 16); + *p++ = static_cast(new_settings[i] >> 8); + *p++ = static_cast(new_settings[i]); + old_settings[i] = new_settings[i]; + } + } + + GPR_ASSERT(p == GRPC_SLICE_END_PTR(output)); + + return output; +} + +grpc_slice grpc_chttp2_settings_ack_create(void) { + grpc_slice output = GRPC_SLICE_MALLOC(9); + fill_header(GRPC_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK); + return output; +} + +grpc_error* grpc_chttp2_settings_parser_begin_frame( + grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags, + uint32_t* settings) { + parser->target_settings = settings; + memcpy(parser->incoming_settings, settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); + parser->is_ack = 0; + parser->state = GRPC_CHTTP2_SPS_ID0; + if (flags == GRPC_CHTTP2_FLAG_ACK) { + parser->is_ack = 1; + if (length != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "non-empty settings ack frame received"); + } + return GRPC_ERROR_NONE; + } else if (flags != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "invalid flags on settings frame"); + } else if (length % 6 != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "settings frames must be a multiple of six bytes"); + } else { + return GRPC_ERROR_NONE; + } +} + +grpc_error* grpc_chttp2_settings_parser_parse(void* p, grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last) { + grpc_chttp2_settings_parser* parser = + static_cast(p); + const uint8_t* cur = GRPC_SLICE_START_PTR(slice); + const uint8_t* end = GRPC_SLICE_END_PTR(slice); + char* msg; + grpc_chttp2_setting_id id; + + if (parser->is_ack) { + return GRPC_ERROR_NONE; + } + + for (;;) { + switch (parser->state) { + case GRPC_CHTTP2_SPS_ID0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID0; + if (is_last) { + memcpy(parser->target_settings, parser->incoming_settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); + grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_settings_ack_create()); + if (t->notify_on_receive_settings != nullptr) { + GRPC_CLOSURE_SCHED(t->notify_on_receive_settings, + GRPC_ERROR_NONE); + t->notify_on_receive_settings = nullptr; + } + } + return GRPC_ERROR_NONE; + } + parser->id = static_cast((static_cast(*cur)) << 8); + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_ID1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID1; + return GRPC_ERROR_NONE; + } + parser->id = static_cast(parser->id | (*cur)); + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL0; + return GRPC_ERROR_NONE; + } + parser->value = (static_cast(*cur)) << 24; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL1; + return GRPC_ERROR_NONE; + } + parser->value |= (static_cast(*cur)) << 16; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL2: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL2; + return GRPC_ERROR_NONE; + } + parser->value |= (static_cast(*cur)) << 8; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL3: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL3; + return GRPC_ERROR_NONE; + } else { + parser->state = GRPC_CHTTP2_SPS_ID0; + } + parser->value |= *cur; + cur++; + + if (grpc_wire_id_to_setting_id(parser->id, &id)) { + const grpc_chttp2_setting_parameters* sp = + &grpc_chttp2_settings_parameters[id]; + // If flow control is disabled we skip these. + if (!t->flow_control->flow_control_enabled() && + (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE || + id == GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE)) { + continue; + } + 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: + grpc_chttp2_goaway_append( + t->last_new_stream_id, sp->error_value, + grpc_slice_from_static_string("HTTP2 settings error"), + &t->qbuf); + gpr_asprintf(&msg, "invalid value %u passed for %s", + parser->value, sp->name); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + } + if (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE && + parser->incoming_settings[id] != parser->value) { + t->initial_window_update += static_cast(parser->value) - + parser->incoming_settings[id]; + if (grpc_http_trace.enabled() || grpc_flowctl_trace.enabled()) { + gpr_log(GPR_INFO, "%p[%s] adding %d for initial_window change", t, + t->is_client ? "cli" : "svr", + static_cast(t->initial_window_update)); + } + } + parser->incoming_settings[id] = parser->value; + if (grpc_http_trace.enabled()) { + gpr_log(GPR_INFO, "CHTTP2:%s:%s: got setting %s = %d", + t->is_client ? "CLI" : "SVR", t->peer_string, sp->name, + parser->value); + } + } else if (grpc_http_trace.enabled()) { + gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)", + parser->id, parser->value); + } + break; + } + } +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.h new file mode 100644 index 00000000000000..8d8d9b1a914caa --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_settings.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H + +#include + +#include +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/ext/transport/chttp2/transport/http2_settings.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; + +typedef struct { + grpc_chttp2_settings_parse_state state; + uint32_t* target_settings; + uint8_t is_ack; + uint16_t id; + uint32_t value; + uint32_t incoming_settings[GRPC_CHTTP2_NUM_SETTINGS]; +} grpc_chttp2_settings_parser; + +/* Create a settings frame by diffing old & new, and updating old to be new */ +grpc_slice grpc_chttp2_settings_create(uint32_t* old, const uint32_t* newval, + uint32_t force_mask, size_t count); +/* Create an ack settings frame */ +grpc_slice grpc_chttp2_settings_ack_create(void); + +grpc_error* grpc_chttp2_settings_parser_begin_frame( + grpc_chttp2_settings_parser* parser, uint32_t length, uint8_t flags, + uint32_t* settings); +grpc_error* grpc_chttp2_settings_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.cc new file mode 100644 index 00000000000000..4b586dc3e7f36b --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/frame_window_update.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include +#include + +grpc_slice grpc_chttp2_window_update_create( + uint32_t id, uint32_t window_update, grpc_transport_one_way_stats* stats) { + static const size_t frame_size = 13; + grpc_slice slice = GRPC_SLICE_MALLOC(frame_size); + stats->header_bytes += frame_size; + uint8_t* p = GRPC_SLICE_START_PTR(slice); + + GPR_ASSERT(window_update); + + *p++ = 0; + *p++ = 0; + *p++ = 4; + *p++ = GRPC_CHTTP2_FRAME_WINDOW_UPDATE; + *p++ = 0; + *p++ = static_cast(id >> 24); + *p++ = static_cast(id >> 16); + *p++ = static_cast(id >> 8); + *p++ = static_cast(id); + *p++ = static_cast(window_update >> 24); + *p++ = static_cast(window_update >> 16); + *p++ = static_cast(window_update >> 8); + *p++ = static_cast(window_update); + + return slice; +} + +grpc_error* grpc_chttp2_window_update_parser_begin_frame( + grpc_chttp2_window_update_parser* parser, uint32_t length, uint8_t flags) { + if (flags || length != 4) { + char* msg; + gpr_asprintf(&msg, "invalid window update: length=%d, flags=%02x", length, + flags); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + parser->byte = 0; + parser->amount = 0; + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_chttp2_window_update_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, + int is_last) { + uint8_t* const beg = GRPC_SLICE_START_PTR(slice); + uint8_t* const end = GRPC_SLICE_END_PTR(slice); + uint8_t* cur = beg; + grpc_chttp2_window_update_parser* p = + static_cast(parser); + + while (p->byte != 4 && cur != end) { + p->amount |= (static_cast(*cur)) << (8 * (3 - p->byte)); + cur++; + p->byte++; + } + + if (s != nullptr) { + s->stats.incoming.framing_bytes += static_cast(end - cur); + } + + if (p->byte == 4) { + uint32_t received_update = p->amount; + if (received_update == 0 || (received_update & 0x80000000u)) { + char* msg; + gpr_asprintf(&msg, "invalid window update bytes: %d", p->amount); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + GPR_ASSERT(is_last); + + if (t->incoming_stream_id != 0) { + if (s != nullptr) { + s->flow_control->RecvUpdate(received_update); + if (grpc_chttp2_list_remove_stalled_by_stream(t, s)) { + grpc_chttp2_mark_stream_writable(t, s); + grpc_chttp2_initiate_write( + t, GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE); + } + } + } else { + bool was_zero = t->flow_control->remote_window() <= 0; + t->flow_control->RecvUpdate(received_update); + bool is_zero = t->flow_control->remote_window() <= 0; + if (was_zero && !is_zero) { + grpc_chttp2_initiate_write( + t, GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED); + } + } + } + + return GRPC_ERROR_NONE; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.h new file mode 100644 index 00000000000000..3d2391f637d2d9 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/frame_window_update.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H + +#include + +#include +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/transport/transport.h" + +typedef struct { + uint8_t byte; + uint8_t is_connection_update; + uint32_t amount; +} grpc_chttp2_window_update_parser; + +grpc_slice grpc_chttp2_window_update_create( + uint32_t id, uint32_t window_delta, grpc_transport_one_way_stats* stats); + +grpc_error* grpc_chttp2_window_update_parser_begin_frame( + grpc_chttp2_window_update_parser* parser, uint32_t length, uint8_t flags); +grpc_error* grpc_chttp2_window_update_parser_parse(void* parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, + int is_last); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.cc new file mode 100644 index 00000000000000..dbe9df6ae38373 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.cc @@ -0,0 +1,717 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" + +#include +#include + +/* This is here for grpc_is_binary_header + * TODO(murgatroid99): Remove this + */ +#include + +#include +#include + +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/ext/transport/chttp2/transport/hpack_table.h" +#include "src/core/ext/transport/chttp2/transport/varint.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/timeout_encoding.h" + +#define HASH_FRAGMENT_MASK (GRPC_CHTTP2_HPACKC_NUM_VALUES - 1) +#define HASH_FRAGMENT_1(x) ((x)&HASH_FRAGMENT_MASK) +#define HASH_FRAGMENT_2(x) \ + (((x) >> GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS) & HASH_FRAGMENT_MASK) +#define HASH_FRAGMENT_3(x) \ + (((x) >> (GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS * 2)) & HASH_FRAGMENT_MASK) +#define HASH_FRAGMENT_4(x) \ + (((x) >> (GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS * 3)) & HASH_FRAGMENT_MASK) + +/* 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 (GRPC_CHTTP2_HPACKC_NUM_VALUES >> 1) +/* don't consider adding anything bigger than this to the hpack table */ +#define MAX_DECODER_SPACE_USAGE 512 + +static grpc_slice_refcount terminal_slice_refcount = {nullptr, nullptr}; +static const grpc_slice terminal_slice = { + &terminal_slice_refcount, /* refcount */ + {{nullptr, 0}} /* data.refcounted */ +}; + +typedef struct { + int is_first_frame; + /* 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; + /* have we seen a regular (non-colon-prefixed) header yet? */ + uint8_t seen_regular_header; + /* output stream id */ + uint32_t stream_id; + grpc_slice_buffer* output; + grpc_transport_one_way_stats* stats; + /* maximum size of a frame */ + size_t max_frame_size; + bool use_true_binary_metadata; +} framer_state; + +/* fills p (which is expected to be 9 bytes long) with a data frame header */ +static void fill_header(uint8_t* p, uint8_t type, uint32_t id, size_t len, + uint8_t flags) { + GPR_ASSERT(len < 16777316); + *p++ = static_cast(len >> 16); + *p++ = static_cast(len >> 8); + *p++ = static_cast(len); + *p++ = type; + *p++ = flags; + *p++ = static_cast(id >> 24); + *p++ = static_cast(id >> 16); + *p++ = static_cast(id >> 8); + *p++ = static_cast(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) { + uint8_t type = 0xff; + type = st->is_first_frame ? GRPC_CHTTP2_FRAME_HEADER + : GRPC_CHTTP2_FRAME_CONTINUATION; + fill_header( + GRPC_SLICE_START_PTR(st->output->slices[st->header_idx]), type, + st->stream_id, st->output->length - st->output_length_at_start_of_frame, + static_cast( + (is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | + (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0))); + st->stats->framing_bytes += 9; + st->is_first_frame = 0; +} + +/* begin a new frame: reserve off header space, remember how many bytes we'd + output before beginning */ +static void begin_frame(framer_state* st) { + st->header_idx = + grpc_slice_buffer_add_indexed(st->output, GRPC_SLICE_MALLOC(9)); + st->output_length_at_start_of_frame = st->output->length; +} + +/* 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_space(framer_state* st, size_t need_bytes) { + if (st->output->length - st->output_length_at_start_of_frame + need_bytes <= + st->max_frame_size) { + return; + } + finish_frame(st, 0, 0); + begin_frame(st); +} + +/* increment a filter count, halve all counts if one element reaches max */ +static void inc_filter(uint8_t idx, uint32_t* sum, uint8_t* elems) { + elems[idx]++; + if (elems[idx] < 255) { + (*sum)++; + } else { + int i; + *sum = 0; + for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) { + elems[i] /= 2; + (*sum) += elems[i]; + } + } +} + +static void add_header_data(framer_state* st, grpc_slice slice) { + size_t len = GRPC_SLICE_LENGTH(slice); + size_t remaining; + if (len == 0) return; + remaining = st->max_frame_size + st->output_length_at_start_of_frame - + st->output->length; + if (len <= remaining) { + st->stats->header_bytes += len; + grpc_slice_buffer_add(st->output, slice); + } else { + st->stats->header_bytes += remaining; + grpc_slice_buffer_add(st->output, grpc_slice_split_head(&slice, remaining)); + finish_frame(st, 0, 0); + begin_frame(st); + add_header_data(st, slice); + } +} + +static uint8_t* add_tiny_header_data(framer_state* st, size_t len) { + ensure_space(st, len); + st->stats->header_bytes += len; + return grpc_slice_buffer_tiny_add(st->output, len); +} + +static void evict_entry(grpc_chttp2_hpack_compressor* c) { + c->tail_remote_index++; + GPR_ASSERT(c->tail_remote_index > 0); + GPR_ASSERT(c->table_size >= + c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); + GPR_ASSERT(c->table_elems > 0); + c->table_size = static_cast( + c->table_size - + c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); + c->table_elems--; +} + +// Reserve space in table for the new element, evict entries if needed. +// Return the new index of the element. Return 0 to indicate not adding to +// table. +static uint32_t prepare_space_for_new_elem(grpc_chttp2_hpack_compressor* c, + size_t elem_size) { + uint32_t new_index = c->tail_remote_index + c->table_elems + 1; + GPR_ASSERT(elem_size < 65536); + + if (elem_size > c->max_table_size) { + while (c->table_size > 0) { + evict_entry(c); + } + return 0; + } + + /* Reserve space for this element in the remote table: if this overflows + the current table, drop elements until it fits, matching the decompressor + algorithm */ + while (c->table_size + elem_size > c->max_table_size) { + evict_entry(c); + } + GPR_ASSERT(c->table_elems < c->max_table_size); + c->table_elem_size[new_index % c->cap_table_elems] = + static_cast(elem_size); + c->table_size = static_cast(c->table_size + elem_size); + c->table_elems++; + + return new_index; +} + +// Add a key to the dynamic table. Both key and value will be added to table at +// the decoder. +static void add_key_with_index(grpc_chttp2_hpack_compressor* c, + grpc_mdelem elem, uint32_t new_index) { + if (new_index == 0) { + return; + } + + uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem)); + + /* Store the key into {entries,indices}_keys */ + if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)], + GRPC_MDKEY(elem))) { + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)], + GRPC_MDKEY(elem))) { + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)].refcount == + &terminal_slice_refcount) { + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)].refcount == + &terminal_slice_refcount) { + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + 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_slice_unref_internal(c->entries_keys[HASH_FRAGMENT_2(key_hash)]); + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else { + grpc_slice_unref_internal(c->entries_keys[HASH_FRAGMENT_3(key_hash)]); + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } +} + +/* add an element to the decoder table */ +static void add_elem_with_index(grpc_chttp2_hpack_compressor* c, + grpc_mdelem elem, uint32_t new_index) { + if (new_index == 0) { + return; + } + GPR_ASSERT(GRPC_MDELEM_IS_INTERNED(elem)); + + uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem)); + uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem)); + uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash); + + /* Store this element into {entries,indices}_elem */ + if (grpc_mdelem_eq(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; + } else if (grpc_mdelem_eq(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; + } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_2(elem_hash)])) { + /* not there, but a free element: add */ + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_3(elem_hash)])) { + /* not there (cuckoo), but a free element: add */ + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] < + c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) { + /* not there: replace oldest */ + GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_2(elem_hash)]); + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + } else { + /* not there: replace oldest */ + GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_3(elem_hash)]); + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + } + + add_key_with_index(c, elem, new_index); +} + +static void add_elem(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem, + size_t elem_size) { + uint32_t new_index = prepare_space_for_new_elem(c, elem_size); + add_elem_with_index(c, elem, new_index); +} + +static void add_key(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem, + size_t elem_size) { + uint32_t new_index = prepare_space_for_new_elem(c, elem_size); + add_key_with_index(c, elem, new_index); +} + +static void emit_indexed(grpc_chttp2_hpack_compressor* c, uint32_t elem_index, + framer_state* st) { + GRPC_STATS_INC_HPACK_SEND_INDEXED(); + uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(elem_index, 1); + GRPC_CHTTP2_WRITE_VARINT(elem_index, 1, 0x80, add_tiny_header_data(st, len), + len); +} + +typedef struct { + grpc_slice data; + uint8_t huffman_prefix; + bool insert_null_before_wire_value; +} wire_value; + +static wire_value get_wire_value(grpc_mdelem elem, bool true_binary_enabled) { + wire_value wire_val; + if (grpc_is_binary_header(GRPC_MDKEY(elem))) { + if (true_binary_enabled) { + GRPC_STATS_INC_HPACK_SEND_BINARY(); + wire_val.huffman_prefix = 0x00; + wire_val.insert_null_before_wire_value = true; + wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem)); + + } else { + GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64(); + wire_val.huffman_prefix = 0x80; + wire_val.insert_null_before_wire_value = false; + wire_val.data = + grpc_chttp2_base64_encode_and_huffman_compress(GRPC_MDVALUE(elem)); + } + } else { + /* TODO(ctiller): opportunistically compress non-binary headers */ + GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(); + wire_val.huffman_prefix = 0x00; + wire_val.insert_null_before_wire_value = false; + wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem)); + } + return wire_val; +} + +static size_t wire_value_length(wire_value v) { + return GPR_SLICE_LENGTH(v.data) + v.insert_null_before_wire_value; +} + +static void add_wire_value(framer_state* st, wire_value v) { + if (v.insert_null_before_wire_value) *add_tiny_header_data(st, 1) = 0; + add_header_data(st, v.data); +} + +static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor* c, + uint32_t key_index, grpc_mdelem elem, + framer_state* st) { + GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX(); + uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); + wire_value value = get_wire_value(elem, st->use_true_binary_metadata); + size_t len_val = wire_value_length(value); + uint32_t len_val_len; + GPR_ASSERT(len_val <= UINT32_MAX); + len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor* c, + uint32_t key_index, grpc_mdelem elem, + framer_state* st) { + GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX(); + uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); + wire_value value = get_wire_value(elem, st->use_true_binary_metadata); + size_t len_val = wire_value_length(value); + uint32_t len_val_len; + GPR_ASSERT(len_val <= UINT32_MAX); + len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor* c, + uint32_t unused_index, grpc_mdelem elem, + framer_state* st) { + GPR_ASSERT(unused_index == 0); + GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V(); + GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(); + uint32_t len_key = static_cast GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); + wire_value value = get_wire_value(elem, st->use_true_binary_metadata); + uint32_t len_val = static_cast(wire_value_length(value)); + uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GPR_ASSERT(len_key <= UINT32_MAX); + GPR_ASSERT(wire_value_length(value) <= UINT32_MAX); + *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, grpc_slice_ref_internal(GRPC_MDKEY(elem))); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor* c, + uint32_t unused_index, grpc_mdelem elem, + framer_state* st) { + GPR_ASSERT(unused_index == 0); + GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V(); + GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(); + uint32_t len_key = static_cast GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); + wire_value value = get_wire_value(elem, st->use_true_binary_metadata); + uint32_t len_val = static_cast(wire_value_length(value)); + uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GPR_ASSERT(len_key <= UINT32_MAX); + GPR_ASSERT(wire_value_length(value) <= UINT32_MAX); + *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, grpc_slice_ref_internal(GRPC_MDKEY(elem))); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor* c, + framer_state* st) { + uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(c->max_table_size, 3); + GRPC_CHTTP2_WRITE_VARINT(c->max_table_size, 3, 0x20, + add_tiny_header_data(st, len), len); + c->advertise_table_size_change = 0; +} + +static uint32_t dynidx(grpc_chttp2_hpack_compressor* c, uint32_t elem_index) { + return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + + c->table_elems - elem_index; +} + +/* encode an mdelem */ +static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem, + framer_state* st) { + GPR_ASSERT(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)) > 0); + if (GRPC_SLICE_START_PTR(GRPC_MDKEY(elem))[0] != ':') { /* regular header */ + st->seen_regular_header = 1; + } else { + GPR_ASSERT( + st->seen_regular_header == 0 && + "Reserved header (colon-prefixed) happening after regular ones."); + } + + if (grpc_http_trace.enabled()) { + char* k = grpc_slice_to_c_string(GRPC_MDKEY(elem)); + char* v = nullptr; + if (grpc_is_binary_header(GRPC_MDKEY(elem))) { + v = grpc_dump_slice(GRPC_MDVALUE(elem), GPR_DUMP_HEX); + } else { + v = grpc_slice_to_c_string(GRPC_MDVALUE(elem)); + } + gpr_log( + GPR_INFO, + "Encode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d", + k, v, GRPC_MDELEM_IS_INTERNED(elem), GRPC_MDELEM_STORAGE(elem), + grpc_slice_is_interned(GRPC_MDKEY(elem)), + grpc_slice_is_interned(GRPC_MDVALUE(elem))); + gpr_free(k); + gpr_free(v); + } + + bool elem_interned = GRPC_MDELEM_IS_INTERNED(elem); + bool key_interned = elem_interned || grpc_slice_is_interned(GRPC_MDKEY(elem)); + + // Key is not interned, emit literals. + if (!key_interned) { + emit_lithdr_noidx_v(c, 0, elem, st); + return; + } + + uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem)); + uint32_t elem_hash = 0; + + if (elem_interned) { + uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem)); + elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash); + + inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, + c->filter_elems); + + /* is this elem currently in the decoders table? */ + + if (grpc_mdelem_eq(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; + } + + if (grpc_mdelem_eq(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; + } + } + + uint32_t indices_key; + + /* should this elem be in the table? */ + const size_t decoder_space_usage = + grpc_chttp2_get_size_in_hpack_table(elem, st->use_true_binary_metadata); + const bool should_add_elem = elem_interned && + decoder_space_usage < MAX_DECODER_SPACE_USAGE && + c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >= + c->filter_elems_sum / ONE_ON_ADD_PROBABILITY; + + auto emit_maybe_add = [&should_add_elem, &elem, &st, &c, &indices_key, + &decoder_space_usage] { + if (should_add_elem) { + emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st); + add_elem(c, elem, decoder_space_usage); + } else { + emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); + } + }; + + /* no hits for the elem... maybe there's a key? */ + indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)]; + if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)], + GRPC_MDKEY(elem)) && + indices_key > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + emit_maybe_add(); + return; + } + + indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)]; + if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)], + GRPC_MDKEY(elem)) && + indices_key > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + emit_maybe_add(); + return; + } + + /* no elem, key in the table... fall back to literal emission */ + const bool should_add_key = + !elem_interned && decoder_space_usage < MAX_DECODER_SPACE_USAGE; + if (should_add_elem || should_add_key) { + emit_lithdr_incidx_v(c, 0, elem, st); + } else { + emit_lithdr_noidx_v(c, 0, elem, st); + } + if (should_add_elem) { + add_elem(c, elem, decoder_space_usage); + } else if (should_add_key) { + add_key(c, elem, decoder_space_usage); + } +} + +#define STRLEN_LIT(x) (sizeof(x) - 1) +#define TIMEOUT_KEY "grpc-timeout" + +static void deadline_enc(grpc_chttp2_hpack_compressor* c, grpc_millis deadline, + framer_state* st) { + char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE]; + grpc_mdelem mdelem; + grpc_http2_encode_timeout(deadline - grpc_core::ExecCtx::Get()->Now(), + timeout_str); + mdelem = grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_TIMEOUT, + grpc_slice_from_copied_string(timeout_str)); + hpack_enc(c, mdelem, st); + GRPC_MDELEM_UNREF(mdelem); +} + +static uint32_t elems_for_bytes(uint32_t bytes) { return (bytes + 31) / 32; } + +void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor* c) { + memset(c, 0, sizeof(*c)); + c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; + c->cap_table_elems = elems_for_bytes(c->max_table_size); + c->max_table_elems = c->cap_table_elems; + c->max_usable_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; + c->table_elem_size = static_cast( + gpr_malloc(sizeof(*c->table_elem_size) * c->cap_table_elems)); + memset(c->table_elem_size, 0, + sizeof(*c->table_elem_size) * c->cap_table_elems); + for (size_t i = 0; i < GPR_ARRAY_SIZE(c->entries_keys); i++) { + c->entries_keys[i] = terminal_slice; + } +} + +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].refcount != &terminal_slice_refcount) { + grpc_slice_unref_internal(c->entries_keys[i]); + } + GRPC_MDELEM_UNREF(c->entries_elems[i]); + } + gpr_free(c->table_elem_size); +} + +void grpc_chttp2_hpack_compressor_set_max_usable_size( + grpc_chttp2_hpack_compressor* c, uint32_t max_table_size) { + c->max_usable_size = max_table_size; + grpc_chttp2_hpack_compressor_set_max_table_size( + c, GPR_MIN(c->max_table_size, max_table_size)); +} + +static void rebuild_elems(grpc_chttp2_hpack_compressor* c, uint32_t new_cap) { + uint16_t* table_elem_size = + static_cast(gpr_malloc(sizeof(*table_elem_size) * new_cap)); + uint32_t i; + + memset(table_elem_size, 0, sizeof(*table_elem_size) * new_cap); + GPR_ASSERT(c->table_elems <= new_cap); + + for (i = 0; i < c->table_elems; i++) { + uint32_t ofs = c->tail_remote_index + i + 1; + table_elem_size[ofs % new_cap] = + c->table_elem_size[ofs % c->cap_table_elems]; + } + + c->cap_table_elems = new_cap; + gpr_free(c->table_elem_size); + c->table_elem_size = table_elem_size; +} + +void grpc_chttp2_hpack_compressor_set_max_table_size( + grpc_chttp2_hpack_compressor* c, uint32_t max_table_size) { + max_table_size = GPR_MIN(max_table_size, c->max_usable_size); + if (max_table_size == c->max_table_size) { + return; + } + while (c->table_size > 0 && c->table_size > max_table_size) { + evict_entry(c); + } + c->max_table_size = max_table_size; + c->max_table_elems = elems_for_bytes(max_table_size); + if (c->max_table_elems > c->cap_table_elems) { + rebuild_elems(c, GPR_MAX(c->max_table_elems, 2 * c->cap_table_elems)); + } else if (c->max_table_elems < c->cap_table_elems / 3) { + uint32_t new_cap = GPR_MAX(c->max_table_elems, 16); + if (new_cap != c->cap_table_elems) { + rebuild_elems(c, new_cap); + } + } + c->advertise_table_size_change = 1; + if (grpc_http_trace.enabled()) { + gpr_log(GPR_INFO, "set max table size from encoder to %d", max_table_size); + } +} + +void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c, + grpc_mdelem** extra_headers, + size_t extra_headers_size, + grpc_metadata_batch* metadata, + const grpc_encode_header_options* options, + grpc_slice_buffer* outbuf) { + GPR_ASSERT(options->stream_id != 0); + + framer_state st; + st.seen_regular_header = 0; + st.stream_id = options->stream_id; + st.output = outbuf; + st.is_first_frame = 1; + st.stats = options->stats; + st.max_frame_size = options->max_frame_size; + st.use_true_binary_metadata = options->use_true_binary_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_frame(&st); + if (c->advertise_table_size_change != 0) { + emit_advertise_table_size_change(c, &st); + } + for (size_t i = 0; i < extra_headers_size; ++i) { + grpc_mdelem md = *extra_headers[i]; + uint8_t static_index = grpc_chttp2_get_static_hpack_table_index(md); + if (static_index) { + emit_indexed(c, static_index, &st); + } else { + hpack_enc(c, md, &st); + } + } + grpc_metadata_batch_assert_ok(metadata); + for (grpc_linked_mdelem* l = metadata->list.head; l; l = l->next) { + uint8_t static_index = grpc_chttp2_get_static_hpack_table_index(l->md); + if (static_index) { + emit_indexed(c, static_index, &st); + } else { + hpack_enc(c, l->md, &st); + } + } + grpc_millis deadline = metadata->deadline; + if (deadline != GRPC_MILLIS_INF_FUTURE) { + deadline_enc(c, deadline, &st); + } + + finish_frame(&st, 1, options->is_eof); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.h new file mode 100644 index 00000000000000..e31a7399d782a1 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_encoder.h @@ -0,0 +1,96 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H + +#include + +#include +#include +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/transport.h" + +// This should be <= 8. We use 6 to save space. +#define GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS 6 +#define GRPC_CHTTP2_HPACKC_NUM_VALUES (1 << GRPC_CHTTP2_HPACKC_NUM_VALUES_BITS) +/* initial table size, per spec */ +#define GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE 4096 +/* maximum table size we'll actually use */ +#define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024) + +extern grpc_core::TraceFlag grpc_http_trace; + +typedef struct { + uint32_t filter_elems_sum; + uint32_t max_table_size; + uint32_t max_table_elems; + uint32_t cap_table_elems; + /** if non-zero, advertise to the decoder that we'll start using a table + of this size */ + uint8_t advertise_table_size_change; + /** maximum number of bytes we'll use for the decode table (to guard against + peers ooming us by setting decode table size high) */ + uint32_t max_usable_size; + /* one before the lowest usable table index */ + uint32_t tail_remote_index; + uint32_t table_size; + uint32_t 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. */ + uint8_t filter_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + + /* entry tables for keys & elems: these tables track values that have been + seen and *may* be in the decompressor table */ + grpc_slice entries_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + grpc_mdelem entries_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + uint32_t indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + uint32_t indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; + + uint16_t* table_elem_size; +} grpc_chttp2_hpack_compressor; + +void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor* c); +void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor* c); +void grpc_chttp2_hpack_compressor_set_max_table_size( + grpc_chttp2_hpack_compressor* c, uint32_t max_table_size); +void grpc_chttp2_hpack_compressor_set_max_usable_size( + grpc_chttp2_hpack_compressor* c, uint32_t max_table_size); + +typedef struct { + uint32_t stream_id; + bool is_eof; + bool use_true_binary_metadata; + size_t max_frame_size; + grpc_transport_one_way_stats* stats; +} grpc_encode_header_options; + +void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c, + grpc_mdelem** extra_headers, + size_t extra_headers_size, + grpc_metadata_batch* metadata, + const grpc_encode_header_options* options, + grpc_slice_buffer* outbuf); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/hpack_parser.c b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.cc similarity index 59% rename from third_party/grpc/src/core/transport/chttp2/hpack_parser.c rename to third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.cc index a63c7db1f61371..ccf2256974a2b2 100644 --- a/third_party/grpc/src/core/transport/chttp2/hpack_parser.c +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.cc @@ -1,59 +1,45 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include "src/core/transport/chttp2/hpack_parser.h" -#include "src/core/transport/chttp2/internal.h" +#include + +#include "src/core/ext/transport/chttp2/transport/hpack_parser.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include #include #include -#include - -/* This is here for grpc_is_binary_header - * TODO(murgatroid99): Remove this - */ -#include #include #include -#include -#include +#include -#include "src/core/profiling/timers.h" -#include "src/core/support/string.h" -#include "src/core/transport/chttp2/bin_encoder.h" +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/http2_errors.h" typedef enum { NOT_BINARY, + BINARY_BEGIN, B64_BYTE0, B64_BYTE1, B64_BYTE2, @@ -75,63 +61,70 @@ typedef enum { 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 uint8_t *cur, - const uint8_t *end); -static int parse_error(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); - -static int parse_string_prefix(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_key_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); - -static int parse_value0(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_value1(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_value2(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_value3(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_value5up(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); - -static int parse_indexed_field(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); +static grpc_error* parse_begin(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_error(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end, grpc_error* error); +static grpc_error* still_parse_error(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_illegal_op(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); + +static grpc_error* parse_string_prefix(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_key_string(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_value_string_with_indexed_key( + grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_value_string_with_literal_key( + grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* end); + +static grpc_error* parse_value0(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_value1(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_value2(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_value3(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_value4(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_value5up(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); + +static grpc_error* parse_indexed_field(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_indexed_field_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_lithdr_incidx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_lithdr_incidx_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_lithdr_incidx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_lithdr_notidx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_lithdr_notidx_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_lithdr_notidx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_lithdr_nvridx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end); +static grpc_error* parse_max_tbl_size(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end); +static grpc_error* parse_max_tbl_size_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* 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. @@ -629,118 +622,165 @@ static const uint8_t inverse_base64[256] = { }; /* emission helpers */ -static int on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md, - int add_to_table) { - if (add_to_table) { - if (!grpc_chttp2_hptbl_add(&p->table, md)) { - return 0; +static grpc_error* on_hdr(grpc_chttp2_hpack_parser* p, grpc_mdelem md, + int add_to_table) { + if (grpc_http_trace.enabled()) { + char* k = grpc_slice_to_c_string(GRPC_MDKEY(md)); + char* v = nullptr; + if (grpc_is_binary_header(GRPC_MDKEY(md))) { + v = grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX); + } else { + v = grpc_slice_to_c_string(GRPC_MDVALUE(md)); } + gpr_log( + GPR_INFO, + "Decode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d", + k, v, GRPC_MDELEM_IS_INTERNED(md), GRPC_MDELEM_STORAGE(md), + grpc_slice_is_interned(GRPC_MDKEY(md)), + grpc_slice_is_interned(GRPC_MDVALUE(md))); + gpr_free(k); + gpr_free(v); + } + if (add_to_table) { + GPR_ASSERT(GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_INTERNED || + GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC); + grpc_error* err = grpc_chttp2_hptbl_add(&p->table, md); + if (err != GRPC_ERROR_NONE) return err; + } + if (p->on_header == nullptr) { + GRPC_MDELEM_UNREF(md); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("on_header callback not set"); } p->on_header(p->on_header_user_data, md); - return 1; + return GRPC_ERROR_NONE; } -static grpc_mdstr *take_string(grpc_chttp2_hpack_parser *p, - grpc_chttp2_hpack_parser_string *str) { - grpc_mdstr *s = grpc_mdstr_from_buffer((uint8_t *)str->str, str->length); - str->length = 0; +static grpc_slice take_string(grpc_chttp2_hpack_parser* p, + grpc_chttp2_hpack_parser_string* str, + bool intern) { + grpc_slice s; + if (!str->copied) { + if (intern) { + s = grpc_slice_intern(str->data.referenced); + grpc_slice_unref_internal(str->data.referenced); + } else { + s = str->data.referenced; + } + str->copied = true; + str->data.referenced = grpc_empty_slice(); + } else if (intern) { + s = grpc_slice_intern(grpc_slice_from_static_buffer( + str->data.copied.str, str->data.copied.length)); + } else { + s = grpc_slice_from_copied_buffer(str->data.copied.str, + str->data.copied.length); + } + str->data.copied.length = 0; return s; } /* jump to the next state */ -static int parse_next(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_next(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_begin(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { if (cur == end) { p->state = parse_begin; - return 1; + return GRPC_ERROR_NONE; } 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_stream_weight(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (cur == end) { p->state = parse_stream_weight; - return 1; + return GRPC_ERROR_NONE; } return p->after_prioritization(p, cur + 1, end); } -static int parse_stream_dep3(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_stream_dep3(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (cur == end) { p->state = parse_stream_dep3; - return 1; + return GRPC_ERROR_NONE; } return parse_stream_weight(p, cur + 1, end); } -static int parse_stream_dep2(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_stream_dep2(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (cur == end) { p->state = parse_stream_dep2; - return 1; + return GRPC_ERROR_NONE; } return parse_stream_dep3(p, cur + 1, end); } -static int parse_stream_dep1(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_stream_dep1(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (cur == end) { p->state = parse_stream_dep1; - return 1; + return GRPC_ERROR_NONE; } return parse_stream_dep2(p, cur + 1, end); } -static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_stream_dep0(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (cur == end) { p->state = parse_stream_dep0; - return 1; + return GRPC_ERROR_NONE; } 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 uint8_t *cur, - const uint8_t *end) { - grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - if (md == NULL) { - gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index); - return 0; +/* emit an indexed field; jumps to begin the next field on completion */ +static grpc_error* finish_indexed_field(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + if (GRPC_MDISNULL(md)) { + return grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid HPACK index received"), + GRPC_ERROR_INT_INDEX, + static_cast(p->index)), + GRPC_ERROR_INT_SIZE, static_cast(p->table.num_ents)); } GRPC_MDELEM_REF(md); - return on_hdr(p, md, 0) && parse_begin(p, cur, end); + GRPC_STATS_INC_HPACK_RECV_INDEXED(); + grpc_error* err = on_hdr(p, md, 0); + if (err != GRPC_ERROR_NONE) return err; + return parse_begin(p, cur, end); } /* parse an indexed field with index < 127 */ -static int parse_indexed_field(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_indexed_field(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { p->dynamic_table_update_allowed = 0; 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 uint8_t *cur, const uint8_t *end) { +static grpc_error* parse_indexed_field_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { finish_indexed_field}; p->dynamic_table_update_allowed = 0; @@ -750,30 +790,39 @@ static int parse_indexed_field_x(grpc_chttp2_hpack_parser *p, 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 uint8_t *cur, - const uint8_t *end) { - grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - GPR_ASSERT(md != NULL); /* handled in string parsing */ - return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key), - take_string(p, &p->value)), - 1) && - parse_begin(p, cur, end); +/* finish a literal header with incremental indexing */ +static grpc_error* finish_lithdr_incidx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ + GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX(); + grpc_error* err = + on_hdr(p, + grpc_mdelem_from_slices(grpc_slice_ref_internal(GRPC_MDKEY(md)), + take_string(p, &p->value, true)), + 1); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + 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 uint8_t *cur, const uint8_t *end) { - return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key), - take_string(p, &p->value)), - 1) && - parse_begin(p, cur, end); +static grpc_error* finish_lithdr_incidx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { + GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V(); + grpc_error* err = + on_hdr(p, + grpc_mdelem_from_slices(take_string(p, &p->key, true), + take_string(p, &p->value, true)), + 1); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_lithdr_incidx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_value_string_with_indexed_key, finish_lithdr_incidx}; p->dynamic_table_update_allowed = 0; @@ -783,8 +832,9 @@ static int parse_lithdr_incidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } /* parse a literal header with incremental indexing; index >= 63 */ -static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { +static grpc_error* parse_lithdr_incidx_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_string_prefix, parse_value_string_with_indexed_key, finish_lithdr_incidx}; @@ -796,8 +846,9 @@ static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p, } /* parse a literal header with incremental indexing; index = 0 */ -static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { +static grpc_error* parse_lithdr_incidx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* 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}; @@ -807,28 +858,38 @@ static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p, } /* finish a literal header without incremental indexing */ -static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - GPR_ASSERT(md != NULL); /* handled in string parsing */ - return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key), - take_string(p, &p->value)), - 0) && - parse_begin(p, cur, end); +static grpc_error* finish_lithdr_notidx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ + GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX(); + grpc_error* err = + on_hdr(p, + grpc_mdelem_from_slices(grpc_slice_ref_internal(GRPC_MDKEY(md)), + take_string(p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + 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 uint8_t *cur, const uint8_t *end) { - return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key), - take_string(p, &p->value)), - 0) && - parse_begin(p, cur, end); +static grpc_error* finish_lithdr_notidx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { + GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V(); + grpc_error* err = + on_hdr(p, + grpc_mdelem_from_slices(take_string(p, &p->key, true), + take_string(p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_lithdr_notidx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_value_string_with_indexed_key, finish_lithdr_notidx}; p->dynamic_table_update_allowed = 0; @@ -838,8 +899,9 @@ static int parse_lithdr_notidx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } /* parse a literal header without incremental indexing; index >= 15 */ -static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { +static grpc_error* parse_lithdr_notidx_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_string_prefix, parse_value_string_with_indexed_key, finish_lithdr_notidx}; @@ -851,8 +913,9 @@ static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p, } /* parse a literal header without incremental indexing; index == 0 */ -static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { +static grpc_error* parse_lithdr_notidx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* 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}; @@ -862,28 +925,38 @@ static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p, } /* finish a literal header that is never indexed */ -static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - GPR_ASSERT(md != NULL); /* handled in string parsing */ - return on_hdr(p, grpc_mdelem_from_metadata_strings(GRPC_MDSTR_REF(md->key), - take_string(p, &p->value)), - 0) && - parse_begin(p, cur, end); +static grpc_error* finish_lithdr_nvridx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ + GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX(); + grpc_error* err = + on_hdr(p, + grpc_mdelem_from_slices(grpc_slice_ref_internal(GRPC_MDKEY(md)), + take_string(p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + 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 uint8_t *cur, const uint8_t *end) { - return on_hdr(p, grpc_mdelem_from_metadata_strings(take_string(p, &p->key), - take_string(p, &p->value)), - 0) && - parse_begin(p, cur, end); +static grpc_error* finish_lithdr_nvridx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { + GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V(); + grpc_error* err = + on_hdr(p, + grpc_mdelem_from_slices(take_string(p, &p->key, true), + take_string(p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_lithdr_nvridx(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_value_string_with_indexed_key, finish_lithdr_nvridx}; p->dynamic_table_update_allowed = 0; @@ -893,8 +966,9 @@ static int parse_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } /* parse a literal header that is never indexed; index >= 15 */ -static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { +static grpc_error* parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { parse_string_prefix, parse_value_string_with_indexed_key, finish_lithdr_nvridx}; @@ -906,8 +980,9 @@ static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p, } /* parse a literal header that is never indexed; index == 0 */ -static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { +static grpc_error* parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* 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}; @@ -917,18 +992,25 @@ static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p, } /* finish parsing a max table size change */ -static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index); - return grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index) && - parse_begin(p, cur, end); +static grpc_error* finish_max_tbl_size(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { + if (grpc_http_trace.enabled()) { + gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index); + } + grpc_error* err = + grpc_chttp2_hptbl_set_current_table_size(&p->table, p->index); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_max_tbl_size(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (p->dynamic_table_update_allowed == 0) { - return 0; + return parse_error( + p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "More than two max table size changes in a single frame")); } p->dynamic_table_update_allowed--; p->index = (*cur) & 0x1f; @@ -936,12 +1018,16 @@ static int parse_max_tbl_size(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } /* parse a max table size change, max size >= 15 */ -static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_max_tbl_size_x(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, + const uint8_t* end) { static const grpc_chttp2_hpack_parser_state and_then[] = { finish_max_tbl_size}; if (p->dynamic_table_update_allowed == 0) { - return 0; + return parse_error( + p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "More than two max table size changes in a single frame")); } p->dynamic_table_update_allowed--; p->next_state = and_then; @@ -951,26 +1037,38 @@ static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } /* a parse error: jam the parse state into parse_error, and return error */ -static int parse_error(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - p->state = parse_error; - return 0; +static grpc_error* parse_error(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end, grpc_error* err) { + GPR_ASSERT(err != GRPC_ERROR_NONE); + if (p->last_error == GRPC_ERROR_NONE) { + p->last_error = GRPC_ERROR_REF(err); + } + p->state = still_parse_error; + return err; +} + +static grpc_error* still_parse_error(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { + return GRPC_ERROR_REF(p->last_error); } -static int parse_illegal_op(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_illegal_op(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { GPR_ASSERT(cur != end); - gpr_log(GPR_DEBUG, "Illegal hpack op code %d", *cur); - return parse_error(p, cur, end); + char* msg; + gpr_asprintf(&msg, "Illegal hpack op code %d", *cur); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(p, cur, end, err); } /* 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_value0(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { if (cur == end) { p->state = parse_value0; - return 1; + return GRPC_ERROR_NONE; } *p->parsing.value += (*cur) & 0x7f; @@ -984,14 +1082,14 @@ static int parse_value0(grpc_chttp2_hpack_parser *p, const uint8_t *cur, /* 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_value1(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { if (cur == end) { p->state = parse_value1; - return 1; + return GRPC_ERROR_NONE; } - *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 7; + *p->parsing.value += ((static_cast(*cur)) & 0x7f) << 7; if ((*cur) & 0x80) { return parse_value2(p, cur + 1, end); @@ -1002,14 +1100,14 @@ static int parse_value1(grpc_chttp2_hpack_parser *p, const uint8_t *cur, /* 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_value2(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { if (cur == end) { p->state = parse_value2; - return 1; + return GRPC_ERROR_NONE; } - *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 14; + *p->parsing.value += ((static_cast(*cur)) & 0x7f) << 14; if ((*cur) & 0x80) { return parse_value3(p, cur + 1, end); @@ -1020,14 +1118,14 @@ static int parse_value2(grpc_chttp2_hpack_parser *p, const uint8_t *cur, /* 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_value3(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { if (cur == end) { p->state = parse_value3; - return 1; + return GRPC_ERROR_NONE; } - *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 21; + *p->parsing.value += ((static_cast(*cur)) & 0x7f) << 21; if ((*cur) & 0x80) { return parse_value4(p, cur + 1, end); @@ -1038,15 +1136,16 @@ static int parse_value3(grpc_chttp2_hpack_parser *p, const uint8_t *cur, /* 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_value4(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { uint8_t c; uint32_t cur_value; uint32_t add_value; + char* msg; if (cur == end) { p->state = parse_value4; - return 1; + return GRPC_ERROR_NONE; } c = (*cur) & 0x7f; @@ -1055,7 +1154,7 @@ static int parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } cur_value = *p->parsing.value; - add_value = ((uint32_t)c) << 28; + add_value = (static_cast(c)) << 28; if (add_value > 0xffffffffu - cur_value) { goto error; } @@ -1069,44 +1168,49 @@ static int parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } error: - gpr_log(GPR_ERROR, - "integer overflow in hpack integer decoding: have 0x%08x, " - "got byte 0x%02x on byte 5", - *p->parsing.value, *cur); - return parse_error(p, cur, end); + gpr_asprintf(&msg, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x on byte 5", + *p->parsing.value, *cur); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(p, cur, end, err); } /* 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 uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_value5up(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { while (cur != end && *cur == 0x80) { ++cur; } if (cur == end) { p->state = parse_value5up; - return 1; + return GRPC_ERROR_NONE; } 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 5", - *p->parsing.value, *cur); - return parse_error(p, cur, end); + char* msg; + gpr_asprintf(&msg, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x sometime after byte 5", + *p->parsing.value, *cur); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(p, cur, end, err); } /* parse a string prefix */ -static int parse_string_prefix(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_string_prefix(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (cur == end) { p->state = parse_string_prefix; - return 1; + return GRPC_ERROR_NONE; } p->strlen = (*cur) & 0x7f; @@ -1120,37 +1224,57 @@ static int parse_string_prefix(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } /* append some bytes to a string */ -static void append_bytes(grpc_chttp2_hpack_parser_string *str, - const uint8_t *data, size_t length) { - if (length + str->length > str->capacity) { - GPR_ASSERT(str->length + length <= UINT32_MAX); - str->capacity = (uint32_t)(str->length + length); - str->str = gpr_realloc(str->str, str->capacity); +static void append_bytes(grpc_chttp2_hpack_parser_string* str, + const uint8_t* data, size_t length) { + if (length == 0) return; + if (length + str->data.copied.length > str->data.copied.capacity) { + GPR_ASSERT(str->data.copied.length + length <= UINT32_MAX); + str->data.copied.capacity = + static_cast(str->data.copied.length + length); + str->data.copied.str = static_cast( + gpr_realloc(str->data.copied.str, str->data.copied.capacity)); } - memcpy(str->str + str->length, data, length); - GPR_ASSERT(length <= UINT32_MAX - str->length); - str->length += (uint32_t)length; + memcpy(str->data.copied.str + str->data.copied.length, data, length); + GPR_ASSERT(length <= UINT32_MAX - str->data.copied.length); + str->data.copied.length += static_cast(length); } -static int append_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - grpc_chttp2_hpack_parser_string *str = p->parsing.str; +static grpc_error* append_string(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { + grpc_chttp2_hpack_parser_string* str = p->parsing.str; uint32_t bits; uint8_t decoded[3]; - switch ((binary_state)p->binary) { + switch (static_cast(p->binary)) { case NOT_BINARY: - append_bytes(str, cur, (size_t)(end - cur)); - return 1; + append_bytes(str, cur, static_cast(end - cur)); + return GRPC_ERROR_NONE; + case BINARY_BEGIN: + if (cur == end) { + p->binary = BINARY_BEGIN; + return GRPC_ERROR_NONE; + } + if (*cur == 0) { + /* 'true-binary' case */ + ++cur; + p->binary = NOT_BINARY; + GRPC_STATS_INC_HPACK_RECV_BINARY(); + append_bytes(str, cur, static_cast(end - cur)); + return GRPC_ERROR_NONE; + } + GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64(); + /* fallthrough */ b64_byte0: case B64_BYTE0: if (cur == end) { p->binary = B64_BYTE0; - return 1; + return GRPC_ERROR_NONE; } bits = inverse_base64[*cur]; ++cur; if (bits == 255) - return 0; + return parse_error( + p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); else if (bits == 64) goto b64_byte0; p->base64_buffer = bits << 18; @@ -1159,12 +1283,14 @@ static int append_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, case B64_BYTE1: if (cur == end) { p->binary = B64_BYTE1; - return 1; + return GRPC_ERROR_NONE; } bits = inverse_base64[*cur]; ++cur; if (bits == 255) - return 0; + return parse_error( + p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); else if (bits == 64) goto b64_byte1; p->base64_buffer |= bits << 12; @@ -1173,12 +1299,14 @@ static int append_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, case B64_BYTE2: if (cur == end) { p->binary = B64_BYTE2; - return 1; + return GRPC_ERROR_NONE; } bits = inverse_base64[*cur]; ++cur; if (bits == 255) - return 0; + return parse_error( + p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); else if (bits == 64) goto b64_byte2; p->base64_buffer |= bits << 6; @@ -1187,95 +1315,109 @@ static int append_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, case B64_BYTE3: if (cur == end) { p->binary = B64_BYTE3; - return 1; + return GRPC_ERROR_NONE; } bits = inverse_base64[*cur]; ++cur; if (bits == 255) - return 0; + return parse_error( + p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); else if (bits == 64) goto b64_byte3; p->base64_buffer |= bits; bits = p->base64_buffer; - decoded[0] = (uint8_t)(bits >> 16); - decoded[1] = (uint8_t)(bits >> 8); - decoded[2] = (uint8_t)(bits); + decoded[0] = static_cast(bits >> 16); + decoded[1] = static_cast(bits >> 8); + decoded[2] = static_cast(bits); append_bytes(str, decoded, 3); goto b64_byte0; } - GPR_UNREACHABLE_CODE(return 1); + GPR_UNREACHABLE_CODE(return parse_error( + p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here"))); } -/* append a null terminator to a string */ -static int finish_str(grpc_chttp2_hpack_parser *p) { - uint8_t terminator = 0; +static grpc_error* finish_str(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { uint8_t decoded[2]; uint32_t bits; - grpc_chttp2_hpack_parser_string *str = p->parsing.str; - switch ((binary_state)p->binary) { + grpc_chttp2_hpack_parser_string* str = p->parsing.str; + switch (static_cast(p->binary)) { case NOT_BINARY: break; + case BINARY_BEGIN: + break; case B64_BYTE0: break; case B64_BYTE1: - gpr_log(GPR_ERROR, "illegal base64 encoding"); - return 0; /* illegal encoding */ + return parse_error(p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "illegal base64 encoding")); /* 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; + char* msg; + gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%04x", + bits & 0xffff); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(p, cur, end, err); } - decoded[0] = (uint8_t)(bits >> 16); + decoded[0] = static_cast(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; + char* msg; + gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%02x", + bits & 0xff); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(p, cur, end, err); } - decoded[0] = (uint8_t)(bits >> 16); - decoded[1] = (uint8_t)(bits >> 8); + decoded[0] = static_cast(bits >> 16); + decoded[1] = static_cast(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; + return GRPC_ERROR_NONE; } /* decode a nibble from a huffman encoded stream */ -static int huff_nibble(grpc_chttp2_hpack_parser *p, uint8_t nibble) { +static grpc_error* huff_nibble(grpc_chttp2_hpack_parser* p, uint8_t nibble) { int16_t emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble]; int16_t next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble]; if (emit != -1) { if (emit >= 0 && emit < 256) { - uint8_t c = (uint8_t)emit; - if (!append_string(p, &c, (&c) + 1)) return 0; + uint8_t c = static_cast(emit); + grpc_error* err = append_string(p, &c, (&c) + 1); + if (err != GRPC_ERROR_NONE) return err; } else { assert(emit == 256); } } p->huff_state = next; - return 1; + return GRPC_ERROR_NONE; } /* decode full bytes from a huffman encoded stream */ -static int add_huff_bytes(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* add_huff_bytes(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { for (; cur != end; ++cur) { - if (!huff_nibble(p, *cur >> 4) || !huff_nibble(p, *cur & 0xf)) return 0; + grpc_error* err = huff_nibble(p, *cur >> 4); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + err = huff_nibble(p, *cur & 0xf); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); } - return 1; + return GRPC_ERROR_NONE; } /* decode some string bytes based on the current decoding mode (huffman or not) */ -static int add_str_bytes(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* add_str_bytes(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end) { if (p->huff) { return add_huff_bytes(p, cur, end); } else { @@ -1284,166 +1426,255 @@ static int add_str_bytes(grpc_chttp2_hpack_parser *p, const uint8_t *cur, } /* parse a string - tries to do large chunks at a time */ -static int parse_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_string(grpc_chttp2_hpack_parser* p, const uint8_t* cur, + const uint8_t* end) { size_t remaining = p->strlen - p->strgot; - size_t given = (size_t)(end - cur); + size_t given = static_cast(end - cur); if (remaining <= given) { - return add_str_bytes(p, cur, cur + remaining) && finish_str(p) && - parse_next(p, cur + remaining, end); + grpc_error* err = add_str_bytes(p, cur, cur + remaining); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + err = finish_str(p, cur + remaining, end); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + return parse_next(p, cur + remaining, end); } else { - if (!add_str_bytes(p, cur, cur + given)) return 0; + grpc_error* err = add_str_bytes(p, cur, cur + given); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); GPR_ASSERT(given <= UINT32_MAX - p->strgot); - p->strgot += (uint32_t)given; + p->strgot += static_cast(given); p->state = parse_string; - return 1; + return GRPC_ERROR_NONE; } } /* begin parsing a string - performs setup, calls parse_string */ -static int begin_parse_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end, uint8_t binary, - grpc_chttp2_hpack_parser_string *str) { +static grpc_error* begin_parse_string(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end, + uint8_t binary, + grpc_chttp2_hpack_parser_string* str) { + if (!p->huff && binary == NOT_BINARY && + (end - cur) >= static_cast(p->strlen) && + p->current_slice_refcount != nullptr) { + GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(); + str->copied = false; + str->data.referenced.refcount = p->current_slice_refcount; + str->data.referenced.data.refcounted.bytes = const_cast(cur); + str->data.referenced.data.refcounted.length = p->strlen; + grpc_slice_ref_internal(str->data.referenced); + return parse_next(p, cur + p->strlen, end); + } p->strgot = 0; - str->length = 0; + str->copied = true; + str->data.copied.length = 0; p->parsing.str = str; p->huff_state = 0; p->binary = binary; + switch (p->binary) { + case NOT_BINARY: + if (p->huff) { + GRPC_STATS_INC_HPACK_RECV_HUFFMAN(); + } else { + GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(); + } + break; + case BINARY_BEGIN: + /* stats incremented later: don't know true binary or not */ + break; + default: + abort(); + } return parse_string(p, cur, end); } /* parse the key string */ -static int parse_key_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_key_string(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* 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 bool is_binary_literal_header(grpc_chttp2_hpack_parser* p) { + return grpc_is_binary_header( + p->key.copied ? grpc_slice_from_static_buffer(p->key.data.copied.str, + p->key.data.copied.length) + : p->key.data.referenced); } -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) { - gpr_log(GPR_ERROR, "Invalid HPACK index received: %d", p->index); - return ERROR_HEADER; +static grpc_error* is_binary_indexed_header(grpc_chttp2_hpack_parser* p, + bool* is) { + grpc_mdelem elem = grpc_chttp2_hptbl_lookup(&p->table, p->index); + if (GRPC_MDISNULL(elem)) { + return grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid HPACK index received"), + GRPC_ERROR_INT_INDEX, + static_cast(p->index)), + GRPC_ERROR_INT_SIZE, static_cast(p->table.num_ents)); } - return grpc_is_binary_header( - (const char *)GPR_SLICE_START_PTR(elem->key->slice), - GPR_SLICE_LENGTH(elem->key->slice)) - ? BINARY_HEADER - : PLAINTEXT_HEADER; + *is = grpc_is_binary_header(GRPC_MDKEY(elem)); + return GRPC_ERROR_NONE; } /* parse the value string */ -static int parse_value_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *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_UNREACHABLE_CODE(return 0); +static grpc_error* parse_value_string(grpc_chttp2_hpack_parser* p, + const uint8_t* cur, const uint8_t* end, + bool is_binary) { + return begin_parse_string(p, cur, end, is_binary ? BINARY_BEGIN : NOT_BINARY, + &p->value); } -static int parse_value_string_with_indexed_key(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - return parse_value_string(p, cur, end, is_binary_indexed_header(p)); +static grpc_error* parse_value_string_with_indexed_key( + grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* end) { + bool is_binary = false; + grpc_error* err = is_binary_indexed_header(p, &is_binary); + if (err != GRPC_ERROR_NONE) return parse_error(p, cur, end, err); + return parse_value_string(p, cur, end, is_binary); } -static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { +static grpc_error* parse_value_string_with_literal_key( + grpc_chttp2_hpack_parser* p, const uint8_t* cur, const uint8_t* 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) { - GPR_UNREACHABLE_CODE(return ); -} - -void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p) { - p->on_header = on_header_not_set; - p->on_header_user_data = NULL; +void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser* p) { + p->on_header = nullptr; + p->on_header_user_data = nullptr; 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; + p->key.data.referenced = grpc_empty_slice(); + p->key.data.copied.str = nullptr; + p->key.data.copied.capacity = 0; + p->key.data.copied.length = 0; + p->value.data.referenced = grpc_empty_slice(); + p->value.data.copied.str = nullptr; + p->value.data.copied.capacity = 0; + p->value.data.copied.length = 0; p->dynamic_table_update_allowed = 2; + p->last_error = GRPC_ERROR_NONE; grpc_chttp2_hptbl_init(&p->table); } -void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) { +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) { +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); + GRPC_ERROR_UNREF(p->last_error); + grpc_slice_unref_internal(p->key.data.referenced); + grpc_slice_unref_internal(p->value.data.referenced); + gpr_free(p->key.data.copied.str); + gpr_free(p->value.data.copied.str); } -int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p, - const uint8_t *beg, const uint8_t *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_error* grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser* p, + grpc_slice slice) { +/* max number of bytes to parse at a time... limits call stack depth on + * compilers without TCO */ +#define MAX_PARSE_LENGTH 1024 + p->current_slice_refcount = slice.refcount; + uint8_t* start = GRPC_SLICE_START_PTR(slice); + uint8_t* end = GRPC_SLICE_END_PTR(slice); + grpc_error* error = GRPC_ERROR_NONE; + while (start != end && error == GRPC_ERROR_NONE) { + uint8_t* target = start + GPR_MIN(MAX_PARSE_LENGTH, end - start); + error = p->state(p, start, target); + start = target; + } + p->current_slice_refcount = nullptr; + return error; } -grpc_chttp2_parse_error grpc_chttp2_header_parser_parse( - grpc_exec_ctx *exec_ctx, 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; - GPR_TIMER_BEGIN("grpc_chttp2_hpack_parser_parse", 0); - if (!grpc_chttp2_hpack_parser_parse(parser, GPR_SLICE_START_PTR(slice), - GPR_SLICE_END_PTR(slice))) { - GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); - return GRPC_CHTTP2_CONNECTION_ERROR; +typedef void (*maybe_complete_func_type)(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +static const maybe_complete_func_type maybe_complete_funcs[] = { + grpc_chttp2_maybe_complete_recv_initial_metadata, + grpc_chttp2_maybe_complete_recv_trailing_metadata}; + +static void force_client_rst_stream(void* sp, grpc_error* error) { + grpc_chttp2_stream* s = static_cast(sp); + grpc_chttp2_transport* t = s->t; + if (!s->write_closed) { + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR, + &s->stats.outgoing)); + grpc_chttp2_initiate_write(t, GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM); + grpc_chttp2_mark_stream_closed(t, s, true, true, GRPC_ERROR_NONE); + } + GRPC_CHTTP2_STREAM_UNREF(s, "final_rst"); +} + +static void parse_stream_compression_md(grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_metadata_batch* initial_metadata) { + if (initial_metadata->idx.named.content_encoding == nullptr || + grpc_stream_compression_method_parse( + GRPC_MDVALUE(initial_metadata->idx.named.content_encoding->md), false, + &s->stream_decompression_method) == 0) { + s->stream_decompression_method = + GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; + } +} + +grpc_error* grpc_chttp2_header_parser_parse(void* hpack_parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last) { + GPR_TIMER_SCOPE("grpc_chttp2_header_parser_parse", 0); + grpc_chttp2_hpack_parser* parser = + static_cast(hpack_parser); + if (s != nullptr) { + s->stats.incoming.header_bytes += GRPC_SLICE_LENGTH(slice); + } + grpc_error* error = grpc_chttp2_hpack_parser_parse(parser, slice); + if (error != GRPC_ERROR_NONE) { + return 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"); - GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); - return GRPC_CHTTP2_CONNECTION_ERROR; + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "end of header frame not aligned with a hpack record boundary"); } /* need to check for null stream: this can occur if we receive an invalid stream id on a header */ - if (stream_parsing != NULL) { + if (s != nullptr) { if (parser->is_boundary) { - stream_parsing - ->got_metadata_on_parse[stream_parsing->header_frames_received] = 1; - stream_parsing->header_frames_received++; - grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, - stream_parsing); + if (s->header_frames_received == GPR_ARRAY_SIZE(s->metadata_buffer)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Too many trailer frames"); + } + /* Process stream compression md element if it exists */ + if (s->header_frames_received == + 0) { /* Only acts on initial metadata */ + parse_stream_compression_md(t, s, &s->metadata_buffer[0].batch); + } + s->published_metadata[s->header_frames_received] = + GRPC_METADATA_PUBLISHED_FROM_WIRE; + maybe_complete_funcs[s->header_frames_received](t, s); + s->header_frames_received++; } if (parser->is_eof) { - stream_parsing->received_close = 1; + if (t->is_client && !s->write_closed) { + /* server eof ==> complete closure; we may need to forcefully close + the stream. Wait until the combiner lock is ready to be released + however -- it might be that we receive a RST_STREAM following this + and can avoid the extra write */ + GRPC_CHTTP2_STREAM_REF(s, "final_rst"); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(force_client_rst_stream, s, + grpc_combiner_finally_scheduler(t->combiner)), + GRPC_ERROR_NONE); + } + grpc_chttp2_mark_stream_closed(t, s, true, false, GRPC_ERROR_NONE); } } - parser->on_header = on_header_not_set; - parser->on_header_user_data = NULL; + parser->on_header = nullptr; + parser->on_header_user_data = nullptr; parser->is_boundary = 0xde; parser->is_eof = 0xde; parser->dynamic_table_update_allowed = 2; } - GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); - return GRPC_CHTTP2_PARSE_OK; + return GRPC_ERROR_NONE; } diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.h new file mode 100644 index 00000000000000..3e05de4b925109 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_parser.h @@ -0,0 +1,109 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H + +#include + +#include + +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/ext/transport/chttp2/transport/hpack_table.h" +#include "src/core/lib/transport/metadata.h" + +typedef struct grpc_chttp2_hpack_parser grpc_chttp2_hpack_parser; + +typedef grpc_error* (*grpc_chttp2_hpack_parser_state)( + grpc_chttp2_hpack_parser* p, const uint8_t* beg, const uint8_t* end); + +typedef struct { + bool copied; + struct { + grpc_slice referenced; + struct { + char* str; + uint32_t length; + uint32_t capacity; + } copied; + } data; +} 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; + + grpc_error* last_error; + + /* 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 refcount of the slice that we're currently parsing */ + grpc_slice_refcount* current_slice_refcount; + /* the value we're currently parsing */ + union { + uint32_t* 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 */ + uint32_t index; + /* length of source bytes for the currently parsing string */ + uint32_t strlen; + /* number of source bytes read for the currently parsing string */ + uint32_t strgot; + /* huffman decoding state */ + int16_t huff_state; + /* is the string being decoded binary? */ + uint8_t binary; + /* is the current string huffman encoded? */ + uint8_t huff; + /* is a dynamic table update allowed? */ + uint8_t dynamic_table_update_allowed; + /* set by higher layers, used by grpc_chttp2_header_parser_parse to signal + it should append a metadata boundary at the end of frame */ + uint8_t is_boundary; + uint8_t is_eof; + uint32_t base64_buffer; + + /* hpack table */ + grpc_chttp2_hptbl table; +}; + +void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser* p); +void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser* p); + +void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser* p); + +grpc_error* grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser* p, + grpc_slice slice); + +/* wraps grpc_chttp2_hpack_parser_parse to provide a frame level parser for + the transport */ +grpc_error* grpc_chttp2_header_parser_parse(void* hpack_parser, + grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_slice slice, int is_last); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/hpack_table.c b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.cc similarity index 56% rename from third_party/grpc/src/core/transport/chttp2/hpack_table.c rename to third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.cc index f1ce3b84fdccae..fcfb01872bfa41 100644 --- a/third_party/grpc/src/core/transport/chttp2/hpack_table.c +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.cc @@ -1,52 +1,44 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include "src/core/transport/chttp2/hpack_table.h" +#include + +#include "src/core/ext/transport/chttp2/transport/hpack_table.h" #include #include #include #include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/murmur_hash.h" +#include "src/core/lib/transport/static_metadata.h" -#include "src/core/support/murmur_hash.h" +extern grpc_core::TraceFlag grpc_http_trace; static struct { - const char *key; - const char *value; + const char* key; + const char* value; } static_table[] = { /* 0: */ - {NULL, NULL}, + {nullptr, nullptr}, /* 1: */ {":authority", ""}, /* 2: */ @@ -176,7 +168,7 @@ static uint32_t entries_for_bytes(uint32_t bytes) { GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; } -void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) { +void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl* tbl) { size_t i; memset(tbl, 0, sizeof(*tbl)); @@ -184,15 +176,18 @@ void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) { GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE; tbl->max_entries = tbl->cap_entries = entries_for_bytes(tbl->current_table_bytes); - tbl->ents = gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries); + tbl->ents = static_cast( + gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries)); memset(tbl->ents, 0, sizeof(*tbl->ents) * tbl->cap_entries); for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { - tbl->static_ents[i - 1] = - grpc_mdelem_from_strings(static_table[i].key, static_table[i].value); + tbl->static_ents[i - 1] = grpc_mdelem_from_slices( + grpc_slice_intern(grpc_slice_from_static_string(static_table[i].key)), + grpc_slice_intern( + grpc_slice_from_static_string(static_table[i].value))); } } -void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) { +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]); @@ -203,8 +198,8 @@ void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) { gpr_free(tbl->ents); } -grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, - uint32_t tbl_index) { +grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl* tbl, + uint32_t tbl_index) { /* Static table comes first, just return an entry from it */ if (tbl_index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { return tbl->static_ents[tbl_index - 1]; @@ -217,24 +212,25 @@ grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, return tbl->ents[offset]; } /* Invalid entry: return error */ - return NULL; + return GRPC_MDNULL; } /* Evict one element from the table */ -static void evict1(grpc_chttp2_hptbl *tbl) { - grpc_mdelem *first_ent = tbl->ents[tbl->first_ent]; - size_t elem_bytes = GPR_SLICE_LENGTH(first_ent->key->slice) + - GPR_SLICE_LENGTH(first_ent->value->slice) + +static void evict1(grpc_chttp2_hptbl* tbl) { + grpc_mdelem first_ent = tbl->ents[tbl->first_ent]; + size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(first_ent)) + + GRPC_SLICE_LENGTH(GRPC_MDVALUE(first_ent)) + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; GPR_ASSERT(elem_bytes <= tbl->mem_used); - tbl->mem_used -= (uint32_t)elem_bytes; + tbl->mem_used -= static_cast(elem_bytes); tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries); tbl->num_ents--; GRPC_MDELEM_UNREF(first_ent); } -static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) { - grpc_mdelem **ents = gpr_malloc(sizeof(*ents) * new_cap); +static void rebuild_ents(grpc_chttp2_hptbl* tbl, uint32_t new_cap) { + grpc_mdelem* ents = + static_cast(gpr_malloc(sizeof(*ents) * new_cap)); uint32_t i; for (i = 0; i < tbl->num_ents; i++) { @@ -246,30 +242,37 @@ static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) { tbl->first_ent = 0; } -void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl, +void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl* tbl, uint32_t max_bytes) { if (tbl->max_bytes == max_bytes) { return; } - gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes); + if (grpc_http_trace.enabled()) { + gpr_log(GPR_INFO, "Update hpack parser max size to %d", max_bytes); + } while (tbl->mem_used > max_bytes) { evict1(tbl); } tbl->max_bytes = max_bytes; } -int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl, - uint32_t bytes) { +grpc_error* grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl* tbl, + uint32_t bytes) { if (tbl->current_table_bytes == bytes) { - return 1; + return GRPC_ERROR_NONE; } if (bytes > tbl->max_bytes) { - gpr_log(GPR_ERROR, - "Attempt to make hpack table %d bytes when max is %d bytes", bytes, - tbl->max_bytes); - return 0; + char* msg; + gpr_asprintf(&msg, + "Attempt to make hpack table %d bytes when max is %d bytes", + bytes, tbl->max_bytes); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + if (grpc_http_trace.enabled()) { + gpr_log(GPR_INFO, "Update hpack parser table size to %d", bytes); } - gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes); while (tbl->mem_used > bytes) { evict1(tbl); } @@ -283,21 +286,25 @@ int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl, rebuild_ents(tbl, new_cap); } } - return 1; + return GRPC_ERROR_NONE; } -int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { +grpc_error* grpc_chttp2_hptbl_add(grpc_chttp2_hptbl* tbl, grpc_mdelem md) { /* determine how many bytes of buffer this entry represents */ - size_t elem_bytes = GPR_SLICE_LENGTH(md->key->slice) + - GPR_SLICE_LENGTH(md->value->slice) + + size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(md)) + + GRPC_SLICE_LENGTH(GRPC_MDVALUE(md)) + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; if (tbl->current_table_bytes > tbl->max_bytes) { - gpr_log(GPR_ERROR, - "HPACK max table size reduced to %d but not reflected by hpack " - "stream (still at %d)", - tbl->max_bytes, tbl->current_table_bytes); - return 0; + char* msg; + gpr_asprintf( + &msg, + "HPACK max table size reduced to %d but not reflected by hpack " + "stream (still at %d)", + tbl->max_bytes, tbl->current_table_bytes); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; } /* we can't add elements bigger than the max table size */ @@ -314,11 +321,12 @@ int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { while (tbl->num_ents) { evict1(tbl); } - return 1; + return GRPC_ERROR_NONE; } /* evict entries to ensure no overflow */ - while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) { + while (elem_bytes > + static_cast(tbl->current_table_bytes) - tbl->mem_used) { evict1(tbl); } @@ -328,34 +336,62 @@ int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { /* update accounting values */ tbl->num_ents++; - tbl->mem_used += (uint32_t)elem_bytes; - return 1; + tbl->mem_used += static_cast(elem_bytes); + return GRPC_ERROR_NONE; } grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( - const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { + const grpc_chttp2_hptbl* tbl, grpc_mdelem md) { grpc_chttp2_hptbl_find_result r = {0, 0}; uint32_t 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; + grpc_mdelem ent = tbl->static_ents[i]; + if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue; r.index = i + 1u; - r.has_value = md->value == ent->value; + r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent)); if (r.has_value) return r; } /* Scan the dynamic table */ for (i = 0; i < tbl->num_ents; i++) { - uint32_t idx = - (uint32_t)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY); - grpc_mdelem *ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]; - if (md->key != ent->key) continue; + uint32_t idx = static_cast(tbl->num_ents - i + + GRPC_CHTTP2_LAST_STATIC_ENTRY); + grpc_mdelem ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]; + if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue; r.index = idx; - r.has_value = md->value == ent->value; + r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent)); if (r.has_value) return r; } return r; } + +static size_t get_base64_encoded_size(size_t raw_length) { + static const uint8_t tail_xtra[3] = {0, 2, 3}; + return raw_length / 3 * 4 + tail_xtra[raw_length % 3]; +} + +size_t grpc_chttp2_get_size_in_hpack_table(grpc_mdelem elem, + bool use_true_binary_metadata) { + size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); + size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem)); + if (grpc_is_binary_header(GRPC_MDKEY(elem))) { + return overhead_and_key + (use_true_binary_metadata + ? value_len + 1 + : get_base64_encoded_size(value_len)); + } else { + return overhead_and_key + value_len; + } +} + +uint8_t grpc_chttp2_get_static_hpack_table_index(grpc_mdelem md) { + if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { + uint8_t index = GRPC_MDELEM_DATA(md) - grpc_static_mdelem_table; + if (index < GRPC_CHTTP2_LAST_STATIC_ENTRY) { + return index + 1; // Hpack static metadata element indices start at 1 + } + } + return 0; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.h new file mode 100644 index 00000000000000..a0ffc6fab77077 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_table.h @@ -0,0 +1,104 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H + +#include + +#include +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/transport/metadata.h" + +/* 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 +#if 0 +/* 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) +#endif + +/* hpack decoder table */ +typedef struct { + /* the first used entry in ents */ + uint32_t first_ent; + /* how many entries are in the table */ + uint32_t num_ents; + /* the amount of memory used by the table, according to the hpack algorithm */ + uint32_t mem_used; + /* the max memory allowed to be used by the table, according to the hpack + algorithm */ + uint32_t max_bytes; + /* the currently agreed size of the table, according to the hpack algorithm */ + uint32_t current_table_bytes; + /* Maximum number of entries we could possibly fit in the table, given defined + overheads */ + uint32_t max_entries; + /* Number of entries allocated in ents */ + uint32_t cap_entries; + /* 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_mdelem static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY]; +} grpc_chttp2_hptbl; + +/* initialize a hpack table */ +void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl* tbl); +void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl* tbl); +void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl* tbl, + uint32_t max_bytes); +grpc_error* grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl* tbl, + uint32_t bytes); + +/* lookup a table entry based on its hpack index */ +grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl* tbl, + uint32_t index); +/* add a table entry to the index */ +grpc_error* grpc_chttp2_hptbl_add(grpc_chttp2_hptbl* tbl, + grpc_mdelem md) GRPC_MUST_USE_RESULT; + +size_t grpc_chttp2_get_size_in_hpack_table(grpc_mdelem elem, + bool use_true_binary_metadata); + +/* Returns the static hpack table index that corresponds to /a elem. Returns 0 + if /a elem is not statically stored or if it is not in the static hpack + table */ +uint8_t grpc_chttp2_get_static_hpack_table_index(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 { + uint32_t index; + int 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_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/hpack_tables.txt b/third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_tables.txt similarity index 100% rename from third_party/grpc/src/core/transport/chttp2/hpack_tables.txt rename to third_party/grpc/src/core/ext/transport/chttp2/transport/hpack_tables.txt diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.cc new file mode 100644 index 00000000000000..294ee8e4aa8b7c --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -0,0 +1,62 @@ +/* + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Automatically generated by tools/codegen/core/gen_settings_ids.py + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/http2_settings.h" + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/transport/http2_errors.h" + +const uint16_t grpc_setting_id_to_wire_id[] = {1, 2, 3, 4, 5, 6, 65027}; + +bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id* out) { + uint32_t i = wire_id - 1; + uint32_t x = i % 256; + uint32_t y = i / 256; + uint32_t h = x; + switch (y) { + case 254: + h += 4; + break; + } + *out = static_cast(h); + return h < GPR_ARRAY_SIZE(grpc_setting_id_to_wire_id) && + grpc_setting_id_to_wire_id[h] == wire_id; +} + +const grpc_chttp2_setting_parameters + grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = { + {"HEADER_TABLE_SIZE", 4096u, 0u, 4294967295u, + GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"ENABLE_PUSH", 1u, 0u, 1u, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, + GRPC_HTTP2_PROTOCOL_ERROR}, + {"MAX_CONCURRENT_STREAMS", 4294967295u, 0u, 4294967295u, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"INITIAL_WINDOW_SIZE", 65535u, 0u, 2147483647u, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, + GRPC_HTTP2_FLOW_CONTROL_ERROR}, + {"MAX_FRAME_SIZE", 16384u, 16384u, 16777215u, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"MAX_HEADER_LIST_SIZE", 16777216u, 0u, 16777216u, + GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"GRPC_ALLOW_TRUE_BINARY_METADATA", 0u, 0u, 1u, + GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, +}; diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.h new file mode 100644 index 00000000000000..07ce0621b2138d --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/http2_settings.h @@ -0,0 +1,62 @@ +/* + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Automatically generated by tools/codegen/core/gen_settings_ids.py + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H + +#include + +#include +#include + +typedef enum { + GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE = 0, /* wire id 1 */ + GRPC_CHTTP2_SETTINGS_ENABLE_PUSH = 1, /* wire id 2 */ + GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 2, /* wire id 3 */ + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 3, /* wire id 4 */ + GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE = 4, /* wire id 5 */ + GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 5, /* wire id 6 */ + GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA = 6, /* wire id 65027 */ +} grpc_chttp2_setting_id; + +#define GRPC_CHTTP2_NUM_SETTINGS 7 + +extern const uint16_t grpc_setting_id_to_wire_id[]; + +bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id* out); + +typedef enum { + GRPC_CHTTP2_CLAMP_INVALID_VALUE, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE +} grpc_chttp2_invalid_value_behavior; + +typedef struct { + const char* name; + uint32_t default_value; + uint32_t min_value; + uint32_t max_value; + grpc_chttp2_invalid_value_behavior invalid_value_behavior; + uint32_t error_value; +} grpc_chttp2_setting_parameters; + +extern const grpc_chttp2_setting_parameters + grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS]; + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.cc new file mode 100644 index 00000000000000..813e4c91b190bc --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.cc @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/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/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.h new file mode 100644 index 00000000000000..2e2a5dacaed6c2 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/huffsyms.h @@ -0,0 +1,33 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HUFFSYMS_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_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_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HUFFSYMS_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.cc new file mode 100644 index 00000000000000..dca15e76803e7b --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.cc @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" + +#include + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include + +grpc_error* grpc_chttp2_incoming_metadata_buffer_add( + grpc_chttp2_incoming_metadata_buffer* buffer, grpc_mdelem elem) { + buffer->size += GRPC_MDELEM_LENGTH(elem); + return grpc_metadata_batch_add_tail( + &buffer->batch, + static_cast( + gpr_arena_alloc(buffer->arena, sizeof(grpc_linked_mdelem))), + elem); +} + +grpc_error* grpc_chttp2_incoming_metadata_buffer_replace_or_add( + grpc_chttp2_incoming_metadata_buffer* buffer, grpc_mdelem elem) { + for (grpc_linked_mdelem* l = buffer->batch.list.head; l != nullptr; + l = l->next) { + if (grpc_slice_eq(GRPC_MDKEY(l->md), GRPC_MDKEY(elem))) { + GRPC_MDELEM_UNREF(l->md); + l->md = elem; + return GRPC_ERROR_NONE; + } + } + return grpc_chttp2_incoming_metadata_buffer_add(buffer, elem); +} + +void grpc_chttp2_incoming_metadata_buffer_set_deadline( + grpc_chttp2_incoming_metadata_buffer* buffer, grpc_millis deadline) { + buffer->batch.deadline = deadline; +} + +void grpc_chttp2_incoming_metadata_buffer_publish( + grpc_chttp2_incoming_metadata_buffer* buffer, grpc_metadata_batch* batch) { + grpc_metadata_batch_move(&buffer->batch, batch); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.h new file mode 100644 index 00000000000000..c551b3cc8be3cf --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/incoming_metadata.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H + +#include + +#include "src/core/lib/transport/transport.h" + +struct grpc_chttp2_incoming_metadata_buffer { + grpc_chttp2_incoming_metadata_buffer(gpr_arena* arena) : arena(arena) { + grpc_metadata_batch_init(&batch); + batch.deadline = GRPC_MILLIS_INF_FUTURE; + } + ~grpc_chttp2_incoming_metadata_buffer() { + grpc_metadata_batch_destroy(&batch); + } + + gpr_arena* arena; + grpc_metadata_batch batch; + size_t size = 0; // total size of metadata +}; + +void grpc_chttp2_incoming_metadata_buffer_publish( + grpc_chttp2_incoming_metadata_buffer* buffer, grpc_metadata_batch* batch); + +grpc_error* grpc_chttp2_incoming_metadata_buffer_add( + grpc_chttp2_incoming_metadata_buffer* buffer, + grpc_mdelem elem) GRPC_MUST_USE_RESULT; +grpc_error* grpc_chttp2_incoming_metadata_buffer_replace_or_add( + grpc_chttp2_incoming_metadata_buffer* buffer, + grpc_mdelem elem) GRPC_MUST_USE_RESULT; +void grpc_chttp2_incoming_metadata_buffer_set_deadline( + grpc_chttp2_incoming_metadata_buffer* buffer, grpc_millis deadline); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/internal.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/internal.h new file mode 100644 index 00000000000000..341f5b3977f733 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/internal.h @@ -0,0 +1,851 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H + +#include + +#include +#include + +#include "src/core/ext/transport/chttp2/transport/flow_control.h" +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/ext/transport/chttp2/transport/frame_data.h" +#include "src/core/ext/transport/chttp2/transport/frame_goaway.h" +#include "src/core/ext/transport/chttp2/transport/frame_ping.h" +#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" +#include "src/core/ext/transport/chttp2/transport/frame_settings.h" +#include "src/core/ext/transport/chttp2/transport/frame_window_update.h" +#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" +#include "src/core/ext/transport/chttp2/transport/hpack_parser.h" +#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" +#include "src/core/ext/transport/chttp2/transport/stream_map.h" +#include "src/core/lib/channel/channelz.h" +#include "src/core/lib/compression/stream_compression.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/transport_impl.h" + +namespace grpc_core { +class ContextList; +} + +/* 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_WRITABLE, + GRPC_CHTTP2_LIST_WRITING, + GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT, + GRPC_CHTTP2_LIST_STALLED_BY_STREAM, + /** 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; + +typedef enum { + GRPC_CHTTP2_WRITE_STATE_IDLE, + GRPC_CHTTP2_WRITE_STATE_WRITING, + GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE, +} grpc_chttp2_write_state; + +typedef enum { + GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY, + GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT, +} grpc_chttp2_optimization_target; + +typedef enum { + GRPC_CHTTP2_PCL_INITIATE = 0, + GRPC_CHTTP2_PCL_NEXT, + GRPC_CHTTP2_PCL_INFLIGHT, + GRPC_CHTTP2_PCL_COUNT /* must be last */ +} grpc_chttp2_ping_closure_list; + +typedef enum { + GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE, + GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM, + GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE, + GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA, + GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA, + GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING, + GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS, + GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT, + GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM, + GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API, + GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL, + GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL, + GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS, + GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING, + GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE, + GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING, + GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING, + GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED, + GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE, + GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM, +} grpc_chttp2_initiate_write_reason; + +const char* grpc_chttp2_initiate_write_reason_string( + grpc_chttp2_initiate_write_reason reason); + +typedef struct { + grpc_closure_list lists[GRPC_CHTTP2_PCL_COUNT] = {}; + uint64_t inflight_id = 0; +} grpc_chttp2_ping_queue; + +typedef struct { + int max_pings_without_data; + int max_ping_strikes; + grpc_millis min_sent_ping_interval_without_data; + grpc_millis min_recv_ping_interval_without_data; +} grpc_chttp2_repeated_ping_policy; + +typedef struct { + grpc_millis last_ping_sent_time; + int pings_before_data_required; + grpc_timer delayed_ping_timer; + bool is_delayed_ping_timer_set; +} grpc_chttp2_repeated_ping_state; + +typedef struct { + grpc_millis last_ping_recv_time; + int ping_strikes; +} grpc_chttp2_server_ping_recv_state; + +/* 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 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; + +typedef enum { + GRPC_CHTTP2_NO_GOAWAY_SEND, + GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED, + GRPC_CHTTP2_GOAWAY_SENT, +} grpc_chttp2_sent_goaway_state; + +typedef struct grpc_chttp2_write_cb { + int64_t call_at_byte; + grpc_closure* closure; + struct grpc_chttp2_write_cb* next; +} grpc_chttp2_write_cb; + +namespace grpc_core { + +class Chttp2IncomingByteStream : public ByteStream { + public: + Chttp2IncomingByteStream(grpc_chttp2_transport* transport, + grpc_chttp2_stream* stream, uint32_t frame_size, + uint32_t flags); + + void Orphan() override; + + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; + + // TODO(roth): When I converted this class to C++, I wanted to make it + // inherit from RefCounted or InternallyRefCounted instead of continuing + // to use its own custom ref-counting code. However, that would require + // using multiple inheritence, which sucks in general. And to make matters + // worse, it causes problems with our New<> and Delete<> wrappers. + // Specifically, unless RefCounted is first in the list of parent classes, + // it will see a different value of the address of the object than the one + // we actually allocated, in which case gpr_free() will be called on a + // different address than the one we got from gpr_malloc(), thus causing a + // crash. Given the fragility of depending on that, as well as a desire to + // avoid multiple inheritence in general, I've decided to leave this + // alone for now. We can revisit this once we're able to link against + // libc++, at which point we can eliminate New<> and Delete<> and + // switch to std::shared_ptr<>. + void Ref() { refs_.Ref(); } + void Unref() { + if (refs_.Unref()) { + grpc_core::Delete(this); + } + } + + void PublishError(grpc_error* error); + + grpc_error* Push(grpc_slice slice, grpc_slice* slice_out); + + grpc_error* Finished(grpc_error* error, bool reset_on_error); + + uint32_t remaining_bytes() const { return remaining_bytes_; } + + private: + static void NextLocked(void* arg, grpc_error* error_ignored); + static void OrphanLocked(void* arg, grpc_error* error_ignored); + + void MaybeCreateStreamDecompressionCtx(); + + grpc_chttp2_transport* transport_; // Immutable. + grpc_chttp2_stream* stream_; // Immutable. + + grpc_core::RefCount refs_; + + /* Accessed only by transport thread when stream->pending_byte_stream == false + * Accessed only by application thread when stream->pending_byte_stream == + * true */ + uint32_t remaining_bytes_; + + /* Accessed only by transport thread when stream->pending_byte_stream == false + * Accessed only by application thread when stream->pending_byte_stream == + * true */ + struct { + grpc_closure closure; + size_t max_size_hint; + grpc_closure* on_complete; + } next_action_; + grpc_closure destroy_action_; +}; + +} // namespace grpc_core + +typedef enum { + GRPC_CHTTP2_KEEPALIVE_STATE_WAITING, + GRPC_CHTTP2_KEEPALIVE_STATE_PINGING, + GRPC_CHTTP2_KEEPALIVE_STATE_DYING, + GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED, +} grpc_chttp2_keepalive_state; + +struct grpc_chttp2_transport { + grpc_chttp2_transport(const grpc_channel_args* channel_args, + grpc_endpoint* ep, bool is_client, + grpc_resource_user* resource_user); + ~grpc_chttp2_transport(); + + grpc_transport base; /* must be first */ + grpc_core::RefCount refs; + grpc_endpoint* ep; + char* peer_string; + + grpc_resource_user* resource_user; + + grpc_combiner* combiner; + + grpc_closure* notify_on_receive_settings = nullptr; + + /** write execution state of the transport */ + grpc_chttp2_write_state write_state = GRPC_CHTTP2_WRITE_STATE_IDLE; + /** is this the first write in a series of writes? + set when we initiate writing from idle, cleared when we + initiate writing from writing+more */ + bool is_first_write_in_batch = false; + + /** is the transport destroying itself? */ + uint8_t destroying = false; + /** has the upper layer closed the transport? */ + grpc_error* closed_with_error = GRPC_ERROR_NONE; + + /** is there a read request to the endpoint outstanding? */ + uint8_t endpoint_reading = 1; + + grpc_chttp2_optimization_target opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; + + /** various lists of streams */ + grpc_chttp2_stream_list lists[STREAM_LIST_COUNT] = {}; + + /** maps stream id to grpc_chttp2_stream objects */ + grpc_chttp2_stream_map stream_map; + + grpc_closure write_action_begin_locked; + grpc_closure write_action; + grpc_closure write_action_end_locked; + + grpc_closure read_action_locked; + + /** incoming read bytes */ + grpc_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 = nullptr; + + 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; + + /** data to write now */ + grpc_slice_buffer outbuf; + /** hpack encoding */ + grpc_chttp2_hpack_compressor hpack_compressor; + /** is this a client? */ + bool is_client; + + /** data to write next write */ + grpc_slice_buffer qbuf; + + /** how much data are we willing to buffer when the WRITE_BUFFER_HINT is set? + */ + uint32_t write_buffer_size = grpc_core::chttp2::kDefaultWindow; + + /** Set to a grpc_error object if a goaway frame is received. By default, set + * to GRPC_ERROR_NONE */ + grpc_error* goaway_error = GRPC_ERROR_NONE; + + grpc_chttp2_sent_goaway_state sent_goaway_state = GRPC_CHTTP2_NO_GOAWAY_SEND; + + /** are the local settings dirty and need to be sent? */ + bool dirtied_local_settings = true; + /** have local settings been sent? */ + bool sent_local_settings = false; + /** bitmask of setting indexes to send out + Hack: it's common for implementations to assume 65536 bytes initial send + window -- this should by rights be 0 */ + uint32_t force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + /** settings values */ + uint32_t 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 */ + uint32_t next_stream_id = 0; + + /** last new stream id */ + uint32_t last_new_stream_id = 0; + + /** ping queues for various ping insertion points */ + grpc_chttp2_ping_queue ping_queue = grpc_chttp2_ping_queue(); + grpc_chttp2_repeated_ping_policy ping_policy; + grpc_chttp2_repeated_ping_state ping_state; + uint64_t ping_ctr = 0; /* unique id for pings */ + grpc_closure retry_initiate_ping_locked; + + /** ping acks */ + size_t ping_ack_count = 0; + size_t ping_ack_capacity = 0; + uint64_t* ping_acks = nullptr; + grpc_chttp2_server_ping_recv_state ping_recv_state; + + /** 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; + + grpc_core::PolymorphicManualConstructor< + grpc_core::chttp2::TransportFlowControlBase, + grpc_core::chttp2::TransportFlowControl, + grpc_core::chttp2::TransportFlowControlDisabled> + flow_control; + /** initial window change. This is tracked as we parse settings frames from + * the remote peer. If there is a positive delta, then we will make all + * streams readable since they may have become unstalled */ + int64_t initial_window_update = 0; + + /* deframing */ + grpc_chttp2_deframe_transport_state deframe_state = GRPC_DTS_CLIENT_PREFIX_0; + uint8_t incoming_frame_type = 0; + uint8_t incoming_frame_flags = 0; + uint8_t header_eof = 0; + bool is_first_frame = true; + uint32_t expect_continuation_stream_id = 0; + uint32_t incoming_frame_size = 0; + uint32_t incoming_stream_id = 0; + + /* active parser */ + void* parser_data = nullptr; + grpc_chttp2_stream* incoming_stream = nullptr; + grpc_error* (*parser)(void* parser_user_data, grpc_chttp2_transport* t, + grpc_chttp2_stream* s, grpc_slice slice, int is_last); + + grpc_chttp2_write_cb* write_cb_pool = nullptr; + + /* bdp estimator */ + grpc_closure next_bdp_ping_timer_expired_locked; + grpc_closure start_bdp_ping_locked; + grpc_closure finish_bdp_ping_locked; + + /* if non-NULL, close the transport with this error when writes are finished + */ + grpc_error* close_transport_on_writes_finished = GRPC_ERROR_NONE; + + /* a list of closures to run after writes are finished */ + grpc_closure_list run_after_write = GRPC_CLOSURE_LIST_INIT; + + /* buffer pool state */ + /** have we scheduled a benign cleanup? */ + bool benign_reclaimer_registered = false; + /** have we scheduled a destructive cleanup? */ + bool destructive_reclaimer_registered = false; + /** benign cleanup closure */ + grpc_closure benign_reclaimer_locked; + /** destructive cleanup closure */ + grpc_closure destructive_reclaimer_locked; + + /* next bdp ping timer */ + bool have_next_bdp_ping_timer = false; + grpc_timer next_bdp_ping_timer; + + /* keep-alive ping support */ + /** Closure to initialize a keepalive ping */ + grpc_closure init_keepalive_ping_locked; + /** Closure to run when the keepalive ping is sent */ + grpc_closure start_keepalive_ping_locked; + /** Cousure to run when the keepalive ping ack is received */ + grpc_closure finish_keepalive_ping_locked; + /** Closrue to run when the keepalive ping timeouts */ + grpc_closure keepalive_watchdog_fired_locked; + /** timer to initiate ping events */ + grpc_timer keepalive_ping_timer; + /** watchdog to kill the transport when waiting for the keepalive ping */ + grpc_timer keepalive_watchdog_timer; + /** time duration in between pings */ + grpc_millis keepalive_time; + /** grace period for a ping to complete before watchdog kicks in */ + grpc_millis keepalive_timeout; + /** if keepalive pings are allowed when there's no outstanding streams */ + bool keepalive_permit_without_calls = false; + /** keep-alive state machine state */ + grpc_chttp2_keepalive_state keepalive_state; + grpc_core::ContextList* cl = nullptr; + grpc_core::RefCountedPtr channelz_socket; + uint32_t num_messages_in_next_write = 0; +}; + +typedef enum { + GRPC_METADATA_NOT_PUBLISHED, + GRPC_METADATA_SYNTHESIZED_FROM_FAKE, + GRPC_METADATA_PUBLISHED_FROM_WIRE, + GPRC_METADATA_PUBLISHED_AT_CLOSE +} grpc_published_metadata_method; + +struct grpc_chttp2_stream { + grpc_chttp2_stream(grpc_chttp2_transport* t, grpc_stream_refcount* refcount, + const void* server_data, gpr_arena* arena); + ~grpc_chttp2_stream(); + + void* context; + grpc_chttp2_transport* t; + grpc_stream_refcount* refcount; + + grpc_closure destroy_stream; + grpc_closure* destroy_stream_arg; + + grpc_chttp2_stream_link links[STREAM_LIST_COUNT]; + uint8_t included[STREAM_LIST_COUNT] = {}; + + /** HTTP2 stream id for this stream, or zero if one has not been assigned */ + uint32_t id = 0; + + /** things the upper layers would like to send */ + grpc_metadata_batch* send_initial_metadata = nullptr; + grpc_closure* send_initial_metadata_finished = nullptr; + grpc_metadata_batch* send_trailing_metadata = nullptr; + grpc_closure* send_trailing_metadata_finished = nullptr; + + grpc_core::OrphanablePtr fetching_send_message; + uint32_t fetched_send_message_length = 0; + grpc_slice fetching_slice = grpc_empty_slice(); + int64_t next_message_end_offset; + int64_t flow_controlled_bytes_written = 0; + int64_t flow_controlled_bytes_flowed = 0; + grpc_closure complete_fetch_locked; + grpc_closure* fetching_send_message_finished = nullptr; + + grpc_metadata_batch* recv_initial_metadata; + grpc_closure* recv_initial_metadata_ready = nullptr; + bool* trailing_metadata_available = nullptr; + grpc_core::OrphanablePtr* recv_message; + grpc_closure* recv_message_ready = nullptr; + grpc_metadata_batch* recv_trailing_metadata; + grpc_closure* recv_trailing_metadata_finished = nullptr; + + grpc_transport_stream_stats* collecting_stats = nullptr; + grpc_transport_stream_stats stats = grpc_transport_stream_stats(); + + /** Is this stream closed for writing. */ + bool write_closed = false; + /** Is this stream reading half-closed. */ + bool read_closed = false; + /** Are all published incoming byte streams closed. */ + bool all_incoming_byte_streams_finished = false; + /** Has this stream seen an error. + If true, then pending incoming frames can be thrown away. */ + bool seen_error = false; + /** Are we buffering writes on this stream? If yes, we won't become writable + until there's enough queued up in the flow_controlled_buffer */ + bool write_buffering = false; + /** Has trailing metadata been received. */ + bool received_trailing_metadata = false; + + /* have we sent or received the EOS bit? */ + bool eos_received = false; + bool eos_sent = false; + + /** the error that resulted in this stream being read-closed */ + grpc_error* read_closed_error = GRPC_ERROR_NONE; + /** the error that resulted in this stream being write-closed */ + grpc_error* write_closed_error = GRPC_ERROR_NONE; + + grpc_published_metadata_method published_metadata[2] = {}; + bool final_metadata_requested = false; + + grpc_chttp2_incoming_metadata_buffer metadata_buffer[2]; + + grpc_slice_buffer frame_storage; /* protected by t combiner */ + + /* Accessed only by transport thread when stream->pending_byte_stream == false + * Accessed only by application thread when stream->pending_byte_stream == + * true */ + grpc_slice_buffer unprocessed_incoming_frames_buffer; + grpc_closure* on_next = nullptr; /* protected by t combiner */ + bool pending_byte_stream = false; /* protected by t combiner */ + // cached length of buffer to be used by the transport thread in cases where + // stream->pending_byte_stream == true. The value is saved before + // application threads are allowed to modify + // unprocessed_incoming_frames_buffer + size_t unprocessed_incoming_frames_buffer_cached_length = 0; + grpc_closure reset_byte_stream; + grpc_error* byte_stream_error = GRPC_ERROR_NONE; /* protected by t combiner */ + bool received_last_frame = false; /* protected by t combiner */ + + grpc_millis deadline = GRPC_MILLIS_INF_FUTURE; + + /** saw some stream level error */ + grpc_error* forced_close_error = GRPC_ERROR_NONE; + /** how many header frames have we received? */ + uint8_t header_frames_received = 0; + /** parsing state for data frames */ + /* Accessed only by transport thread when stream->pending_byte_stream == false + * Accessed only by application thread when stream->pending_byte_stream == + * true */ + grpc_chttp2_data_parser data_parser; + /** number of bytes received - reset at end of parse thread execution */ + int64_t received_bytes = 0; + + bool sent_initial_metadata = false; + bool sent_trailing_metadata = false; + + grpc_core::PolymorphicManualConstructor< + grpc_core::chttp2::StreamFlowControlBase, + grpc_core::chttp2::StreamFlowControl, + grpc_core::chttp2::StreamFlowControlDisabled> + flow_control; + + grpc_slice_buffer flow_controlled_buffer; + + grpc_chttp2_write_cb* on_flow_controlled_cbs = nullptr; + grpc_chttp2_write_cb* on_write_finished_cbs = nullptr; + grpc_chttp2_write_cb* finish_after_write = nullptr; + size_t sending_bytes = 0; + + /* Stream compression method to be used. */ + grpc_stream_compression_method stream_compression_method = + GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS; + /* Stream decompression method to be used. */ + grpc_stream_compression_method stream_decompression_method = + GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS; + /** Stream compression decompress context */ + grpc_stream_compression_context* stream_decompression_ctx = nullptr; + /** Stream compression compress context */ + grpc_stream_compression_context* stream_compression_ctx = nullptr; + + /** Buffer storing data that is compressed but not sent */ + grpc_slice_buffer compressed_data_buffer; + /** Amount of uncompressed bytes sent out when compressed_data_buffer is + * emptied */ + size_t uncompressed_data_size = 0; + /** Temporary buffer storing decompressed data */ + grpc_slice_buffer decompressed_data_buffer; + /** Whether bytes stored in unprocessed_incoming_byte_stream is decompressed + */ + bool unprocessed_incoming_frames_decompressed = false; + /** Whether the bytes needs to be traced using Fathom */ + bool traced = false; + /** gRPC header bytes that are already decompressed */ + size_t decompressed_header_bytes = 0; + /** Byte counter for number of bytes written */ + size_t byte_counter = 0; +}; + +/** Transport writing call flow: + grpc_chttp2_initiate_write() is called anywhere that we know bytes need to + go out on the wire. + If no other write has been started, a task is enqueued onto our workqueue. + When that task executes, it obtains the global lock, and gathers the data + to write. + The global lock is dropped and we do the syscall to write. + After writing, a follow-up check is made to see if another round of writing + should be performed. + + The actual call chain is documented in the implementation of this function. + */ +void grpc_chttp2_initiate_write(grpc_chttp2_transport* t, + grpc_chttp2_initiate_write_reason reason); + +typedef struct { + /** are we writing? */ + bool writing; + /** if writing: was it a complete flush (false) or a partial flush (true) */ + bool partial; + /** did we queue any completions as part of beginning the write */ + bool early_results_scheduled; +} grpc_chttp2_begin_write_result; + +grpc_chttp2_begin_write_result grpc_chttp2_begin_write( + grpc_chttp2_transport* t); +void grpc_chttp2_end_write(grpc_chttp2_transport* t, grpc_error* error); + +/** 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 */ +grpc_error* grpc_chttp2_perform_read(grpc_chttp2_transport* t, + grpc_slice slice); + +bool grpc_chttp2_list_add_writable_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +/** Get a writable stream + returns non-zero if there was a stream available */ +bool grpc_chttp2_list_pop_writable_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream** s); +bool grpc_chttp2_list_remove_writable_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); + +bool grpc_chttp2_list_add_writing_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +bool grpc_chttp2_list_have_writing_streams(grpc_chttp2_transport* t); +bool grpc_chttp2_list_pop_writing_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream** s); + +void grpc_chttp2_list_add_written_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +bool grpc_chttp2_list_pop_written_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream** s); + +void grpc_chttp2_list_add_waiting_for_concurrency(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +bool grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport* t, + grpc_chttp2_stream** s); +void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); + +void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +bool grpc_chttp2_list_pop_stalled_by_transport(grpc_chttp2_transport* t, + grpc_chttp2_stream** s); +void grpc_chttp2_list_remove_stalled_by_transport(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); + +void grpc_chttp2_list_add_stalled_by_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +bool grpc_chttp2_list_pop_stalled_by_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream** s); +bool grpc_chttp2_list_remove_stalled_by_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); + +/********* Flow Control ***************/ + +// Takes in a flow control action and performs all the needed operations. +void grpc_chttp2_act_on_flowctl_action( + const grpc_core::chttp2::FlowControlAction& action, + grpc_chttp2_transport* t, grpc_chttp2_stream* s); + +/********* End of Flow Control ***************/ + +grpc_chttp2_stream* grpc_chttp2_parsing_lookup_stream(grpc_chttp2_transport* t, + uint32_t id); +grpc_chttp2_stream* grpc_chttp2_parsing_accept_stream(grpc_chttp2_transport* t, + uint32_t id); + +void grpc_chttp2_add_incoming_goaway(grpc_chttp2_transport* t, + uint32_t goaway_error, + grpc_slice goaway_text); + +void grpc_chttp2_parsing_become_skip_parser(grpc_chttp2_transport* t); + +void grpc_chttp2_complete_closure_step(grpc_chttp2_transport* t, + grpc_chttp2_stream* s, + grpc_closure** pclosure, + grpc_error* error, const char* desc); + +#define GRPC_HEADER_SIZE_IN_BYTES 5 +#define MAX_SIZE_T (~(size_t)0) + +#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 grpc_core::TraceFlag grpc_http_trace; +// extern grpc_core::TraceFlag grpc_flowctl_trace; + +#define GRPC_CHTTP2_IF_TRACING(stmt) \ + if (!(grpc_http_trace.enabled())) \ + ; \ + else \ + stmt + +void grpc_chttp2_fake_status(grpc_chttp2_transport* t, + grpc_chttp2_stream* stream, grpc_error* error); +void grpc_chttp2_mark_stream_closed(grpc_chttp2_transport* t, + grpc_chttp2_stream* s, int close_reads, + int close_writes, grpc_error* error); +void grpc_chttp2_start_writing(grpc_chttp2_transport* t); + +#ifndef NDEBUG +#define GRPC_CHTTP2_STREAM_REF(stream, reason) \ + grpc_chttp2_stream_ref(stream, reason) +#define GRPC_CHTTP2_STREAM_UNREF(stream, reason) \ + grpc_chttp2_stream_unref(stream, reason) +void grpc_chttp2_stream_ref(grpc_chttp2_stream* s, const char* reason); +void grpc_chttp2_stream_unref(grpc_chttp2_stream* s, const char* reason); +#else +#define GRPC_CHTTP2_STREAM_REF(stream, reason) grpc_chttp2_stream_ref(stream) +#define GRPC_CHTTP2_STREAM_UNREF(stream, reason) \ + grpc_chttp2_stream_unref(stream) +void grpc_chttp2_stream_ref(grpc_chttp2_stream* s); +void grpc_chttp2_stream_unref(grpc_chttp2_stream* s); +#endif + +#ifndef NDEBUG +#define GRPC_CHTTP2_REF_TRANSPORT(t, r) \ + grpc_chttp2_ref_transport(t, r, __FILE__, __LINE__) +#define GRPC_CHTTP2_UNREF_TRANSPORT(t, r) \ + grpc_chttp2_unref_transport(t, r, __FILE__, __LINE__) +inline void grpc_chttp2_unref_transport(grpc_chttp2_transport* t, + const char* reason, const char* file, + int line) { + if (t->refs.Unref(grpc_core::DebugLocation(file, line), reason)) { + grpc_core::Delete(t); + } +} +inline void grpc_chttp2_ref_transport(grpc_chttp2_transport* t, + const char* reason, const char* file, + int line) { + t->refs.Ref(grpc_core::DebugLocation(file, line), reason); +} +#else +#define GRPC_CHTTP2_REF_TRANSPORT(t, r) grpc_chttp2_ref_transport(t) +#define GRPC_CHTTP2_UNREF_TRANSPORT(t, r) grpc_chttp2_unref_transport(t) +inline void grpc_chttp2_unref_transport(grpc_chttp2_transport* t) { + if (t->refs.Unref()) { + grpc_core::Delete(t); + } +} +inline void grpc_chttp2_ref_transport(grpc_chttp2_transport* t) { + t->refs.Ref(); +} +#endif + +void grpc_chttp2_ack_ping(grpc_chttp2_transport* t, uint64_t id); + +/** Add a new ping strike to ping_recv_state.ping_strikes. If + ping_recv_state.ping_strikes > ping_policy.max_ping_strikes, it sends GOAWAY + with error code ENHANCE_YOUR_CALM and additional debug data resembling + "too_many_pings" followed by immediately closing the connection. */ +void grpc_chttp2_add_ping_strike(grpc_chttp2_transport* t); + +/** add a ref to the stream and add it to the writable list; + ref will be dropped in writing.c */ +void grpc_chttp2_mark_stream_writable(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); + +void grpc_chttp2_cancel_stream(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_error* due_to_error); + +void grpc_chttp2_maybe_complete_recv_initial_metadata(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +void grpc_chttp2_maybe_complete_recv_message(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); +void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_chttp2_transport* t, + grpc_chttp2_stream* s); + +void grpc_chttp2_fail_pending_writes(grpc_chttp2_transport* t, + grpc_chttp2_stream* s, grpc_error* error); + +/** Set the default keepalive configurations, must only be called at + initialization */ +void grpc_chttp2_config_default_keepalive_args(grpc_channel_args* args, + bool is_client); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/parsing.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/parsing.cc new file mode 100644 index 00000000000000..1ff96d3cd363c7 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/parsing.cc @@ -0,0 +1,780 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include + +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/http2_errors.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_conversion.h" +#include "src/core/lib/transport/timeout_encoding.h" + +static grpc_error* init_frame_parser(grpc_chttp2_transport* t); +static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t, + int is_continuation); +static grpc_error* init_data_frame_parser(grpc_chttp2_transport* t); +static grpc_error* init_rst_stream_parser(grpc_chttp2_transport* t); +static grpc_error* init_settings_frame_parser(grpc_chttp2_transport* t); +static grpc_error* init_window_update_frame_parser(grpc_chttp2_transport* t); +static grpc_error* init_ping_parser(grpc_chttp2_transport* t); +static grpc_error* init_goaway_parser(grpc_chttp2_transport* t); +static grpc_error* init_skip_frame_parser(grpc_chttp2_transport* t, + int is_header); + +static grpc_error* parse_frame_slice(grpc_chttp2_transport* t, grpc_slice slice, + int is_last); + +grpc_error* grpc_chttp2_perform_read(grpc_chttp2_transport* t, + grpc_slice slice) { + uint8_t* beg = GRPC_SLICE_START_PTR(slice); + uint8_t* end = GRPC_SLICE_END_PTR(slice); + uint8_t* cur = beg; + grpc_error* err; + + if (cur == end) return GRPC_ERROR_NONE; + + switch (t->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 && t->deframe_state != GRPC_DTS_FH_0) { + if (*cur != GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state]) { + char* msg; + gpr_asprintf( + &msg, + "Connect string mismatch: expected '%c' (%d) got '%c' (%d) " + "at byte %d", + GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state], + static_cast(static_cast( + GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state])), + *cur, static_cast(*cur), t->deframe_state); + err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + ++cur; + t->deframe_state = static_cast( + 1 + static_cast(t->deframe_state)); + } + if (cur == end) { + return GRPC_ERROR_NONE; + } + /* fallthrough */ + dts_fh_0: + case GRPC_DTS_FH_0: + GPR_ASSERT(cur < end); + t->incoming_frame_size = (static_cast(*cur)) << 16; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_1; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_1: + GPR_ASSERT(cur < end); + t->incoming_frame_size |= (static_cast(*cur)) << 8; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_2; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_2: + GPR_ASSERT(cur < end); + t->incoming_frame_size |= *cur; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_3; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_3: + GPR_ASSERT(cur < end); + t->incoming_frame_type = *cur; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_4; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_4: + GPR_ASSERT(cur < end); + t->incoming_frame_flags = *cur; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_5; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_5: + GPR_ASSERT(cur < end); + t->incoming_stream_id = ((static_cast(*cur)) & 0x7f) << 24; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_6; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_6: + GPR_ASSERT(cur < end); + t->incoming_stream_id |= (static_cast(*cur)) << 16; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_7; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_7: + GPR_ASSERT(cur < end); + t->incoming_stream_id |= (static_cast(*cur)) << 8; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_8; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_8: + GPR_ASSERT(cur < end); + t->incoming_stream_id |= (static_cast(*cur)); + t->deframe_state = GRPC_DTS_FRAME; + err = init_frame_parser(t); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (t->incoming_frame_size == 0) { + err = parse_frame_slice(t, grpc_empty_slice(), 1); + if (err != GRPC_ERROR_NONE) { + return err; + } + t->incoming_stream = nullptr; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_0; + return GRPC_ERROR_NONE; + } + goto dts_fh_0; /* loop */ + } else if (t->flow_control->flow_control_enabled() && + t->incoming_frame_size > + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]) { + char* msg; + gpr_asprintf(&msg, "Frame size %d is larger than max frame size %d", + t->incoming_frame_size, + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]); + err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + if (++cur == end) { + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FRAME: + GPR_ASSERT(cur < end); + if (static_cast(end - cur) == t->incoming_frame_size) { + err = parse_frame_slice( + t, + grpc_slice_sub_no_ref(slice, static_cast(cur - beg), + static_cast(end - beg)), + 1); + if (err != GRPC_ERROR_NONE) { + return err; + } + t->deframe_state = GRPC_DTS_FH_0; + t->incoming_stream = nullptr; + return GRPC_ERROR_NONE; + } else if (static_cast(end - cur) > t->incoming_frame_size) { + size_t cur_offset = static_cast(cur - beg); + err = parse_frame_slice( + t, + grpc_slice_sub_no_ref(slice, cur_offset, + cur_offset + t->incoming_frame_size), + 1); + if (err != GRPC_ERROR_NONE) { + return err; + } + cur += t->incoming_frame_size; + t->incoming_stream = nullptr; + goto dts_fh_0; /* loop */ + } else { + err = parse_frame_slice( + t, + grpc_slice_sub_no_ref(slice, static_cast(cur - beg), + static_cast(end - beg)), + 0); + if (err != GRPC_ERROR_NONE) { + return err; + } + t->incoming_frame_size -= static_cast(end - cur); + return GRPC_ERROR_NONE; + } + GPR_UNREACHABLE_CODE(return nullptr); + } + + GPR_UNREACHABLE_CODE(return nullptr); +} + +static grpc_error* init_frame_parser(grpc_chttp2_transport* t) { + if (t->is_first_frame && + t->incoming_frame_type != GRPC_CHTTP2_FRAME_SETTINGS) { + char* msg; + gpr_asprintf( + &msg, "Expected SETTINGS frame as the first frame, got frame type %d", + t->incoming_frame_type); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + t->is_first_frame = false; + if (t->expect_continuation_stream_id != 0) { + if (t->incoming_frame_type != GRPC_CHTTP2_FRAME_CONTINUATION) { + char* msg; + gpr_asprintf(&msg, "Expected CONTINUATION frame, got frame type %02x", + t->incoming_frame_type); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + if (t->expect_continuation_stream_id != t->incoming_stream_id) { + char* msg; + gpr_asprintf( + &msg, + "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got " + "grpc_chttp2_stream %08x", + t->expect_continuation_stream_id, t->incoming_stream_id); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + return init_header_frame_parser(t, 1); + } + switch (t->incoming_frame_type) { + case GRPC_CHTTP2_FRAME_DATA: + return init_data_frame_parser(t); + case GRPC_CHTTP2_FRAME_HEADER: + return init_header_frame_parser(t, 0); + case GRPC_CHTTP2_FRAME_CONTINUATION: + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Unexpected CONTINUATION frame"); + case GRPC_CHTTP2_FRAME_RST_STREAM: + return init_rst_stream_parser(t); + case GRPC_CHTTP2_FRAME_SETTINGS: + return init_settings_frame_parser(t); + case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: + return init_window_update_frame_parser(t); + case GRPC_CHTTP2_FRAME_PING: + return init_ping_parser(t); + case GRPC_CHTTP2_FRAME_GOAWAY: + return init_goaway_parser(t); + default: + if (grpc_http_trace.enabled()) { + gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type); + } + return init_skip_frame_parser(t, 0); + } +} + +static grpc_error* skip_parser(void* parser, grpc_chttp2_transport* t, + grpc_chttp2_stream* s, grpc_slice slice, + int is_last) { + return GRPC_ERROR_NONE; +} + +static void skip_header(void* tp, grpc_mdelem md) { GRPC_MDELEM_UNREF(md); } + +static grpc_error* init_skip_frame_parser(grpc_chttp2_transport* t, + int is_header) { + if (is_header) { + uint8_t is_eoh = t->expect_continuation_stream_id != 0; + t->parser = grpc_chttp2_header_parser_parse; + t->parser_data = &t->hpack_parser; + t->hpack_parser.on_header = skip_header; + t->hpack_parser.on_header_user_data = nullptr; + t->hpack_parser.is_boundary = is_eoh; + t->hpack_parser.is_eof = static_cast(is_eoh ? t->header_eof : 0); + } else { + t->parser = skip_parser; + } + return GRPC_ERROR_NONE; +} + +void grpc_chttp2_parsing_become_skip_parser(grpc_chttp2_transport* t) { + init_skip_frame_parser(t, t->parser == grpc_chttp2_header_parser_parse); +} + +static grpc_error* init_data_frame_parser(grpc_chttp2_transport* t) { + grpc_chttp2_stream* s = + grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + grpc_error* err = GRPC_ERROR_NONE; + grpc_core::chttp2::FlowControlAction action; + if (s == nullptr) { + err = t->flow_control->RecvData(t->incoming_frame_size); + action = t->flow_control->MakeAction(); + } else { + err = s->flow_control->RecvData(t->incoming_frame_size); + action = s->flow_control->MakeAction(); + } + grpc_chttp2_act_on_flowctl_action(action, t, s); + if (err != GRPC_ERROR_NONE) { + goto error_handler; + } + if (s == nullptr) { + return init_skip_frame_parser(t, 0); + } + s->received_bytes += t->incoming_frame_size; + s->stats.incoming.framing_bytes += 9; + if (err == GRPC_ERROR_NONE && s->read_closed) { + return init_skip_frame_parser(t, 0); + } + if (err == GRPC_ERROR_NONE) { + err = grpc_chttp2_data_parser_begin_frame( + &s->data_parser, t->incoming_frame_flags, s->id, s); + } +error_handler: + intptr_t unused; + if (err == GRPC_ERROR_NONE) { + t->incoming_stream = s; + /* t->parser = grpc_chttp2_data_parser_parse;*/ + t->parser = grpc_chttp2_data_parser_parse; + t->parser_data = &s->data_parser; + t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST; + return GRPC_ERROR_NONE; + } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, &unused)) { + /* handle stream errors by closing the stream */ + if (s != nullptr) { + grpc_chttp2_mark_stream_closed(t, s, true, false, err); + } + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, + GRPC_HTTP2_PROTOCOL_ERROR, + &s->stats.outgoing)); + return init_skip_frame_parser(t, 0); + } else { + return err; + } +} + +static void free_timeout(void* p) { gpr_free(p); } + +static void on_initial_header(void* tp, grpc_mdelem md) { + GPR_TIMER_SCOPE("on_initial_header", 0); + + grpc_chttp2_transport* t = static_cast(tp); + grpc_chttp2_stream* s = t->incoming_stream; + GPR_ASSERT(s != nullptr); + + if (grpc_http_trace.enabled()) { + char* key = grpc_slice_to_c_string(GRPC_MDKEY(md)); + char* value = + grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", s->id, + t->is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } + + if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { + // We don't use grpc_mdelem_eq here to avoid executing additional + // instructions. The reasoning is if the payload is not equal, we already + // know that the metadata elements are not equal because the md is + // confirmed to be static. If we had used grpc_mdelem_eq here, then if the + // payloads are not equal, grpc_mdelem_eq executes more instructions to + // determine if they're equal or not. + if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload || + md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) { + s->seen_error = true; + } + } else { + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && + !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + /* TODO(ctiller): check for a status like " 0" */ + s->seen_error = true; + } + + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { + grpc_millis* cached_timeout = static_cast( + grpc_mdelem_get_user_data(md, free_timeout)); + grpc_millis timeout; + if (cached_timeout != nullptr) { + timeout = *cached_timeout; + } else { + if (GPR_UNLIKELY( + !grpc_http2_decode_timeout(GRPC_MDVALUE(md), &timeout))) { + char* val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val); + gpr_free(val); + timeout = GRPC_MILLIS_INF_FUTURE; + } + if (GRPC_MDELEM_IS_INTERNED(md)) { + /* store the result */ + cached_timeout = + static_cast(gpr_malloc(sizeof(grpc_millis))); + *cached_timeout = timeout; + grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); + } + } + if (timeout != GRPC_MILLIS_INF_FUTURE) { + grpc_chttp2_incoming_metadata_buffer_set_deadline( + &s->metadata_buffer[0], grpc_core::ExecCtx::Get()->Now() + timeout); + } + GRPC_MDELEM_UNREF(md); + return; + } + } + + const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md); + const size_t metadata_size_limit = + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (new_size > metadata_size_limit) { + gpr_log(GPR_DEBUG, + "received initial metadata size exceeds limit (%" PRIuPTR + " vs. %" PRIuPTR ")", + new_size, metadata_size_limit); + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "received initial metadata size exceeds limit"), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_RESOURCE_EXHAUSTED)); + grpc_chttp2_parsing_become_skip_parser(t); + s->seen_error = true; + GRPC_MDELEM_UNREF(md); + } else { + grpc_error* error = + grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[0], md); + if (error != GRPC_ERROR_NONE) { + grpc_chttp2_cancel_stream(t, s, error); + grpc_chttp2_parsing_become_skip_parser(t); + s->seen_error = true; + GRPC_MDELEM_UNREF(md); + } + } +} + +static void on_trailing_header(void* tp, grpc_mdelem md) { + GPR_TIMER_SCOPE("on_trailing_header", 0); + + grpc_chttp2_transport* t = static_cast(tp); + grpc_chttp2_stream* s = t->incoming_stream; + GPR_ASSERT(s != nullptr); + + if (grpc_http_trace.enabled()) { + char* key = grpc_slice_to_c_string(GRPC_MDKEY(md)); + char* value = + grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "HTTP:%d:TRL:%s: %s: %s", s->id, + t->is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } + + if (GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC) { + // We don't use grpc_mdelem_eq here to avoid executing additional + // instructions. The reasoning is if the payload is not equal, we already + // know that the metadata elements are not equal because the md is + // confirmed to be static. If we had used grpc_mdelem_eq here, then if the + // payloads are not equal, grpc_mdelem_eq executes more instructions to + // determine if they're equal or not. + if (md.payload == GRPC_MDELEM_GRPC_STATUS_1.payload || + md.payload == GRPC_MDELEM_GRPC_STATUS_2.payload) { + s->seen_error = true; + } + } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && + !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + /* TODO(ctiller): check for a status like " 0" */ + s->seen_error = true; + } + + const size_t new_size = s->metadata_buffer[1].size + GRPC_MDELEM_LENGTH(md); + const size_t metadata_size_limit = + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (new_size > metadata_size_limit) { + gpr_log(GPR_DEBUG, + "received trailing metadata size exceeds limit (%" PRIuPTR + " vs. %" PRIuPTR ")", + new_size, metadata_size_limit); + grpc_chttp2_cancel_stream( + t, s, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "received trailing metadata size exceeds limit"), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_RESOURCE_EXHAUSTED)); + grpc_chttp2_parsing_become_skip_parser(t); + s->seen_error = true; + GRPC_MDELEM_UNREF(md); + } else { + grpc_error* error = + grpc_chttp2_incoming_metadata_buffer_add(&s->metadata_buffer[1], md); + if (error != GRPC_ERROR_NONE) { + grpc_chttp2_cancel_stream(t, s, error); + grpc_chttp2_parsing_become_skip_parser(t); + s->seen_error = true; + GRPC_MDELEM_UNREF(md); + } + } +} + +static grpc_error* init_header_frame_parser(grpc_chttp2_transport* t, + int is_continuation) { + uint8_t is_eoh = + (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; + grpc_chttp2_stream* s; + + /* TODO(ctiller): when to increment header_frames_received? */ + + if (is_eoh) { + t->expect_continuation_stream_id = 0; + } else { + t->expect_continuation_stream_id = t->incoming_stream_id; + } + + if (!is_continuation) { + t->header_eof = + (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) != 0; + } + + t->ping_state.last_ping_sent_time = GRPC_MILLIS_INF_PAST; + + /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */ + s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + if (s == nullptr) { + if (GPR_UNLIKELY(is_continuation)) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_ERROR, + "grpc_chttp2_stream disbanded before CONTINUATION received")); + return init_skip_frame_parser(t, 1); + } + if (t->is_client) { + if (GPR_LIKELY((t->incoming_stream_id & 1) && + t->incoming_stream_id < t->next_stream_id)) { + /* this is an old (probably cancelled) grpc_chttp2_stream */ + } else { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_ERROR, "ignoring new grpc_chttp2_stream creation on client")); + } + grpc_error* err = init_skip_frame_parser(t, 1); + if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY) { + grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser); + } + return err; + } else if (GPR_UNLIKELY(t->last_new_stream_id >= t->incoming_stream_id)) { + GRPC_CHTTP2_IF_TRACING(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", + t->last_new_stream_id, t->incoming_stream_id)); + return init_skip_frame_parser(t, 1); + } else if (GPR_UNLIKELY((t->incoming_stream_id & 1) == 0)) { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_ERROR, + "ignoring grpc_chttp2_stream with non-client generated index %d", + t->incoming_stream_id)); + return init_skip_frame_parser(t, 1); + } else if (GPR_UNLIKELY( + grpc_chttp2_stream_map_size(&t->stream_map) >= + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS])) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Max stream count exceeded"); + } + t->last_new_stream_id = t->incoming_stream_id; + s = t->incoming_stream = + grpc_chttp2_parsing_accept_stream(t, t->incoming_stream_id); + if (GPR_UNLIKELY(s == nullptr)) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted")); + return init_skip_frame_parser(t, 1); + } + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordStreamStartedFromRemote(); + } + } else { + t->incoming_stream = s; + } + GPR_ASSERT(s != nullptr); + s->stats.incoming.framing_bytes += 9; + if (GPR_UNLIKELY(s->read_closed)) { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_ERROR, "skipping already closed grpc_chttp2_stream header")); + t->incoming_stream = nullptr; + return init_skip_frame_parser(t, 1); + } + t->parser = grpc_chttp2_header_parser_parse; + t->parser_data = &t->hpack_parser; + if (t->header_eof) { + s->eos_received = true; + } + switch (s->header_frames_received) { + case 0: + if (t->is_client && t->header_eof) { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing Trailers-Only")); + if (s->trailing_metadata_available != nullptr) { + *s->trailing_metadata_available = true; + } + t->hpack_parser.on_header = on_trailing_header; + s->received_trailing_metadata = true; + } else { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing initial_metadata")); + t->hpack_parser.on_header = on_initial_header; + } + break; + case 1: + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing trailing_metadata")); + t->hpack_parser.on_header = on_trailing_header; + s->received_trailing_metadata = true; + break; + case 2: + gpr_log(GPR_ERROR, "too many header frames received"); + return init_skip_frame_parser(t, 1); + } + t->hpack_parser.on_header_user_data = t; + t->hpack_parser.is_boundary = is_eoh; + t->hpack_parser.is_eof = static_cast(is_eoh ? t->header_eof : 0); + if (!is_continuation && + (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY)) { + grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser); + } + return GRPC_ERROR_NONE; +} + +static grpc_error* init_window_update_frame_parser(grpc_chttp2_transport* t) { + grpc_error* err = grpc_chttp2_window_update_parser_begin_frame( + &t->simple.window_update, t->incoming_frame_size, + t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + if (t->incoming_stream_id != 0) { + grpc_chttp2_stream* s = t->incoming_stream = + grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + if (s == nullptr) { + return init_skip_frame_parser(t, 0); + } + s->stats.incoming.framing_bytes += 9; + } + t->parser = grpc_chttp2_window_update_parser_parse; + t->parser_data = &t->simple.window_update; + return GRPC_ERROR_NONE; +} + +static grpc_error* init_ping_parser(grpc_chttp2_transport* t) { + grpc_error* err = grpc_chttp2_ping_parser_begin_frame( + &t->simple.ping, t->incoming_frame_size, t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + t->parser = grpc_chttp2_ping_parser_parse; + t->parser_data = &t->simple.ping; + return GRPC_ERROR_NONE; +} + +static grpc_error* init_rst_stream_parser(grpc_chttp2_transport* t) { + grpc_error* err = grpc_chttp2_rst_stream_parser_begin_frame( + &t->simple.rst_stream, t->incoming_frame_size, t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + grpc_chttp2_stream* s = t->incoming_stream = + grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + if (!t->incoming_stream) { + return init_skip_frame_parser(t, 0); + } + s->stats.incoming.framing_bytes += 9; + t->parser = grpc_chttp2_rst_stream_parser_parse; + t->parser_data = &t->simple.rst_stream; + return GRPC_ERROR_NONE; +} + +static grpc_error* init_goaway_parser(grpc_chttp2_transport* t) { + grpc_error* err = grpc_chttp2_goaway_parser_begin_frame( + &t->goaway_parser, t->incoming_frame_size, t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + t->parser = grpc_chttp2_goaway_parser_parse; + t->parser_data = &t->goaway_parser; + return GRPC_ERROR_NONE; +} + +static grpc_error* init_settings_frame_parser(grpc_chttp2_transport* t) { + if (t->incoming_stream_id != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Settings frame received for grpc_chttp2_stream"); + } + + grpc_error* err = grpc_chttp2_settings_parser_begin_frame( + &t->simple.settings, t->incoming_frame_size, t->incoming_frame_flags, + t->settings[GRPC_PEER_SETTINGS]); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) { + memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS], + GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); + grpc_chttp2_hptbl_set_max_bytes( + &t->hpack_parser.table, + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); + t->sent_local_settings = 0; + } + t->parser = grpc_chttp2_settings_parser_parse; + t->parser_data = &t->simple.settings; + return GRPC_ERROR_NONE; +} + +static grpc_error* parse_frame_slice(grpc_chttp2_transport* t, grpc_slice slice, + int is_last) { + grpc_chttp2_stream* s = t->incoming_stream; + grpc_error* err = t->parser(t->parser_data, t, s, slice, is_last); + intptr_t unused; + if (GPR_LIKELY(err == GRPC_ERROR_NONE)) { + return err; + } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, &unused)) { + if (grpc_http_trace.enabled()) { + const char* msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "%s", msg); + } + grpc_chttp2_parsing_become_skip_parser(t); + if (s) { + s->forced_close_error = err; + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, + GRPC_HTTP2_PROTOCOL_ERROR, + &s->stats.outgoing)); + } else { + GRPC_ERROR_UNREF(err); + } + } + return err; +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_lists.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_lists.cc new file mode 100644 index 00000000000000..6626170a7e4442 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_lists.cc @@ -0,0 +1,216 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +static const char* stream_list_id_string(grpc_chttp2_stream_list_id id) { + switch (id) { + case GRPC_CHTTP2_LIST_WRITABLE: + return "writable"; + case GRPC_CHTTP2_LIST_WRITING: + return "writing"; + case GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT: + return "stalled_by_transport"; + case GRPC_CHTTP2_LIST_STALLED_BY_STREAM: + return "stalled_by_stream"; + case GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY: + return "waiting_for_concurrency"; + case STREAM_LIST_COUNT: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +grpc_core::TraceFlag grpc_trace_http2_stream_state(false, "http2_stream_state"); + +/* core list management */ + +static bool stream_list_empty(grpc_chttp2_transport* t, + grpc_chttp2_stream_list_id id) { + return t->lists[id].head == nullptr; +} + +static bool 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 = nullptr; + } else { + t->lists[id].head = nullptr; + t->lists[id].tail = nullptr; + } + s->included[id] = 0; + } + *stream = s; + if (s && grpc_trace_http2_stream_state.enabled()) { + gpr_log(GPR_INFO, "%p[%d][%s]: pop from %s", t, s->id, + t->is_client ? "cli" : "svr", stream_list_id_string(id)); + } + return s != nullptr; +} + +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; + } + if (grpc_trace_http2_stream_state.enabled()) { + gpr_log(GPR_INFO, "%p[%d][%s]: remove from %s", t, s->id, + t->is_client ? "cli" : "svr", stream_list_id_string(id)); + } +} + +static bool 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); + return true; + } else { + return false; + } +} + +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 = nullptr; + 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; + if (grpc_trace_http2_stream_state.enabled()) { + gpr_log(GPR_INFO, "%p[%d][%s]: add to %s", t, s->id, + t->is_client ? "cli" : "svr", stream_list_id_string(id)); + } +} + +static bool stream_list_add(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_chttp2_stream_list_id id) { + if (s->included[id]) { + return false; + } + stream_list_add_tail(t, s, id); + return true; +} + +/* wrappers for specializations */ + +bool grpc_chttp2_list_add_writable_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + GPR_ASSERT(s->id != 0); + return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITABLE); +} + +bool grpc_chttp2_list_pop_writable_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream** s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITABLE); +} + +bool grpc_chttp2_list_remove_writable_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WRITABLE); +} + +bool grpc_chttp2_list_add_writing_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITING); +} + +bool grpc_chttp2_list_have_writing_streams(grpc_chttp2_transport* t) { + return !stream_list_empty(t, GRPC_CHTTP2_LIST_WRITING); +} + +bool grpc_chttp2_list_pop_writing_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream** s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITING); +} + +void grpc_chttp2_list_add_waiting_for_concurrency(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + stream_list_add(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); +} + +bool grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport* t, + grpc_chttp2_stream** s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); +} + +void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); +} + +void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + GPR_ASSERT(t->flow_control->flow_control_enabled()); + stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); +} + +bool grpc_chttp2_list_pop_stalled_by_transport(grpc_chttp2_transport* t, + grpc_chttp2_stream** s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); +} + +void grpc_chttp2_list_remove_stalled_by_transport(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); +} + +void grpc_chttp2_list_add_stalled_by_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + GPR_ASSERT(t->flow_control->flow_control_enabled()); + stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); +} + +bool grpc_chttp2_list_pop_stalled_by_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream** s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); +} + +bool grpc_chttp2_list_remove_stalled_by_stream(grpc_chttp2_transport* t, + grpc_chttp2_stream* s) { + return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); +} diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.cc new file mode 100644 index 00000000000000..f300e2356c955b --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.cc @@ -0,0 +1,167 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/stream_map.h" + +#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 = + static_cast(gpr_malloc(sizeof(uint32_t) * initial_capacity)); + map->values = + static_cast(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(uint32_t* 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, uint32_t key, + void* value) { + size_t count = map->count; + size_t capacity = map->capacity; + uint32_t* keys = map->keys; + void** values = map->values; + + GPR_ASSERT(count == 0 || keys[count - 1] < key); + GPR_ASSERT(value); + GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == nullptr); + + 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 = static_cast( + gpr_realloc(keys, capacity * sizeof(uint32_t))); + map->values = values = + static_cast(gpr_realloc(values, capacity * sizeof(void*))); + } + } + + keys[count] = key; + values[count] = value; + map->count = count + 1; +} + +static void** find(grpc_chttp2_stream_map* map, uint32_t key) { + size_t min_idx = 0; + size_t max_idx = map->count; + size_t mid_idx; + uint32_t* keys = map->keys; + void** values = map->values; + uint32_t mid_key; + + if (max_idx == 0) return nullptr; + + 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 nullptr; +} + +void* grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map* map, uint32_t key) { + void** pvalue = find(map, key); + void* out = nullptr; + if (pvalue != nullptr) { + out = *pvalue; + *pvalue = nullptr; + map->free += (out != nullptr); + /* recognize complete emptyness and ensure we can skip + * defragmentation later */ + if (map->free == map->count) { + map->free = map->count = 0; + } + GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == nullptr); + } + return out; +} + +void* grpc_chttp2_stream_map_find(grpc_chttp2_stream_map* map, uint32_t key) { + void** pvalue = find(map, key); + return pvalue != nullptr ? *pvalue : nullptr; +} + +size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map* map) { + return map->count - map->free; +} + +void* grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map* map) { + if (map->count == map->free) { + return nullptr; + } + if (map->free != 0) { + map->count = compact(map->keys, map->values, map->count); + map->free = 0; + GPR_ASSERT(map->count > 0); + } + return map->values[(static_cast(rand())) % map->count]; +} + +void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map* map, + void (*f)(void* user_data, uint32_t 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/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.h new file mode 100644 index 00000000000000..9fb8826e8e682e --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/stream_map.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H + +#include + +#include + +/* Data structure to map a uint32_t 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 { + uint32_t* 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, uint32_t 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, uint32_t key); + +/* Return an existing key, or NULL if it does not exist */ +void* grpc_chttp2_stream_map_find(grpc_chttp2_stream_map* map, uint32_t key); + +/* Return a random entry */ +void* grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map* map); + +/* 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, uint32_t key, + void* value), + void* user_data); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.cc new file mode 100644 index 00000000000000..d4b01788b94119 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.cc @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/varint.h" + +uint32_t grpc_chttp2_hpack_varint_length(uint32_t 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(uint32_t tail_value, uint8_t* target, + uint32_t tail_length) { + switch (tail_length) { + case 5: + target[4] = static_cast((tail_value >> 28) | 0x80); + /* fallthrough */ + case 4: + target[3] = static_cast((tail_value >> 21) | 0x80); + /* fallthrough */ + case 3: + target[2] = static_cast((tail_value >> 14) | 0x80); + /* fallthrough */ + case 2: + target[1] = static_cast((tail_value >> 7) | 0x80); + /* fallthrough */ + case 1: + target[0] = static_cast((tail_value) | 0x80); + } + target[tail_length - 1] &= 0x7f; +} diff --git a/third_party/grpc/src/core/transport/chttp2/varint.h b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.h similarity index 52% rename from third_party/grpc/src/core/transport/chttp2/varint.h rename to third_party/grpc/src/core/ext/transport/chttp2/transport/varint.h index 2d92b6693e9c87..5a2b670f065a95 100644 --- a/third_party/grpc/src/core/transport/chttp2/varint.h +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/varint.h @@ -1,38 +1,23 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_VARINT_H -#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_VARINT_H +#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_VARINT_H +#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_VARINT_H #include @@ -72,4 +57,4 @@ void grpc_chttp2_hpack_write_varint_tail(uint32_t tail_value, uint8_t* target, } \ } while (0) -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_VARINT_H */ +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_VARINT_H */ diff --git a/third_party/grpc/src/core/ext/transport/chttp2/transport/writing.cc b/third_party/grpc/src/core/ext/transport/chttp2/transport/writing.cc new file mode 100644 index 00000000000000..265d3365d3ce12 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/chttp2/transport/writing.cc @@ -0,0 +1,657 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/chttp2/transport/context_list.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/transport/http2_errors.h" + +static void add_to_write_list(grpc_chttp2_write_cb** list, + grpc_chttp2_write_cb* cb) { + cb->next = *list; + *list = cb; +} + +static void finish_write_cb(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + grpc_chttp2_write_cb* cb, grpc_error* error) { + grpc_chttp2_complete_closure_step(t, s, &cb->closure, error, + "finish_write_cb"); + cb->next = t->write_cb_pool; + t->write_cb_pool = cb; +} + +static void maybe_initiate_ping(grpc_chttp2_transport* t) { + grpc_chttp2_ping_queue* pq = &t->ping_queue; + if (grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) { + /* no ping needed: wait */ + return; + } + if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) { + /* ping already in-flight: wait */ + if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, "%s: Ping delayed [%p]: already pinging", + t->is_client ? "CLIENT" : "SERVER", t->peer_string); + } + return; + } + if (t->ping_state.pings_before_data_required == 0 && + t->ping_policy.max_pings_without_data != 0) { + /* need to receive something of substance before sending a ping again */ + if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, "%s: Ping delayed [%p]: too many recent pings: %d/%d", + t->is_client ? "CLIENT" : "SERVER", t->peer_string, + t->ping_state.pings_before_data_required, + t->ping_policy.max_pings_without_data); + } + return; + } + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + + grpc_millis next_allowed_ping_interval = + (t->keepalive_permit_without_calls == 0 && + grpc_chttp2_stream_map_size(&t->stream_map) == 0) + ? 7200 * GPR_MS_PER_SEC + : t->ping_policy.min_sent_ping_interval_without_data; + grpc_millis next_allowed_ping = + t->ping_state.last_ping_sent_time + next_allowed_ping_interval; + + if (next_allowed_ping > now) { + /* not enough elapsed time between successive pings */ + if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, + "%s: Ping delayed [%p]: not enough time elapsed since last ping. " + " Last ping %f: Next ping %f: Now %f", + t->is_client ? "CLIENT" : "SERVER", t->peer_string, + static_cast(t->ping_state.last_ping_sent_time), + static_cast(next_allowed_ping), static_cast(now)); + } + if (!t->ping_state.is_delayed_ping_timer_set) { + t->ping_state.is_delayed_ping_timer_set = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "retry_initiate_ping_locked"); + grpc_timer_init(&t->ping_state.delayed_ping_timer, next_allowed_ping, + &t->retry_initiate_ping_locked); + } + return; + } + + pq->inflight_id = t->ping_ctr; + t->ping_ctr++; + GRPC_CLOSURE_LIST_SCHED(&pq->lists[GRPC_CHTTP2_PCL_INITIATE]); + grpc_closure_list_move(&pq->lists[GRPC_CHTTP2_PCL_NEXT], + &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]); + grpc_slice_buffer_add(&t->outbuf, + grpc_chttp2_ping_create(false, pq->inflight_id)); + GRPC_STATS_INC_HTTP2_PINGS_SENT(); + t->ping_state.last_ping_sent_time = now; + if (grpc_http_trace.enabled() || grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, "%s: Ping sent [%p]: %d/%d", + t->is_client ? "CLIENT" : "SERVER", t->peer_string, + t->ping_state.pings_before_data_required, + t->ping_policy.max_pings_without_data); + } + t->ping_state.pings_before_data_required -= + (t->ping_state.pings_before_data_required != 0); +} + +static bool update_list(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + int64_t send_bytes, grpc_chttp2_write_cb** list, + int64_t* ctr, grpc_error* error) { + bool sched_any = false; + grpc_chttp2_write_cb* cb = *list; + *list = nullptr; + *ctr += send_bytes; + while (cb) { + grpc_chttp2_write_cb* next = cb->next; + if (cb->call_at_byte <= *ctr) { + sched_any = true; + finish_write_cb(t, s, cb, GRPC_ERROR_REF(error)); + } else { + add_to_write_list(list, cb); + } + cb = next; + } + GRPC_ERROR_UNREF(error); + return sched_any; +} + +static void report_stall(grpc_chttp2_transport* t, grpc_chttp2_stream* s, + const char* staller) { + if (grpc_flowctl_trace.enabled()) { + gpr_log( + GPR_DEBUG, + "%s:%p stream %d moved to stalled list by %s. This is FULLY expected " + "to happen in a healthy program that is not seeing flow control stalls." + " However, if you know that there are unwanted stalls, here is some " + "helpful data: [fc:pending=%" PRIdPTR ":pending-compressed=%" PRIdPTR + ":flowed=%" PRId64 ":peer_initwin=%d:t_win=%" PRId64 + ":s_win=%d:s_delta=%" PRId64 "]", + t->peer_string, t, s->id, staller, s->flow_controlled_buffer.length, + s->compressed_data_buffer.length, s->flow_controlled_bytes_flowed, + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE], + t->flow_control->remote_window(), + static_cast GPR_MAX( + 0, + s->flow_control->remote_window_delta() + + (int64_t)t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]), + s->flow_control->remote_window_delta()); + } +} + +static bool stream_ref_if_not_destroyed(gpr_refcount* r) { + gpr_atm count; + do { + count = gpr_atm_acq_load(&r->count); + if (count == 0) return false; + } while (!gpr_atm_rel_cas(&r->count, count, count + 1)); + return true; +} + +/* How many bytes would we like to put on the wire during a single syscall */ +static uint32_t target_write_size(grpc_chttp2_transport* t) { + return 1024 * 1024; +} + +// Returns true if initial_metadata contains only default headers. +static bool is_default_initial_metadata(grpc_metadata_batch* initial_metadata) { + return initial_metadata->list.default_count == initial_metadata->list.count; +} + +namespace { +class StreamWriteContext; + +class WriteContext { + public: + WriteContext(grpc_chttp2_transport* t) : t_(t) { + GRPC_STATS_INC_HTTP2_WRITES_BEGUN(); + GPR_TIMER_SCOPE("grpc_chttp2_begin_write", 0); + } + + // TODO(ctiller): make this the destructor + void FlushStats() { + GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE( + initial_metadata_writes_); + GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(message_writes_); + GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE( + trailing_metadata_writes_); + GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(flow_control_writes_); + } + + void FlushSettings() { + if (t_->dirtied_local_settings && !t_->sent_local_settings) { + grpc_slice_buffer_add( + &t_->outbuf, grpc_chttp2_settings_create( + t_->settings[GRPC_SENT_SETTINGS], + t_->settings[GRPC_LOCAL_SETTINGS], + t_->force_send_settings, GRPC_CHTTP2_NUM_SETTINGS)); + t_->force_send_settings = false; + t_->dirtied_local_settings = false; + t_->sent_local_settings = true; + GRPC_STATS_INC_HTTP2_SETTINGS_WRITES(); + } + } + + void FlushQueuedBuffers() { + /* simple writes are queued to qbuf, and flushed here */ + grpc_slice_buffer_move_into(&t_->qbuf, &t_->outbuf); + GPR_ASSERT(t_->qbuf.count == 0); + } + + void FlushWindowUpdates() { + uint32_t transport_announce = + t_->flow_control->MaybeSendUpdate(t_->outbuf.count > 0); + if (transport_announce) { + grpc_transport_one_way_stats throwaway_stats; + grpc_slice_buffer_add( + &t_->outbuf, grpc_chttp2_window_update_create(0, transport_announce, + &throwaway_stats)); + ResetPingClock(); + } + } + + void FlushPingAcks() { + for (size_t i = 0; i < t_->ping_ack_count; i++) { + grpc_slice_buffer_add(&t_->outbuf, + grpc_chttp2_ping_create(true, t_->ping_acks[i])); + } + t_->ping_ack_count = 0; + } + + void EnactHpackSettings() { + grpc_chttp2_hpack_compressor_set_max_table_size( + &t_->hpack_compressor, + t_->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); + } + + void UpdateStreamsNoLongerStalled() { + grpc_chttp2_stream* s; + while (grpc_chttp2_list_pop_stalled_by_transport(t_, &s)) { + if (t_->closed_with_error == GRPC_ERROR_NONE && + grpc_chttp2_list_add_writable_stream(t_, s)) { + if (!stream_ref_if_not_destroyed(&s->refcount->refs)) { + grpc_chttp2_list_remove_writable_stream(t_, s); + } + } + } + } + + grpc_chttp2_stream* NextStream() { + if (t_->outbuf.length > target_write_size(t_)) { + result_.partial = true; + return nullptr; + } + + grpc_chttp2_stream* s; + if (!grpc_chttp2_list_pop_writable_stream(t_, &s)) { + return nullptr; + } + + return s; + } + + void ResetPingClock() { + if (!t_->is_client) { + t_->ping_recv_state.last_ping_recv_time = GRPC_MILLIS_INF_PAST; + t_->ping_recv_state.ping_strikes = 0; + } + t_->ping_state.pings_before_data_required = + t_->ping_policy.max_pings_without_data; + } + + void IncInitialMetadataWrites() { ++initial_metadata_writes_; } + void IncWindowUpdateWrites() { ++flow_control_writes_; } + void IncMessageWrites() { ++message_writes_; } + void IncTrailingMetadataWrites() { ++trailing_metadata_writes_; } + + void NoteScheduledResults() { result_.early_results_scheduled = true; } + + grpc_chttp2_transport* transport() const { return t_; } + + grpc_chttp2_begin_write_result Result() { + result_.writing = t_->outbuf.count > 0; + return result_; + } + + private: + grpc_chttp2_transport* const t_; + + /* stats histogram counters: we increment these throughout this function, + and at the end publish to the central stats histograms */ + int flow_control_writes_ = 0; + int initial_metadata_writes_ = 0; + int trailing_metadata_writes_ = 0; + int message_writes_ = 0; + grpc_chttp2_begin_write_result result_ = {false, false, false}; +}; + +class DataSendContext { + public: + DataSendContext(WriteContext* write_context, grpc_chttp2_transport* t, + grpc_chttp2_stream* s) + : write_context_(write_context), + t_(t), + s_(s), + sending_bytes_before_(s_->sending_bytes) {} + + uint32_t stream_remote_window() const { + return static_cast GPR_MAX( + 0, s_->flow_control->remote_window_delta() + + (int64_t)t_->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); + } + + uint32_t max_outgoing() const { + return static_cast GPR_MIN( + t_->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + GPR_MIN(stream_remote_window(), t_->flow_control->remote_window())); + } + + bool AnyOutgoing() const { return max_outgoing() > 0; } + + void FlushCompressedBytes() { + uint32_t send_bytes = static_cast GPR_MIN( + max_outgoing(), s_->compressed_data_buffer.length); + bool is_last_data_frame = + (send_bytes == s_->compressed_data_buffer.length && + s_->flow_controlled_buffer.length == 0 && + s_->fetching_send_message == nullptr); + if (is_last_data_frame && s_->send_trailing_metadata != nullptr && + s_->stream_compression_ctx != nullptr) { + if (GPR_UNLIKELY(!grpc_stream_compress( + s_->stream_compression_ctx, &s_->flow_controlled_buffer, + &s_->compressed_data_buffer, nullptr, MAX_SIZE_T, + GRPC_STREAM_COMPRESSION_FLUSH_FINISH))) { + gpr_log(GPR_ERROR, "Stream compression failed."); + } + grpc_stream_compression_context_destroy(s_->stream_compression_ctx); + s_->stream_compression_ctx = nullptr; + /* After finish, bytes in s->compressed_data_buffer may be + * more than max_outgoing. Start another round of the current + * while loop so that send_bytes and is_last_data_frame are + * recalculated. */ + return; + } + is_last_frame_ = is_last_data_frame && + s_->send_trailing_metadata != nullptr && + grpc_metadata_batch_is_empty(s_->send_trailing_metadata); + grpc_chttp2_encode_data(s_->id, &s_->compressed_data_buffer, send_bytes, + is_last_frame_, &s_->stats.outgoing, &t_->outbuf); + s_->flow_control->SentData(send_bytes); + s_->byte_counter += send_bytes; + if (s_->compressed_data_buffer.length == 0) { + s_->sending_bytes += s_->uncompressed_data_size; + } + } + + void CompressMoreBytes() { + if (s_->stream_compression_ctx == nullptr) { + s_->stream_compression_ctx = + grpc_stream_compression_context_create(s_->stream_compression_method); + } + s_->uncompressed_data_size = s_->flow_controlled_buffer.length; + if (GPR_UNLIKELY(!grpc_stream_compress( + s_->stream_compression_ctx, &s_->flow_controlled_buffer, + &s_->compressed_data_buffer, nullptr, MAX_SIZE_T, + GRPC_STREAM_COMPRESSION_FLUSH_SYNC))) { + gpr_log(GPR_ERROR, "Stream compression failed."); + } + } + + bool is_last_frame() const { return is_last_frame_; } + + void CallCallbacks() { + if (update_list( + t_, s_, + static_cast(s_->sending_bytes - sending_bytes_before_), + &s_->on_flow_controlled_cbs, &s_->flow_controlled_bytes_flowed, + GRPC_ERROR_NONE)) { + write_context_->NoteScheduledResults(); + } + } + + private: + WriteContext* write_context_; + grpc_chttp2_transport* t_; + grpc_chttp2_stream* s_; + const size_t sending_bytes_before_; + bool is_last_frame_ = false; +}; + +class StreamWriteContext { + public: + StreamWriteContext(WriteContext* write_context, grpc_chttp2_stream* s) + : write_context_(write_context), t_(write_context->transport()), s_(s) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_INFO, "W:%p %s[%d] im-(sent,send)=(%d,%d) announce=%d", t_, + t_->is_client ? "CLIENT" : "SERVER", s->id, + s->sent_initial_metadata, s->send_initial_metadata != nullptr, + (int)(s->flow_control->local_window_delta() - + s->flow_control->announced_window_delta()))); + } + + void FlushInitialMetadata() { + /* send initial metadata if it's available */ + if (s_->sent_initial_metadata) return; + if (s_->send_initial_metadata == nullptr) return; + + // We skip this on the server side if there is no custom initial + // metadata, there are no messages to send, and we are also sending + // trailing metadata. This results in a Trailers-Only response, + // which is required for retries, as per: + // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid + if (!t_->is_client && s_->fetching_send_message == nullptr && + s_->flow_controlled_buffer.length == 0 && + s_->compressed_data_buffer.length == 0 && + s_->send_trailing_metadata != nullptr && + is_default_initial_metadata(s_->send_initial_metadata)) { + ConvertInitialMetadataToTrailingMetadata(); + } else { + grpc_encode_header_options hopt = { + s_->id, // stream_id + false, // is_eof + t_->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != + 0, // use_true_binary_metadata + t_->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], // max_frame_size + &s_->stats.outgoing // stats + }; + grpc_chttp2_encode_header(&t_->hpack_compressor, nullptr, 0, + s_->send_initial_metadata, &hopt, &t_->outbuf); + write_context_->ResetPingClock(); + write_context_->IncInitialMetadataWrites(); + } + + s_->send_initial_metadata = nullptr; + s_->sent_initial_metadata = true; + write_context_->NoteScheduledResults(); + grpc_chttp2_complete_closure_step( + t_, s_, &s_->send_initial_metadata_finished, GRPC_ERROR_NONE, + "send_initial_metadata_finished"); + } + + void FlushWindowUpdates() { + /* send any window updates */ + const uint32_t stream_announce = s_->flow_control->MaybeSendUpdate(); + if (stream_announce == 0) return; + + grpc_slice_buffer_add( + &t_->outbuf, grpc_chttp2_window_update_create(s_->id, stream_announce, + &s_->stats.outgoing)); + write_context_->ResetPingClock(); + write_context_->IncWindowUpdateWrites(); + } + + void FlushData() { + if (!s_->sent_initial_metadata) return; + + if (s_->flow_controlled_buffer.length == 0 && + s_->compressed_data_buffer.length == 0) { + return; // early out: nothing to do + } + + DataSendContext data_send_context(write_context_, t_, s_); + + if (!data_send_context.AnyOutgoing()) { + if (t_->flow_control->remote_window() <= 0) { + report_stall(t_, s_, "transport"); + grpc_chttp2_list_add_stalled_by_transport(t_, s_); + } else if (data_send_context.stream_remote_window() <= 0) { + report_stall(t_, s_, "stream"); + grpc_chttp2_list_add_stalled_by_stream(t_, s_); + } + return; // early out: nothing to do + } + + while ((s_->flow_controlled_buffer.length > 0 || + s_->compressed_data_buffer.length > 0) && + data_send_context.max_outgoing() > 0) { + if (s_->compressed_data_buffer.length > 0) { + data_send_context.FlushCompressedBytes(); + } else { + data_send_context.CompressMoreBytes(); + } + } + if (s_->traced && grpc_endpoint_can_track_err(t_->ep)) { + grpc_core::ContextList::Append(&t_->cl, s_); + } + write_context_->ResetPingClock(); + if (data_send_context.is_last_frame()) { + SentLastFrame(); + } + data_send_context.CallCallbacks(); + stream_became_writable_ = true; + if (s_->flow_controlled_buffer.length > 0 || + s_->compressed_data_buffer.length > 0) { + GRPC_CHTTP2_STREAM_REF(s_, "chttp2_writing:fork"); + grpc_chttp2_list_add_writable_stream(t_, s_); + } + write_context_->IncMessageWrites(); + } + + void FlushTrailingMetadata() { + if (!s_->sent_initial_metadata) return; + + if (s_->send_trailing_metadata == nullptr) return; + if (s_->fetching_send_message != nullptr) return; + if (s_->flow_controlled_buffer.length != 0) return; + if (s_->compressed_data_buffer.length != 0) return; + + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata")); + if (grpc_metadata_batch_is_empty(s_->send_trailing_metadata)) { + grpc_chttp2_encode_data(s_->id, &s_->flow_controlled_buffer, 0, true, + &s_->stats.outgoing, &t_->outbuf); + } else { + grpc_encode_header_options hopt = { + s_->id, true, + t_->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != + 0, + + t_->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + &s_->stats.outgoing}; + grpc_chttp2_encode_header(&t_->hpack_compressor, + extra_headers_for_trailing_metadata_, + num_extra_headers_for_trailing_metadata_, + s_->send_trailing_metadata, &hopt, &t_->outbuf); + } + write_context_->IncTrailingMetadataWrites(); + write_context_->ResetPingClock(); + SentLastFrame(); + + write_context_->NoteScheduledResults(); + grpc_chttp2_complete_closure_step( + t_, s_, &s_->send_trailing_metadata_finished, GRPC_ERROR_NONE, + "send_trailing_metadata_finished"); + } + + bool stream_became_writable() { return stream_became_writable_; } + + private: + void ConvertInitialMetadataToTrailingMetadata() { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_INFO, "not sending initial_metadata (Trailers-Only)")); + // When sending Trailers-Only, we need to move the :status and + // content-type headers to the trailers. + if (s_->send_initial_metadata->idx.named.status != nullptr) { + extra_headers_for_trailing_metadata_ + [num_extra_headers_for_trailing_metadata_++] = + &s_->send_initial_metadata->idx.named.status->md; + } + if (s_->send_initial_metadata->idx.named.content_type != nullptr) { + extra_headers_for_trailing_metadata_ + [num_extra_headers_for_trailing_metadata_++] = + &s_->send_initial_metadata->idx.named.content_type->md; + } + } + + void SentLastFrame() { + s_->send_trailing_metadata = nullptr; + s_->sent_trailing_metadata = true; + s_->eos_sent = true; + + if (!t_->is_client && !s_->read_closed) { + grpc_slice_buffer_add( + &t_->outbuf, grpc_chttp2_rst_stream_create( + s_->id, GRPC_HTTP2_NO_ERROR, &s_->stats.outgoing)); + } + grpc_chttp2_mark_stream_closed(t_, s_, !t_->is_client, true, + GRPC_ERROR_NONE); + } + + WriteContext* const write_context_; + grpc_chttp2_transport* const t_; + grpc_chttp2_stream* const s_; + bool stream_became_writable_ = false; + grpc_mdelem* extra_headers_for_trailing_metadata_[2]; + size_t num_extra_headers_for_trailing_metadata_ = 0; +}; +} // namespace + +grpc_chttp2_begin_write_result grpc_chttp2_begin_write( + grpc_chttp2_transport* t) { + WriteContext ctx(t); + ctx.FlushSettings(); + ctx.FlushPingAcks(); + ctx.FlushQueuedBuffers(); + ctx.EnactHpackSettings(); + + if (t->flow_control->remote_window() > 0) { + ctx.UpdateStreamsNoLongerStalled(); + } + + /* 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_stream* s = ctx.NextStream()) { + StreamWriteContext stream_ctx(&ctx, s); + stream_ctx.FlushInitialMetadata(); + stream_ctx.FlushWindowUpdates(); + stream_ctx.FlushData(); + stream_ctx.FlushTrailingMetadata(); + + if (stream_ctx.stream_became_writable()) { + if (!grpc_chttp2_list_add_writing_stream(t, s)) { + /* already in writing list: drop ref */ + GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:already_writing"); + } else { + /* ref will be dropped at end of write */ + } + } else { + GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:no_write"); + } + } + + ctx.FlushWindowUpdates(); + + maybe_initiate_ping(t); + + return ctx.Result(); +} + +void grpc_chttp2_end_write(grpc_chttp2_transport* t, grpc_error* error) { + GPR_TIMER_SCOPE("grpc_chttp2_end_write", 0); + grpc_chttp2_stream* s; + + if (t->channelz_socket != nullptr) { + t->channelz_socket->RecordMessagesSent(t->num_messages_in_next_write); + } + t->num_messages_in_next_write = 0; + + while (grpc_chttp2_list_pop_writing_stream(t, &s)) { + if (s->sending_bytes != 0) { + update_list(t, s, static_cast(s->sending_bytes), + &s->on_write_finished_cbs, &s->flow_controlled_bytes_written, + GRPC_ERROR_REF(error)); + s->sending_bytes = 0; + } + GRPC_CHTTP2_STREAM_UNREF(s, "chttp2_writing:end"); + } + grpc_slice_buffer_reset_and_unref_internal(&t->outbuf); + GRPC_ERROR_UNREF(error); +} diff --git a/third_party/grpc/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc b/third_party/grpc/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc new file mode 100644 index 00000000000000..40a30e4a31db99 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc @@ -0,0 +1,54 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +#include +#include + +#include "src/core/ext/transport/cronet/transport/cronet_transport.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/transport_impl.h" + +// Cronet transport object +typedef struct cronet_transport { + grpc_transport base; // must be first element in this structure + void* engine; + char* host; +} cronet_transport; + +extern grpc_transport_vtable grpc_cronet_vtable; + +GRPCAPI grpc_channel* grpc_cronet_secure_channel_create( + void* engine, const char* target, const grpc_channel_args* args, + void* reserved) { + gpr_log(GPR_DEBUG, + "grpc_create_cronet_transport: stream_engine = %p, target=%s", engine, + target); + + grpc_transport* ct = + grpc_create_cronet_transport(engine, target, args, reserved); + + grpc_core::ExecCtx exec_ctx; + return grpc_channel_create(target, args, GRPC_CLIENT_DIRECT_CHANNEL, ct); +} diff --git a/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc new file mode 100644 index 00000000000000..1a6bded6afd05f --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* This file has empty implementation of all the functions exposed by the cronet +library, so we can build it in all environments */ + +#include + +#include + +#include + +#include "third_party/objective_c/Cronet/bidirectional_stream_c.h" + +#ifdef GRPC_COMPILE_WITH_CRONET +/* link with the real CRONET library in the build system */ +#else +/* Dummy implementation of cronet API just to test for build-ability */ +bidirectional_stream* bidirectional_stream_create( + stream_engine* engine, void* annotation, + bidirectional_stream_callback* callback) { + GPR_ASSERT(0); + return nullptr; +} + +int bidirectional_stream_destroy(bidirectional_stream* stream) { + GPR_ASSERT(0); + return 0; +} + +int bidirectional_stream_start(bidirectional_stream* stream, const char* url, + int priority, const char* method, + const bidirectional_stream_header_array* headers, + bool end_of_stream) { + GPR_ASSERT(0); + return 0; +} + +int bidirectional_stream_read(bidirectional_stream* stream, char* buffer, + int capacity) { + GPR_ASSERT(0); + return 0; +} + +int bidirectional_stream_write(bidirectional_stream* stream, const char* buffer, + int count, bool end_of_stream) { + GPR_ASSERT(0); + return 0; +} + +void bidirectional_stream_cancel(bidirectional_stream* stream) { + GPR_ASSERT(0); +} + +void bidirectional_stream_disable_auto_flush(bidirectional_stream* stream, + bool disable_auto_flush) { + GPR_ASSERT(0); +} + +void bidirectional_stream_delay_request_headers_until_flush( + bidirectional_stream* stream, bool delay_headers_until_flush) { + GPR_ASSERT(0); +} + +void bidirectional_stream_flush(bidirectional_stream* stream) { GPR_ASSERT(0); } + +#endif /* GRPC_COMPILE_WITH_CRONET */ diff --git a/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.cc b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.cc new file mode 100644 index 00000000000000..349d8681d55c99 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.cc @@ -0,0 +1,1491 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/bin_decoder.h" +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" +#include "src/core/ext/transport/cronet/transport/cronet_transport.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/transport_impl.h" +#include "third_party/objective_c/Cronet/bidirectional_stream_c.h" + +#define GRPC_HEADER_SIZE_IN_BYTES 5 +#define GRPC_FLUSH_READ_SIZE 4096 + +#define CRONET_LOG(...) \ + do { \ + if (grpc_cronet_trace) gpr_log(__VA_ARGS__); \ + } while (0) + +/* TODO (makdharma): Hook up into the wider tracing mechanism */ +int grpc_cronet_trace = 0; + +enum e_op_result { + ACTION_TAKEN_WITH_CALLBACK, + ACTION_TAKEN_NO_CALLBACK, + NO_ACTION_POSSIBLE +}; + +enum e_op_id { + OP_SEND_INITIAL_METADATA = 0, + OP_SEND_MESSAGE, + OP_SEND_TRAILING_METADATA, + OP_RECV_MESSAGE, + OP_RECV_INITIAL_METADATA, + OP_RECV_TRAILING_METADATA, + OP_CANCEL_ERROR, + OP_ON_COMPLETE, + OP_FAILED, + OP_SUCCEEDED, + OP_CANCELED, + OP_RECV_MESSAGE_AND_ON_COMPLETE, + OP_READ_REQ_MADE, + OP_NUM_OPS +}; + +/* Cronet callbacks. See cronet_c_for_grpc.h for documentation for each. */ + +static void on_stream_ready(bidirectional_stream*); +static void on_response_headers_received( + bidirectional_stream*, const bidirectional_stream_header_array*, + const char*); +static void on_write_completed(bidirectional_stream*, const char*); +static void on_read_completed(bidirectional_stream*, char*, int); +static void on_response_trailers_received( + bidirectional_stream*, const bidirectional_stream_header_array*); +static void on_succeeded(bidirectional_stream*); +static void on_failed(bidirectional_stream*, int); +static void on_canceled(bidirectional_stream*); +static bidirectional_stream_callback cronet_callbacks = { + on_stream_ready, + on_response_headers_received, + on_read_completed, + on_write_completed, + on_response_trailers_received, + on_succeeded, + on_failed, + on_canceled}; + +/* Cronet transport object */ +struct grpc_cronet_transport { + grpc_transport base; /* must be first element in this structure */ + stream_engine* engine; + char* host; + bool use_packet_coalescing; +}; +typedef struct grpc_cronet_transport grpc_cronet_transport; + +/* TODO (makdharma): reorder structure for memory efficiency per + http://www.catb.org/esr/structure-packing/#_structure_reordering: */ +struct read_state { + read_state(gpr_arena* arena) + : trailing_metadata(arena), initial_metadata(arena) { + grpc_slice_buffer_init(&read_slice_buffer); + } + + /* vars to store data coming from server */ + char* read_buffer = nullptr; + bool length_field_received = false; + int received_bytes = 0; + int remaining_bytes = 0; + int length_field = 0; + bool compressed = 0; + char grpc_header_bytes[GRPC_HEADER_SIZE_IN_BYTES] = {}; + char* payload_field = nullptr; + bool read_stream_closed = 0; + + /* vars for holding data destined for the application */ + grpc_core::ManualConstructor sbs; + grpc_slice_buffer read_slice_buffer; + + /* vars for trailing metadata */ + grpc_chttp2_incoming_metadata_buffer trailing_metadata; + bool trailing_metadata_valid = false; + + /* vars for initial metadata */ + grpc_chttp2_incoming_metadata_buffer initial_metadata; +}; + +struct write_state { + char* write_buffer = nullptr; +}; + +/* track state of one stream op */ +struct op_state { + op_state(gpr_arena* arena) : rs(arena) {} + + bool state_op_done[OP_NUM_OPS] = {}; + bool state_callback_received[OP_NUM_OPS] = {}; + /* A non-zero gRPC status code has been seen */ + bool fail_state = false; + /* Transport is discarding all buffered messages */ + bool flush_read = false; + bool flush_cronet_when_ready = false; + bool pending_write_for_trailer = false; + bool pending_send_message = false; + /* User requested RECV_TRAILING_METADATA */ + bool pending_recv_trailing_metadata = false; + /* Cronet has not issued a callback of a bidirectional read */ + bool pending_read_from_cronet = false; + grpc_error* cancel_error = GRPC_ERROR_NONE; + /* data structure for storing data coming from server */ + struct read_state rs; + /* data structure for storing data going to the server */ + struct write_state ws; +}; + +struct stream_obj; + +struct op_and_state { + op_and_state(stream_obj* s, const grpc_transport_stream_op_batch& op); + + grpc_transport_stream_op_batch op; + struct op_state state; + bool done = false; + struct stream_obj* s; /* Pointer back to the stream object */ + /* next op_and_state in the linked list */ + struct op_and_state* next; +}; + +struct op_storage { + int num_pending_ops = 0; + struct op_and_state* head = nullptr; +}; + +struct stream_obj { + stream_obj(grpc_transport* gt, grpc_stream* gs, + grpc_stream_refcount* refcount, gpr_arena* arena); + ~stream_obj(); + + gpr_arena* arena; + struct op_and_state* oas = nullptr; + grpc_transport_stream_op_batch* curr_op = nullptr; + grpc_cronet_transport* curr_ct; + grpc_stream* curr_gs; + bidirectional_stream* cbs = nullptr; + bidirectional_stream_header_array header_array = + bidirectional_stream_header_array(); // Zero-initialize the structure. + + /* Stream level state. Some state will be tracked both at stream and stream_op + * level */ + struct op_state state; + + /* OP storage */ + struct op_storage storage; + + /* Mutex to protect storage */ + gpr_mu mu; + + /* Refcount object of the stream */ + grpc_stream_refcount* refcount; +}; + +#ifndef NDEBUG +#define GRPC_CRONET_STREAM_REF(stream, reason) \ + grpc_cronet_stream_ref((stream), (reason)) +#define GRPC_CRONET_STREAM_UNREF(stream, reason) \ + grpc_cronet_stream_unref((stream), (reason)) +void grpc_cronet_stream_ref(stream_obj* s, const char* reason) { + grpc_stream_ref(s->refcount, reason); +} +void grpc_cronet_stream_unref(stream_obj* s, const char* reason) { + grpc_stream_unref(s->refcount, reason); +} +#else +#define GRPC_CRONET_STREAM_REF(stream, reason) grpc_cronet_stream_ref((stream)) +#define GRPC_CRONET_STREAM_UNREF(stream, reason) \ + grpc_cronet_stream_unref((stream)) +void grpc_cronet_stream_ref(stream_obj* s) { grpc_stream_ref(s->refcount); } +void grpc_cronet_stream_unref(stream_obj* s) { grpc_stream_unref(s->refcount); } +#endif + +static enum e_op_result execute_stream_op(struct op_and_state* oas); + +/* + Utility function to translate enum into string for printing +*/ +static const char* op_result_string(enum e_op_result i) { + switch (i) { + case ACTION_TAKEN_WITH_CALLBACK: + return "ACTION_TAKEN_WITH_CALLBACK"; + case ACTION_TAKEN_NO_CALLBACK: + return "ACTION_TAKEN_NO_CALLBACK"; + case NO_ACTION_POSSIBLE: + return "NO_ACTION_POSSIBLE"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +static const char* op_id_string(enum e_op_id i) { + switch (i) { + case OP_SEND_INITIAL_METADATA: + return "OP_SEND_INITIAL_METADATA"; + case OP_SEND_MESSAGE: + return "OP_SEND_MESSAGE"; + case OP_SEND_TRAILING_METADATA: + return "OP_SEND_TRAILING_METADATA"; + case OP_RECV_MESSAGE: + return "OP_RECV_MESSAGE"; + case OP_RECV_INITIAL_METADATA: + return "OP_RECV_INITIAL_METADATA"; + case OP_RECV_TRAILING_METADATA: + return "OP_RECV_TRAILING_METADATA"; + case OP_CANCEL_ERROR: + return "OP_CANCEL_ERROR"; + case OP_ON_COMPLETE: + return "OP_ON_COMPLETE"; + case OP_FAILED: + return "OP_FAILED"; + case OP_SUCCEEDED: + return "OP_SUCCEEDED"; + case OP_CANCELED: + return "OP_CANCELED"; + case OP_RECV_MESSAGE_AND_ON_COMPLETE: + return "OP_RECV_MESSAGE_AND_ON_COMPLETE"; + case OP_READ_REQ_MADE: + return "OP_READ_REQ_MADE"; + case OP_NUM_OPS: + return "OP_NUM_OPS"; + } + return "UNKNOWN"; +} + +static void null_and_maybe_free_read_buffer(stream_obj* s) { + if (s->state.rs.read_buffer && + s->state.rs.read_buffer != s->state.rs.grpc_header_bytes) { + gpr_free(s->state.rs.read_buffer); + } + s->state.rs.read_buffer = nullptr; +} + +static void maybe_flush_read(stream_obj* s) { + /* To enter flush read state (discarding all the buffered messages in + * transport layer), two conditions must be satisfied: 1) non-zero grpc status + * has been received, and 2) an op requesting the status code + * (RECV_TRAILING_METADATA) is issued by the user. (See + * doc/status_ordering.md) */ + /* Whenever the evaluation of any of the two condition is changed, we check + * whether we should enter the flush read state. */ + if (s->state.pending_recv_trailing_metadata && s->state.fail_state) { + if (!s->state.flush_read && !s->state.rs.read_stream_closed) { + CRONET_LOG(GPR_DEBUG, "%p: Flush read", s); + s->state.flush_read = true; + null_and_maybe_free_read_buffer(s); + s->state.rs.read_buffer = + static_cast(gpr_malloc(GRPC_FLUSH_READ_SIZE)); + if (!s->state.pending_read_from_cronet) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, + GRPC_FLUSH_READ_SIZE); + s->state.pending_read_from_cronet = true; + } + } + } +} + +static grpc_error* make_error_with_desc(int error_code, const char* desc) { + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, error_code); + return error; +} + +inline op_and_state::op_and_state(stream_obj* s, + const grpc_transport_stream_op_batch& op) + : op(op), state(s->arena), s(s), next(s->storage.head) {} + +/* + Add a new stream op to op storage. +*/ +static void add_to_storage(struct stream_obj* s, + grpc_transport_stream_op_batch* op) { + struct op_storage* storage = &s->storage; + /* add new op at the beginning of the linked list. The memory is freed + in remove_from_storage */ + op_and_state* new_op = grpc_core::New(s, *op); + gpr_mu_lock(&s->mu); + storage->head = new_op; + storage->num_pending_ops++; + if (op->send_message) { + s->state.pending_send_message = true; + } + if (op->recv_trailing_metadata) { + s->state.pending_recv_trailing_metadata = true; + maybe_flush_read(s); + } + CRONET_LOG(GPR_DEBUG, "adding new op %p. %d in the queue.", new_op, + storage->num_pending_ops); + gpr_mu_unlock(&s->mu); +} + +/* + Traverse the linked list and delete op and free memory +*/ +static void remove_from_storage(struct stream_obj* s, + struct op_and_state* oas) { + struct op_and_state* curr; + if (s->storage.head == nullptr || oas == nullptr) { + return; + } + if (s->storage.head == oas) { + s->storage.head = oas->next; + grpc_core::Delete(oas); + s->storage.num_pending_ops--; + CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas, + s->storage.num_pending_ops); + } else { + for (curr = s->storage.head; curr != nullptr; curr = curr->next) { + if (curr->next == oas) { + curr->next = oas->next; + s->storage.num_pending_ops--; + CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas, + s->storage.num_pending_ops); + grpc_core::Delete(oas); + break; + } else if (GPR_UNLIKELY(curr->next == nullptr)) { + CRONET_LOG(GPR_ERROR, "Reached end of LL and did not find op to free"); + } + } + } +} + +/* + Cycle through ops and try to take next action. Break when either + an action with callback is taken, or no action is possible. + This can get executed from the Cronet network thread via cronet callback + or on the application supplied thread via the perform_stream_op function. +*/ +static void execute_from_storage(stream_obj* s) { + gpr_mu_lock(&s->mu); + for (struct op_and_state* curr = s->storage.head; curr != nullptr;) { + CRONET_LOG(GPR_DEBUG, "calling op at %p. done = %d", curr, curr->done); + GPR_ASSERT(curr->done == 0); + enum e_op_result result = execute_stream_op(curr); + CRONET_LOG(GPR_DEBUG, "execute_stream_op[%p] returns %s", curr, + op_result_string(result)); + /* if this op is done, then remove it and free memory */ + if (curr->done) { + struct op_and_state* next = curr->next; + remove_from_storage(s, curr); + curr = next; + } + /* continue processing the same op if ACTION_TAKEN_WITHOUT_CALLBACK */ + if (result == NO_ACTION_POSSIBLE) { + curr = curr->next; + } else if (result == ACTION_TAKEN_WITH_CALLBACK) { + break; + } + } + gpr_mu_unlock(&s->mu); +} + +static void convert_cronet_array_to_metadata( + const bidirectional_stream_header_array* header_array, + grpc_chttp2_incoming_metadata_buffer* mds) { + for (size_t i = 0; i < header_array->count; i++) { + CRONET_LOG(GPR_DEBUG, "header key=%s, value=%s", + header_array->headers[i].key, header_array->headers[i].value); + grpc_slice key = grpc_slice_intern( + grpc_slice_from_static_string(header_array->headers[i].key)); + grpc_slice value; + if (grpc_is_binary_header(key)) { + value = grpc_slice_from_static_string(header_array->headers[i].value); + value = grpc_slice_intern(grpc_chttp2_base64_decode_with_length( + value, grpc_chttp2_base64_infer_length_after_decode(value))); + } else { + value = grpc_slice_intern( + grpc_slice_from_static_string(header_array->headers[i].value)); + } + GRPC_LOG_IF_ERROR("convert_cronet_array_to_metadata", + grpc_chttp2_incoming_metadata_buffer_add( + mds, grpc_mdelem_from_slices(key, value))); + } +} + +/* + Cronet callback +*/ +static void on_failed(bidirectional_stream* stream, int net_error) { + gpr_log(GPR_ERROR, "on_failed(%p, %d)", stream, net_error); + grpc_core::ExecCtx exec_ctx; + + stream_obj* s = static_cast(stream->annotation); + gpr_mu_lock(&s->mu); + bidirectional_stream_destroy(s->cbs); + s->state.state_callback_received[OP_FAILED] = true; + s->cbs = nullptr; + if (s->header_array.headers) { + gpr_free(s->header_array.headers); + s->header_array.headers = nullptr; + } + if (s->state.ws.write_buffer) { + gpr_free(s->state.ws.write_buffer); + s->state.ws.write_buffer = nullptr; + } + null_and_maybe_free_read_buffer(s); + gpr_mu_unlock(&s->mu); + execute_from_storage(s); + GRPC_CRONET_STREAM_UNREF(s, "cronet transport"); +} + +/* + Cronet callback +*/ +static void on_canceled(bidirectional_stream* stream) { + CRONET_LOG(GPR_DEBUG, "on_canceled(%p)", stream); + grpc_core::ExecCtx exec_ctx; + + stream_obj* s = static_cast(stream->annotation); + gpr_mu_lock(&s->mu); + bidirectional_stream_destroy(s->cbs); + s->state.state_callback_received[OP_CANCELED] = true; + s->cbs = nullptr; + if (s->header_array.headers) { + gpr_free(s->header_array.headers); + s->header_array.headers = nullptr; + } + if (s->state.ws.write_buffer) { + gpr_free(s->state.ws.write_buffer); + s->state.ws.write_buffer = nullptr; + } + null_and_maybe_free_read_buffer(s); + gpr_mu_unlock(&s->mu); + execute_from_storage(s); + GRPC_CRONET_STREAM_UNREF(s, "cronet transport"); +} + +/* + Cronet callback +*/ +static void on_succeeded(bidirectional_stream* stream) { + CRONET_LOG(GPR_DEBUG, "on_succeeded(%p)", stream); + grpc_core::ExecCtx exec_ctx; + + stream_obj* s = static_cast(stream->annotation); + gpr_mu_lock(&s->mu); + bidirectional_stream_destroy(s->cbs); + s->state.state_callback_received[OP_SUCCEEDED] = true; + s->cbs = nullptr; + null_and_maybe_free_read_buffer(s); + gpr_mu_unlock(&s->mu); + execute_from_storage(s); + GRPC_CRONET_STREAM_UNREF(s, "cronet transport"); +} + +/* + Cronet callback +*/ +static void on_stream_ready(bidirectional_stream* stream) { + CRONET_LOG(GPR_DEBUG, "W: on_stream_ready(%p)", stream); + grpc_core::ExecCtx exec_ctx; + stream_obj* s = static_cast(stream->annotation); + grpc_cronet_transport* t = s->curr_ct; + gpr_mu_lock(&s->mu); + s->state.state_op_done[OP_SEND_INITIAL_METADATA] = true; + s->state.state_callback_received[OP_SEND_INITIAL_METADATA] = true; + /* Free the memory allocated for headers */ + if (s->header_array.headers) { + gpr_free(s->header_array.headers); + s->header_array.headers = nullptr; + } + /* Send the initial metadata on wire if there is no SEND_MESSAGE or + * SEND_TRAILING_METADATA ops pending */ + if (t->use_packet_coalescing) { + if (s->state.flush_cronet_when_ready) { + CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(stream); + } + } + gpr_mu_unlock(&s->mu); + execute_from_storage(s); +} + +/* + Cronet callback +*/ +static void on_response_headers_received( + bidirectional_stream* stream, + const bidirectional_stream_header_array* headers, + const char* negotiated_protocol) { + grpc_core::ExecCtx exec_ctx; + CRONET_LOG(GPR_DEBUG, "R: on_response_headers_received(%p, %p, %s)", stream, + headers, negotiated_protocol); + stream_obj* s = static_cast(stream->annotation); + + /* Identify if this is a header or a trailer (in a trailer-only response case) + */ + for (size_t i = 0; i < headers->count; i++) { + if (0 == strcmp("grpc-status", headers->headers[i].key)) { + on_response_trailers_received(stream, headers); + return; + } + } + + gpr_mu_lock(&s->mu); + convert_cronet_array_to_metadata(headers, &s->state.rs.initial_metadata); + s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true; + if (!(s->state.state_op_done[OP_CANCEL_ERROR] || + s->state.state_callback_received[OP_FAILED])) { + /* Do an extra read to trigger on_succeeded() callback in case connection + is closed */ + GPR_ASSERT(s->state.rs.length_field_received == false); + s->state.rs.read_buffer = s->state.rs.grpc_header_bytes; + s->state.rs.compressed = false; + s->state.rs.received_bytes = 0; + s->state.rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, + s->state.rs.remaining_bytes); + s->state.pending_read_from_cronet = true; + } + gpr_mu_unlock(&s->mu); + execute_from_storage(s); +} + +/* + Cronet callback +*/ +static void on_write_completed(bidirectional_stream* stream, const char* data) { + grpc_core::ExecCtx exec_ctx; + stream_obj* s = static_cast(stream->annotation); + CRONET_LOG(GPR_DEBUG, "W: on_write_completed(%p, %s)", stream, data); + gpr_mu_lock(&s->mu); + if (s->state.ws.write_buffer) { + gpr_free(s->state.ws.write_buffer); + s->state.ws.write_buffer = nullptr; + } + s->state.state_callback_received[OP_SEND_MESSAGE] = true; + gpr_mu_unlock(&s->mu); + execute_from_storage(s); +} + +/* + Cronet callback +*/ +static void on_read_completed(bidirectional_stream* stream, char* data, + int count) { + grpc_core::ExecCtx exec_ctx; + stream_obj* s = static_cast(stream->annotation); + CRONET_LOG(GPR_DEBUG, "R: on_read_completed(%p, %p, %d)", stream, data, + count); + gpr_mu_lock(&s->mu); + s->state.pending_read_from_cronet = false; + s->state.state_callback_received[OP_RECV_MESSAGE] = true; + if (count > 0 && s->state.flush_read) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, + GRPC_FLUSH_READ_SIZE); + s->state.pending_read_from_cronet = true; + gpr_mu_unlock(&s->mu); + } else if (count > 0) { + s->state.rs.received_bytes += count; + s->state.rs.remaining_bytes -= count; + if (s->state.rs.remaining_bytes > 0) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + s->state.state_op_done[OP_READ_REQ_MADE] = true; + bidirectional_stream_read( + s->cbs, s->state.rs.read_buffer + s->state.rs.received_bytes, + s->state.rs.remaining_bytes); + s->state.pending_read_from_cronet = true; + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + execute_from_storage(s); + } + } else { + null_and_maybe_free_read_buffer(s); + s->state.rs.read_stream_closed = true; + gpr_mu_unlock(&s->mu); + execute_from_storage(s); + } +} + +/* + Cronet callback +*/ +static void on_response_trailers_received( + bidirectional_stream* stream, + const bidirectional_stream_header_array* trailers) { + grpc_core::ExecCtx exec_ctx; + CRONET_LOG(GPR_DEBUG, "R: on_response_trailers_received(%p,%p)", stream, + trailers); + stream_obj* s = static_cast(stream->annotation); + grpc_cronet_transport* t = s->curr_ct; + gpr_mu_lock(&s->mu); + s->state.rs.trailing_metadata_valid = false; + convert_cronet_array_to_metadata(trailers, &s->state.rs.trailing_metadata); + if (trailers->count > 0) { + s->state.rs.trailing_metadata_valid = true; + } + for (size_t i = 0; i < trailers->count; i++) { + if (0 == strcmp(trailers->headers[i].key, "grpc-status") && + 0 != strcmp(trailers->headers[i].value, "0")) { + s->state.fail_state = true; + maybe_flush_read(s); + } + } + s->state.state_callback_received[OP_RECV_TRAILING_METADATA] = true; + /* Send a EOS when server terminates the stream (testServerFinishesRequest) to + * trigger on_succeeded */ + if (!s->state.state_op_done[OP_SEND_TRAILING_METADATA] && + !(s->state.state_op_done[OP_CANCEL_ERROR] || + s->state.state_callback_received[OP_FAILED])) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, 0)", s->cbs); + s->state.state_callback_received[OP_SEND_MESSAGE] = false; + bidirectional_stream_write(s->cbs, "", 0, true); + if (t->use_packet_coalescing) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(s->cbs); + } + s->state.state_op_done[OP_SEND_TRAILING_METADATA] = true; + + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + execute_from_storage(s); + } +} + +/* + Utility function that takes the data from s->write_slice_buffer and assembles + into a contiguous byte stream with 5 byte gRPC header prepended. +*/ +static void create_grpc_frame(grpc_slice_buffer* write_slice_buffer, + char** pp_write_buffer, + size_t* p_write_buffer_size, uint32_t flags) { + grpc_slice slice = grpc_slice_buffer_take_first(write_slice_buffer); + size_t length = GRPC_SLICE_LENGTH(slice); + *p_write_buffer_size = length + GRPC_HEADER_SIZE_IN_BYTES; + /* This is freed in the on_write_completed callback */ + char* write_buffer = + static_cast(gpr_malloc(length + GRPC_HEADER_SIZE_IN_BYTES)); + *pp_write_buffer = write_buffer; + uint8_t* p = reinterpret_cast(write_buffer); + /* Append 5 byte header */ + /* Compressed flag */ + *p++ = static_cast((flags & GRPC_WRITE_INTERNAL_COMPRESS) ? 1 : 0); + /* Message length */ + *p++ = static_cast(length >> 24); + *p++ = static_cast(length >> 16); + *p++ = static_cast(length >> 8); + *p++ = static_cast(length); + /* append actual data */ + memcpy(p, GRPC_SLICE_START_PTR(slice), length); + grpc_slice_unref_internal(slice); +} + +/* + Convert metadata in a format that Cronet can consume +*/ +static void convert_metadata_to_cronet_headers( + grpc_linked_mdelem* head, const char* host, char** pp_url, + bidirectional_stream_header** pp_headers, size_t* p_num_headers, + const char** method) { + grpc_linked_mdelem* curr = head; + /* Walk the linked list and get number of header fields */ + size_t num_headers_available = 0; + while (curr != nullptr) { + curr = curr->next; + num_headers_available++; + } + /* Allocate enough memory. It is freed in the on_stream_ready callback + */ + bidirectional_stream_header* headers = + static_cast(gpr_malloc( + sizeof(bidirectional_stream_header) * num_headers_available)); + *pp_headers = headers; + + /* Walk the linked list again, this time copying the header fields. + s->num_headers can be less than num_headers_available, as some headers + are not used for cronet. + TODO (makdharma): Eliminate need to traverse the LL second time for perf. + */ + curr = head; + size_t num_headers = 0; + while (num_headers < num_headers_available) { + grpc_mdelem mdelem = curr->md; + curr = curr->next; + char* key = grpc_slice_to_c_string(GRPC_MDKEY(mdelem)); + char* value; + if (grpc_is_binary_header(GRPC_MDKEY(mdelem))) { + grpc_slice wire_value = grpc_chttp2_base64_encode(GRPC_MDVALUE(mdelem)); + value = grpc_slice_to_c_string(wire_value); + grpc_slice_unref_internal(wire_value); + } else { + value = grpc_slice_to_c_string(GRPC_MDVALUE(mdelem)); + } + if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_SCHEME) || + grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_AUTHORITY)) { + /* Cronet populates these fields on its own */ + gpr_free(key); + gpr_free(value); + continue; + } + if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_METHOD)) { + if (grpc_slice_eq(GRPC_MDVALUE(mdelem), GRPC_MDSTR_PUT)) { + *method = "PUT"; + } else { + /* POST method in default*/ + *method = "POST"; + } + gpr_free(key); + gpr_free(value); + continue; + } + if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_PATH)) { + /* Create URL by appending :path value to the hostname */ + gpr_asprintf(pp_url, "https://%s%s", host, value); + gpr_free(key); + gpr_free(value); + continue; + } + CRONET_LOG(GPR_DEBUG, "header %s = %s", key, value); + headers[num_headers].key = key; + headers[num_headers].value = value; + num_headers++; + if (curr == nullptr) { + break; + } + } + *p_num_headers = num_headers; +} + +static void parse_grpc_header(const uint8_t* data, int* length, + bool* compressed) { + const uint8_t c = *data; + const uint8_t* p = data + 1; + *compressed = ((c & 0x01) == 0x01); + *length = 0; + *length |= (*p++) << 24; + *length |= (*p++) << 16; + *length |= (*p++) << 8; + *length |= (*p++); +} + +static bool header_has_authority(grpc_linked_mdelem* head) { + while (head != nullptr) { + if (grpc_slice_eq(GRPC_MDKEY(head->md), GRPC_MDSTR_AUTHORITY)) { + return true; + } + head = head->next; + } + return false; +} + +/* + Op Execution: Decide if one of the actions contained in the stream op can be + executed. This is the heart of the state machine. +*/ +static bool op_can_be_run(grpc_transport_stream_op_batch* curr_op, + struct stream_obj* s, struct op_state* op_state, + enum e_op_id op_id) { + struct op_state* stream_state = &s->state; + grpc_cronet_transport* t = s->curr_ct; + bool result = true; + /* When call is canceled, every op can be run, except under following + conditions + */ + bool is_canceled_or_failed = stream_state->state_op_done[OP_CANCEL_ERROR] || + stream_state->state_callback_received[OP_FAILED]; + if (is_canceled_or_failed) { + if (op_id == OP_SEND_INITIAL_METADATA) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_SEND_MESSAGE) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_SEND_TRAILING_METADATA) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_CANCEL_ERROR) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* already executed */ + if (op_id == OP_RECV_INITIAL_METADATA && + stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_RECV_MESSAGE && op_state->state_op_done[OP_RECV_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_RECV_TRAILING_METADATA && + stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* ON_COMPLETE can be processed if one of the following conditions is met: + * 1. the stream failed + * 2. the stream is cancelled, and the callback is received + * 3. the stream succeeded before cancel is effective + * 4. the stream is cancelled, and the stream is never started */ + if (op_id == OP_ON_COMPLETE && + !(stream_state->state_callback_received[OP_FAILED] || + stream_state->state_callback_received[OP_CANCELED] || + stream_state->state_callback_received[OP_SUCCEEDED] || + !stream_state->state_op_done[OP_SEND_INITIAL_METADATA])) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + } else if (op_id == OP_SEND_INITIAL_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_SEND_INITIAL_METADATA]) result = false; + } else if (op_id == OP_RECV_INITIAL_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) result = false; + /* we haven't sent headers yet. */ + else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) + result = false; + /* we haven't received headers yet. */ + else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA] && + !stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) + result = false; + } else if (op_id == OP_SEND_MESSAGE) { + /* already executed (note we're checking op specific state, not stream + state) */ + if (op_state->state_op_done[OP_SEND_MESSAGE]) result = false; + /* we haven't sent headers yet. */ + else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) + result = false; + } else if (op_id == OP_RECV_MESSAGE) { + /* already executed */ + if (op_state->state_op_done[OP_RECV_MESSAGE]) result = false; + /* we haven't received headers yet. */ + else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA] && + !stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) + result = false; + } else if (op_id == OP_RECV_TRAILING_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) result = false; + /* we have asked for but haven't received message yet. */ + else if (stream_state->state_op_done[OP_READ_REQ_MADE] && + !stream_state->state_op_done[OP_RECV_MESSAGE]) + result = false; + /* we haven't received trailers yet. */ + else if (!stream_state->state_callback_received[OP_RECV_TRAILING_METADATA]) + result = false; + /* we haven't received on_succeeded yet. */ + else if (!stream_state->state_callback_received[OP_SUCCEEDED]) + result = false; + } else if (op_id == OP_SEND_TRAILING_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) result = false; + /* we haven't sent initial metadata yet */ + else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) + result = false; + /* we haven't sent message yet */ + else if (stream_state->pending_send_message && + !stream_state->state_op_done[OP_SEND_MESSAGE]) + result = false; + /* we haven't got on_write_completed for the send yet */ + else if (stream_state->state_op_done[OP_SEND_MESSAGE] && + !stream_state->state_callback_received[OP_SEND_MESSAGE] && + !(t->use_packet_coalescing && + stream_state->pending_write_for_trailer)) + result = false; + } else if (op_id == OP_CANCEL_ERROR) { + /* already executed */ + if (stream_state->state_op_done[OP_CANCEL_ERROR]) result = false; + } else if (op_id == OP_ON_COMPLETE) { + /* already executed (note we're checking op specific state, not stream + state) */ + if (op_state->state_op_done[OP_ON_COMPLETE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* Check if every op that was asked for is done. */ + /* TODO(muxi): We should not consider the recv ops here, since they + * have their own callbacks. We should invoke a batch's on_complete + * as soon as all of the batch's send ops are complete, even if + * there are still recv ops pending. */ + else if (curr_op->send_initial_metadata && + !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->send_message && + !op_state->state_op_done[OP_SEND_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->send_message && + !stream_state->state_callback_received[OP_SEND_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->send_trailing_metadata && + !stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->recv_initial_metadata && + !stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->recv_message && + !op_state->state_op_done[OP_RECV_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->cancel_stream && + !stream_state->state_callback_received[OP_CANCELED]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->recv_trailing_metadata) { + /* We aren't done with trailing metadata yet */ + if (!stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* We've asked for actual message in an earlier op, and it hasn't been + delivered yet. */ + else if (stream_state->state_op_done[OP_READ_REQ_MADE]) { + /* If this op is not the one asking for read, (which means some earlier + op has asked), and the read hasn't been delivered. */ + if (!curr_op->recv_message && + !stream_state->state_callback_received[OP_SUCCEEDED]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + } + } + /* We should see at least one on_write_completed for the trailers that we + sent */ + else if (curr_op->send_trailing_metadata && + !stream_state->state_callback_received[OP_SEND_MESSAGE]) + result = false; + } + CRONET_LOG(GPR_DEBUG, "op_can_be_run %s : %s", op_id_string(op_id), + result ? "YES" : "NO"); + return result; +} + +/* + TODO (makdharma): Break down this function in smaller chunks for readability. +*/ +static enum e_op_result execute_stream_op(struct op_and_state* oas) { + grpc_transport_stream_op_batch* stream_op = &oas->op; + struct stream_obj* s = oas->s; + grpc_cronet_transport* t = s->curr_ct; + struct op_state* stream_state = &s->state; + enum e_op_result result = NO_ACTION_POSSIBLE; + if (stream_op->send_initial_metadata && + op_can_be_run(stream_op, s, &oas->state, OP_SEND_INITIAL_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_INITIAL_METADATA", oas); + /* Start new cronet stream. It is destroyed in on_succeeded, on_canceled, + * on_failed */ + GPR_ASSERT(s->cbs == nullptr); + GPR_ASSERT(!stream_state->state_op_done[OP_SEND_INITIAL_METADATA]); + s->cbs = + bidirectional_stream_create(t->engine, s->curr_gs, &cronet_callbacks); + CRONET_LOG(GPR_DEBUG, "%p = bidirectional_stream_create()", s->cbs); + if (t->use_packet_coalescing) { + bidirectional_stream_disable_auto_flush(s->cbs, true); + bidirectional_stream_delay_request_headers_until_flush(s->cbs, true); + } + char* url = nullptr; + const char* method = "POST"; + s->header_array.headers = nullptr; + convert_metadata_to_cronet_headers(stream_op->payload->send_initial_metadata + .send_initial_metadata->list.head, + t->host, &url, &s->header_array.headers, + &s->header_array.count, &method); + s->header_array.capacity = s->header_array.count; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_start(%p, %s)", s->cbs, url); + bidirectional_stream_start(s->cbs, url, 0, method, &s->header_array, false); + if (url) { + gpr_free(url); + } + unsigned int header_index; + for (header_index = 0; header_index < s->header_array.count; + header_index++) { + gpr_free((void*)s->header_array.headers[header_index].key); + gpr_free((void*)s->header_array.headers[header_index].value); + } + stream_state->state_op_done[OP_SEND_INITIAL_METADATA] = true; + if (t->use_packet_coalescing) { + if (!stream_op->send_message && !stream_op->send_trailing_metadata) { + s->state.flush_cronet_when_ready = true; + } + } + result = ACTION_TAKEN_WITH_CALLBACK; + } else if (stream_op->send_message && + op_can_be_run(stream_op, s, &oas->state, OP_SEND_MESSAGE)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_MESSAGE", oas); + stream_state->pending_send_message = false; + if (stream_state->state_callback_received[OP_FAILED]) { + result = NO_ACTION_POSSIBLE; + CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed."); + } else { + grpc_slice_buffer write_slice_buffer; + grpc_slice slice; + grpc_slice_buffer_init(&write_slice_buffer); + if (1 != stream_op->payload->send_message.send_message->Next( + stream_op->payload->send_message.send_message->length(), + nullptr)) { + /* Should never reach here */ + GPR_ASSERT(false); + } + if (GRPC_ERROR_NONE != + stream_op->payload->send_message.send_message->Pull(&slice)) { + /* Should never reach here */ + GPR_ASSERT(false); + } + grpc_slice_buffer_add(&write_slice_buffer, slice); + if (GPR_UNLIKELY(write_slice_buffer.count != 1)) { + /* Empty request not handled yet */ + gpr_log(GPR_ERROR, "Empty request is not supported"); + GPR_ASSERT(write_slice_buffer.count == 1); + } + if (write_slice_buffer.count > 0) { + size_t write_buffer_size; + create_grpc_frame( + &write_slice_buffer, &stream_state->ws.write_buffer, + &write_buffer_size, + stream_op->payload->send_message.send_message->flags()); + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs, + stream_state->ws.write_buffer); + stream_state->state_callback_received[OP_SEND_MESSAGE] = false; + bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer, + static_cast(write_buffer_size), false); + grpc_slice_buffer_destroy_internal(&write_slice_buffer); + if (t->use_packet_coalescing) { + if (!stream_op->send_trailing_metadata) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(s->cbs); + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + stream_state->pending_write_for_trailer = true; + result = ACTION_TAKEN_NO_CALLBACK; + } + } else { + result = ACTION_TAKEN_WITH_CALLBACK; + } + } else { + result = NO_ACTION_POSSIBLE; + } + } + stream_state->state_op_done[OP_SEND_MESSAGE] = true; + oas->state.state_op_done[OP_SEND_MESSAGE] = true; + stream_op->payload->send_message.send_message.reset(); + } else if (stream_op->send_trailing_metadata && + op_can_be_run(stream_op, s, &oas->state, + OP_SEND_TRAILING_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_TRAILING_METADATA", oas); + if (stream_state->state_callback_received[OP_FAILED]) { + result = NO_ACTION_POSSIBLE; + CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed."); + } else { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, 0)", s->cbs); + stream_state->state_callback_received[OP_SEND_MESSAGE] = false; + bidirectional_stream_write(s->cbs, "", 0, true); + if (t->use_packet_coalescing) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(s->cbs); + } + result = ACTION_TAKEN_WITH_CALLBACK; + } + stream_state->state_op_done[OP_SEND_TRAILING_METADATA] = true; + } else if (stream_op->recv_initial_metadata && + op_can_be_run(stream_op, s, &oas->state, + OP_RECV_INITIAL_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_INITIAL_METADATA", oas); + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + GRPC_CLOSURE_SCHED( + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } else if (stream_state->state_callback_received[OP_FAILED]) { + GRPC_CLOSURE_SCHED( + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } else if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { + GRPC_CLOSURE_SCHED( + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } else { + grpc_chttp2_incoming_metadata_buffer_publish( + &oas->s->state.rs.initial_metadata, + stream_op->payload->recv_initial_metadata.recv_initial_metadata); + GRPC_CLOSURE_SCHED( + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } + stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_op->recv_message && + op_can_be_run(stream_op, s, &oas->state, OP_RECV_MESSAGE)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_MESSAGE", oas); + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + CRONET_LOG(GPR_DEBUG, "Stream is cancelled."); + GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->state_callback_received[OP_FAILED]) { + CRONET_LOG(GPR_DEBUG, "Stream failed."); + GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->rs.read_stream_closed == true) { + /* No more data will be received */ + CRONET_LOG(GPR_DEBUG, "read stream closed"); + GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->flush_read) { + CRONET_LOG(GPR_DEBUG, "flush read"); + GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->rs.length_field_received == false) { + if (stream_state->rs.received_bytes == GRPC_HEADER_SIZE_IN_BYTES && + stream_state->rs.remaining_bytes == 0) { + /* Start a read operation for data */ + stream_state->rs.length_field_received = true; + parse_grpc_header( + reinterpret_cast(stream_state->rs.read_buffer), + &stream_state->rs.length_field, &stream_state->rs.compressed); + CRONET_LOG(GPR_DEBUG, "length field = %d", + stream_state->rs.length_field); + if (stream_state->rs.length_field > 0) { + stream_state->rs.read_buffer = static_cast( + gpr_malloc(static_cast(stream_state->rs.length_field))); + GPR_ASSERT(stream_state->rs.read_buffer); + stream_state->rs.remaining_bytes = stream_state->rs.length_field; + stream_state->rs.received_bytes = 0; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + stream_state->state_op_done[OP_READ_REQ_MADE] = + true; /* Indicates that at least one read request has been made */ + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + stream_state->rs.remaining_bytes = 0; + CRONET_LOG(GPR_DEBUG, "read operation complete. Empty response."); + /* Clean up read_slice_buffer in case there is unread data. */ + grpc_slice_buffer_destroy_internal( + &stream_state->rs.read_slice_buffer); + grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); + uint32_t flags = 0; + if (stream_state->rs.compressed) { + flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } + stream_state->rs.sbs.Init(&stream_state->rs.read_slice_buffer, flags); + stream_op->payload->recv_message.recv_message->reset( + stream_state->rs.sbs.get()); + GRPC_CLOSURE_SCHED( + stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + + /* Extra read to trigger on_succeed */ + stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; + stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + stream_state->rs.received_bytes = 0; + stream_state->rs.compressed = false; + stream_state->rs.length_field_received = false; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + stream_state->state_op_done[OP_READ_REQ_MADE] = + true; /* Indicates that at least one read request has been made */ + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_NO_CALLBACK; + } + } else if (stream_state->rs.remaining_bytes == 0) { + /* Start a read operation for first 5 bytes (GRPC header) */ + stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; + stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + stream_state->rs.received_bytes = 0; + stream_state->rs.compressed = false; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + stream_state->state_op_done[OP_READ_REQ_MADE] = + true; /* Indicates that at least one read request has been made */ + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + result = NO_ACTION_POSSIBLE; + } + } else if (stream_state->rs.remaining_bytes == 0) { + CRONET_LOG(GPR_DEBUG, "read operation complete"); + grpc_slice read_data_slice = + GRPC_SLICE_MALLOC((uint32_t)stream_state->rs.length_field); + uint8_t* dst_p = GRPC_SLICE_START_PTR(read_data_slice); + memcpy(dst_p, stream_state->rs.read_buffer, + static_cast(stream_state->rs.length_field)); + null_and_maybe_free_read_buffer(s); + /* Clean up read_slice_buffer in case there is unread data. */ + grpc_slice_buffer_destroy_internal(&stream_state->rs.read_slice_buffer); + grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); + grpc_slice_buffer_add(&stream_state->rs.read_slice_buffer, + read_data_slice); + uint32_t flags = 0; + if (stream_state->rs.compressed) { + flags = GRPC_WRITE_INTERNAL_COMPRESS; + } + stream_state->rs.sbs.Init(&stream_state->rs.read_slice_buffer, flags); + stream_op->payload->recv_message.recv_message->reset( + stream_state->rs.sbs.get()); + GRPC_CLOSURE_SCHED(stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + /* Do an extra read to trigger on_succeeded() callback in case connection + is closed */ + stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; + stream_state->rs.compressed = false; + stream_state->rs.received_bytes = 0; + stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + stream_state->rs.length_field_received = false; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_NO_CALLBACK; + } + } else if (stream_op->recv_trailing_metadata && + op_can_be_run(stream_op, s, &oas->state, + OP_RECV_TRAILING_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_TRAILING_METADATA", oas); + grpc_error* error = GRPC_ERROR_NONE; + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + error = GRPC_ERROR_REF(stream_state->cancel_error); + } else if (stream_state->state_callback_received[OP_FAILED]) { + error = make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable."); + } else if (oas->s->state.rs.trailing_metadata_valid) { + grpc_chttp2_incoming_metadata_buffer_publish( + &oas->s->state.rs.trailing_metadata, + stream_op->payload->recv_trailing_metadata.recv_trailing_metadata); + stream_state->rs.trailing_metadata_valid = false; + } + GRPC_CLOSURE_SCHED( + stream_op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + error); + stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_op->cancel_stream && + op_can_be_run(stream_op, s, &oas->state, OP_CANCEL_ERROR)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_CANCEL_ERROR", oas); + if (s->cbs) { + CRONET_LOG(GPR_DEBUG, "W: bidirectional_stream_cancel(%p)", s->cbs); + bidirectional_stream_cancel(s->cbs); + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + result = ACTION_TAKEN_NO_CALLBACK; + } + stream_state->state_op_done[OP_CANCEL_ERROR] = true; + if (!stream_state->cancel_error) { + stream_state->cancel_error = + GRPC_ERROR_REF(stream_op->payload->cancel_stream.cancel_error); + } + } else if (stream_op->on_complete && + op_can_be_run(stream_op, s, &oas->state, OP_ON_COMPLETE)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_ON_COMPLETE", oas); + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + GRPC_CLOSURE_SCHED(stream_op->on_complete, + GRPC_ERROR_REF(stream_state->cancel_error)); + } else if (stream_state->state_callback_received[OP_FAILED]) { + GRPC_CLOSURE_SCHED( + stream_op->on_complete, + make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable.")); + } else { + /* All actions in this stream_op are complete. Call the on_complete + * callback + */ + GRPC_CLOSURE_SCHED(stream_op->on_complete, GRPC_ERROR_NONE); + } + oas->state.state_op_done[OP_ON_COMPLETE] = true; + oas->done = true; + /* reset any send message state, only if this ON_COMPLETE is about a send. + */ + if (stream_op->send_message) { + stream_state->state_callback_received[OP_SEND_MESSAGE] = false; + stream_state->state_op_done[OP_SEND_MESSAGE] = false; + } + result = ACTION_TAKEN_NO_CALLBACK; + /* If this is the on_complete callback being called for a received message - + make a note */ + if (stream_op->recv_message) + stream_state->state_op_done[OP_RECV_MESSAGE_AND_ON_COMPLETE] = true; + } else { + result = NO_ACTION_POSSIBLE; + } + return result; +} + +/* + Functions used by upper layers to access transport functionality. +*/ + +inline stream_obj::stream_obj(grpc_transport* gt, grpc_stream* gs, + grpc_stream_refcount* refcount, gpr_arena* arena) + : arena(arena), + curr_ct(reinterpret_cast(gt)), + curr_gs(gs), + state(arena), + refcount(refcount) { + GRPC_CRONET_STREAM_REF(this, "cronet transport"); + gpr_mu_init(&mu); +} + +inline stream_obj::~stream_obj() { + null_and_maybe_free_read_buffer(this); + /* Clean up read_slice_buffer in case there is unread data. */ + grpc_slice_buffer_destroy_internal(&state.rs.read_slice_buffer); + GRPC_ERROR_UNREF(state.cancel_error); +} + +static int init_stream(grpc_transport* gt, grpc_stream* gs, + grpc_stream_refcount* refcount, const void* server_data, + gpr_arena* arena) { + new (gs) stream_obj(gt, gs, refcount, arena); + return 0; +} + +static void set_pollset_do_nothing(grpc_transport* gt, grpc_stream* gs, + grpc_pollset* pollset) {} + +static void set_pollset_set_do_nothing(grpc_transport* gt, grpc_stream* gs, + grpc_pollset_set* pollset_set) {} + +static void perform_stream_op(grpc_transport* gt, grpc_stream* gs, + grpc_transport_stream_op_batch* op) { + CRONET_LOG(GPR_DEBUG, "perform_stream_op"); + if (op->send_initial_metadata && + header_has_authority(op->payload->send_initial_metadata + .send_initial_metadata->list.head)) { + /* Cronet does not support :authority header field. We cancel the call when + this field is present in metadata */ + if (op->recv_initial_metadata) { + GRPC_CLOSURE_SCHED( + op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_CANCELLED); + } + if (op->recv_message) { + GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready, + GRPC_ERROR_CANCELLED); + } + if (op->recv_trailing_metadata) { + GRPC_CLOSURE_SCHED( + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + GRPC_ERROR_CANCELLED); + } + GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_CANCELLED); + return; + } + stream_obj* s = reinterpret_cast(gs); + add_to_storage(s, op); + execute_from_storage(s); +} + +static void destroy_stream(grpc_transport* gt, grpc_stream* gs, + grpc_closure* then_schedule_closure) { + stream_obj* s = reinterpret_cast(gs); + s->~stream_obj(); + GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE); +} + +static void destroy_transport(grpc_transport* gt) {} + +static grpc_endpoint* get_endpoint(grpc_transport* gt) { return nullptr; } + +static void perform_op(grpc_transport* gt, grpc_transport_op* op) {} + +static const grpc_transport_vtable grpc_cronet_vtable = { + sizeof(stream_obj), + "cronet_http", + init_stream, + set_pollset_do_nothing, + set_pollset_set_do_nothing, + perform_stream_op, + perform_op, + destroy_stream, + destroy_transport, + get_endpoint}; + +grpc_transport* grpc_create_cronet_transport(void* engine, const char* target, + const grpc_channel_args* args, + void* reserved) { + grpc_cronet_transport* ct = static_cast( + gpr_malloc(sizeof(grpc_cronet_transport))); + if (!ct) { + goto error; + } + ct->base.vtable = &grpc_cronet_vtable; + ct->engine = static_cast(engine); + ct->host = static_cast(gpr_malloc(strlen(target) + 1)); + if (!ct->host) { + goto error; + } + strcpy(ct->host, target); + + ct->use_packet_coalescing = true; + if (args) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == + strcmp(args->args[i].key, GRPC_ARG_USE_CRONET_PACKET_COALESCING)) { + if (GPR_UNLIKELY(args->args[i].type != GRPC_ARG_INTEGER)) { + gpr_log(GPR_ERROR, "%s ignored: it must be an integer", + GRPC_ARG_USE_CRONET_PACKET_COALESCING); + } else { + ct->use_packet_coalescing = (args->args[i].value.integer != 0); + } + } + } + } + + return &ct->base; + +error: + if (ct) { + if (ct->host) { + gpr_free(ct->host); + } + gpr_free(ct); + } + + return nullptr; +} diff --git a/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.h b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.h new file mode 100644 index 00000000000000..fb7e149f10fcf4 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/cronet/transport/cronet_transport.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H +#define GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H + +#include + +#include "src/core/lib/transport/transport.h" + +grpc_transport* grpc_create_cronet_transport(void* engine, const char* target, + const grpc_channel_args* args, + void* reserved); + +#endif /* GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H */ diff --git a/third_party/grpc/src/core/ext/transport/inproc/inproc_plugin.cc b/third_party/grpc/src/core/ext/transport/inproc/inproc_plugin.cc new file mode 100644 index 00000000000000..8e251fa2d85edc --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/inproc/inproc_plugin.cc @@ -0,0 +1,28 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/transport/inproc/inproc_transport.h" +#include "src/core/lib/debug/trace.h" + +grpc_core::TraceFlag grpc_inproc_trace(false, "inproc"); + +void grpc_inproc_plugin_init(void) { grpc_inproc_transport_init(); } + +void grpc_inproc_plugin_shutdown(void) { grpc_inproc_transport_shutdown(); } diff --git a/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.cc b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.cc new file mode 100644 index 00000000000000..0b9bf5dd11b548 --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.cc @@ -0,0 +1,1258 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include +#include "src/core/ext/transport/inproc/inproc_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/channel_stack_type.h" +#include "src/core/lib/surface/server.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/transport_impl.h" + +#define INPROC_LOG(...) \ + do { \ + if (grpc_inproc_trace.enabled()) gpr_log(__VA_ARGS__); \ + } while (0) + +namespace { +grpc_slice g_empty_slice; +grpc_slice g_fake_path_key; +grpc_slice g_fake_path_value; +grpc_slice g_fake_auth_key; +grpc_slice g_fake_auth_value; + +struct inproc_stream; +bool cancel_stream_locked(inproc_stream* s, grpc_error* error); +void op_state_machine(void* arg, grpc_error* error); +void log_metadata(const grpc_metadata_batch* md_batch, bool is_client, + bool is_initial); +grpc_error* fill_in_metadata(inproc_stream* s, + const grpc_metadata_batch* metadata, + uint32_t flags, grpc_metadata_batch* out_md, + uint32_t* outflags, bool* markfilled); + +struct shared_mu { + shared_mu() { + // Share one lock between both sides since both sides get affected + gpr_mu_init(&mu); + gpr_ref_init(&refs, 2); + } + + gpr_mu mu; + gpr_refcount refs; +}; + +struct inproc_transport { + inproc_transport(const grpc_transport_vtable* vtable, shared_mu* mu, + bool is_client) + : mu(mu), is_client(is_client) { + base.vtable = vtable; + // Start each side of transport with 2 refs since they each have a ref + // to the other + gpr_ref_init(&refs, 2); + grpc_connectivity_state_init(&connectivity, GRPC_CHANNEL_READY, + is_client ? "inproc_client" : "inproc_server"); + } + + ~inproc_transport() { + grpc_connectivity_state_destroy(&connectivity); + if (gpr_unref(&mu->refs)) { + gpr_free(mu); + } + } + + void ref() { + INPROC_LOG(GPR_INFO, "ref_transport %p", this); + gpr_ref(&refs); + } + + void unref() { + INPROC_LOG(GPR_INFO, "unref_transport %p", this); + if (!gpr_unref(&refs)) { + return; + } + INPROC_LOG(GPR_INFO, "really_destroy_transport %p", this); + this->~inproc_transport(); + gpr_free(this); + } + + grpc_transport base; + shared_mu* mu; + gpr_refcount refs; + bool is_client; + grpc_connectivity_state_tracker connectivity; + void (*accept_stream_cb)(void* user_data, grpc_transport* transport, + const void* server_data); + void* accept_stream_data; + bool is_closed = false; + struct inproc_transport* other_side; + struct inproc_stream* stream_list = nullptr; +}; + +struct inproc_stream { + inproc_stream(inproc_transport* t, grpc_stream_refcount* refcount, + const void* server_data, gpr_arena* arena) + : t(t), refs(refcount), arena(arena) { + // Ref this stream right now for ctor and list. + ref("inproc_init_stream:init"); + ref("inproc_init_stream:list"); + + grpc_metadata_batch_init(&to_read_initial_md); + grpc_metadata_batch_init(&to_read_trailing_md); + GRPC_CLOSURE_INIT(&op_closure, op_state_machine, this, + grpc_schedule_on_exec_ctx); + grpc_metadata_batch_init(&write_buffer_initial_md); + grpc_metadata_batch_init(&write_buffer_trailing_md); + + stream_list_prev = nullptr; + gpr_mu_lock(&t->mu->mu); + stream_list_next = t->stream_list; + if (t->stream_list) { + t->stream_list->stream_list_prev = this; + } + t->stream_list = this; + gpr_mu_unlock(&t->mu->mu); + + if (!server_data) { + t->ref(); + inproc_transport* st = t->other_side; + st->ref(); + other_side = nullptr; // will get filled in soon + // Pass the client-side stream address to the server-side for a ref + ref("inproc_init_stream:clt"); // ref it now on behalf of server + // side to avoid destruction + INPROC_LOG(GPR_INFO, "calling accept stream cb %p %p", + st->accept_stream_cb, st->accept_stream_data); + (*st->accept_stream_cb)(st->accept_stream_data, &st->base, (void*)this); + } else { + // This is the server-side and is being called through accept_stream_cb + inproc_stream* cs = (inproc_stream*)server_data; + other_side = cs; + // Ref the server-side stream on behalf of the client now + ref("inproc_init_stream:srv"); + + // Now we are about to affect the other side, so lock the transport + // to make sure that it doesn't get destroyed + gpr_mu_lock(&t->mu->mu); + cs->other_side = this; + // Now transfer from the other side's write_buffer if any to the to_read + // buffer + if (cs->write_buffer_initial_md_filled) { + fill_in_metadata(this, &cs->write_buffer_initial_md, + cs->write_buffer_initial_md_flags, &to_read_initial_md, + &to_read_initial_md_flags, &to_read_initial_md_filled); + deadline = GPR_MIN(deadline, cs->write_buffer_deadline); + grpc_metadata_batch_clear(&cs->write_buffer_initial_md); + cs->write_buffer_initial_md_filled = false; + } + if (cs->write_buffer_trailing_md_filled) { + fill_in_metadata(this, &cs->write_buffer_trailing_md, 0, + &to_read_trailing_md, nullptr, + &to_read_trailing_md_filled); + grpc_metadata_batch_clear(&cs->write_buffer_trailing_md); + cs->write_buffer_trailing_md_filled = false; + } + if (cs->write_buffer_cancel_error != GRPC_ERROR_NONE) { + cancel_other_error = cs->write_buffer_cancel_error; + cs->write_buffer_cancel_error = GRPC_ERROR_NONE; + } + + gpr_mu_unlock(&t->mu->mu); + } + } + + ~inproc_stream() { + GRPC_ERROR_UNREF(write_buffer_cancel_error); + GRPC_ERROR_UNREF(cancel_self_error); + GRPC_ERROR_UNREF(cancel_other_error); + + if (recv_inited) { + grpc_slice_buffer_destroy_internal(&recv_message); + } + + t->unref(); + + if (closure_at_destroy) { + GRPC_CLOSURE_SCHED(closure_at_destroy, GRPC_ERROR_NONE); + } + } + +#ifndef NDEBUG +#define STREAM_REF(refs, reason) grpc_stream_ref(refs, reason) +#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs, reason) +#else +#define STREAM_REF(refs, reason) grpc_stream_ref(refs) +#define STREAM_UNREF(refs, reason) grpc_stream_unref(refs) +#endif + void ref(const char* reason) { + INPROC_LOG(GPR_INFO, "ref_stream %p %s", this, reason); + STREAM_REF(refs, reason); + } + + void unref(const char* reason) { + INPROC_LOG(GPR_INFO, "unref_stream %p %s", this, reason); + STREAM_UNREF(refs, reason); + } +#undef STREAM_REF +#undef STREAM_UNREF + + inproc_transport* t; + grpc_metadata_batch to_read_initial_md; + uint32_t to_read_initial_md_flags = 0; + bool to_read_initial_md_filled = false; + grpc_metadata_batch to_read_trailing_md; + bool to_read_trailing_md_filled = false; + bool ops_needed = false; + bool op_closure_scheduled = false; + grpc_closure op_closure; + // Write buffer used only during gap at init time when client-side + // stream is set up but server side stream is not yet set up + grpc_metadata_batch write_buffer_initial_md; + bool write_buffer_initial_md_filled = false; + uint32_t write_buffer_initial_md_flags = 0; + grpc_millis write_buffer_deadline = GRPC_MILLIS_INF_FUTURE; + grpc_metadata_batch write_buffer_trailing_md; + bool write_buffer_trailing_md_filled = false; + grpc_error* write_buffer_cancel_error = GRPC_ERROR_NONE; + + struct inproc_stream* other_side; + bool other_side_closed = false; // won't talk anymore + bool write_buffer_other_side_closed = false; // on hold + grpc_stream_refcount* refs; + grpc_closure* closure_at_destroy = nullptr; + + gpr_arena* arena; + + grpc_transport_stream_op_batch* send_message_op = nullptr; + grpc_transport_stream_op_batch* send_trailing_md_op = nullptr; + grpc_transport_stream_op_batch* recv_initial_md_op = nullptr; + grpc_transport_stream_op_batch* recv_message_op = nullptr; + grpc_transport_stream_op_batch* recv_trailing_md_op = nullptr; + + grpc_slice_buffer recv_message; + grpc_core::ManualConstructor recv_stream; + bool recv_inited = false; + + bool initial_md_sent = false; + bool trailing_md_sent = false; + bool initial_md_recvd = false; + bool trailing_md_recvd = false; + + bool closed = false; + + grpc_error* cancel_self_error = GRPC_ERROR_NONE; + grpc_error* cancel_other_error = GRPC_ERROR_NONE; + + grpc_millis deadline = GRPC_MILLIS_INF_FUTURE; + + bool listed = true; + struct inproc_stream* stream_list_prev; + struct inproc_stream* stream_list_next; +}; + +void log_metadata(const grpc_metadata_batch* md_batch, bool is_client, + bool is_initial) { + for (grpc_linked_mdelem* md = md_batch->list.head; md != nullptr; + md = md->next) { + char* key = grpc_slice_to_c_string(GRPC_MDKEY(md->md)); + char* value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md)); + gpr_log(GPR_INFO, "INPROC:%s:%s: %s: %s", is_initial ? "HDR" : "TRL", + is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } +} + +grpc_error* fill_in_metadata(inproc_stream* s, + const grpc_metadata_batch* metadata, + uint32_t flags, grpc_metadata_batch* out_md, + uint32_t* outflags, bool* markfilled) { + if (grpc_inproc_trace.enabled()) { + log_metadata(metadata, s->t->is_client, outflags != nullptr); + } + + if (outflags != nullptr) { + *outflags = flags; + } + if (markfilled != nullptr) { + *markfilled = true; + } + grpc_error* error = GRPC_ERROR_NONE; + for (grpc_linked_mdelem* elem = metadata->list.head; + (elem != nullptr) && (error == GRPC_ERROR_NONE); elem = elem->next) { + grpc_linked_mdelem* nelem = static_cast( + gpr_arena_alloc(s->arena, sizeof(*nelem))); + nelem->md = + grpc_mdelem_from_slices(grpc_slice_intern(GRPC_MDKEY(elem->md)), + grpc_slice_intern(GRPC_MDVALUE(elem->md))); + + error = grpc_metadata_batch_link_tail(out_md, nelem); + } + return error; +} + +int init_stream(grpc_transport* gt, grpc_stream* gs, + grpc_stream_refcount* refcount, const void* server_data, + gpr_arena* arena) { + INPROC_LOG(GPR_INFO, "init_stream %p %p %p", gt, gs, server_data); + inproc_transport* t = reinterpret_cast(gt); + new (gs) inproc_stream(t, refcount, server_data, arena); + return 0; // return value is not important +} + +void close_stream_locked(inproc_stream* s) { + if (!s->closed) { + // Release the metadata that we would have written out + grpc_metadata_batch_destroy(&s->write_buffer_initial_md); + grpc_metadata_batch_destroy(&s->write_buffer_trailing_md); + + if (s->listed) { + inproc_stream* p = s->stream_list_prev; + inproc_stream* n = s->stream_list_next; + if (p != nullptr) { + p->stream_list_next = n; + } else { + s->t->stream_list = n; + } + if (n != nullptr) { + n->stream_list_prev = p; + } + s->listed = false; + s->unref("close_stream:list"); + } + s->closed = true; + s->unref("close_stream:closing"); + } +} + +// This function means that we are done talking/listening to the other side +void close_other_side_locked(inproc_stream* s, const char* reason) { + if (s->other_side != nullptr) { + // First release the metadata that came from the other side's arena + grpc_metadata_batch_destroy(&s->to_read_initial_md); + grpc_metadata_batch_destroy(&s->to_read_trailing_md); + + s->other_side->unref(reason); + s->other_side_closed = true; + s->other_side = nullptr; + } else if (!s->other_side_closed) { + s->write_buffer_other_side_closed = true; + } +} + +// Call the on_complete closure associated with this stream_op_batch if +// this stream_op_batch is only one of the pending operations for this +// stream. This is called when one of the pending operations for the stream +// is done and about to be NULLed out +void complete_if_batch_end_locked(inproc_stream* s, grpc_error* error, + grpc_transport_stream_op_batch* op, + const char* msg) { + int is_sm = static_cast(op == s->send_message_op); + int is_stm = static_cast(op == s->send_trailing_md_op); + // TODO(vjpai): We should not consider the recv ops here, since they + // have their own callbacks. We should invoke a batch's on_complete + // as soon as all of the batch's send ops are complete, even if there + // are still recv ops pending. + int is_rim = static_cast(op == s->recv_initial_md_op); + int is_rm = static_cast(op == s->recv_message_op); + int is_rtm = static_cast(op == s->recv_trailing_md_op); + + if ((is_sm + is_stm + is_rim + is_rm + is_rtm) == 1) { + INPROC_LOG(GPR_INFO, "%s %p %p %p", msg, s, op, error); + GRPC_CLOSURE_SCHED(op->on_complete, GRPC_ERROR_REF(error)); + } +} + +void maybe_schedule_op_closure_locked(inproc_stream* s, grpc_error* error) { + if (s && s->ops_needed && !s->op_closure_scheduled) { + GRPC_CLOSURE_SCHED(&s->op_closure, GRPC_ERROR_REF(error)); + s->op_closure_scheduled = true; + s->ops_needed = false; + } +} + +void fail_helper_locked(inproc_stream* s, grpc_error* error) { + INPROC_LOG(GPR_INFO, "op_state_machine %p fail_helper", s); + // If we're failing this side, we need to make sure that + // we also send or have already sent trailing metadata + if (!s->trailing_md_sent) { + // Send trailing md to the other side indicating cancellation + s->trailing_md_sent = true; + + grpc_metadata_batch fake_md; + grpc_metadata_batch_init(&fake_md); + + inproc_stream* other = s->other_side; + grpc_metadata_batch* dest = (other == nullptr) + ? &s->write_buffer_trailing_md + : &other->to_read_trailing_md; + bool* destfilled = (other == nullptr) ? &s->write_buffer_trailing_md_filled + : &other->to_read_trailing_md_filled; + fill_in_metadata(s, &fake_md, 0, dest, nullptr, destfilled); + grpc_metadata_batch_destroy(&fake_md); + + if (other != nullptr) { + if (other->cancel_other_error == GRPC_ERROR_NONE) { + other->cancel_other_error = GRPC_ERROR_REF(error); + } + maybe_schedule_op_closure_locked(other, error); + } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) { + s->write_buffer_cancel_error = GRPC_ERROR_REF(error); + } + } + if (s->recv_initial_md_op) { + grpc_error* err; + if (!s->t->is_client) { + // If this is a server, provide initial metadata with a path and authority + // since it expects that as well as no error yet + grpc_metadata_batch fake_md; + grpc_metadata_batch_init(&fake_md); + grpc_linked_mdelem* path_md = static_cast( + gpr_arena_alloc(s->arena, sizeof(*path_md))); + path_md->md = grpc_mdelem_from_slices(g_fake_path_key, g_fake_path_value); + GPR_ASSERT(grpc_metadata_batch_link_tail(&fake_md, path_md) == + GRPC_ERROR_NONE); + grpc_linked_mdelem* auth_md = static_cast( + gpr_arena_alloc(s->arena, sizeof(*auth_md))); + auth_md->md = grpc_mdelem_from_slices(g_fake_auth_key, g_fake_auth_value); + GPR_ASSERT(grpc_metadata_batch_link_tail(&fake_md, auth_md) == + GRPC_ERROR_NONE); + + fill_in_metadata( + s, &fake_md, 0, + s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata, + s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags, + nullptr); + grpc_metadata_batch_destroy(&fake_md); + err = GRPC_ERROR_NONE; + } else { + err = GRPC_ERROR_REF(error); + } + if (s->recv_initial_md_op->payload->recv_initial_metadata + .trailing_metadata_available != nullptr) { + // Set to true unconditionally, because we're failing the call, so even + // if we haven't actually seen the send_trailing_metadata op from the + // other side, we're going to return trailing metadata anyway. + *s->recv_initial_md_op->payload->recv_initial_metadata + .trailing_metadata_available = true; + } + INPROC_LOG(GPR_INFO, + "fail_helper %p scheduling initial-metadata-ready %p %p", s, + error, err); + GRPC_CLOSURE_SCHED(s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata_ready, + err); + // Last use of err so no need to REF and then UNREF it + + complete_if_batch_end_locked( + s, error, s->recv_initial_md_op, + "fail_helper scheduling recv-initial-metadata-on-complete"); + s->recv_initial_md_op = nullptr; + } + if (s->recv_message_op) { + INPROC_LOG(GPR_INFO, "fail_helper %p scheduling message-ready %p", s, + error); + GRPC_CLOSURE_SCHED( + s->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_REF(error)); + complete_if_batch_end_locked( + s, error, s->recv_message_op, + "fail_helper scheduling recv-message-on-complete"); + s->recv_message_op = nullptr; + } + if (s->send_message_op) { + s->send_message_op->payload->send_message.send_message.reset(); + complete_if_batch_end_locked( + s, error, s->send_message_op, + "fail_helper scheduling send-message-on-complete"); + s->send_message_op = nullptr; + } + if (s->send_trailing_md_op) { + complete_if_batch_end_locked( + s, error, s->send_trailing_md_op, + "fail_helper scheduling send-trailng-md-on-complete"); + s->send_trailing_md_op = nullptr; + } + if (s->recv_trailing_md_op) { + INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-metadata-ready %p", + s, error); + GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_REF(error)); + INPROC_LOG(GPR_INFO, "fail_helper %p scheduling trailing-md-on-complete %p", + s, error); + complete_if_batch_end_locked( + s, error, s->recv_trailing_md_op, + "fail_helper scheduling recv-trailing-metadata-on-complete"); + s->recv_trailing_md_op = nullptr; + } + close_other_side_locked(s, "fail_helper:other_side"); + close_stream_locked(s); + + GRPC_ERROR_UNREF(error); +} + +// TODO(vjpai): It should not be necessary to drain the incoming byte +// stream and create a new one; instead, we should simply pass the byte +// stream from the sender directly to the receiver as-is. +// +// Note that fixing this will also avoid the assumption in this code +// that the incoming byte stream's next() call will always return +// synchronously. That assumption is true today but may not always be +// true in the future. +void message_transfer_locked(inproc_stream* sender, inproc_stream* receiver) { + size_t remaining = + sender->send_message_op->payload->send_message.send_message->length(); + if (receiver->recv_inited) { + grpc_slice_buffer_destroy_internal(&receiver->recv_message); + } + grpc_slice_buffer_init(&receiver->recv_message); + receiver->recv_inited = true; + do { + grpc_slice message_slice; + grpc_closure unused; + GPR_ASSERT( + sender->send_message_op->payload->send_message.send_message->Next( + SIZE_MAX, &unused)); + grpc_error* error = + sender->send_message_op->payload->send_message.send_message->Pull( + &message_slice); + if (error != GRPC_ERROR_NONE) { + cancel_stream_locked(sender, GRPC_ERROR_REF(error)); + break; + } + GPR_ASSERT(error == GRPC_ERROR_NONE); + remaining -= GRPC_SLICE_LENGTH(message_slice); + grpc_slice_buffer_add(&receiver->recv_message, message_slice); + } while (remaining > 0); + sender->send_message_op->payload->send_message.send_message.reset(); + + receiver->recv_stream.Init(&receiver->recv_message, 0); + receiver->recv_message_op->payload->recv_message.recv_message->reset( + receiver->recv_stream.get()); + INPROC_LOG(GPR_INFO, "message_transfer_locked %p scheduling message-ready", + receiver); + GRPC_CLOSURE_SCHED( + receiver->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + complete_if_batch_end_locked( + sender, GRPC_ERROR_NONE, sender->send_message_op, + "message_transfer scheduling sender on_complete"); + complete_if_batch_end_locked( + receiver, GRPC_ERROR_NONE, receiver->recv_message_op, + "message_transfer scheduling receiver on_complete"); + + receiver->recv_message_op = nullptr; + sender->send_message_op = nullptr; +} + +void op_state_machine(void* arg, grpc_error* error) { + // This function gets called when we have contents in the unprocessed reads + // Get what we want based on our ops wanted + // Schedule our appropriate closures + // and then return to ops_needed state if still needed + + // Since this is a closure directly invoked by the combiner, it should not + // unref the error parameter explicitly; the combiner will do that implicitly + grpc_error* new_err = GRPC_ERROR_NONE; + + bool needs_close = false; + + INPROC_LOG(GPR_INFO, "op_state_machine %p", arg); + inproc_stream* s = static_cast(arg); + gpr_mu* mu = &s->t->mu->mu; // keep aside in case s gets closed + gpr_mu_lock(mu); + s->op_closure_scheduled = false; + // cancellation takes precedence + inproc_stream* other = s->other_side; + + if (s->cancel_self_error != GRPC_ERROR_NONE) { + fail_helper_locked(s, GRPC_ERROR_REF(s->cancel_self_error)); + goto done; + } else if (s->cancel_other_error != GRPC_ERROR_NONE) { + fail_helper_locked(s, GRPC_ERROR_REF(s->cancel_other_error)); + goto done; + } else if (error != GRPC_ERROR_NONE) { + fail_helper_locked(s, GRPC_ERROR_REF(error)); + goto done; + } + + if (s->send_message_op && other) { + if (other->recv_message_op) { + message_transfer_locked(s, other); + maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE); + } else if (!s->t->is_client && s->trailing_md_sent) { + // A server send will never be matched if the server already sent status + s->send_message_op->payload->send_message.send_message.reset(); + complete_if_batch_end_locked( + s, GRPC_ERROR_NONE, s->send_message_op, + "op_state_machine scheduling send-message-on-complete"); + s->send_message_op = nullptr; + } + } + // Pause a send trailing metadata if there is still an outstanding + // send message unless we know that the send message will never get + // matched to a receive. This happens on the client if the server has + // already sent status or on the server if the client has requested + // status + if (s->send_trailing_md_op && + (!s->send_message_op || + (s->t->is_client && + (s->trailing_md_recvd || s->to_read_trailing_md_filled)) || + (!s->t->is_client && other && + (other->trailing_md_recvd || other->to_read_trailing_md_filled || + other->recv_trailing_md_op)))) { + grpc_metadata_batch* dest = (other == nullptr) + ? &s->write_buffer_trailing_md + : &other->to_read_trailing_md; + bool* destfilled = (other == nullptr) ? &s->write_buffer_trailing_md_filled + : &other->to_read_trailing_md_filled; + if (*destfilled || s->trailing_md_sent) { + // The buffer is already in use; that's an error! + INPROC_LOG(GPR_INFO, "Extra trailing metadata %p", s); + new_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra trailing metadata"); + fail_helper_locked(s, GRPC_ERROR_REF(new_err)); + goto done; + } else { + if (!other || !other->closed) { + fill_in_metadata(s, + s->send_trailing_md_op->payload->send_trailing_metadata + .send_trailing_metadata, + 0, dest, nullptr, destfilled); + } + s->trailing_md_sent = true; + if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) { + INPROC_LOG(GPR_INFO, + "op_state_machine %p scheduling trailing-metadata-ready", s); + GRPC_CLOSURE_SCHED( + s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_NONE); + INPROC_LOG(GPR_INFO, + "op_state_machine %p scheduling trailing-md-on-complete", s); + GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete, + GRPC_ERROR_NONE); + s->recv_trailing_md_op = nullptr; + needs_close = true; + } + } + maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE); + complete_if_batch_end_locked( + s, GRPC_ERROR_NONE, s->send_trailing_md_op, + "op_state_machine scheduling send-trailing-metadata-on-complete"); + s->send_trailing_md_op = nullptr; + } + if (s->recv_initial_md_op) { + if (s->initial_md_recvd) { + new_err = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd initial md"); + INPROC_LOG( + GPR_INFO, + "op_state_machine %p scheduling on_complete errors for already " + "recvd initial md %p", + s, new_err); + fail_helper_locked(s, GRPC_ERROR_REF(new_err)); + goto done; + } + + if (s->to_read_initial_md_filled) { + s->initial_md_recvd = true; + new_err = fill_in_metadata( + s, &s->to_read_initial_md, s->to_read_initial_md_flags, + s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata, + s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags, + nullptr); + s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata->deadline = s->deadline; + if (s->recv_initial_md_op->payload->recv_initial_metadata + .trailing_metadata_available != nullptr) { + *s->recv_initial_md_op->payload->recv_initial_metadata + .trailing_metadata_available = + (other != nullptr && other->send_trailing_md_op != nullptr); + } + grpc_metadata_batch_clear(&s->to_read_initial_md); + s->to_read_initial_md_filled = false; + INPROC_LOG(GPR_INFO, + "op_state_machine %p scheduling initial-metadata-ready %p", s, + new_err); + GRPC_CLOSURE_SCHED(s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata_ready, + GRPC_ERROR_REF(new_err)); + complete_if_batch_end_locked( + s, new_err, s->recv_initial_md_op, + "op_state_machine scheduling recv-initial-metadata-on-complete"); + s->recv_initial_md_op = nullptr; + + if (new_err != GRPC_ERROR_NONE) { + INPROC_LOG(GPR_INFO, + "op_state_machine %p scheduling on_complete errors2 %p", s, + new_err); + fail_helper_locked(s, GRPC_ERROR_REF(new_err)); + goto done; + } + } + } + if (s->recv_message_op) { + if (other && other->send_message_op) { + message_transfer_locked(other, s); + maybe_schedule_op_closure_locked(other, GRPC_ERROR_NONE); + } + } + if (s->to_read_trailing_md_filled) { + if (s->trailing_md_recvd) { + new_err = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd trailing md"); + INPROC_LOG( + GPR_INFO, + "op_state_machine %p scheduling on_complete errors for already " + "recvd trailing md %p", + s, new_err); + fail_helper_locked(s, GRPC_ERROR_REF(new_err)); + goto done; + } + if (s->recv_message_op != nullptr) { + // This message needs to be wrapped up because it will never be + // satisfied + *s->recv_message_op->payload->recv_message.recv_message = nullptr; + INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling message-ready", s); + GRPC_CLOSURE_SCHED( + s->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + complete_if_batch_end_locked( + s, new_err, s->recv_message_op, + "op_state_machine scheduling recv-message-on-complete"); + s->recv_message_op = nullptr; + } + if ((s->trailing_md_sent || s->t->is_client) && s->send_message_op) { + // Nothing further will try to receive from this stream, so finish off + // any outstanding send_message op + s->send_message_op->payload->send_message.send_message.reset(); + complete_if_batch_end_locked( + s, new_err, s->send_message_op, + "op_state_machine scheduling send-message-on-complete"); + s->send_message_op = nullptr; + } + if (s->recv_trailing_md_op != nullptr) { + // We wanted trailing metadata and we got it + s->trailing_md_recvd = true; + new_err = + fill_in_metadata(s, &s->to_read_trailing_md, 0, + s->recv_trailing_md_op->payload + ->recv_trailing_metadata.recv_trailing_metadata, + nullptr, nullptr); + grpc_metadata_batch_clear(&s->to_read_trailing_md); + s->to_read_trailing_md_filled = false; + + // We should schedule the recv_trailing_md_op completion if + // 1. this stream is the client-side + // 2. this stream is the server-side AND has already sent its trailing md + // (If the server hasn't already sent its trailing md, it doesn't have + // a final status, so don't mark this op complete) + if (s->t->is_client || s->trailing_md_sent) { + INPROC_LOG(GPR_INFO, + "op_state_machine %p scheduling trailing-md-on-complete %p", + s, new_err); + GRPC_CLOSURE_SCHED( + s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_REF(new_err)); + GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->on_complete, + GRPC_ERROR_REF(new_err)); + s->recv_trailing_md_op = nullptr; + needs_close = true; + } else { + INPROC_LOG(GPR_INFO, + "op_state_machine %p server needs to delay handling " + "trailing-md-on-complete %p", + s, new_err); + } + } else { + INPROC_LOG( + GPR_INFO, + "op_state_machine %p has trailing md but not yet waiting for it", s); + } + } + if (s->trailing_md_recvd && s->recv_message_op) { + // No further message will come on this stream, so finish off the + // recv_message_op + INPROC_LOG(GPR_INFO, "op_state_machine %p scheduling message-ready", s); + *s->recv_message_op->payload->recv_message.recv_message = nullptr; + GRPC_CLOSURE_SCHED( + s->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + complete_if_batch_end_locked( + s, new_err, s->recv_message_op, + "op_state_machine scheduling recv-message-on-complete"); + s->recv_message_op = nullptr; + } + if (s->trailing_md_recvd && (s->trailing_md_sent || s->t->is_client) && + s->send_message_op) { + // Nothing further will try to receive from this stream, so finish off + // any outstanding send_message op + s->send_message_op->payload->send_message.send_message.reset(); + complete_if_batch_end_locked( + s, new_err, s->send_message_op, + "op_state_machine scheduling send-message-on-complete"); + s->send_message_op = nullptr; + } + if (s->send_message_op || s->send_trailing_md_op || s->recv_initial_md_op || + s->recv_message_op || s->recv_trailing_md_op) { + // Didn't get the item we wanted so we still need to get + // rescheduled + INPROC_LOG( + GPR_INFO, "op_state_machine %p still needs closure %p %p %p %p %p", s, + s->send_message_op, s->send_trailing_md_op, s->recv_initial_md_op, + s->recv_message_op, s->recv_trailing_md_op); + s->ops_needed = true; + } +done: + if (needs_close) { + close_other_side_locked(s, "op_state_machine"); + close_stream_locked(s); + } + gpr_mu_unlock(mu); + GRPC_ERROR_UNREF(new_err); +} + +bool cancel_stream_locked(inproc_stream* s, grpc_error* error) { + bool ret = false; // was the cancel accepted + INPROC_LOG(GPR_INFO, "cancel_stream %p with %s", s, grpc_error_string(error)); + if (s->cancel_self_error == GRPC_ERROR_NONE) { + ret = true; + s->cancel_self_error = GRPC_ERROR_REF(error); + maybe_schedule_op_closure_locked(s, s->cancel_self_error); + // Send trailing md to the other side indicating cancellation, even if we + // already have + s->trailing_md_sent = true; + + grpc_metadata_batch cancel_md; + grpc_metadata_batch_init(&cancel_md); + + inproc_stream* other = s->other_side; + grpc_metadata_batch* dest = (other == nullptr) + ? &s->write_buffer_trailing_md + : &other->to_read_trailing_md; + bool* destfilled = (other == nullptr) ? &s->write_buffer_trailing_md_filled + : &other->to_read_trailing_md_filled; + fill_in_metadata(s, &cancel_md, 0, dest, nullptr, destfilled); + grpc_metadata_batch_destroy(&cancel_md); + + if (other != nullptr) { + if (other->cancel_other_error == GRPC_ERROR_NONE) { + other->cancel_other_error = GRPC_ERROR_REF(s->cancel_self_error); + } + maybe_schedule_op_closure_locked(other, other->cancel_other_error); + } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) { + s->write_buffer_cancel_error = GRPC_ERROR_REF(s->cancel_self_error); + } + + // if we are a server and already received trailing md but + // couldn't complete that because we hadn't yet sent out trailing + // md, now's the chance + if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) { + GRPC_CLOSURE_SCHED(s->recv_trailing_md_op->payload->recv_trailing_metadata + .recv_trailing_metadata_ready, + GRPC_ERROR_REF(s->cancel_self_error)); + complete_if_batch_end_locked( + s, s->cancel_self_error, s->recv_trailing_md_op, + "cancel_stream scheduling trailing-md-on-complete"); + s->recv_trailing_md_op = nullptr; + } + } + + close_other_side_locked(s, "cancel_stream:other_side"); + close_stream_locked(s); + + GRPC_ERROR_UNREF(error); + return ret; +} + +void do_nothing(void* arg, grpc_error* error) {} + +void perform_stream_op(grpc_transport* gt, grpc_stream* gs, + grpc_transport_stream_op_batch* op) { + INPROC_LOG(GPR_INFO, "perform_stream_op %p %p %p", gt, gs, op); + inproc_stream* s = reinterpret_cast(gs); + gpr_mu* mu = &s->t->mu->mu; // save aside in case s gets closed + gpr_mu_lock(mu); + + if (grpc_inproc_trace.enabled()) { + if (op->send_initial_metadata) { + log_metadata(op->payload->send_initial_metadata.send_initial_metadata, + s->t->is_client, true); + } + if (op->send_trailing_metadata) { + log_metadata(op->payload->send_trailing_metadata.send_trailing_metadata, + s->t->is_client, false); + } + } + grpc_error* error = GRPC_ERROR_NONE; + grpc_closure* on_complete = op->on_complete; + // TODO(roth): This is a hack needed because we use data inside of the + // closure itself to do the barrier calculation (i.e., to ensure that + // we don't schedule the closure until all ops in the batch have been + // completed). This can go away once we move to a new C++ closure API + // that provides the ability to create a barrier closure. + if (on_complete == nullptr) { + on_complete = GRPC_CLOSURE_INIT(&op->handler_private.closure, do_nothing, + nullptr, grpc_schedule_on_exec_ctx); + } + + if (op->cancel_stream) { + // Call cancel_stream_locked without ref'ing the cancel_error because + // this function is responsible to make sure that that field gets unref'ed + cancel_stream_locked(s, op->payload->cancel_stream.cancel_error); + // this op can complete without an error + } else if (s->cancel_self_error != GRPC_ERROR_NONE) { + // already self-canceled so still give it an error + error = GRPC_ERROR_REF(s->cancel_self_error); + } else { + INPROC_LOG(GPR_INFO, "perform_stream_op %p %s%s%s%s%s%s%s", s, + s->t->is_client ? "client" : "server", + op->send_initial_metadata ? " send_initial_metadata" : "", + op->send_message ? " send_message" : "", + op->send_trailing_metadata ? " send_trailing_metadata" : "", + op->recv_initial_metadata ? " recv_initial_metadata" : "", + op->recv_message ? " recv_message" : "", + op->recv_trailing_metadata ? " recv_trailing_metadata" : ""); + } + + bool needs_close = false; + + inproc_stream* other = s->other_side; + if (error == GRPC_ERROR_NONE && + (op->send_initial_metadata || op->send_trailing_metadata)) { + if (s->t->is_closed) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Endpoint already shutdown"); + } + if (error == GRPC_ERROR_NONE && op->send_initial_metadata) { + grpc_metadata_batch* dest = (other == nullptr) + ? &s->write_buffer_initial_md + : &other->to_read_initial_md; + uint32_t* destflags = (other == nullptr) + ? &s->write_buffer_initial_md_flags + : &other->to_read_initial_md_flags; + bool* destfilled = (other == nullptr) ? &s->write_buffer_initial_md_filled + : &other->to_read_initial_md_filled; + if (*destfilled || s->initial_md_sent) { + // The buffer is already in use; that's an error! + INPROC_LOG(GPR_INFO, "Extra initial metadata %p", s); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra initial metadata"); + } else { + if (!other || !other->closed) { + fill_in_metadata( + s, op->payload->send_initial_metadata.send_initial_metadata, + op->payload->send_initial_metadata.send_initial_metadata_flags, + dest, destflags, destfilled); + } + if (s->t->is_client) { + grpc_millis* dl = + (other == nullptr) ? &s->write_buffer_deadline : &other->deadline; + *dl = GPR_MIN(*dl, op->payload->send_initial_metadata + .send_initial_metadata->deadline); + s->initial_md_sent = true; + } + } + maybe_schedule_op_closure_locked(other, error); + } + } + + if (error == GRPC_ERROR_NONE && + (op->send_message || op->send_trailing_metadata || + op->recv_initial_metadata || op->recv_message || + op->recv_trailing_metadata)) { + // Mark ops that need to be processed by the closure + if (op->send_message) { + s->send_message_op = op; + } + if (op->send_trailing_metadata) { + s->send_trailing_md_op = op; + } + if (op->recv_initial_metadata) { + s->recv_initial_md_op = op; + } + if (op->recv_message) { + s->recv_message_op = op; + } + if (op->recv_trailing_metadata) { + s->recv_trailing_md_op = op; + } + + // We want to initiate the closure if: + // 1. We want to send a message and the other side wants to receive + // 2. We want to send trailing metadata and there isn't an unmatched send + // or the other side wants trailing metadata + // 3. We want initial metadata and the other side has sent it + // 4. We want to receive a message and there is a message ready + // 5. There is trailing metadata, even if nothing specifically wants + // that because that can shut down the receive message as well + if ((op->send_message && other && other->recv_message_op != nullptr) || + (op->send_trailing_metadata && + (!s->send_message_op || (other && other->recv_trailing_md_op))) || + (op->recv_initial_metadata && s->to_read_initial_md_filled) || + (op->recv_message && other && other->send_message_op != nullptr) || + (s->to_read_trailing_md_filled || s->trailing_md_recvd)) { + if (!s->op_closure_scheduled) { + GRPC_CLOSURE_SCHED(&s->op_closure, GRPC_ERROR_NONE); + s->op_closure_scheduled = true; + } + } else { + s->ops_needed = true; + } + } else { + if (error != GRPC_ERROR_NONE) { + // Schedule op's closures that we didn't push to op state machine + if (op->recv_initial_metadata) { + if (op->payload->recv_initial_metadata.trailing_metadata_available != + nullptr) { + // Set to true unconditionally, because we're failing the call, so + // even if we haven't actually seen the send_trailing_metadata op + // from the other side, we're going to return trailing metadata + // anyway. + *op->payload->recv_initial_metadata.trailing_metadata_available = + true; + } + INPROC_LOG( + GPR_INFO, + "perform_stream_op error %p scheduling initial-metadata-ready %p", + s, error); + GRPC_CLOSURE_SCHED( + op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); + } + if (op->recv_message) { + INPROC_LOG( + GPR_INFO, + "perform_stream_op error %p scheduling recv message-ready %p", s, + error); + GRPC_CLOSURE_SCHED(op->payload->recv_message.recv_message_ready, + GRPC_ERROR_REF(error)); + } + if (op->recv_trailing_metadata) { + INPROC_LOG( + GPR_INFO, + "perform_stream_op error %p scheduling trailing-metadata-ready %p", + s, error); + GRPC_CLOSURE_SCHED( + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + GRPC_ERROR_REF(error)); + } + } + INPROC_LOG(GPR_INFO, "perform_stream_op %p scheduling on_complete %p", s, + error); + GRPC_CLOSURE_SCHED(on_complete, GRPC_ERROR_REF(error)); + } + if (needs_close) { + close_other_side_locked(s, "perform_stream_op:other_side"); + close_stream_locked(s); + } + gpr_mu_unlock(mu); + GRPC_ERROR_UNREF(error); +} + +void close_transport_locked(inproc_transport* t) { + INPROC_LOG(GPR_INFO, "close_transport %p %d", t, t->is_closed); + grpc_connectivity_state_set( + &t->connectivity, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Closing transport."), + "close transport"); + if (!t->is_closed) { + t->is_closed = true; + /* Also end all streams on this transport */ + while (t->stream_list != nullptr) { + // cancel_stream_locked also adjusts stream list + cancel_stream_locked( + t->stream_list, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } + } +} + +void perform_transport_op(grpc_transport* gt, grpc_transport_op* op) { + inproc_transport* t = reinterpret_cast(gt); + INPROC_LOG(GPR_INFO, "perform_transport_op %p %p", t, op); + gpr_mu_lock(&t->mu->mu); + if (op->on_connectivity_state_change) { + grpc_connectivity_state_notify_on_state_change( + &t->connectivity, op->connectivity_state, + op->on_connectivity_state_change); + } + if (op->set_accept_stream) { + t->accept_stream_cb = op->set_accept_stream_fn; + t->accept_stream_data = op->set_accept_stream_user_data; + } + if (op->on_consumed) { + GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE); + } + + bool do_close = false; + if (op->goaway_error != GRPC_ERROR_NONE) { + do_close = true; + GRPC_ERROR_UNREF(op->goaway_error); + } + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + do_close = true; + GRPC_ERROR_UNREF(op->disconnect_with_error); + } + + if (do_close) { + close_transport_locked(t); + } + gpr_mu_unlock(&t->mu->mu); +} + +void destroy_stream(grpc_transport* gt, grpc_stream* gs, + grpc_closure* then_schedule_closure) { + INPROC_LOG(GPR_INFO, "destroy_stream %p %p", gs, then_schedule_closure); + inproc_stream* s = reinterpret_cast(gs); + s->closure_at_destroy = then_schedule_closure; + s->~inproc_stream(); +} + +void destroy_transport(grpc_transport* gt) { + inproc_transport* t = reinterpret_cast(gt); + INPROC_LOG(GPR_INFO, "destroy_transport %p", t); + gpr_mu_lock(&t->mu->mu); + close_transport_locked(t); + gpr_mu_unlock(&t->mu->mu); + t->other_side->unref(); + t->unref(); +} + +/******************************************************************************* + * INTEGRATION GLUE + */ + +void set_pollset(grpc_transport* gt, grpc_stream* gs, grpc_pollset* pollset) { + // Nothing to do here +} + +void set_pollset_set(grpc_transport* gt, grpc_stream* gs, + grpc_pollset_set* pollset_set) { + // Nothing to do here +} + +grpc_endpoint* get_endpoint(grpc_transport* t) { return nullptr; } + +const grpc_transport_vtable inproc_vtable = { + sizeof(inproc_stream), "inproc", init_stream, + set_pollset, set_pollset_set, perform_stream_op, + perform_transport_op, destroy_stream, destroy_transport, + get_endpoint}; + +/******************************************************************************* + * Main inproc transport functions + */ +void inproc_transports_create(grpc_transport** server_transport, + const grpc_channel_args* server_args, + grpc_transport** client_transport, + const grpc_channel_args* client_args) { + INPROC_LOG(GPR_INFO, "inproc_transports_create"); + shared_mu* mu = new (gpr_malloc(sizeof(*mu))) shared_mu(); + inproc_transport* st = new (gpr_malloc(sizeof(*st))) + inproc_transport(&inproc_vtable, mu, /*is_client=*/false); + inproc_transport* ct = new (gpr_malloc(sizeof(*ct))) + inproc_transport(&inproc_vtable, mu, /*is_client=*/true); + st->other_side = ct; + ct->other_side = st; + *server_transport = reinterpret_cast(st); + *client_transport = reinterpret_cast(ct); +} +} // namespace + +/******************************************************************************* + * GLOBAL INIT AND DESTROY + */ +void grpc_inproc_transport_init(void) { + grpc_core::ExecCtx exec_ctx; + g_empty_slice = grpc_slice_from_static_buffer(nullptr, 0); + + grpc_slice key_tmp = grpc_slice_from_static_string(":path"); + g_fake_path_key = grpc_slice_intern(key_tmp); + grpc_slice_unref_internal(key_tmp); + + g_fake_path_value = grpc_slice_from_static_string("/"); + + grpc_slice auth_tmp = grpc_slice_from_static_string(":authority"); + g_fake_auth_key = grpc_slice_intern(auth_tmp); + grpc_slice_unref_internal(auth_tmp); + + g_fake_auth_value = grpc_slice_from_static_string("inproc-fail"); +} + +grpc_channel* grpc_inproc_channel_create(grpc_server* server, + grpc_channel_args* args, + void* reserved) { + GRPC_API_TRACE("grpc_inproc_channel_create(server=%p, args=%p)", 2, + (server, args)); + + grpc_core::ExecCtx exec_ctx; + + const grpc_channel_args* server_args = grpc_server_get_channel_args(server); + + // Add a default authority channel argument for the client + + grpc_arg default_authority_arg; + default_authority_arg.type = GRPC_ARG_STRING; + default_authority_arg.key = (char*)GRPC_ARG_DEFAULT_AUTHORITY; + default_authority_arg.value.string = (char*)"inproc.authority"; + grpc_channel_args* client_args = + grpc_channel_args_copy_and_add(args, &default_authority_arg, 1); + + grpc_transport* server_transport; + grpc_transport* client_transport; + inproc_transports_create(&server_transport, server_args, &client_transport, + client_args); + + // TODO(ncteisen): design and support channelz GetSocket for inproc. + grpc_server_setup_transport(server, server_transport, nullptr, server_args, + nullptr); + grpc_channel* channel = grpc_channel_create( + "inproc", client_args, GRPC_CLIENT_DIRECT_CHANNEL, client_transport); + + // Free up created channel args + grpc_channel_args_destroy(client_args); + + // Now finish scheduled operations + + return channel; +} + +void grpc_inproc_transport_shutdown(void) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_unref_internal(g_empty_slice); + grpc_slice_unref_internal(g_fake_path_key); + grpc_slice_unref_internal(g_fake_path_value); + grpc_slice_unref_internal(g_fake_auth_key); + grpc_slice_unref_internal(g_fake_auth_value); +} diff --git a/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.h b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.h new file mode 100644 index 00000000000000..049d1402afeffd --- /dev/null +++ b/third_party/grpc/src/core/ext/transport/inproc/inproc_transport.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H +#define GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H + +#include + +#include "src/core/lib/transport/transport_impl.h" + +grpc_channel* grpc_inproc_channel_create(grpc_server* server, + grpc_channel_args* args, + void* reserved); + +extern grpc_core::TraceFlag grpc_inproc_trace; + +void grpc_inproc_transport_init(void); +void grpc_inproc_transport_shutdown(void); + +#endif /* GRPC_CORE_EXT_TRANSPORT_INPROC_INPROC_TRANSPORT_H */ diff --git a/third_party/grpc/src/core/httpcli/format_request.c b/third_party/grpc/src/core/httpcli/format_request.c deleted file mode 100644 index 04f2a2d99a7a3c..00000000000000 --- a/third_party/grpc/src/core/httpcli/format_request.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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) { - uint8_t 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/third_party/grpc/src/core/httpcli/format_request.h b/third_party/grpc/src/core/httpcli/format_request.h deleted file mode 100644 index c8dc8f7d4ee06f..00000000000000 --- a/third_party/grpc/src/core/httpcli/format_request.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/httpcli/httpcli.c b/third_party/grpc/src/core/httpcli/httpcli.c deleted file mode 100644 index 71237bb6140642..00000000000000 --- a/third_party/grpc/src/core/httpcli/httpcli.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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; - char *ssl_host_override; - 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_closure on_read; - grpc_closure done_write; - grpc_closure connected; -} internal_request; - -static grpc_httpcli_get_override g_get_override = NULL; -static grpc_httpcli_post_override g_post_override = NULL; - -static void plaintext_handshake(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *endpoint, const char *host, - void (*on_done)(grpc_exec_ctx *exec_ctx, - void *arg, - grpc_endpoint *endpoint)) { - on_done(exec_ctx, 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(grpc_exec_ctx *exec_ctx, internal_request *req); - -static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, - int success) { - grpc_pollset_set_del_pollset(exec_ctx, &req->context->pollset_set, - req->pollset); - req->on_response(exec_ctx, 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(exec_ctx, req->ep); - } - gpr_slice_unref(req->request_text); - gpr_free(req->host); - gpr_free(req->ssl_host_override); - 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(grpc_exec_ctx *exec_ctx, void *user_data, bool success); - -static void do_read(grpc_exec_ctx *exec_ctx, internal_request *req) { - grpc_endpoint_read(exec_ctx, req->ep, &req->incoming, &req->on_read); -} - -static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool 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(exec_ctx, req, 0); - return; - } - } - } - - if (success) { - do_read(exec_ctx, req); - } else if (!req->have_read_byte) { - next_address(exec_ctx, req); - } else { - finish(exec_ctx, req, grpc_httpcli_parser_eof(&req->parser)); - } -} - -static void on_written(grpc_exec_ctx *exec_ctx, internal_request *req) { - do_read(exec_ctx, req); -} - -static void done_write(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - internal_request *req = arg; - if (success) { - on_written(exec_ctx, req); - } else { - next_address(exec_ctx, req); - } -} - -static void start_write(grpc_exec_ctx *exec_ctx, internal_request *req) { - gpr_slice_ref(req->request_text); - gpr_slice_buffer_add(&req->outgoing, req->request_text); - grpc_endpoint_write(exec_ctx, req->ep, &req->outgoing, &req->done_write); -} - -static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *ep) { - internal_request *req = arg; - - if (!ep) { - next_address(exec_ctx, req); - return; - } - - req->ep = ep; - start_write(exec_ctx, req); -} - -static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - internal_request *req = arg; - - if (!req->ep) { - next_address(exec_ctx, req); - return; - } - req->handshaker->handshake( - exec_ctx, req, req->ep, - req->ssl_host_override ? req->ssl_host_override : req->host, - on_handshake_done); -} - -static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req) { - grpc_resolved_address *addr; - if (req->next_address == req->addresses->naddrs) { - finish(exec_ctx, req, 0); - return; - } - addr = &req->addresses->addrs[req->next_address++]; - grpc_closure_init(&req->connected, on_connected, req); - grpc_tcp_client_connect( - exec_ctx, &req->connected, &req->ep, &req->context->pollset_set, - (struct sockaddr *)&addr->addr, addr->len, req->deadline); -} - -static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, - grpc_resolved_addresses *addresses) { - internal_request *req = arg; - if (!addresses) { - finish(exec_ctx, req, 0); - return; - } - req->addresses = addresses; - req->next_address = 0; - next_address(exec_ctx, req); -} - -static void internal_request_begin( - grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, - grpc_pollset *pollset, const grpc_httpcli_request *request, - gpr_timespec deadline, grpc_httpcli_response_cb on_response, - void *user_data, const char *name, gpr_slice request_text) { - internal_request *req = gpr_malloc(sizeof(internal_request)); - memset(req, 0, sizeof(*req)); - req->request_text = request_text; - 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_closure_init(&req->on_read, on_read, req); - grpc_closure_init(&req->done_write, done_write, req); - gpr_slice_buffer_init(&req->incoming); - gpr_slice_buffer_init(&req->outgoing); - grpc_iomgr_register_object(&req->iomgr_obj, name); - req->host = gpr_strdup(request->host); - req->ssl_host_override = gpr_strdup(request->ssl_host_override); - - grpc_pollset_set_add_pollset(exec_ctx, &req->context->pollset_set, - req->pollset); - grpc_resolve_address(request->host, req->handshaker->default_port, - on_resolved, req); -} - -void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, - grpc_pollset *pollset, - const grpc_httpcli_request *request, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, void *user_data) { - char *name; - if (g_get_override && - g_get_override(exec_ctx, request, deadline, on_response, user_data)) { - return; - } - gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->path); - internal_request_begin(exec_ctx, context, pollset, request, deadline, - on_response, user_data, name, - grpc_httpcli_format_get_request(request)); - gpr_free(name); -} - -void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, 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) { - char *name; - if (g_post_override && - g_post_override(exec_ctx, request, body_bytes, body_size, deadline, - on_response, user_data)) { - return; - } - gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->path); - internal_request_begin( - exec_ctx, context, pollset, request, deadline, on_response, user_data, - name, grpc_httpcli_format_post_request(request, body_bytes, body_size)); - gpr_free(name); -} - -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/third_party/grpc/src/core/httpcli/httpcli.h b/third_party/grpc/src/core/httpcli/httpcli.h deleted file mode 100644 index 30875d71f13b77..00000000000000 --- a/third_party/grpc/src/core/httpcli/httpcli.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint, - const char *host, - void (*on_done)(grpc_exec_ctx *exec_ctx, 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 host to verify in the SSL handshake (or NULL) */ - char *ssl_host_override; - /* 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)(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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)(grpc_exec_ctx *exec_ctx, - const grpc_httpcli_request *request, - gpr_timespec deadline, - grpc_httpcli_response_cb on_response, - void *user_data); -typedef int (*grpc_httpcli_post_override)( - grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/httpcli/httpcli_security_connector.c b/third_party/grpc/src/core/httpcli/httpcli_security_connector.c deleted file mode 100644 index 41ad1de6c00dba..00000000000000 --- a/third_party/grpc/src/core/httpcli/httpcli_security_connector.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/httpcli/httpcli.h" - -#include - -#include "src/core/security/handshake.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 void httpcli_ssl_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data) { - grpc_httpcli_ssl_channel_security_connector *c = - (grpc_httpcli_ssl_channel_security_connector *)sc; - tsi_result result = TSI_OK; - tsi_handshaker *handshaker; - if (c->handshaker_factory == NULL) { - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL); - return; - } - 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)); - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL); - } else { - grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, - user_data); - } -} - -static void httpcli_ssl_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_security_peer_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; - } - cb(exec_ctx, user_data, status, NULL); - tsi_peer_destruct(&peer); -} - -static grpc_security_connector_vtable httpcli_ssl_vtable = { - httpcli_ssl_destroy, httpcli_ssl_do_handshake, 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)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint); - void *arg; -} on_done_closure; - -static void on_secure_transport_setup_done(grpc_exec_ctx *exec_ctx, void *rp, - grpc_security_status status, - grpc_endpoint *secure_endpoint, - grpc_auth_context *auth_context) { - on_done_closure *c = rp; - if (status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); - c->func(exec_ctx, c->arg, NULL); - } else { - c->func(exec_ctx, c->arg, secure_endpoint); - } - gpr_free(c); -} - -static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *tcp, const char *host, - void (*on_done)(grpc_exec_ctx *exec_ctx, 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(exec_ctx, 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_security_connector_do_handshake(exec_ctx, &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/third_party/grpc/src/core/httpcli/parser.c b/third_party/grpc/src/core/httpcli/parser.c deleted file mode 100644 index c314f025a001cc..00000000000000 --- a/third_party/grpc/src/core/httpcli/parser.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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) { - uint8_t *beg = parser->cur_line; - uint8_t *cur = beg; - uint8_t *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) { - uint8_t *beg = parser->cur_line; - uint8_t *cur = beg; - uint8_t *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; - } - GPR_ASSERT(cur >= beg); - hdr.key = buf2str(beg, (size_t)(cur - beg)); - cur++; /* skip : */ - - while (cur != end && (*cur == ' ' || *cur == '\t')) { - cur++; - } - GPR_ASSERT(end - cur >= 2); - hdr.value = buf2str(cur, (size_t)(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_UNREACHABLE_CODE(return 0); - } - - parser->cur_line_length = 0; - return 1; -} - -static int addbyte(grpc_httpcli_parser *parser, uint8_t 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_UNREACHABLE_CODE(return 0); - 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); - } - parser->r.body[parser->r.body_length] = (char)byte; - parser->r.body_length++; - return 1; - } - GPR_UNREACHABLE_CODE(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/third_party/grpc/src/core/httpcli/parser.h b/third_party/grpc/src/core/httpcli/parser.h deleted file mode 100644 index cd0383637fff9e..00000000000000 --- a/third_party/grpc/src/core/httpcli/parser.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - - uint8_t 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/third_party/grpc/src/core/iomgr/closure.c b/third_party/grpc/src/core/iomgr/closure.c deleted file mode 100644 index 3a96f7385f73b2..00000000000000 --- a/third_party/grpc/src/core/iomgr/closure.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/iomgr/closure.h" - -#include - -void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, - void *cb_arg) { - closure->cb = cb; - closure->cb_arg = cb_arg; - closure->final_data = 0; -} - -void grpc_closure_list_add(grpc_closure_list *closure_list, - grpc_closure *closure, bool success) { - if (closure == NULL) return; - closure->final_data = (success != 0); - if (closure_list->head == NULL) { - closure_list->head = closure; - } else { - closure_list->tail->final_data |= (uintptr_t)closure; - } - closure_list->tail = closure; -} - -bool grpc_closure_list_empty(grpc_closure_list closure_list) { - return closure_list.head == NULL; -} - -void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst) { - if (src->head == NULL) { - return; - } - if (dst->head == NULL) { - *dst = *src; - } else { - dst->tail->final_data |= (uintptr_t)src->head; - dst->tail = src->tail; - } - src->head = src->tail = NULL; -} - -typedef struct { - grpc_iomgr_cb_func cb; - void *cb_arg; - grpc_closure wrapper; -} wrapped_closure; - -static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - wrapped_closure *wc = arg; - grpc_iomgr_cb_func cb = wc->cb; - void *cb_arg = wc->cb_arg; - gpr_free(wc); - cb(exec_ctx, cb_arg, success); -} - -grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg) { - wrapped_closure *wc = gpr_malloc(sizeof(*wc)); - wc->cb = cb; - wc->cb_arg = cb_arg; - grpc_closure_init(&wc->wrapper, closure_wrapper, wc); - return &wc->wrapper; -} - -grpc_closure *grpc_closure_next(grpc_closure *closure) { - return (grpc_closure *)(closure->final_data & ~(uintptr_t)1); -} diff --git a/third_party/grpc/src/core/iomgr/closure.h b/third_party/grpc/src/core/iomgr/closure.h deleted file mode 100644 index ea96c19c71b235..00000000000000 --- a/third_party/grpc/src/core/iomgr/closure.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_IOMGR_CLOSURE_H -#define GRPC_INTERNAL_CORE_IOMGR_CLOSURE_H - -#include -#include - -struct grpc_closure; -typedef struct grpc_closure grpc_closure; - -/* forward declaration for exec_ctx.h */ -struct grpc_exec_ctx; -typedef struct grpc_exec_ctx grpc_exec_ctx; - -typedef struct grpc_closure_list { - grpc_closure *head; - grpc_closure *tail; -} grpc_closure_list; - -/** 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)(grpc_exec_ctx *exec_ctx, void *arg, - bool success); - -/** A closure over a grpc_iomgr_cb_func. */ -struct grpc_closure { - /** Bound callback. */ - grpc_iomgr_cb_func cb; - - /** Arguments to be passed to "cb". */ - void *cb_arg; - - /** Once enqueued, contains in the lower bit the success of the closure, - and in the upper bits the pointer to the next closure in the list. - Before enqueing for execution, this is usable for scratch data. */ - uintptr_t final_data; -}; - -/** Initializes \a closure with \a cb and \a cb_arg. */ -void grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, - void *cb_arg); - -/* Create a heap allocated closure: try to avoid except for very rare events */ -grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg); - -#define GRPC_CLOSURE_LIST_INIT \ - { NULL, NULL } - -/** add \a closure to the end of \a list and set \a closure's success to \a - * success */ -void grpc_closure_list_add(grpc_closure_list *list, grpc_closure *closure, - bool success); - -/** append all closures from \a src to \a dst and empty \a src. */ -void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst); - -/** return whether \a list is empty. */ -bool grpc_closure_list_empty(grpc_closure_list list); - -/** return the next pointer for a queued closure list */ -grpc_closure *grpc_closure_next(grpc_closure *closure); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_CLOSURE_H */ diff --git a/third_party/grpc/src/core/iomgr/endpoint.c b/third_party/grpc/src/core/iomgr/endpoint.c deleted file mode 100644 index bd64707669cad9..00000000000000 --- a/third_party/grpc/src/core/iomgr/endpoint.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" - -void grpc_endpoint_read(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, - gpr_slice_buffer* slices, grpc_closure* cb) { - ep->vtable->read(exec_ctx, ep, slices, cb); -} - -void grpc_endpoint_write(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, - gpr_slice_buffer* slices, grpc_closure* cb) { - ep->vtable->write(exec_ctx, ep, slices, cb); -} - -void grpc_endpoint_add_to_pollset(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, - grpc_pollset* pollset) { - ep->vtable->add_to_pollset(exec_ctx, ep, pollset); -} - -void grpc_endpoint_add_to_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_endpoint* ep, - grpc_pollset_set* pollset_set) { - ep->vtable->add_to_pollset_set(exec_ctx, ep, pollset_set); -} - -void grpc_endpoint_shutdown(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep) { - ep->vtable->shutdown(exec_ctx, ep); -} - -void grpc_endpoint_destroy(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep) { - ep->vtable->destroy(exec_ctx, ep); -} - -char* grpc_endpoint_get_peer(grpc_endpoint* ep) { - return ep->vtable->get_peer(ep); -} diff --git a/third_party/grpc/src/core/iomgr/endpoint.h b/third_party/grpc/src/core/iomgr/endpoint.h deleted file mode 100644 index cbdc947abbfdd0..00000000000000 --- a/third_party/grpc/src/core/iomgr/endpoint.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - -struct grpc_endpoint_vtable { - void (*read)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *slices, grpc_closure *cb); - void (*write)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *slices, grpc_closure *cb); - void (*add_to_pollset)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset *pollset); - void (*add_to_pollset_set)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset_set *pollset); - void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep); - void (*destroy)(grpc_exec_ctx *exec_ctx, 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. */ -void grpc_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *slices, grpc_closure *cb); - -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. - */ -void grpc_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *slices, grpc_closure *cb); - -/* Causes any pending read/write callbacks to run immediately with - success==0 */ -void grpc_endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep); -void grpc_endpoint_destroy(grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset *pollset); -void grpc_endpoint_add_to_pollset_set(grpc_exec_ctx *exec_ctx, - 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/third_party/grpc/src/core/iomgr/endpoint_pair.h b/third_party/grpc/src/core/iomgr/endpoint_pair.h deleted file mode 100644 index 095ec5fcc9fc3f..00000000000000 --- a/third_party/grpc/src/core/iomgr/endpoint_pair.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/endpoint_pair_posix.c b/third_party/grpc/src/core/iomgr/endpoint_pair_posix.c deleted file mode 100644 index 56f6f146fd34ad..00000000000000 --- a/third_party/grpc/src/core/iomgr/endpoint_pair_posix.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/iomgr/socket_utils_posix.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); - GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0])); - GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1])); -} - -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/third_party/grpc/src/core/iomgr/endpoint_pair_windows.c b/third_party/grpc/src/core/iomgr/endpoint_pair_windows.c deleted file mode 100644 index db9d092dcab616..00000000000000 --- a/third_party/grpc/src/core/iomgr/endpoint_pair_windows.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/exec_ctx.c b/third_party/grpc/src/core/iomgr/exec_ctx.c deleted file mode 100644 index 1fd79f6ebaccdb..00000000000000 --- a/third_party/grpc/src/core/iomgr/exec_ctx.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/iomgr/exec_ctx.h" - -#include - -#include "src/core/profiling/timers.h" - -bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) { - bool did_something = 0; - GPR_TIMER_BEGIN("grpc_exec_ctx_flush", 0); - while (!grpc_closure_list_empty(exec_ctx->closure_list)) { - grpc_closure *c = exec_ctx->closure_list.head; - exec_ctx->closure_list.head = exec_ctx->closure_list.tail = NULL; - while (c != NULL) { - bool success = (bool)(c->final_data & 1); - grpc_closure *next = (grpc_closure *)(c->final_data & ~(uintptr_t)1); - did_something = true; - GPR_TIMER_BEGIN("grpc_exec_ctx_flush.cb", 0); - c->cb(exec_ctx, c->cb_arg, success); - GPR_TIMER_END("grpc_exec_ctx_flush.cb", 0); - c = next; - } - } - GPR_TIMER_END("grpc_exec_ctx_flush", 0); - return did_something; -} - -void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) { - grpc_exec_ctx_flush(exec_ctx); -} - -void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - bool success, - grpc_workqueue *offload_target_or_null) { - GPR_ASSERT(offload_target_or_null == NULL); - grpc_closure_list_add(&exec_ctx->closure_list, closure, success); -} - -void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx, - grpc_closure_list *list, - grpc_workqueue *offload_target_or_null) { - GPR_ASSERT(offload_target_or_null == NULL); - grpc_closure_list_move(list, &exec_ctx->closure_list); -} diff --git a/third_party/grpc/src/core/iomgr/exec_ctx.h b/third_party/grpc/src/core/iomgr/exec_ctx.h deleted file mode 100644 index 9a9b2e55fa7294..00000000000000 --- a/third_party/grpc/src/core/iomgr/exec_ctx.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_IOMGR_EXEC_CTX_H -#define GRPC_INTERNAL_CORE_IOMGR_EXEC_CTX_H - -#include "src/core/iomgr/closure.h" - -/** Execution context. - * A bag of data that collects information along a callstack. - * Generally created at public API entry points, and passed down as - * pointer to child functions that manipulate it. - * - * Specific responsibilities (this may grow in the future): - * - track a list of work that needs to be delayed until the top of the - * call stack (this provides a convenient mechanism to run callbacks - * without worrying about locking issues) - * - * CONVENTIONS: - * Instance of this must ALWAYS be constructed on the stack, never - * heap allocated. Instances and pointers to them must always be called - * exec_ctx. Instances are always passed as the first argument - * to a function that takes it, and always as a pointer (grpc_exec_ctx - * is never copied). - */ -struct grpc_exec_ctx { - grpc_closure_list closure_list; -}; - -/** A workqueue represents a list of work to be executed asynchronously. - Forward declared here to avoid a circular dependency with workqueue.h. */ -struct grpc_workqueue; -typedef struct grpc_workqueue grpc_workqueue; - -#define GRPC_EXEC_CTX_INIT \ - { GRPC_CLOSURE_LIST_INIT } - -/** Flush any work that has been enqueued onto this grpc_exec_ctx. - * Caller must guarantee that no interfering locks are held. - * Returns true if work was performed, false otherwise. */ -bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx); -/** Finish any pending work for a grpc_exec_ctx. Must be called before - * the instance is destroyed, or work may be lost. */ -void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx); -/** Add a closure to be executed at the next flush/finish point */ -void grpc_exec_ctx_enqueue(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - bool success, - grpc_workqueue *offload_target_or_null); -/** Add a list of closures to be executed at the next flush/finish point. - * Leaves \a list empty. */ -void grpc_exec_ctx_enqueue_list(grpc_exec_ctx *exec_ctx, - grpc_closure_list *list, - grpc_workqueue *offload_target_or_null); - -#endif diff --git a/third_party/grpc/src/core/iomgr/executor.c b/third_party/grpc/src/core/iomgr/executor.c deleted file mode 100644 index f22d8f30acf5c5..00000000000000 --- a/third_party/grpc/src/core/iomgr/executor.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/iomgr/executor.h" - -#include - -#include -#include -#include -#include -#include "src/core/iomgr/exec_ctx.h" - -typedef struct grpc_executor_data { - int busy; /**< is the thread currently running? */ - int shutting_down; /**< has \a grpc_shutdown() been invoked? */ - int pending_join; /**< has the thread finished but not been joined? */ - grpc_closure_list closures; /**< collection of pending work */ - gpr_thd_id tid; /**< thread id of the thread, only valid if \a busy or \a - pending_join are true */ - gpr_thd_options options; - gpr_mu mu; -} grpc_executor; - -static grpc_executor g_executor; - -void grpc_executor_init() { - memset(&g_executor, 0, sizeof(grpc_executor)); - gpr_mu_init(&g_executor.mu); - g_executor.options = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&g_executor.options); -} - -/* thread body */ -static void closure_exec_thread_func(void *ignored) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - while (1) { - gpr_mu_lock(&g_executor.mu); - if (g_executor.shutting_down != 0) { - gpr_mu_unlock(&g_executor.mu); - break; - } - if (grpc_closure_list_empty(g_executor.closures)) { - /* no more work, time to die */ - GPR_ASSERT(g_executor.busy == 1); - g_executor.busy = 0; - gpr_mu_unlock(&g_executor.mu); - break; - } else { - grpc_exec_ctx_enqueue_list(&exec_ctx, &g_executor.closures, NULL); - } - gpr_mu_unlock(&g_executor.mu); - grpc_exec_ctx_flush(&exec_ctx); - } - grpc_exec_ctx_finish(&exec_ctx); -} - -/* Spawn the thread if new work has arrived a no thread is up */ -static void maybe_spawn_locked() { - if (grpc_closure_list_empty(g_executor.closures) == 1) { - return; - } - if (g_executor.shutting_down == 1) { - return; - } - - if (g_executor.busy != 0) { - /* Thread still working. New work will be picked up by already running - * thread. Not spawning anything. */ - return; - } else if (g_executor.pending_join != 0) { - /* Pickup the remains of the previous incarnations of the thread. */ - gpr_thd_join(g_executor.tid); - g_executor.pending_join = 0; - } - - /* All previous instances of the thread should have been joined at this point. - * Spawn time! */ - g_executor.busy = 1; - gpr_thd_new(&g_executor.tid, closure_exec_thread_func, NULL, - &g_executor.options); - g_executor.pending_join = 1; -} - -void grpc_executor_enqueue(grpc_closure *closure, bool success) { - gpr_mu_lock(&g_executor.mu); - if (g_executor.shutting_down == 0) { - grpc_closure_list_add(&g_executor.closures, closure, success); - maybe_spawn_locked(); - } - gpr_mu_unlock(&g_executor.mu); -} - -void grpc_executor_shutdown() { - int pending_join; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - gpr_mu_lock(&g_executor.mu); - pending_join = g_executor.pending_join; - g_executor.shutting_down = 1; - gpr_mu_unlock(&g_executor.mu); - /* we can release the lock at this point despite the access to the closure - * list below because we aren't accepting new work */ - - /* Execute pending callbacks, some may be performing cleanups */ - grpc_exec_ctx_enqueue_list(&exec_ctx, &g_executor.closures, NULL); - grpc_exec_ctx_finish(&exec_ctx); - GPR_ASSERT(grpc_closure_list_empty(g_executor.closures)); - if (pending_join) { - gpr_thd_join(g_executor.tid); - } - gpr_mu_destroy(&g_executor.mu); -} diff --git a/third_party/grpc/src/core/iomgr/executor.h b/third_party/grpc/src/core/iomgr/executor.h deleted file mode 100644 index aac057ddf5fb96..00000000000000 --- a/third_party/grpc/src/core/iomgr/executor.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_IOMGR_EXECUTOR_H -#define GRPC_INTERNAL_CORE_IOMGR_EXECUTOR_H - -#include "src/core/iomgr/closure.h" - -/** Initialize the global executor. - * - * This mechanism is meant to outsource work (grpc_closure instances) to a - * thread, for those cases where blocking isn't an option but there isn't a - * non-blocking solution available. */ -void grpc_executor_init(); - -/** Enqueue \a closure for its eventual execution of \a f(arg) on a separate - * thread */ -void grpc_executor_enqueue(grpc_closure *closure, bool success); - -/** Shutdown the executor, running all pending work as part of the call */ -void grpc_executor_shutdown(); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_EXECUTOR_H */ diff --git a/third_party/grpc/src/core/iomgr/fd_posix.c b/third_party/grpc/src/core/iomgr/fd_posix.c deleted file mode 100644 index 85eadd754b9fd8..00000000000000 --- a/third_party/grpc/src/core/iomgr/fd_posix.c +++ /dev/null @@ -1,449 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_POSIX_SOCKET - -#include "src/core/iomgr/fd_posix.h" - -#include -#include -#include - -#include -#include -#include -#include - -#define CLOSURE_NOT_READY ((grpc_closure *)0) -#define CLOSURE_READY ((grpc_closure *)1) - -/* 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->mu); - } - - gpr_atm_rel_store(&r->refst, 1); - r->shutdown = 0; - r->read_closure = CLOSURE_NOT_READY; - r->write_closure = CLOSURE_NOT_READY; - 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; - r->released = 0; - return r; -} - -static void destroy(grpc_fd *fd) { - gpr_mu_destroy(&fd->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) { - gpr_mu_lock(&fd_freelist_mu); - gpr_mu_unlock(&fd_freelist_mu); - 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); - char *name2; - gpr_asprintf(&name2, "%s fd=%d", name, fd); - grpc_iomgr_register_object(&r->iomgr_object, name2); - gpr_free(name2); -#ifdef GRPC_FD_REF_COUNT_DEBUG - gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, r, name); -#endif - 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_fd_watcher *watcher) { - gpr_mu_lock(GRPC_POLLSET_MU(watcher->pollset)); - GPR_ASSERT(watcher->worker); - grpc_pollset_kick_ext(watcher->pollset, watcher->worker, - GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP); - gpr_mu_unlock(GRPC_POLLSET_MU(watcher->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); - } else if (fd->read_watcher) { - pollset_kick_locked(fd->read_watcher); - } else if (fd->write_watcher) { - pollset_kick_locked(fd->write_watcher); - } -} - -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); - } - if (fd->read_watcher) { - pollset_kick_locked(fd->read_watcher); - } - if (fd->write_watcher && fd->write_watcher != fd->read_watcher) { - pollset_kick_locked(fd->write_watcher); - } -} - -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; -} - -static void close_fd_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - fd->closed = 1; - if (!fd->released) { - close(fd->fd); - } else { - grpc_remove_fd_from_all_epoll_sets(fd->fd); - } - grpc_exec_ctx_enqueue(exec_ctx, fd->on_done_closure, true, NULL); -} - -int grpc_fd_wrapped_fd(grpc_fd *fd) { - if (fd->released || fd->closed) { - return -1; - } else { - return fd->fd; - } -} - -void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done, - int *release_fd, const char *reason) { - fd->on_done_closure = on_done; - fd->released = release_fd != NULL; - if (!fd->released) { - shutdown(fd->fd, SHUT_RDWR); - } else { - *release_fd = fd->fd; - } - gpr_mu_lock(&fd->mu); - REF_BY(fd, 1, reason); /* remove active status, but keep referenced */ - if (!has_watchers(fd)) { - close_fd_locked(exec_ctx, fd); - } else { - wake_all_watchers_locked(fd); - } - gpr_mu_unlock(&fd->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 notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure **st, grpc_closure *closure) { - if (*st == CLOSURE_NOT_READY) { - /* not ready ==> switch to a waiting state by setting the closure */ - *st = closure; - } else if (*st == CLOSURE_READY) { - /* already ready ==> queue the closure to run immediately */ - *st = CLOSURE_NOT_READY; - grpc_exec_ctx_enqueue(exec_ctx, closure, !fd->shutdown, NULL); - maybe_wake_one_watcher_locked(fd); - } else { - /* 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(); - } -} - -/* returns 1 if state becomes not ready */ -static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure **st) { - if (*st == CLOSURE_READY) { - /* duplicate ready ==> ignore */ - return 0; - } else if (*st == CLOSURE_NOT_READY) { - /* not ready, and not waiting ==> flag ready */ - *st = CLOSURE_READY; - return 0; - } else { - /* waiting ==> queue closure */ - grpc_exec_ctx_enqueue(exec_ctx, *st, !fd->shutdown, NULL); - *st = CLOSURE_NOT_READY; - return 1; - } -} - -static void set_ready(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure **st) { - /* only one set_ready can be active at once (but there may be a racing - notify_on) */ - gpr_mu_lock(&fd->mu); - set_ready_locked(exec_ctx, fd, st); - gpr_mu_unlock(&fd->mu); -} - -void grpc_fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - gpr_mu_lock(&fd->mu); - GPR_ASSERT(!fd->shutdown); - fd->shutdown = 1; - set_ready_locked(exec_ctx, fd, &fd->read_closure); - set_ready_locked(exec_ctx, fd, &fd->write_closure); - gpr_mu_unlock(&fd->mu); -} - -void grpc_fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - gpr_mu_lock(&fd->mu); - notify_on_locked(exec_ctx, fd, &fd->read_closure, closure); - gpr_mu_unlock(&fd->mu); -} - -void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - gpr_mu_lock(&fd->mu); - notify_on_locked(exec_ctx, fd, &fd->write_closure, closure); - gpr_mu_unlock(&fd->mu); -} - -uint32_t grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, - grpc_pollset_worker *worker, uint32_t read_mask, - uint32_t write_mask, grpc_fd_watcher *watcher) { - uint32_t mask = 0; - grpc_closure *cur; - int requested; - /* keep track of pollers that have requested our events, in case they change - */ - GRPC_FD_REF(fd, "poll"); - - gpr_mu_lock(&fd->mu); - - /* if we are shutdown, then don't add to the watcher set */ - if (fd->shutdown) { - watcher->fd = NULL; - watcher->pollset = NULL; - watcher->worker = NULL; - gpr_mu_unlock(&fd->mu); - GRPC_FD_UNREF(fd, "poll"); - return 0; - } - - /* if there is nobody polling for read, but we need to, then start doing so */ - cur = fd->read_closure; - requested = cur != CLOSURE_READY; - if (read_mask && fd->read_watcher == NULL && requested) { - fd->read_watcher = watcher; - mask |= read_mask; - } - /* if there is nobody polling for write, but we need to, then start doing so - */ - cur = fd->write_closure; - requested = cur != CLOSURE_READY; - if (write_mask && fd->write_watcher == NULL && requested) { - fd->write_watcher = watcher; - mask |= write_mask; - } - /* if not polling, remember this watcher in case we need someone to later */ - if (mask == 0 && worker != NULL) { - watcher->next = &fd->inactive_watcher_root; - watcher->prev = watcher->next->prev; - watcher->next->prev = watcher->prev->next = watcher; - } - watcher->pollset = pollset; - watcher->worker = worker; - watcher->fd = fd; - gpr_mu_unlock(&fd->mu); - - return mask; -} - -void grpc_fd_end_poll(grpc_exec_ctx *exec_ctx, 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->mu); - - if (watcher == fd->read_watcher) { - /* remove read watcher, kick if we still need a read */ - was_polling = 1; - if (!got_read) { - kick = 1; - } - fd->read_watcher = NULL; - } - if (watcher == fd->write_watcher) { - /* remove write watcher, kick if we still need a write */ - was_polling = 1; - if (!got_write) { - kick = 1; - } - fd->write_watcher = NULL; - } - if (!was_polling && watcher->worker != NULL) { - /* remove from inactive list */ - watcher->next->prev = watcher->prev; - watcher->prev->next = watcher->next; - } - if (got_read) { - if (set_ready_locked(exec_ctx, fd, &fd->read_closure)) { - kick = 1; - } - } - if (got_write) { - if (set_ready_locked(exec_ctx, fd, &fd->write_closure)) { - kick = 1; - } - } - if (kick) { - maybe_wake_one_watcher_locked(fd); - } - if (grpc_fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) { - close_fd_locked(exec_ctx, fd); - } - gpr_mu_unlock(&fd->mu); - - GRPC_FD_UNREF(fd, "poll"); -} - -void grpc_fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - set_ready(exec_ctx, fd, &fd->read_closure); -} - -void grpc_fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - set_ready(exec_ctx, fd, &fd->write_closure); -} - -#endif diff --git a/third_party/grpc/src/core/iomgr/fd_posix.h b/third_party/grpc/src/core/iomgr/fd_posix.h deleted file mode 100644 index 17e7de88ffaa64..00000000000000 --- a/third_party/grpc/src/core/iomgr/fd_posix.h +++ /dev/null @@ -1,192 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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_pollset_worker *worker; - 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 mu; - int shutdown; - int closed; - int released; - - /* 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. */ - grpc_fd_watcher inactive_watcher_root; - grpc_fd_watcher *read_watcher; - grpc_fd_watcher *write_watcher; - - grpc_closure *read_closure; - grpc_closure *write_closure; - - struct grpc_fd *freelist_next; - - grpc_closure *on_done_closure; - - 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); - -/* Return the wrapped fd, or -1 if it has been released or closed. */ -int grpc_fd_wrapped_fd(grpc_fd *fd); - -/* 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. - If release_fd is not NULL, it's set to fd and fd will not be closed. - 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_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done, - int *release_fd, 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 */ -uint32_t grpc_fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, - grpc_pollset_worker *worker, uint32_t read_mask, - uint32_t write_mask, grpc_fd_watcher *rec); -/* Complete polling previously started with grpc_fd_begin_poll - MUST NOT be called with a pollset lock taken - if got_read or got_write are 1, also does the become_{readable,writable} as - appropriate. */ -void grpc_fd_end_poll(grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure); - -/* Exactly the same semantics as above, except based on writable events. */ -void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_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_exec_ctx *exec_ctx, grpc_fd *fd); -void grpc_fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd); - -/* Reference counting for fds */ -/*#define GRPC_FD_REF_COUNT_DEBUG*/ -#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/third_party/grpc/src/core/iomgr/iocp_windows.c b/third_party/grpc/src/core/iomgr/iocp_windows.c deleted file mode 100644 index 759340e00ef936..00000000000000 --- a/third_party/grpc/src/core/iomgr/iocp_windows.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_WINSOCK_SOCKET - -#include - -#include -#include -#include -#include - -#include "src/core/iomgr/timer_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_atm g_custom_events = 0; - -static HANDLE g_iocp; - -static DWORD deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - static const int64_t max_spin_polling_us = 10; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { - return INFINITE; - } - 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 (DWORD)gpr_time_to_millis(gpr_time_add( - timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); -} - -void grpc_iocp_work(grpc_exec_ctx *exec_ctx, gpr_timespec deadline) { - BOOL success; - DWORD bytes = 0; - DWORD flags = 0; - ULONG_PTR completion_key; - LPOVERLAPPED overlapped; - grpc_winsocket *socket; - grpc_winsocket_callback_info *info; - grpc_closure *closure = NULL; - success = GetQueuedCompletionStatus( - g_iocp, &bytes, &completion_key, &overlapped, - deadline_to_millis_timeout(deadline, gpr_now(deadline.clock_type))); - if (success == 0 && overlapped == NULL) { - return; - } - 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->closure) { - closure = info->closure; - info->closure = NULL; - } else { - info->has_pending_iocp = 1; - } - gpr_mu_unlock(&socket->state_mu); - grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL); -} - -void grpc_iocp_init(void) { - g_iocp = - CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0); - GPR_ASSERT(g_iocp); -} - -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_flush(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - do { - grpc_iocp_work(&exec_ctx, gpr_inf_past(GPR_CLOCK_MONOTONIC)); - } while (grpc_exec_ctx_flush(&exec_ctx)); -} - -void grpc_iocp_shutdown(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - while (gpr_atm_acq_load(&g_custom_events)) { - grpc_iocp_work(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC)); - grpc_exec_ctx_flush(&exec_ctx); - } - grpc_exec_ctx_finish(&exec_ctx); - GPR_ASSERT(CloseHandle(g_iocp)); -} - -void grpc_iocp_add_socket(grpc_winsocket *socket) { - HANDLE ret; - if (socket->added_to_iocp) return; - ret = CreateIoCompletionPort((HANDLE)socket->socket, g_iocp, - (uintptr_t)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_exec_ctx *exec_ctx, - grpc_winsocket *socket, grpc_closure *closure, - grpc_winsocket_callback_info *info) { - GPR_ASSERT(info->closure == NULL); - gpr_mu_lock(&socket->state_mu); - if (info->has_pending_iocp) { - info->has_pending_iocp = 0; - grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL); - } else { - info->closure = closure; - } - gpr_mu_unlock(&socket->state_mu); -} - -void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx, - grpc_winsocket *socket, - grpc_closure *closure) { - socket_notify_on_iocp(exec_ctx, socket, closure, &socket->write_info); -} - -void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, - grpc_closure *closure) { - socket_notify_on_iocp(exec_ctx, socket, closure, &socket->read_info); -} - -#endif /* GPR_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/iocp_windows.h b/third_party/grpc/src/core/iomgr/iocp_windows.h deleted file mode 100644 index 75f3ba84770fca..00000000000000 --- a/third_party/grpc/src/core/iomgr/iocp_windows.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_work(grpc_exec_ctx *exec_ctx, gpr_timespec deadline); -void grpc_iocp_init(void); -void grpc_iocp_kick(void); -void grpc_iocp_flush(void); -void grpc_iocp_shutdown(void); -void grpc_iocp_add_socket(grpc_winsocket *); - -void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx, - grpc_winsocket *winsocket, - grpc_closure *closure); - -void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, - grpc_winsocket *winsocket, - grpc_closure *closure); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_IOCP_WINDOWS_H */ diff --git a/third_party/grpc/src/core/iomgr/iomgr.c b/third_party/grpc/src/core/iomgr/iomgr.c deleted file mode 100644 index 212ce5534dd9af..00000000000000 --- a/third_party/grpc/src/core/iomgr/iomgr.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -#include -#include -#include -#include -#include - -#include "src/core/iomgr/iomgr_internal.h" -#include "src/core/iomgr/timer_internal.h" -#include "src/core/support/string.h" - -static gpr_mu g_mu; -static gpr_cv g_rcv; -static int g_shutdown; -static grpc_iomgr_object g_root_object; - -void grpc_iomgr_init(void) { - g_shutdown = 0; - gpr_mu_init(&g_mu); - gpr_cv_init(&g_rcv); - grpc_timer_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(); - grpc_pollset_global_init(); -} - -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) { - 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); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - grpc_iomgr_platform_flush(); - - gpr_mu_lock(&g_mu); - g_shutdown = 1; - while (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_root_object.next != &g_root_object) { - gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed", - count_objects()); - } - last_warning_time = gpr_now(GPR_CLOCK_REALTIME); - } - if (grpc_timer_check(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC), - NULL)) { - gpr_mu_unlock(&g_mu); - grpc_exec_ctx_flush(&exec_ctx); - gpr_mu_lock(&g_mu); - continue; - } - if (g_root_object.next != &g_root_object) { - 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)) { - if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) { - if (g_root_object.next != &g_root_object) { - 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_timer_list_shutdown(&exec_ctx); - grpc_exec_ctx_finish(&exec_ctx); - - /* ensure all threads have left g_mu */ - gpr_mu_lock(&g_mu); - gpr_mu_unlock(&g_mu); - - grpc_pollset_global_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); -} diff --git a/third_party/grpc/src/core/iomgr/iomgr.h b/third_party/grpc/src/core/iomgr/iomgr.h deleted file mode 100644 index c9ea84c6050b97..00000000000000 --- a/third_party/grpc/src/core/iomgr/iomgr.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -/** Initializes the iomgr. */ -void grpc_iomgr_init(void); - -/** Signals the intention to shutdown the iomgr. */ -void grpc_iomgr_shutdown(void); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_H */ diff --git a/third_party/grpc/src/core/iomgr/iomgr_internal.h b/third_party/grpc/src/core/iomgr/iomgr_internal.h deleted file mode 100644 index e372c18e8a070c..00000000000000 --- a/third_party/grpc/src/core/iomgr/iomgr_internal.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - -void grpc_pollset_global_init(void); -void grpc_pollset_global_shutdown(void); - -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); -/** flush any globally queued work from iomgr */ -void grpc_iomgr_platform_flush(void); -/** tear down all platform specific global iomgr structures */ -void grpc_iomgr_platform_shutdown(void); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_INTERNAL_H */ diff --git a/third_party/grpc/src/core/iomgr/iomgr_posix.c b/third_party/grpc/src/core/iomgr/iomgr_posix.c deleted file mode 100644 index fecb7b9760aacd..00000000000000 --- a/third_party/grpc/src/core/iomgr/iomgr_posix.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_register_tracer("tcp", &grpc_tcp_trace); -} - -void grpc_iomgr_platform_flush(void) {} - -void grpc_iomgr_platform_shutdown(void) { grpc_fd_global_shutdown(); } - -#endif /* GRPC_POSIX_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/iomgr_posix.h b/third_party/grpc/src/core/iomgr/iomgr_posix.h deleted file mode 100644 index 068a5c6d7cf17a..00000000000000 --- a/third_party/grpc/src/core/iomgr/iomgr_posix.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" - -#endif /* GRPC_INTERNAL_CORE_IOMGR_IOMGR_POSIX_H */ diff --git a/third_party/grpc/src/core/iomgr/iomgr_windows.c b/third_party/grpc/src/core/iomgr/iomgr_windows.c deleted file mode 100644 index 14775516bb1d6c..00000000000000 --- a/third_party/grpc/src/core/iomgr/iomgr_windows.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_flush(void) { grpc_iocp_flush(); } - -void grpc_iomgr_platform_shutdown(void) { - grpc_iocp_shutdown(); - winsock_shutdown(); -} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/pollset.h b/third_party/grpc/src/core/iomgr/pollset.h deleted file mode 100644 index c6b0214dea2cb1..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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); -/* Begin shutting down the pollset, and call closure when done. - * GRPC_POLLSET_MU(pollset) must be held */ -void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure); -/** Reset the pollset to its initial state (perhaps with some cached objects); - * must have been previously shutdown */ -void grpc_pollset_reset(grpc_pollset *pollset); -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. - May call grpc_closure_list_run on grpc_closure_list, without holding the - pollset - lock */ -void grpc_pollset_work(grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/iomgr/pollset_multipoller_with_epoll.c b/third_party/grpc/src/core/iomgr/pollset_multipoller_with_epoll.c deleted file mode 100644 index 4acae2bb7122cc..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_multipoller_with_epoll.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL - -#include -#include -#include -#include -#include - -#include -#include -#include -#include "src/core/iomgr/fd_posix.h" -#include "src/core/profiling/timers.h" -#include "src/core/support/block_annotate.h" - -struct epoll_fd_list { - int *epoll_fds; - size_t count; - size_t capacity; -}; - -static struct epoll_fd_list epoll_fd_global_list; -static gpr_once init_epoll_fd_list_mu = GPR_ONCE_INIT; -static gpr_mu epoll_fd_list_mu; - -static void init_mu(void) { gpr_mu_init(&epoll_fd_list_mu); } - -static void add_epoll_fd_to_global_list(int epoll_fd) { - gpr_once_init(&init_epoll_fd_list_mu, init_mu); - - gpr_mu_lock(&epoll_fd_list_mu); - if (epoll_fd_global_list.count == epoll_fd_global_list.capacity) { - epoll_fd_global_list.capacity = - GPR_MAX((size_t)8, epoll_fd_global_list.capacity * 2); - epoll_fd_global_list.epoll_fds = - gpr_realloc(epoll_fd_global_list.epoll_fds, - epoll_fd_global_list.capacity * sizeof(int)); - } - epoll_fd_global_list.epoll_fds[epoll_fd_global_list.count++] = epoll_fd; - gpr_mu_unlock(&epoll_fd_list_mu); -} - -static void remove_epoll_fd_from_global_list(int epoll_fd) { - gpr_mu_lock(&epoll_fd_list_mu); - GPR_ASSERT(epoll_fd_global_list.count > 0); - for (size_t i = 0; i < epoll_fd_global_list.count; i++) { - if (epoll_fd == epoll_fd_global_list.epoll_fds[i]) { - epoll_fd_global_list.epoll_fds[i] = - epoll_fd_global_list.epoll_fds[--(epoll_fd_global_list.count)]; - break; - } - } - gpr_mu_unlock(&epoll_fd_list_mu); -} - -void grpc_remove_fd_from_all_epoll_sets(int fd) { - int err; - gpr_once_init(&init_epoll_fd_list_mu, init_mu); - gpr_mu_lock(&epoll_fd_list_mu); - if (epoll_fd_global_list.count == 0) { - gpr_mu_unlock(&epoll_fd_list_mu); - return; - } - for (size_t i = 0; i < epoll_fd_global_list.count; i++) { - err = epoll_ctl(epoll_fd_global_list.epoll_fds[i], EPOLL_CTL_DEL, fd, NULL); - if (err < 0 && errno != ENOENT) { - gpr_log(GPR_ERROR, "epoll_ctl del for %d failed: %s", fd, - strerror(errno)); - } - } - gpr_mu_unlock(&epoll_fd_list_mu); -} - -typedef struct { - grpc_pollset *pollset; - grpc_fd *fd; - grpc_closure closure; -} delayed_add; - -typedef struct { int epoll_fd; } pollset_hdr; - -static void finally_add_fd(grpc_exec_ctx *exec_ctx, 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, NULL, 0, 0, &watcher) == 0); - if (watcher.fd != NULL) { - ev.events = (uint32_t)(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(exec_ctx, &watcher, 0, 0); -} - -static void perform_delayed_add(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_status) { - delayed_add *da = arg; - - if (!grpc_fd_is_orphaned(da->fd)) { - finally_add_fd(exec_ctx, 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; - grpc_exec_ctx_enqueue(exec_ctx, da->pollset->shutdown_done, true, NULL); - } - } - gpr_mu_unlock(&da->pollset->mu); - - GRPC_FD_UNREF(da->fd, "delayed_add"); - - gpr_free(da); -} - -static void multipoll_with_epoll_pollset_add_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_fd *fd, - int and_unlock_pollset) { - if (and_unlock_pollset) { - gpr_mu_unlock(&pollset->mu); - finally_add_fd(exec_ctx, pollset, fd); - } else { - delayed_add *da = gpr_malloc(sizeof(*da)); - da->pollset = pollset; - da->fd = fd; - GRPC_FD_REF(fd, "delayed_add"); - grpc_closure_init(&da->closure, perform_delayed_add, da); - pollset->in_flight_cbs++; - grpc_exec_ctx_enqueue(exec_ctx, &da->closure, true, NULL); - } -} - -/* 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_and_unlock( - grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker, - gpr_timespec deadline, gpr_timespec now) { - 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->fd); - pfds[0].events = POLLIN; - pfds[0].revents = 0; - pfds[1].fd = h->epoll_fd; - pfds[1].events = POLLIN; - pfds[1].revents = 0; - - /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid - even going into the blocking annotation if possible */ - GPR_TIMER_BEGIN("poll", 0); - GRPC_SCHEDULING_START_BLOCKING_REGION; - poll_rv = grpc_poll_function(pfds, 2, timeout_ms); - GRPC_SCHEDULING_END_BLOCKING_REGION; - GPR_TIMER_END("poll", 0); - - 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->fd); - } - if (pfds[1].revents) { - do { - /* The following epoll_wait never blocks; it has a timeout of 0 */ - 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_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI); - int write_ev = ep_ev[i].events & EPOLLOUT; - if (fd == NULL) { - grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd); - } else { - if (read_ev || cancel) { - grpc_fd_become_readable(exec_ctx, fd); - } - if (write_ev || cancel) { - grpc_fd_become_writable(exec_ctx, fd); - } - } - } - } - } while (ep_rv == GRPC_EPOLL_MAX_EVENTS); - } - } -} - -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); - remove_epoll_fd_from_global_list(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_maybe_work_and_unlock, - multipoll_with_epoll_pollset_finish_shutdown, - multipoll_with_epoll_pollset_destroy}; - -static void epoll_become_multipoller(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, grpc_fd **fds, - size_t nfds) { - size_t i; - pollset_hdr *h = gpr_malloc(sizeof(pollset_hdr)); - struct epoll_event ev; - int err; - - 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(); - } - add_epoll_fd_to_global_list(h->epoll_fd); - - ev.events = (uint32_t)(EPOLLIN | EPOLLET); - ev.data.ptr = NULL; - err = epoll_ctl(h->epoll_fd, EPOLL_CTL_ADD, - GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), &ev); - if (err < 0) { - gpr_log(GPR_ERROR, "epoll_ctl add for %d failed: %s", - GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), - strerror(errno)); - } - - for (i = 0; i < nfds; i++) { - multipoll_with_epoll_pollset_add_fd(exec_ctx, pollset, fds[i], 0); - } -} - -grpc_platform_become_multipoller_type grpc_platform_become_multipoller = - epoll_become_multipoller; - -#else /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */ - -void grpc_remove_fd_from_all_epoll_sets(int fd) {} - -#endif /* GPR_LINUX_MULTIPOLL_WITH_EPOLL */ diff --git a/third_party/grpc/src/core/iomgr/pollset_multipoller_with_poll_posix.c b/third_party/grpc/src/core/iomgr/pollset_multipoller_with_poll_posix.c deleted file mode 100644 index 809f8f39daabab..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_multipoller_with_poll_posix.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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 "src/core/support/block_annotate.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_exec_ctx *exec_ctx, - 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_maybe_work_and_unlock( - grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *worker, - gpr_timespec deadline, gpr_timespec now) { -#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR) -#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR) - - int timeout; - int r; - size_t i, j, fd_count; - nfds_t pfd_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 + 2)); - watchers = gpr_malloc(sizeof(*watchers) * (h->fd_count + 2)); - fd_count = 0; - pfd_count = 2; - pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd); - pfds[0].events = POLLIN; - pfds[0].revents = 0; - pfds[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd->fd); - pfds[1].events = POLLIN; - pfds[1].revents = 0; - 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 = 2; i < pfd_count; i++) { - pfds[i].events = (short)grpc_fd_begin_poll(watchers[i].fd, pollset, worker, - POLLIN, POLLOUT, &watchers[i]); - } - - /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid - even going into the blocking annotation if possible */ - GRPC_SCHEDULING_START_BLOCKING_REGION; - r = grpc_poll_function(pfds, pfd_count, timeout); - GRPC_SCHEDULING_END_BLOCKING_REGION; - - if (r < 0) { - if (errno != EINTR) { - gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); - } - for (i = 2; i < pfd_count; i++) { - grpc_fd_end_poll(exec_ctx, &watchers[i], 0, 0); - } - } else if (r == 0) { - for (i = 2; i < pfd_count; i++) { - grpc_fd_end_poll(exec_ctx, &watchers[i], 0, 0); - } - } else { - if (pfds[0].revents & POLLIN_CHECK) { - grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd); - } - if (pfds[1].revents & POLLIN_CHECK) { - grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd); - } - for (i = 2; i < pfd_count; i++) { - if (watchers[i].fd == NULL) { - grpc_fd_end_poll(exec_ctx, &watchers[i], 0, 0); - continue; - } - grpc_fd_end_poll(exec_ctx, &watchers[i], pfds[i].revents & POLLIN_CHECK, - pfds[i].revents & POLLOUT_CHECK); - } - } - - gpr_free(pfds); - gpr_free(watchers); -} - -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_maybe_work_and_unlock, - multipoll_with_poll_pollset_finish_shutdown, - multipoll_with_poll_pollset_destroy}; - -void grpc_poll_become_multipoller(grpc_exec_ctx *exec_ctx, - 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/third_party/grpc/src/core/iomgr/pollset_posix.c b/third_party/grpc/src/core/iomgr/pollset_posix.c deleted file mode 100644 index 19ee6650f006c2..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_posix.c +++ /dev/null @@ -1,638 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_POSIX_SOCKET - -#include "src/core/iomgr/pollset_posix.h" - -#include -#include -#include -#include - -#include "src/core/iomgr/timer_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 "src/core/support/block_annotate.h" -#include -#include -#include -#include -#include - -GPR_TLS_DECL(g_current_thread_poller); -GPR_TLS_DECL(g_current_thread_worker); - -/** Default poll() function - a pointer so that it can be overridden by some - * tests */ -grpc_poll_function_type grpc_poll_function = poll; - -/** The alarm system needs to be able to wakeup 'some poller' sometimes - * (specifically when a new alarm needs to be triggered earlier than the next - * alarm 'epoch'). - * This wakeup_fd gives us something to alert on when such a case occurs. */ -grpc_wakeup_fd grpc_global_wakeup_fd; - -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_ext(grpc_pollset *p, - grpc_pollset_worker *specific_worker, - uint32_t flags) { - GPR_TIMER_BEGIN("grpc_pollset_kick_ext", 0); - - /* pollset->mu already held */ - if (specific_worker != NULL) { - if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { - GPR_TIMER_BEGIN("grpc_pollset_kick_ext.broadcast", 0); - GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); - 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->fd); - } - p->kicked_without_pollers = 1; - GPR_TIMER_END("grpc_pollset_kick_ext.broadcast", 0); - } else if (gpr_tls_get(&g_current_thread_worker) != - (intptr_t)specific_worker) { - GPR_TIMER_MARK("different_thread_worker", 0); - if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { - specific_worker->reevaluate_polling_on_wakeup = 1; - } - specific_worker->kicked_specifically = 1; - grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd); - } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) { - GPR_TIMER_MARK("kick_yoself", 0); - if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { - specific_worker->reevaluate_polling_on_wakeup = 1; - } - specific_worker->kicked_specifically = 1; - grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd); - } - } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) { - GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); - GPR_TIMER_MARK("kick_anonymous", 0); - specific_worker = pop_front_worker(p); - if (specific_worker != NULL) { - if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) { - GPR_TIMER_MARK("kick_anonymous_not_self", 0); - push_back_worker(p, specific_worker); - specific_worker = pop_front_worker(p); - if ((flags & GRPC_POLLSET_CAN_KICK_SELF) == 0 && - gpr_tls_get(&g_current_thread_worker) == - (intptr_t)specific_worker) { - push_back_worker(p, specific_worker); - specific_worker = NULL; - } - } - if (specific_worker != NULL) { - GPR_TIMER_MARK("finally_kick", 0); - push_back_worker(p, specific_worker); - grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd); - } - } else { - GPR_TIMER_MARK("kicked_no_pollers", 0); - p->kicked_without_pollers = 1; - } - } - - GPR_TIMER_END("grpc_pollset_kick_ext", 0); -} - -void grpc_pollset_kick(grpc_pollset *p, grpc_pollset_worker *specific_worker) { - grpc_pollset_kick_ext(p, specific_worker, 0); -} - -/* global state management */ - -void grpc_pollset_global_init(void) { - gpr_tls_init(&g_current_thread_poller); - gpr_tls_init(&g_current_thread_worker); - grpc_wakeup_fd_global_init(); - grpc_wakeup_fd_init(&grpc_global_wakeup_fd); -} - -void grpc_pollset_global_shutdown(void) { - grpc_wakeup_fd_destroy(&grpc_global_wakeup_fd); - gpr_tls_destroy(&g_current_thread_poller); - gpr_tls_destroy(&g_current_thread_worker); - grpc_wakeup_fd_global_destroy(); -} - -void grpc_kick_poller(void) { grpc_wakeup_fd_wakeup(&grpc_global_wakeup_fd); } - -/* 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; - pollset->kicked_without_pollers = 0; - pollset->idle_jobs.head = pollset->idle_jobs.tail = NULL; - pollset->local_wakeup_cache = NULL; - pollset->kicked_without_pollers = 0; - become_basic_pollset(pollset, NULL); -} - -void grpc_pollset_destroy(grpc_pollset *pollset) { - GPR_ASSERT(pollset->in_flight_cbs == 0); - GPR_ASSERT(!grpc_pollset_has_workers(pollset)); - GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail); - pollset->vtable->destroy(pollset); - gpr_mu_destroy(&pollset->mu); - while (pollset->local_wakeup_cache) { - grpc_cached_wakeup_fd *next = pollset->local_wakeup_cache->next; - grpc_wakeup_fd_destroy(&pollset->local_wakeup_cache->fd); - gpr_free(pollset->local_wakeup_cache); - pollset->local_wakeup_cache = next; - } -} - -void grpc_pollset_reset(grpc_pollset *pollset) { - GPR_ASSERT(pollset->shutting_down); - GPR_ASSERT(pollset->in_flight_cbs == 0); - GPR_ASSERT(!grpc_pollset_has_workers(pollset)); - GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail); - pollset->vtable->destroy(pollset); - pollset->shutting_down = 0; - pollset->called_shutdown = 0; - pollset->kicked_without_pollers = 0; - become_basic_pollset(pollset, NULL); -} - -void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) { - gpr_mu_lock(&pollset->mu); - pollset->vtable->add_fd(exec_ctx, pollset, fd, 1); -/* the following (enabled only in debug) will reacquire and then release - our lock - meaning that if the unlocking flag passed to add_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_exec_ctx *exec_ctx, grpc_pollset *pollset) { - GPR_ASSERT(grpc_closure_list_empty(pollset->idle_jobs)); - pollset->vtable->finish_shutdown(pollset); - grpc_exec_ctx_enqueue(exec_ctx, pollset->shutdown_done, true, NULL); -} - -void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *worker, gpr_timespec now, - gpr_timespec deadline) { - /* pollset->mu already held */ - int added_worker = 0; - int locked = 1; - int queued_work = 0; - int keep_polling = 0; - GPR_TIMER_BEGIN("grpc_pollset_work", 0); - /* this must happen before we (potentially) drop pollset->mu */ - worker->next = worker->prev = NULL; - worker->reevaluate_polling_on_wakeup = 0; - if (pollset->local_wakeup_cache != NULL) { - worker->wakeup_fd = pollset->local_wakeup_cache; - pollset->local_wakeup_cache = worker->wakeup_fd->next; - } else { - worker->wakeup_fd = gpr_malloc(sizeof(*worker->wakeup_fd)); - grpc_wakeup_fd_init(&worker->wakeup_fd->fd); - } - worker->kicked_specifically = 0; - /* If there's work waiting for the pollset to be idle, and the - pollset is idle, then do that work */ - if (!grpc_pollset_has_workers(pollset) && - !grpc_closure_list_empty(pollset->idle_jobs)) { - GPR_TIMER_MARK("grpc_pollset_work.idle_jobs", 0); - grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL); - goto done; - } - /* Check alarms - these are a global resource so we just ping - each time through on every pollset. - May update deadline to ensure timely wakeups. - TODO(ctiller): can this work be localized? */ - if (grpc_timer_check(exec_ctx, now, &deadline)) { - GPR_TIMER_MARK("grpc_pollset_work.alarm_triggered", 0); - gpr_mu_unlock(&pollset->mu); - locked = 0; - goto done; - } - /* If we're shutting down then we don't execute any extended work */ - if (pollset->shutting_down) { - GPR_TIMER_MARK("grpc_pollset_work.shutting_down", 0); - goto done; - } - /* Give do_promote priority so we don't starve it out */ - if (pollset->in_flight_cbs) { - GPR_TIMER_MARK("grpc_pollset_work.in_flight_cbs", 0); - gpr_mu_unlock(&pollset->mu); - locked = 0; - goto done; - } - /* Start polling, and keep doing so while we're being asked to - re-evaluate our pollers (this allows poll() based pollers to - ensure they don't miss wakeups) */ - keep_polling = 1; - while (keep_polling) { - keep_polling = 0; - if (!pollset->kicked_without_pollers) { - if (!added_worker) { - push_front_worker(pollset, worker); - added_worker = 1; - gpr_tls_set(&g_current_thread_worker, (intptr_t)worker); - } - gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset); - GPR_TIMER_BEGIN("maybe_work_and_unlock", 0); - pollset->vtable->maybe_work_and_unlock(exec_ctx, pollset, worker, - deadline, now); - GPR_TIMER_END("maybe_work_and_unlock", 0); - locked = 0; - gpr_tls_set(&g_current_thread_poller, 0); - } else { - GPR_TIMER_MARK("grpc_pollset_work.kicked_without_pollers", 0); - pollset->kicked_without_pollers = 0; - } - /* Finished execution - start cleaning up. - Note that we may arrive here from outside the enclosing while() loop. - In that case we won't loop though as we haven't added worker to the - worker list, which means nobody could ask us to re-evaluate polling). */ - done: - if (!locked) { - queued_work |= grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - locked = 1; - } - /* If we're forced to re-evaluate polling (via grpc_pollset_kick with - GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force - a loop */ - if (worker->reevaluate_polling_on_wakeup) { - worker->reevaluate_polling_on_wakeup = 0; - pollset->kicked_without_pollers = 0; - if (queued_work || worker->kicked_specifically) { - /* If there's queued work on the list, then set the deadline to be - immediate so we get back out of the polling loop quickly */ - deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC); - } - keep_polling = 1; - } - } - if (added_worker) { - remove_worker(pollset, worker); - gpr_tls_set(&g_current_thread_worker, 0); - } - /* release wakeup fd to the local pool */ - worker->wakeup_fd->next = pollset->local_wakeup_cache; - pollset->local_wakeup_cache = worker->wakeup_fd; - /* check shutdown conditions */ - 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(exec_ctx, pollset); - grpc_exec_ctx_flush(exec_ctx); - /* 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); - } else if (!grpc_closure_list_empty(pollset->idle_jobs)) { - grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL); - gpr_mu_unlock(&pollset->mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - } - } - GPR_TIMER_END("grpc_pollset_work", 0); -} - -void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_ASSERT(!pollset->shutting_down); - pollset->shutting_down = 1; - pollset->shutdown_done = closure; - grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); - if (!grpc_pollset_has_workers(pollset)) { - grpc_exec_ctx_enqueue_list(exec_ctx, &pollset->idle_jobs, NULL); - } - if (!pollset->called_shutdown && pollset->in_flight_cbs == 0 && - !grpc_pollset_has_workers(pollset)) { - pollset->called_shutdown = 1; - finish_shutdown(exec_ctx, pollset); - } -} - -int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - static const int64_t 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_MS - 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_closure promotion_closure; -} grpc_unary_promote_args; - -static void basic_do_promote(grpc_exec_ctx *exec_ctx, void *args, - bool 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; - - /* - * 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 */ - GPR_ASSERT(!grpc_pollset_has_workers(pollset)); - - 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) { - pollset->called_shutdown = 1; - finish_shutdown(exec_ctx, pollset); - } - } 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(exec_ctx, 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(exec_ctx, 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); - - /* Matching ref in basic_pollset_add_fd */ - GRPC_FD_UNREF(fd, "basicpoll_add"); -} - -static void basic_pollset_add_fd(grpc_exec_ctx *exec_ctx, 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(exec_ctx, 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->fd = fd; - up_args->original_vtable = pollset->vtable; - up_args->pollset = pollset; - up_args->promotion_closure.cb = basic_do_promote; - up_args->promotion_closure.cb_arg = up_args; - - grpc_closure_list_add(&pollset->idle_jobs, &up_args->promotion_closure, 1); - grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); - -exit: - if (and_unlock_pollset) { - gpr_mu_unlock(&pollset->mu); - } -} - -static void basic_pollset_maybe_work_and_unlock(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_pollset_worker *worker, - gpr_timespec deadline, - gpr_timespec now) { -#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR) -#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR) - - struct pollfd pfd[3]; - grpc_fd *fd; - grpc_fd_watcher fd_watcher; - int timeout; - int r; - nfds_t 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(&grpc_global_wakeup_fd); - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker->wakeup_fd->fd); - pfd[1].events = POLLIN; - pfd[1].revents = 0; - nfds = 2; - if (fd) { - pfd[2].fd = fd->fd; - pfd[2].revents = 0; - GRPC_FD_REF(fd, "basicpoll_begin"); - gpr_mu_unlock(&pollset->mu); - pfd[2].events = (short)grpc_fd_begin_poll(fd, pollset, worker, POLLIN, - POLLOUT, &fd_watcher); - if (pfd[2].events != 0) { - nfds++; - } - } else { - gpr_mu_unlock(&pollset->mu); - } - - /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid - even going into the blocking annotation if possible */ - /* poll fd count (argument 2) is shortened by one if we have no events - to poll on - such that it only includes the kicker */ - GPR_TIMER_BEGIN("poll", 0); - GRPC_SCHEDULING_START_BLOCKING_REGION; - r = grpc_poll_function(pfd, nfds, timeout); - GRPC_SCHEDULING_END_BLOCKING_REGION; - GPR_TIMER_END("poll", 0); - - if (r < 0) { - if (errno != EINTR) { - gpr_log(GPR_ERROR, "poll() failed: %s", strerror(errno)); - } - if (fd) { - grpc_fd_end_poll(exec_ctx, &fd_watcher, 0, 0); - } - } else if (r == 0) { - if (fd) { - grpc_fd_end_poll(exec_ctx, &fd_watcher, 0, 0); - } - } else { - if (pfd[0].revents & POLLIN_CHECK) { - grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd); - } - if (pfd[1].revents & POLLIN_CHECK) { - grpc_wakeup_fd_consume_wakeup(&worker->wakeup_fd->fd); - } - if (nfds > 2) { - grpc_fd_end_poll(exec_ctx, &fd_watcher, pfd[2].revents & POLLIN_CHECK, - pfd[2].revents & POLLOUT_CHECK); - } else if (fd) { - grpc_fd_end_poll(exec_ctx, &fd_watcher, 0, 0); - } - } - - if (fd) { - GRPC_FD_UNREF(fd, "basicpoll_begin"); - } -} - -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_maybe_work_and_unlock, - 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/third_party/grpc/src/core/iomgr/pollset_posix.h b/third_party/grpc/src/core/iomgr/pollset_posix.h deleted file mode 100644 index b34bb094268229..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_posix.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H -#define GRPC_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H - -#include - -#include -#include "src/core/iomgr/exec_ctx.h" -#include "src/core/iomgr/iomgr.h" -#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_cached_wakeup_fd { - grpc_wakeup_fd fd; - struct grpc_cached_wakeup_fd *next; -} grpc_cached_wakeup_fd; - -typedef struct grpc_pollset_worker { - grpc_cached_wakeup_fd *wakeup_fd; - int reevaluate_polling_on_wakeup; - int kicked_specifically; - 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; - grpc_closure *shutdown_done; - grpc_closure_list idle_jobs; - union { - int fd; - void *ptr; - } data; - /* Local cache of eventfds for workers */ - grpc_cached_wakeup_fd *local_wakeup_cache; -} grpc_pollset; - -struct grpc_pollset_vtable { - void (*add_fd)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - struct grpc_fd *fd, int and_unlock_pollset); - void (*maybe_work_and_unlock)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *worker, - gpr_timespec deadline, gpr_timespec now); - 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_exec_ctx *exec_ctx, 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); - -/* Allow kick to wakeup the currently polling worker */ -#define GRPC_POLLSET_CAN_KICK_SELF 1 -/* Force the wakee to repoll when awoken */ -#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2 -/* As per grpc_pollset_kick, with an extended set of flags (defined above) - -- mostly for fd_posix's use. */ -void grpc_pollset_kick_ext(grpc_pollset *p, - grpc_pollset_worker *specific_worker, - uint32_t flags); - -/* turn a pollset into a multipoller: platform specific */ -typedef void (*grpc_platform_become_multipoller_type)(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - 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); - -void grpc_remove_fd_from_all_epoll_sets(int fd); - -/* 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; -extern grpc_wakeup_fd grpc_global_wakeup_fd; - -#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_POSIX_H */ diff --git a/third_party/grpc/src/core/iomgr/pollset_set.h b/third_party/grpc/src/core/iomgr/pollset_set.h deleted file mode 100644 index 09c04438f71e5a..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_set.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, - grpc_pollset *pollset); -void grpc_pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, - grpc_pollset *pollset); -void grpc_pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item); -void grpc_pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_H */ diff --git a/third_party/grpc/src/core/iomgr/pollset_set_posix.c b/third_party/grpc/src/core/iomgr/pollset_set_posix.c deleted file mode 100644 index 4ec92202e30d3c..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_set_posix.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_set"); - } - gpr_free(pollset_set->pollsets); - gpr_free(pollset_set->pollset_sets); - gpr_free(pollset_set->fds); -} - -void grpc_pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - 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_set"); - } else { - grpc_pollset_add_fd(exec_ctx, 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_exec_ctx *exec_ctx, - 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_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - size_t i, j; - gpr_mu_lock(&bag->mu); - if (bag->pollset_set_count == bag->pollset_set_capacity) { - bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity); - bag->pollset_sets = - gpr_realloc(bag->pollset_sets, - bag->pollset_set_capacity * sizeof(*bag->pollset_sets)); - } - bag->pollset_sets[bag->pollset_set_count++] = item; - for (i = 0, j = 0; i < bag->fd_count; i++) { - if (grpc_fd_is_orphaned(bag->fds[i])) { - GRPC_FD_UNREF(bag->fds[i], "pollset_set"); - } else { - grpc_pollset_set_add_fd(exec_ctx, item, bag->fds[i]); - bag->fds[j++] = bag->fds[i]; - } - } - bag->fd_count = j; - gpr_mu_unlock(&bag->mu); -} - -void grpc_pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - size_t i; - gpr_mu_lock(&bag->mu); - for (i = 0; i < bag->pollset_set_count; i++) { - if (bag->pollset_sets[i] == item) { - bag->pollset_set_count--; - GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i], - bag->pollset_sets[bag->pollset_set_count]); - break; - } - } - gpr_mu_unlock(&bag->mu); -} - -void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, pollset_set->pollsets[i], fd); - } - for (i = 0; i < pollset_set->pollset_set_count; i++) { - grpc_pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd); - } - gpr_mu_unlock(&pollset_set->mu); -} - -void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, - 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; - } - } - for (i = 0; i < pollset_set->pollset_set_count; i++) { - grpc_pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd); - } - gpr_mu_unlock(&pollset_set->mu); -} - -#endif /* GPR_POSIX_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/pollset_set_posix.h b/third_party/grpc/src/core/iomgr/pollset_set_posix.h deleted file mode 100644 index 4820a61e4b29d0..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_set_posix.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 pollset_set_count; - size_t pollset_set_capacity; - struct grpc_pollset_set **pollset_sets; - - size_t fd_count; - size_t fd_capacity; - grpc_fd **fds; -} grpc_pollset_set; - -void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, grpc_fd *fd); -void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, grpc_fd *fd); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H */ diff --git a/third_party/grpc/src/core/iomgr/pollset_set_windows.c b/third_party/grpc/src/core/iomgr/pollset_set_windows.c deleted file mode 100644 index 157b46ec32a6a1..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_set_windows.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -void grpc_pollset_set_del_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -#endif /* GPR_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/pollset_set_windows.h b/third_party/grpc/src/core/iomgr/pollset_set_windows.h deleted file mode 100644 index cada0d2b61f213..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_set_windows.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/pollset_windows.c b/third_party/grpc/src/core/iomgr/pollset_windows.c deleted file mode 100644 index 02c667836316a4..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_windows.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_WINSOCK_SOCKET - -#include -#include - -#include "src/core/iomgr/timer_internal.h" -#include "src/core/iomgr/iomgr_internal.h" -#include "src/core/iomgr/iocp_windows.h" -#include "src/core/iomgr/pollset.h" -#include "src/core/iomgr/pollset_windows.h" - -gpr_mu grpc_polling_mu; -static grpc_pollset_worker *g_active_poller; -static grpc_pollset_worker g_global_root_worker; - -void grpc_pollset_global_init() { - gpr_mu_init(&grpc_polling_mu); - g_active_poller = NULL; - g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = - g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = - &g_global_root_worker; -} - -void grpc_pollset_global_shutdown() { gpr_mu_destroy(&grpc_polling_mu); } - -static void remove_worker(grpc_pollset_worker *worker, - grpc_pollset_worker_link_type type) { - worker->links[type].prev->links[type].next = worker->links[type].next; - worker->links[type].next->links[type].prev = worker->links[type].prev; - worker->links[type].next = worker->links[type].prev = worker; -} - -static int has_workers(grpc_pollset_worker *root, - grpc_pollset_worker_link_type type) { - return root->links[type].next != root; -} - -static grpc_pollset_worker *pop_front_worker( - grpc_pollset_worker *root, grpc_pollset_worker_link_type type) { - if (has_workers(root, type)) { - grpc_pollset_worker *w = root->links[type].next; - remove_worker(w, type); - return w; - } else { - return NULL; - } -} - -static void push_front_worker(grpc_pollset_worker *root, - grpc_pollset_worker_link_type type, - grpc_pollset_worker *worker) { - worker->links[type].prev = root; - worker->links[type].next = worker->links[type].prev->links[type].next; - worker->links[type].prev->links[type].next = - worker->links[type].next->links[type].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)); - pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = - pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = - &pollset->root_worker; -} - -void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - pollset->shutting_down = 1; - grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); - if (!pollset->is_iocp_worker) { - grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL); - } else { - pollset->on_shutdown = closure; - } -} - -void grpc_pollset_destroy(grpc_pollset *pollset) {} - -void grpc_pollset_reset(grpc_pollset *pollset) { - GPR_ASSERT(pollset->shutting_down); - GPR_ASSERT( - !has_workers(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET)); - pollset->shutting_down = 0; - pollset->is_iocp_worker = 0; - pollset->kicked_without_pollers = 0; - pollset->on_shutdown = NULL; -} - -void grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *worker, gpr_timespec now, - gpr_timespec deadline) { - int added_worker = 0; - worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = - worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = - worker->links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = - worker->links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = NULL; - worker->kicked = 0; - worker->pollset = pollset; - gpr_cv_init(&worker->cv); - if (grpc_timer_check(exec_ctx, now, &deadline)) { - goto done; - } - if (!pollset->kicked_without_pollers && !pollset->shutting_down) { - if (g_active_poller == NULL) { - grpc_pollset_worker *next_worker; - /* become poller */ - pollset->is_iocp_worker = 1; - g_active_poller = worker; - gpr_mu_unlock(&grpc_polling_mu); - grpc_iocp_work(exec_ctx, deadline); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&grpc_polling_mu); - pollset->is_iocp_worker = 0; - g_active_poller = NULL; - /* try to get a worker from this pollsets worker list */ - next_worker = pop_front_worker(&pollset->root_worker, - GRPC_POLLSET_WORKER_LINK_POLLSET); - if (next_worker == NULL) { - /* try to get a worker from the global list */ - next_worker = pop_front_worker(&g_global_root_worker, - GRPC_POLLSET_WORKER_LINK_GLOBAL); - } - if (next_worker != NULL) { - next_worker->kicked = 1; - gpr_cv_signal(&next_worker->cv); - } - - if (pollset->shutting_down && pollset->on_shutdown != NULL) { - grpc_exec_ctx_enqueue(exec_ctx, pollset->on_shutdown, true, NULL); - pollset->on_shutdown = NULL; - } - goto done; - } - push_front_worker(&g_global_root_worker, GRPC_POLLSET_WORKER_LINK_GLOBAL, - worker); - push_front_worker(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET, - worker); - added_worker = 1; - while (!worker->kicked) { - if (gpr_cv_wait(&worker->cv, &grpc_polling_mu, deadline)) { - break; - } - } - } else { - pollset->kicked_without_pollers = 0; - } -done: - if (!grpc_closure_list_empty(exec_ctx->closure_list)) { - gpr_mu_unlock(&grpc_polling_mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&grpc_polling_mu); - } - if (added_worker) { - remove_worker(worker, GRPC_POLLSET_WORKER_LINK_GLOBAL); - remove_worker(worker, GRPC_POLLSET_WORKER_LINK_POLLSET); - } - gpr_cv_destroy(&worker->cv); -} - -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.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next; - specific_worker != &p->root_worker; - specific_worker = - specific_worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next) { - specific_worker->kicked = 1; - gpr_cv_signal(&specific_worker->cv); - } - p->kicked_without_pollers = 1; - if (p->is_iocp_worker) { - grpc_iocp_kick(); - } - } else { - if (p->is_iocp_worker && g_active_poller == specific_worker) { - grpc_iocp_kick(); - } else { - specific_worker->kicked = 1; - gpr_cv_signal(&specific_worker->cv); - } - } - } else { - specific_worker = - pop_front_worker(&p->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET); - if (specific_worker != NULL) { - grpc_pollset_kick(p, specific_worker); - } else if (p->is_iocp_worker) { - grpc_iocp_kick(); - } else { - p->kicked_without_pollers = 1; - } - } -} - -void grpc_kick_poller(void) { grpc_iocp_kick(); } - -#endif /* GPR_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/pollset_windows.h b/third_party/grpc/src/core/iomgr/pollset_windows.h deleted file mode 100644 index 65ba80619b7743..00000000000000 --- a/third_party/grpc/src/core/iomgr/pollset_windows.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 enum { - GRPC_POLLSET_WORKER_LINK_POLLSET = 0, - GRPC_POLLSET_WORKER_LINK_GLOBAL, - GRPC_POLLSET_WORKER_LINK_TYPES -} grpc_pollset_worker_link_type; - -typedef struct grpc_pollset_worker_link { - struct grpc_pollset_worker *next; - struct grpc_pollset_worker *prev; -} grpc_pollset_worker_link; - -struct grpc_pollset; -typedef struct grpc_pollset grpc_pollset; - -typedef struct grpc_pollset_worker { - gpr_cv cv; - int kicked; - struct grpc_pollset *pollset; - grpc_pollset_worker_link links[GRPC_POLLSET_WORKER_LINK_TYPES]; -} grpc_pollset_worker; - -struct grpc_pollset { - int shutting_down; - int kicked_without_pollers; - int is_iocp_worker; - grpc_pollset_worker root_worker; - grpc_closure *on_shutdown; -}; - -extern gpr_mu grpc_polling_mu; - -#define GRPC_POLLSET_MU(pollset) (&grpc_polling_mu) - -#endif /* GRPC_INTERNAL_CORE_IOMGR_POLLSET_WINDOWS_H */ diff --git a/third_party/grpc/src/core/iomgr/resolve_address.h b/third_party/grpc/src/core/iomgr/resolve_address.h deleted file mode 100644 index 01eedffa889fae..00000000000000 --- a/third_party/grpc/src/core/iomgr/resolve_address.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 -#include "src/core/iomgr/exec_ctx.h" -#include "src/core/iomgr/iomgr.h" - -#define GRPC_MAX_SOCKADDR_SIZE 128 - -typedef struct { - char addr[GRPC_MAX_SOCKADDR_SIZE]; - size_t 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)(grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/iomgr/resolve_address_posix.c b/third_party/grpc/src/core/iomgr/resolve_address_posix.c deleted file mode 100644 index c51745b9187a0a..00000000000000 --- a/third_party/grpc/src/core/iomgr/resolve_address_posix.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/executor.h" -#include "src/core/iomgr/iomgr_internal.h" -#include "src/core/iomgr/sockaddr_utils.h" -#include "src/core/support/block_annotate.h" -#include "src/core/support/string.h" -#include -#include -#include -#include -#include -#include -#include - -typedef struct { - char *name; - char *default_port; - grpc_resolve_cb cb; - grpc_closure request_closure; - void *arg; -} 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 */ - - GRPC_SCHEDULING_START_BLOCKING_REGION; - s = getaddrinfo(host, port, &hints, &result); - GRPC_SCHEDULING_END_BLOCKING_REGION; - - if (s != 0) { - /* Retry if well-known service name is recognized */ - char *svc[][2] = {{"http", "80"}, {"https", "443"}}; - for (i = 0; i < GPR_ARRAY_SIZE(svc); i++) { - if (strcmp(port, svc[i][0]) == 0) { - GRPC_SCHEDULING_START_BLOCKING_REGION; - s = getaddrinfo(host, svc[i][1], &hints, &result); - GRPC_SCHEDULING_END_BLOCKING_REGION; - 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; -} - -/* Callback to be passed to grpc_executor to asynch-ify - * grpc_blocking_resolve_address */ -static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, bool success) { - 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(exec_ctx, arg, resolved); - 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)); - grpc_closure_init(&r->request_closure, do_request_thread, r); - r->name = gpr_strdup(name); - r->default_port = gpr_strdup(default_port); - r->cb = cb; - r->arg = arg; - grpc_executor_enqueue(&r->request_closure, 1); -} - -#endif diff --git a/third_party/grpc/src/core/iomgr/resolve_address_windows.c b/third_party/grpc/src/core/iomgr/resolve_address_windows.c deleted file mode 100644 index 28c8661e73b173..00000000000000 --- a/third_party/grpc/src/core/iomgr/resolve_address_windows.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#ifdef GPR_WINSOCK_SOCKET - -#include "src/core/iomgr/sockaddr.h" -#include "src/core/iomgr/resolve_address.h" - -#include -#include - -#include "src/core/iomgr/executor.h" -#include "src/core/iomgr/iomgr_internal.h" -#include "src/core/iomgr/sockaddr_utils.h" -#include "src/core/support/block_annotate.h" -#include "src/core/support/string.h" -#include -#include -#include -#include -#include -#include -#include - -typedef struct { - char *name; - char *default_port; - grpc_resolve_cb cb; - grpc_closure request_closure; - void *arg; -} 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 */ - - GRPC_SCHEDULING_START_BLOCKING_REGION; - s = getaddrinfo(host, port, &hints, &result); - GRPC_SCHEDULING_END_BLOCKING_REGION; - if (s != 0) { - char *error_message = gpr_format_message(s); - gpr_log(GPR_ERROR, "getaddrinfo: %s", error_message); - gpr_free(error_message); - 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; -} - -/* Callback to be passed to grpc_executor to asynch-ify - * grpc_blocking_resolve_address */ -static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, bool success) { - 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(exec_ctx, arg, resolved); - 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)); - grpc_closure_init(&r->request_closure, do_request_thread, r); - r->name = gpr_strdup(name); - r->default_port = gpr_strdup(default_port); - r->cb = cb; - r->arg = arg; - grpc_executor_enqueue(&r->request_closure, 1); -} - -#endif diff --git a/third_party/grpc/src/core/iomgr/sockaddr.h b/third_party/grpc/src/core/iomgr/sockaddr.h deleted file mode 100644 index e41e1ec6b48ed8..00000000000000 --- a/third_party/grpc/src/core/iomgr/sockaddr.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/sockaddr_posix.h b/third_party/grpc/src/core/iomgr/sockaddr_posix.h deleted file mode 100644 index 388abb33066915..00000000000000 --- a/third_party/grpc/src/core/iomgr/sockaddr_posix.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/sockaddr_utils.c b/third_party/grpc/src/core/iomgr/sockaddr_utils.c deleted file mode 100644 index 61006d7a7aab2c..00000000000000 --- a/third_party/grpc/src/core/iomgr/sockaddr_utils.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 uint8_t 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) { - GPR_ASSERT(port >= 0 && port < 65536); - memset(wild_out, 0, sizeof(*wild_out)); - wild_out->sin_family = AF_INET; - wild_out->sin_port = htons((uint16_t)port); -} - -void grpc_sockaddr_make_wildcard6(int port, struct sockaddr_in6 *wild_out) { - GPR_ASSERT(port >= 0 && port < 65536); - memset(wild_out, 0, sizeof(*wild_out)); - wild_out->sin6_family = AF_INET6; - wild_out->sin6_port = htons((uint16_t)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); - } - /* Windows inet_ntop wants a mutable ip pointer */ - if (ip != NULL && - inet_ntop(addr->sa_family, (void *)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: - GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in *)addr)->sin_port = htons((uint16_t)port); - return 1; - case AF_INET6: - GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in6 *)addr)->sin6_port = htons((uint16_t)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/third_party/grpc/src/core/iomgr/sockaddr_utils.h b/third_party/grpc/src/core/iomgr/sockaddr_utils.h deleted file mode 100644 index 6f7a279900dff0..00000000000000 --- a/third_party/grpc/src/core/iomgr/sockaddr_utils.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/sockaddr_win32.h b/third_party/grpc/src/core/iomgr/sockaddr_win32.h deleted file mode 100644 index 8e3946a7d847ce..00000000000000 --- a/third_party/grpc/src/core/iomgr/sockaddr_win32.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H -#define GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H - -#include -#include -#include - -#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKADDR_WIN32_H */ diff --git a/third_party/grpc/src/core/iomgr/socket_utils_common_posix.c b/third_party/grpc/src/core/iomgr/socket_utils_common_posix.c deleted file mode 100644 index a9af5947009188..00000000000000 --- a/third_party/grpc/src/core/iomgr/socket_utils_common_posix.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/socket_utils_linux.c b/third_party/grpc/src/core/iomgr/socket_utils_linux.c deleted file mode 100644 index a87625262b26d5..00000000000000 --- a/third_party/grpc/src/core/iomgr/socket_utils_linux.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/socket_utils_posix.c b/third_party/grpc/src/core/iomgr/socket_utils_posix.c deleted file mode 100644 index 3c56b4674431fb..00000000000000 --- a/third_party/grpc/src/core/iomgr/socket_utils_posix.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/socket_utils_posix.h b/third_party/grpc/src/core/iomgr/socket_utils_posix.h deleted file mode 100644 index d330d1986ebd54..00000000000000 --- a/third_party/grpc/src/core/iomgr/socket_utils_posix.h +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/socket_windows.c b/third_party/grpc/src/core/iomgr/socket_windows.c deleted file mode 100644 index fafb7b6622b593..00000000000000 --- a/third_party/grpc/src/core/iomgr/socket_windows.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/tcp_client.h b/third_party/grpc/src/core/iomgr/tcp_client.h deleted file mode 100644 index 5e18e71ca2fde8..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_client.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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(grpc_exec_ctx *exec_ctx, grpc_closure *on_connect, - grpc_endpoint **endpoint, - grpc_pollset_set *interested_parties, - const struct sockaddr *addr, size_t addr_len, - gpr_timespec deadline); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_CLIENT_H */ diff --git a/third_party/grpc/src/core/iomgr/tcp_client_posix.c b/third_party/grpc/src/core/iomgr/tcp_client_posix.c deleted file mode 100644 index c76c2e3b0f2cd7..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_client_posix.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_POSIX_SOCKET - -#include "src/core/iomgr/tcp_client.h" - -#include -#include -#include -#include - -#include "src/core/iomgr/timer.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 - -extern int grpc_tcp_trace; - -typedef struct { - gpr_mu mu; - grpc_fd *fd; - gpr_timespec deadline; - grpc_timer alarm; - int refs; - grpc_closure write_closure; - grpc_pollset_set *interested_parties; - char *addr_str; - grpc_endpoint **ep; - grpc_closure *closure; -} 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(grpc_exec_ctx *exec_ctx, void *acp, bool success) { - int done; - async_connect *ac = acp; - if (grpc_tcp_trace) { - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: success=%d", ac->addr_str, - success); - } - gpr_mu_lock(&ac->mu); - if (ac->fd != NULL) { - grpc_fd_shutdown(exec_ctx, 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(grpc_exec_ctx *exec_ctx, void *acp, bool success) { - async_connect *ac = acp; - int so_error = 0; - socklen_t so_error_size; - int err; - int done; - grpc_endpoint **ep = ac->ep; - grpc_closure *closure = ac->closure; - grpc_fd *fd; - - if (grpc_tcp_trace) { - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: success=%d", - ac->addr_str, success); - } - - gpr_mu_lock(&ac->mu); - GPR_ASSERT(ac->fd); - fd = ac->fd; - ac->fd = NULL; - gpr_mu_unlock(&ac->mu); - - grpc_timer_cancel(exec_ctx, &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, "failed to connect to '%s': getsockopt(ERROR): %s", - ac->addr_str, 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(exec_ctx, fd, &ac->write_closure); - return; - } else { - switch (so_error) { - case ECONNREFUSED: - gpr_log( - GPR_ERROR, - "failed to connect to '%s': socket error: connection refused", - ac->addr_str); - break; - default: - gpr_log(GPR_ERROR, "failed to connect to '%s': socket error: %d", - ac->addr_str, so_error); - break; - } - goto finish; - } - } else { - grpc_pollset_set_del_fd(exec_ctx, 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, "failed to connect to '%s': timeout occurred", - ac->addr_str); - goto finish; - } - - GPR_UNREACHABLE_CODE(return ); - -finish: - if (fd != NULL) { - grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); - grpc_fd_orphan(exec_ctx, fd, NULL, 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); - } - grpc_exec_ctx_enqueue(exec_ctx, closure, *ep != NULL, NULL); -} - -void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_endpoint **ep, - grpc_pollset_set *interested_parties, - const struct sockaddr *addr, size_t 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; - - *ep = NULL; - - /* 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)) { - grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL); - return; - } - - do { - GPR_ASSERT(addr_len < ~(socklen_t)0); - err = connect(fd, addr, (socklen_t)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) { - *ep = grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str); - grpc_exec_ctx_enqueue(exec_ctx, closure, true, NULL); - goto done; - } - - if (errno != EWOULDBLOCK && errno != EINPROGRESS) { - gpr_log(GPR_ERROR, "connect error to '%s': %s", addr_str, strerror(errno)); - grpc_fd_orphan(exec_ctx, fdobj, NULL, NULL, "tcp_client_connect_error"); - grpc_exec_ctx_enqueue(exec_ctx, closure, false, NULL); - goto done; - } - - grpc_pollset_set_add_fd(exec_ctx, interested_parties, fdobj); - - ac = gpr_malloc(sizeof(async_connect)); - ac->closure = closure; - ac->ep = ep; - 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; - - if (grpc_tcp_trace) { - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting", - ac->addr_str); - } - - gpr_mu_lock(&ac->mu); - grpc_timer_init(exec_ctx, &ac->alarm, - gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), - tc_on_alarm, ac, gpr_now(GPR_CLOCK_MONOTONIC)); - grpc_fd_notify_on_write(exec_ctx, ac->fd, &ac->write_closure); - gpr_mu_unlock(&ac->mu); - -done: - gpr_free(name); - gpr_free(addr_str); -} - -#endif diff --git a/third_party/grpc/src/core/iomgr/tcp_client_windows.c b/third_party/grpc/src/core/iomgr/tcp_client_windows.c deleted file mode 100644 index 689c6f7b104a8a..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_client_windows.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_WINSOCK_SOCKET - -#include "src/core/iomgr/sockaddr_win32.h" - -#include -#include -#include -#include -#include - -#include "src/core/iomgr/timer.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 { - grpc_closure *on_done; - gpr_mu mu; - grpc_winsocket *socket; - gpr_timespec deadline; - grpc_timer alarm; - char *addr_name; - int refs; - grpc_closure on_connect; - grpc_endpoint **endpoint; -} 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(grpc_exec_ctx *exec_ctx, void *acp, bool 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(grpc_exec_ctx *exec_ctx, void *acp, bool from_iocp) { - async_connect *ac = acp; - SOCKET sock = ac->socket->socket; - grpc_endpoint **ep = ac->endpoint; - grpc_winsocket_callback_info *info = &ac->socket->write_info; - grpc_closure *on_done = ac->on_done; - - grpc_timer_cancel(exec_ctx, &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. */ - on_done->cb(exec_ctx, on_done->cb_arg, *ep != NULL); -} - -/* 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(grpc_exec_ctx *exec_ctx, grpc_closure *on_done, - grpc_endpoint **endpoint, - grpc_pollset_set *interested_parties, - const struct sockaddr *addr, size_t 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; - - *endpoint = NULL; - - /* 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, (int)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->on_done = on_done; - ac->socket = socket; - gpr_mu_init(&ac->mu); - ac->refs = 2; - ac->addr_name = grpc_sockaddr_to_uri(addr); - ac->endpoint = endpoint; - grpc_closure_init(&ac->on_connect, on_connect, ac); - - grpc_timer_init(exec_ctx, &ac->alarm, deadline, on_alarm, ac, - gpr_now(GPR_CLOCK_MONOTONIC)); - grpc_socket_notify_on_write(exec_ctx, socket, &ac->on_connect); - 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); - } - grpc_exec_ctx_enqueue(exec_ctx, on_done, false, NULL); -} - -#endif /* GPR_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/tcp_posix.c b/third_party/grpc/src/core/iomgr/tcp_posix.c deleted file mode 100644 index 048e90744121fd..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_posix.c +++ /dev/null @@ -1,491 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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 - -#ifdef GPR_MSG_IOVLEN_TYPE -typedef GPR_MSG_IOVLEN_TYPE msg_iovlen_type; -#else -typedef size_t msg_iovlen_type; -#endif - -int grpc_tcp_trace = 0; - -typedef struct { - grpc_endpoint base; - grpc_fd *em_fd; - int fd; - int finished_edge; - msg_iovlen_type iov_size; /* Number of slices to allocate per read attempt */ - size_t slice_size; - gpr_refcount refcount; - - /* garbage after the last read */ - gpr_slice_buffer last_read_buffer; - - 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_closure *read_cb; - grpc_closure *write_cb; - grpc_closure *release_fd_cb; - int *release_fd; - - grpc_closure read_closure; - grpc_closure write_closure; - - char *peer_string; -} grpc_tcp; - -static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - bool success); -static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - bool success); - -static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_fd_shutdown(exec_ctx, tcp->em_fd); -} - -static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - grpc_fd_orphan(exec_ctx, tcp->em_fd, tcp->release_fd_cb, tcp->release_fd, - "tcp_unref_orphan"); - gpr_slice_buffer_destroy(&tcp->last_read_buffer); - gpr_free(tcp->peer_string); - gpr_free(tcp); -} - -/*#define GRPC_TCP_REFCOUNT_DEBUG*/ -#ifdef GRPC_TCP_REFCOUNT_DEBUG -#define TCP_UNREF(cl, tcp, reason) \ - tcp_unref((cl), (tcp), (reason), __FILE__, __LINE__) -#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) -static void tcp_unref(grpc_exec_ctx *exec_ctx, 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(exec_ctx, 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(cl, tcp, reason) tcp_unref((cl), (tcp)) -#define TCP_REF(tcp, reason) tcp_ref((tcp)) -static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - if (gpr_unref(&tcp->refcount)) { - tcp_free(exec_ctx, tcp); - } -} - -static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } -#endif - -static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - TCP_UNREF(exec_ctx, tcp, "destroy"); -} - -static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, int success) { - grpc_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(exec_ctx, cb->cb_arg, success); -} - -#define MAX_READ_IOVEC 4 -static void tcp_continue_read(grpc_exec_ctx *exec_ctx, 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); - GPR_TIMER_BEGIN("tcp_continue_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; - - GPR_TIMER_BEGIN("recvmsg", 1); - do { - read_bytes = recvmsg(tcp->fd, &msg, 0); - } while (read_bytes < 0 && errno == EINTR); - GPR_TIMER_END("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(exec_ctx, tcp->em_fd, &tcp->read_closure); - } else { - /* TODO(klempner): Log interesting errors */ - gpr_slice_buffer_reset_and_unref(tcp->incoming_buffer); - call_read_cb(exec_ctx, tcp, 0); - TCP_UNREF(exec_ctx, 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(exec_ctx, tcp, 0); - TCP_UNREF(exec_ctx, 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 - (size_t)read_bytes, - &tcp->last_read_buffer); - } else if (tcp->iov_size < MAX_READ_IOVEC) { - ++tcp->iov_size; - } - GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length); - call_read_cb(exec_ctx, tcp, 1); - TCP_UNREF(exec_ctx, tcp, "read"); - } - - GPR_TIMER_END("tcp_continue_read", 0); -} - -static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - bool 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(exec_ctx, tcp, 0); - TCP_UNREF(exec_ctx, tcp, "read"); - } else { - tcp_continue_read(exec_ctx, tcp); - } -} - -static void tcp_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *incoming_buffer, grpc_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); - gpr_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer); - TCP_REF(tcp, "read"); - if (tcp->finished_edge) { - tcp->finished_edge = 0; - grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_closure); - } else { - grpc_exec_ctx_enqueue(exec_ctx, &tcp->read_closure, true, NULL); - } -} - -typedef enum { FLUSH_DONE, FLUSH_PENDING, FLUSH_ERROR } flush_result; - -#define MAX_WRITE_IOVEC 16 -static flush_result tcp_flush(grpc_tcp *tcp) { - struct msghdr msg; - struct iovec iov[MAX_WRITE_IOVEC]; - msg_iovlen_type iov_size; - ssize_t sent_length; - size_t sending_length; - size_t trailing; - size_t unwind_slice_idx; - size_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; - - GPR_TIMER_BEGIN("sendmsg", 1); - do { - /* TODO(klempner): Cork if this is a partial write */ - sent_length = sendmsg(tcp->fd, &msg, SENDMSG_FLAGS); - } while (sent_length < 0 && errno == EINTR); - GPR_TIMER_END("sendmsg", 0); - - if (sent_length < 0) { - if (errno == EAGAIN) { - tcp->outgoing_slice_idx = unwind_slice_idx; - tcp->outgoing_byte_idx = unwind_byte_idx; - return FLUSH_PENDING; - } else { - /* TODO(klempner): Log some of these */ - return FLUSH_ERROR; - } - } - - GPR_ASSERT(tcp->outgoing_byte_idx == 0); - trailing = sending_length - (size_t)sent_length; - while (trailing > 0) { - size_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 FLUSH_DONE; - } - }; -} - -static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - bool success) { - grpc_tcp *tcp = (grpc_tcp *)arg; - flush_result status; - grpc_closure *cb; - - if (!success) { - cb = tcp->write_cb; - tcp->write_cb = NULL; - cb->cb(exec_ctx, cb->cb_arg, 0); - TCP_UNREF(exec_ctx, tcp, "write"); - return; - } - - status = tcp_flush(tcp); - if (status == FLUSH_PENDING) { - grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure); - } else { - cb = tcp->write_cb; - tcp->write_cb = NULL; - GPR_TIMER_BEGIN("tcp_handle_write.cb", 0); - cb->cb(exec_ctx, cb->cb_arg, status == FLUSH_DONE); - GPR_TIMER_END("tcp_handle_write.cb", 0); - TCP_UNREF(exec_ctx, tcp, "write"); - } -} - -static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *buf, grpc_closure *cb) { - grpc_tcp *tcp = (grpc_tcp *)ep; - flush_result 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); - } - } - - GPR_TIMER_BEGIN("tcp_write", 0); - GPR_ASSERT(tcp->write_cb == NULL); - - if (buf->length == 0) { - GPR_TIMER_END("tcp_write", 0); - grpc_exec_ctx_enqueue(exec_ctx, cb, true, NULL); - return; - } - tcp->outgoing_buffer = buf; - tcp->outgoing_slice_idx = 0; - tcp->outgoing_byte_idx = 0; - - status = tcp_flush(tcp); - if (status == FLUSH_PENDING) { - TCP_REF(tcp, "write"); - tcp->write_cb = cb; - grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_closure); - } else { - grpc_exec_ctx_enqueue(exec_ctx, cb, status == FLUSH_DONE, NULL); - } - - GPR_TIMER_END("tcp_write", 0); -} - -static void tcp_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset *pollset) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_pollset_add_fd(exec_ctx, pollset, tcp->em_fd); -} - -static void tcp_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset_set *pollset_set) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_pollset_set_add_fd(exec_ctx, 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->release_fd_cb = NULL; - tcp->release_fd = 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; - gpr_slice_buffer_init(&tcp->last_read_buffer); - - return &tcp->base; -} - -int grpc_tcp_fd(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - GPR_ASSERT(ep->vtable == &vtable); - return grpc_fd_wrapped_fd(tcp->em_fd); -} - -void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - int *fd, grpc_closure *done) { - grpc_tcp *tcp = (grpc_tcp *)ep; - GPR_ASSERT(ep->vtable == &vtable); - tcp->release_fd = fd; - tcp->release_fd_cb = done; - TCP_UNREF(exec_ctx, tcp, "destroy"); -} - -#endif diff --git a/third_party/grpc/src/core/iomgr/tcp_posix.h b/third_party/grpc/src/core/iomgr/tcp_posix.h deleted file mode 100644 index 2a40cdd38590b2..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_posix.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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); - -/* Return the tcp endpoint's fd, or -1 if this is not available. Does not - release the fd. - Requires: ep must be a tcp endpoint. - */ -int grpc_tcp_fd(grpc_endpoint *ep); - -/* Destroy the tcp endpoint without closing its fd. *fd will be set and done - * will be called when the endpoint is destroyed. - * Requires: ep must be a tcp endpoint and fd must not be NULL. */ -void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - int *fd, grpc_closure *done); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_POSIX_H */ diff --git a/third_party/grpc/src/core/iomgr/tcp_server.h b/third_party/grpc/src/core/iomgr/tcp_server.h deleted file mode 100644 index a39dd3bafce4a5..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_server.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H -#define GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H - -#include "src/core/iomgr/closure.h" -#include "src/core/iomgr/endpoint.h" - -/* Forward decl of grpc_tcp_server */ -typedef struct grpc_tcp_server grpc_tcp_server; - -typedef struct grpc_tcp_server_acceptor { - /* grpc_tcp_server_cb functions share a ref on from_server that is valid - until the function returns. */ - grpc_tcp_server *from_server; - /* Indices that may be passed to grpc_tcp_server_port_fd(). */ - unsigned port_index; - unsigned fd_index; -} grpc_tcp_server_acceptor; - -/* Called for newly connected TCP connections. */ -typedef void (*grpc_tcp_server_cb)(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *ep, - grpc_tcp_server_acceptor *acceptor); - -/* Create a server, initially not bound to any ports. The caller owns one ref. - If shutdown_complete is not NULL, it will be used by - grpc_tcp_server_unref() when the ref count reaches zero. */ -grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete); - -/* Start listening to bound ports */ -void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, - grpc_pollset **pollsets, size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, void *cb_arg); - -/* Add a port to the server, returning the newly allocated port on success, or - -1 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_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, - size_t addr_len); - -/* Number of fds at the given port_index, or 0 if port_index is out of - bounds. */ -unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server *s, unsigned port_index); - -/* Returns the file descriptor of the Mth (fd_index) listening socket of the Nth - (port_index) call to add_port() on this server, or -1 if the indices are out - of bounds. The file descriptor remains owned by the server, and will be - cleaned up when the ref count reaches zero. */ -int grpc_tcp_server_port_fd(grpc_tcp_server *s, unsigned port_index, - unsigned fd_index); - -/* Ref s and return s. */ -grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s); - -/* shutdown_starting is called when ref count has reached zero and the server is - about to be destroyed. The server will be deleted after it returns. Calling - grpc_tcp_server_ref() from it has no effect. */ -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, - grpc_closure *shutdown_starting); - -/* If the refcount drops to zero, delete s, and call (exec_ctx==NULL) or enqueue - a call (exec_ctx!=NULL) to shutdown_complete. */ -void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_TCP_SERVER_H */ diff --git a/third_party/grpc/src/core/iomgr/tcp_server_posix.c b/third_party/grpc/src/core/iomgr/tcp_server_posix.c deleted file mode 100644 index 44b970d72e4168..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_server_posix.c +++ /dev/null @@ -1,644 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* 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 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 grpc_tcp_listener grpc_tcp_listener; -struct grpc_tcp_listener { - int fd; - grpc_fd *emfd; - grpc_tcp_server *server; - union { - uint8_t untyped[GRPC_MAX_SOCKADDR_SIZE]; - struct sockaddr sockaddr; - struct sockaddr_un un; - } addr; - size_t addr_len; - int port; - unsigned port_index; - unsigned fd_index; - grpc_closure read_closure; - grpc_closure destroyed_closure; - struct grpc_tcp_listener *next; - /* When we add a listener, more than one can be created, mainly because of - IPv6. A sibling will still be in the normal list, but will be flagged - as such. Any action, such as ref or unref, will affect all of the - siblings in the list. */ - struct grpc_tcp_listener *sibling; - int is_sibling; -}; - -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 { - gpr_refcount refs; - /* Called whenever accept() succeeds on a server port. */ - grpc_tcp_server_cb on_accept_cb; - void *on_accept_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; - - /* linked list of server ports */ - grpc_tcp_listener *head; - grpc_tcp_listener *tail; - unsigned nports; - - /* List of closures passed to shutdown_starting_add(). */ - grpc_closure_list shutdown_starting; - - /* shutdown callback */ - grpc_closure *shutdown_complete; - - /* 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(grpc_closure *shutdown_complete) { - grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); - gpr_ref_init(&s->refs, 1); - gpr_mu_init(&s->mu); - s->active_ports = 0; - s->destroyed_ports = 0; - s->shutdown = 0; - s->shutdown_starting.head = NULL; - s->shutdown_starting.tail = NULL; - s->shutdown_complete = shutdown_complete; - s->on_accept_cb = NULL; - s->on_accept_cb_arg = NULL; - s->head = NULL; - s->tail = NULL; - s->nports = 0; - return s; -} - -static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - if (s->shutdown_complete != NULL) { - grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, true, NULL); - } - - gpr_mu_destroy(&s->mu); - - while (s->head) { - grpc_tcp_listener *sp = s->head; - s->head = sp->next; - gpr_free(sp); - } - - gpr_free(s); -} - -static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, - bool 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(exec_ctx, s); - } else { - GPR_ASSERT(s->destroyed_ports < s->nports); - gpr_mu_unlock(&s->mu); - } -} - -/* 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_exec_ctx *exec_ctx, grpc_tcp_server *s) { - /* delete ALL the things */ - gpr_mu_lock(&s->mu); - - if (!s->shutdown) { - gpr_mu_unlock(&s->mu); - return; - } - - if (s->head) { - grpc_tcp_listener *sp; - for (sp = s->head; sp; sp = sp->next) { - 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(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL, - "tcp_listener_shutdown"); - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - finish_shutdown(exec_ctx, s); - } -} - -static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - gpr_mu_lock(&s->mu); - - GPR_ASSERT(!s->shutdown); - s->shutdown = 1; - - /* shutdown all fd's */ - if (s->active_ports) { - grpc_tcp_listener *sp; - for (sp = s->head; sp; sp = sp->next) { - grpc_fd_shutdown(exec_ctx, sp->emfd); - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - deactivated_all_ports(exec_ctx, 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 = (int)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, - size_t addr_len) { - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; - - if (fd < 0) { - goto error; - } - - - if (!grpc_set_socket_nonblocking(fd, 1)) { - gpr_log(GPR_ERROR, "Unable to set socket %d to nonblocking mode: %s", fd, - strerror(errno)); - goto error; - } - if (!grpc_set_socket_cloexec(fd, 1)) { - gpr_log(GPR_ERROR, "Unable to set socket %d to close-on-exec: %s", fd, - strerror(errno)); - goto error; - } - - if (addr->sa_family != AF_UNIX) { - if (!grpc_set_socket_low_latency(fd, 1)) { - gpr_log(GPR_ERROR, "Unable to set socket %d to low-latency mode: %s", fd, - strerror(errno)); - goto error; - } - -#if !defined(__MSYS__) - if (!grpc_set_socket_reuse_addr(fd, 1)) { - gpr_log(GPR_ERROR, "Unable to set SO_REUSEADDR on socket %d: %s", fd, - strerror(errno)); - goto error; - } -#endif - - if (!grpc_set_socket_no_sigpipe_if_possible(fd)) { - gpr_log(GPR_ERROR, "Unable to set SO_NOSIGPIPE on socket %d: %s", fd, - strerror(errno)); - goto error; - } - } - - GPR_ASSERT(addr_len < ~(socklen_t)0); - if (bind(fd, addr, (socklen_t)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(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - grpc_tcp_listener *sp = arg; - grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index, - sp->fd_index}; - 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(exec_ctx, 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); - - if (grpc_tcp_trace) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming 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(exec_ctx, sp->server->pollsets[i], fdobj); - } - sp->server->on_accept_cb( - exec_ctx, sp->server->on_accept_cb_arg, - grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str), - &acceptor); - - gpr_free(name); - gpr_free(addr_str); - } - - GPR_UNREACHABLE_CODE(return ); - -error: - gpr_mu_lock(&sp->server->mu); - if (0 == --sp->server->active_ports) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(exec_ctx, sp->server); - } else { - gpr_mu_unlock(&sp->server->mu); - } -} - -static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd, - const struct sockaddr *addr, - size_t addr_len, - unsigned port_index, - unsigned fd_index) { - grpc_tcp_listener *sp = NULL; - 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); - s->nports++; - GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = gpr_malloc(sizeof(grpc_tcp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - 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->port = port; - sp->port_index = port_index; - sp->fd_index = fd_index; - sp->is_sibling = 0; - sp->sibling = NULL; - GPR_ASSERT(sp->emfd); - gpr_mu_unlock(&s->mu); - gpr_free(addr_str); - gpr_free(name); - } - - return sp; -} - -int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - size_t addr_len) { - grpc_tcp_listener *sp; - grpc_tcp_listener *sp2 = NULL; - 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; - unsigned port_index = 0; - unsigned fd_index = 0; - if (s->tail != NULL) { - port_index = s->tail->port_index + 1; - } - 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 (sp = s->head; sp; sp = sp->next) { - sockname_len = sizeof(sockname_temp); - if (0 == getsockname(sp->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; - } - } - } - } - - sp = NULL; - - 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); - sp = add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index); - if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { - goto done; - } - if (sp != NULL) { - ++fd_index; - } - /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ - if (port == 0 && sp != NULL) { - grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port); - sp2 = sp; - } - 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); - } - sp = add_socket_to_server(s, fd, addr, addr_len, port_index, fd_index); - if (sp2 != NULL && sp != NULL) { - sp2->sibling = sp; - sp->is_sibling = 1; - } - -done: - gpr_free(allocated_addr); - if (sp != NULL) { - return sp->port; - } else { - return -1; - } -} - -unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server *s, - unsigned port_index) { - unsigned num_fds = 0; - grpc_tcp_listener *sp; - for (sp = s->head; sp && port_index != 0; sp = sp->next) { - if (!sp->is_sibling) { - --port_index; - } - } - for (; sp; sp = sp->sibling, ++num_fds) - ; - return num_fds; -} - -int grpc_tcp_server_port_fd(grpc_tcp_server *s, unsigned port_index, - unsigned fd_index) { - grpc_tcp_listener *sp; - for (sp = s->head; sp && port_index != 0; sp = sp->next) { - if (!sp->is_sibling) { - --port_index; - } - } - for (; sp && fd_index != 0; sp = sp->sibling, --fd_index) - ; - if (sp) { - return sp->fd; - } else { - return -1; - } -} - -void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, - grpc_pollset **pollsets, size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void *on_accept_cb_arg) { - size_t i; - grpc_tcp_listener *sp; - GPR_ASSERT(on_accept_cb); - gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->on_accept_cb); - GPR_ASSERT(s->active_ports == 0); - s->on_accept_cb = on_accept_cb; - s->on_accept_cb_arg = on_accept_cb_arg; - s->pollsets = pollsets; - s->pollset_count = pollset_count; - for (sp = s->head; sp; sp = sp->next) { - for (i = 0; i < pollset_count; i++) { - grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); - } - sp->read_closure.cb = on_read; - sp->read_closure.cb_arg = sp; - grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); - s->active_ports++; - } - gpr_mu_unlock(&s->mu); -} - -grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { - gpr_ref(&s->refs); - return s; -} - -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, - grpc_closure *shutdown_starting) { - gpr_mu_lock(&s->mu); - grpc_closure_list_add(&s->shutdown_starting, shutdown_starting, 1); - gpr_mu_unlock(&s->mu); -} - -void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - if (gpr_unref(&s->refs)) { - /* Complete shutdown_starting work before destroying. */ - grpc_exec_ctx local_exec_ctx = GRPC_EXEC_CTX_INIT; - gpr_mu_lock(&s->mu); - grpc_exec_ctx_enqueue_list(&local_exec_ctx, &s->shutdown_starting, NULL); - gpr_mu_unlock(&s->mu); - if (exec_ctx == NULL) { - grpc_exec_ctx_flush(&local_exec_ctx); - tcp_server_destroy(&local_exec_ctx, s); - grpc_exec_ctx_finish(&local_exec_ctx); - } else { - grpc_exec_ctx_finish(&local_exec_ctx); - tcp_server_destroy(exec_ctx, s); - } - } -} - -#endif diff --git a/third_party/grpc/src/core/iomgr/tcp_server_windows.c b/third_party/grpc/src/core/iomgr/tcp_server_windows.c deleted file mode 100644 index ce930b8f41d4e9..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_server_windows.c +++ /dev/null @@ -1,558 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_WINSOCK_SOCKET - -#include - -#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 MIN_SAFE_ACCEPT_QUEUE_SIZE 100 - -/* one listening port */ -typedef struct grpc_tcp_listener grpc_tcp_listener; -struct grpc_tcp_listener { - /* This seemingly magic number comes from AcceptEx's documentation. each - address buffer needs to have at least 16 more bytes at their end. */ - uint8_t addresses[(sizeof(struct sockaddr_in6) + 16) * 2]; - /* This will hold the socket for the next accept. */ - SOCKET new_socket; - /* The listener winsocket. */ - grpc_winsocket *socket; - /* The actual TCP port number. */ - int port; - unsigned port_index; - grpc_tcp_server *server; - /* The cached AcceptEx for that port. */ - LPFN_ACCEPTEX AcceptEx; - int shutting_down; - /* closure for socket notification of accept being ready */ - grpc_closure on_accept; - /* linked list */ - struct grpc_tcp_listener *next; -}; - -/* the overall server */ -struct grpc_tcp_server { - gpr_refcount refs; - /* Called whenever accept() succeeds on a server port. */ - grpc_tcp_server_cb on_accept_cb; - void *on_accept_cb_arg; - - gpr_mu mu; - - /* active port count: how many ports are actually still listening */ - int active_ports; - - /* linked list of server ports */ - grpc_tcp_listener *head; - grpc_tcp_listener *tail; - - /* List of closures passed to shutdown_starting_add(). */ - grpc_closure_list shutdown_starting; - - /* shutdown callback */ - grpc_closure *shutdown_complete; -}; - -/* Public function. Allocates the proper data structures to hold a - grpc_tcp_server. */ -grpc_tcp_server *grpc_tcp_server_create(grpc_closure *shutdown_complete) { - grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); - gpr_ref_init(&s->refs, 1); - gpr_mu_init(&s->mu); - s->active_ports = 0; - s->on_accept_cb = NULL; - s->on_accept_cb_arg = NULL; - s->head = NULL; - s->tail = NULL; - s->shutdown_starting.head = NULL; - s->shutdown_starting.tail = NULL; - s->shutdown_complete = shutdown_complete; - return s; -} - -static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - if (s->shutdown_complete != NULL) { - grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, true, NULL); - } - - /* 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. */ - while (s->head) { - grpc_tcp_listener *sp = s->head; - s->head = sp->next; - sp->next = NULL; - grpc_winsocket_destroy(sp->socket); - gpr_free(sp); - } - gpr_free(s); -} - -grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { - gpr_ref(&s->refs); - return s; -} - -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, - grpc_closure *shutdown_starting) { - gpr_mu_lock(&s->mu); - grpc_closure_list_add(&s->shutdown_starting, shutdown_starting, 1); - gpr_mu_unlock(&s->mu); -} - -static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - int immediately_done = 0; - grpc_tcp_listener *sp; - gpr_mu_lock(&s->mu); - - /* 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 (sp = s->head; sp; sp = sp->next) { - sp->shutting_down = 1; - grpc_winsocket_shutdown(sp->socket); - } - gpr_mu_unlock(&s->mu); - - if (immediately_done) { - finish_shutdown(exec_ctx, s); - } -} - -void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - if (gpr_unref(&s->refs)) { - /* Complete shutdown_starting work before destroying. */ - grpc_exec_ctx local_exec_ctx = GRPC_EXEC_CTX_INIT; - gpr_mu_lock(&s->mu); - grpc_exec_ctx_enqueue_list(&local_exec_ctx, &s->shutdown_starting, NULL); - gpr_mu_unlock(&s->mu); - if (exec_ctx == NULL) { - grpc_exec_ctx_flush(&local_exec_ctx); - tcp_server_destroy(&local_exec_ctx, s); - grpc_exec_ctx_finish(&local_exec_ctx); - } else { - grpc_exec_ctx_finish(&local_exec_ctx); - tcp_server_destroy(exec_ctx, s); - } - } -} - -/* Prepare (bind) a recently-created socket for listening. */ -static int prepare_socket(SOCKET sock, const struct sockaddr *addr, - size_t 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, (int)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(grpc_exec_ctx *exec_ctx, - grpc_tcp_listener *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(exec_ctx, sp->server); - } -} - -/* 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(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *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(exec_ctx, port->socket, &port->on_accept); - 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(exec_ctx, 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(grpc_exec_ctx *exec_ctx, void *arg, bool from_iocp) { - grpc_tcp_listener *sp = arg; - grpc_tcp_server_acceptor acceptor = {sp->server, sp->port_index, 0}; - 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(exec_ctx, 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); - } else { - closesocket(sock); - } - } - - /* 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->on_accept_cb(exec_ctx, sp->server->on_accept_cb_arg, ep, - &acceptor); - /* 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(exec_ctx, sp); -} - -static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, SOCKET sock, - const struct sockaddr *addr, - size_t addr_len, - unsigned port_index) { - grpc_tcp_listener *sp = NULL; - int port; - int status; - GUID guid = WSAID_ACCEPTEX; - DWORD ioctl_num_bytes; - LPFN_ACCEPTEX AcceptEx; - - if (sock == INVALID_SOCKET) return NULL; - - /* 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 NULL; - } - - port = prepare_socket(sock, addr, addr_len); - if (port >= 0) { - gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = gpr_malloc(sizeof(grpc_tcp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->socket = grpc_winsocket_create(sock, "listener"); - sp->shutting_down = 0; - sp->AcceptEx = AcceptEx; - sp->new_socket = INVALID_SOCKET; - sp->port = port; - sp->port_index = port_index; - grpc_closure_init(&sp->on_accept, on_accept, sp); - GPR_ASSERT(sp->socket); - gpr_mu_unlock(&s->mu); - } - - return sp; -} - -int grpc_tcp_server_add_port(grpc_tcp_server *s, const void *addr, - size_t addr_len) { - grpc_tcp_listener *sp; - 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; - unsigned port_index = 0; - if (s->tail != NULL) { - port_index = s->tail->port_index + 1; - } - - /* 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 (sp = s->head; sp; sp = sp->next) { - sockname_len = sizeof(sockname_temp); - if (0 == getsockname(sp->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); - } - - sp = add_socket_to_server(s, sock, addr, addr_len, port_index); - gpr_free(allocated_addr); - - if (sp) { - return sp->port; - } else { - return -1; - } -} - -unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server *s, - unsigned port_index) { - grpc_tcp_listener *sp; - for (sp = s->head; sp && port_index != 0; sp = sp->next, --port_index) - ; - if (sp) { - return 1; - } else { - return 0; - } -} - -int grpc_tcp_server_port_fd(grpc_tcp_server *s, unsigned port_index, - unsigned fd_index) { - grpc_tcp_listener *sp; - if (fd_index != 0) { - /* Windows implementation has only one fd per port_index. */ - return -1; - } - for (sp = s->head; sp && port_index != 0; sp = sp->next, --port_index) - ; - if (sp) { - return _open_osfhandle((intptr_t)sp->socket->socket, 0); - } else { - return -1; - } -} - -void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, - grpc_pollset **pollset, size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void *on_accept_cb_arg) { - grpc_tcp_listener *sp; - GPR_ASSERT(on_accept_cb); - gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->on_accept_cb); - GPR_ASSERT(s->active_ports == 0); - s->on_accept_cb = on_accept_cb; - s->on_accept_cb_arg = on_accept_cb_arg; - for (sp = s->head; sp; sp = sp->next) { - start_accept(exec_ctx, sp); - s->active_ports++; - } - gpr_mu_unlock(&s->mu); -} - -#endif /* GPR_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/tcp_windows.c b/third_party/grpc/src/core/iomgr/tcp_windows.c deleted file mode 100644 index 038e4158c89b9f..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_windows.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_WINSOCK_SOCKET - -#include "src/core/iomgr/sockaddr_win32.h" - -#include -#include -#include -#include -#include -#include - -#include "src/core/iomgr/timer.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_closure on_read; - grpc_closure on_write; - - grpc_closure *read_cb; - grpc_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 void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, bool success) { - grpc_tcp *tcp = tcpp; - grpc_closure *cb = tcp->read_cb; - grpc_winsocket *socket = tcp->socket; - gpr_slice sub; - grpc_winsocket_callback_info *info = &socket->read_info; - - 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; - } - } - } - - tcp->read_cb = NULL; - TCP_UNREF(tcp, "read"); - if (cb) { - cb->cb(exec_ctx, cb->cb_arg, success); - } -} - -static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *read_slices, grpc_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) { - grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL); - return; - } - - 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 = (ULONG)GPR_SLICE_LENGTH( - tcp->read_slice); // we know slice size fits in 32bit. - buffer.buf = (char *)GPR_SLICE_START_PTR(tcp->read_slice); - - TCP_REF(tcp, "read"); - - /* 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) { - info->bytes_transfered = bytes_read; - grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, true, NULL); - return; - } - - /* 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) { - info->wsa_error = wsa_error; - grpc_exec_ctx_enqueue(exec_ctx, &tcp->on_read, false, NULL); - return; - } - } - - grpc_socket_notify_on_read(exec_ctx, tcp->socket, &tcp->on_read); -} - -/* Asynchronous callback from the IOCP, or the background thread. */ -static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, bool success) { - grpc_tcp *tcp = (grpc_tcp *)tcpp; - grpc_winsocket *handle = tcp->socket; - grpc_winsocket_callback_info *info = &handle->write_info; - grpc_closure *cb; - - 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(exec_ctx, cb->cb_arg, success); -} - -/* Initiates a write. */ -static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - gpr_slice_buffer *slices, grpc_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; - size_t len; - - if (tcp->shutting_down) { - grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL); - return; - } - - tcp->write_cb = cb; - tcp->write_slices = slices; - GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); - 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++) { - len = GPR_SLICE_LENGTH(tcp->write_slices->slices[i]); - GPR_ASSERT(len <= ULONG_MAX); - buffers[i].len = (ULONG)len; - 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, (DWORD)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) { - bool ok = false; - if (status == 0) { - ok = true; - 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); - grpc_exec_ctx_enqueue(exec_ctx, cb, ok, NULL); - return; - } - - 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, (DWORD)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"); - grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL); - return; - } - } - - /* 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(exec_ctx, socket, &tcp->on_write); -} - -static void win_add_to_pollset(grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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); - grpc_closure_init(&tcp->on_read, on_read, tcp); - grpc_closure_init(&tcp->on_write, on_write, tcp); - tcp->peer_string = gpr_strdup(peer_string); - return &tcp->base; -} - -#endif /* GPR_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/tcp_windows.h b/third_party/grpc/src/core/iomgr/tcp_windows.h deleted file mode 100644 index deb3e48293c6b3..00000000000000 --- a/third_party/grpc/src/core/iomgr/tcp_windows.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/timer.c b/third_party/grpc/src/core/iomgr/timer.c deleted file mode 100644 index a33d8f63a05bb1..00000000000000 --- a/third_party/grpc/src/core/iomgr/timer.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/iomgr/timer.h" - -#include "src/core/iomgr/timer_heap.h" -#include "src/core/iomgr/timer_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 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 timers with deadlines <= this will be in the heap. */ - gpr_timespec queue_deadline_cap; - gpr_timespec min_deadline; - /* Index in the g_shard_queue */ - uint32_t shard_queue_index; - /* This holds all timers with deadlines < queue_deadline_cap. Timers in this - list have the top bit of their deadline set to 0. */ - grpc_timer_heap heap; - /* This holds timers whose deadline is >= queue_deadline_cap. */ - grpc_timer list; -} shard_type; - -/* Protects g_shard_queue */ -static gpr_mu g_mu; -/* Allow only one run_some_expired_timers 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_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now, - gpr_timespec *next, int success); - -static gpr_timespec compute_min_deadline(shard_type *shard) { - return grpc_timer_heap_is_empty(&shard->heap) - ? shard->queue_deadline_cap - : grpc_timer_heap_top(&shard->heap)->deadline; -} - -void grpc_timer_list_init(gpr_timespec now) { - uint32_t 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_timer_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_timer_list_shutdown(grpc_exec_ctx *exec_ctx) { - int i; - run_some_expired_timers(exec_ctx, 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_timer_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_timer *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 (double)ts.tv_sec + 1e-9 * ts.tv_nsec; -} - -static gpr_timespec dbl_to_ts(double d) { - gpr_timespec ts; - ts.tv_sec = (int64_t)d; - ts.tv_nsec = (int32_t)(1e9 * (d - (double)ts.tv_sec)); - ts.clock_type = GPR_TIMESPAN; - return ts; -} - -static void list_join(grpc_timer *head, grpc_timer *timer) { - timer->next = head; - timer->prev = head->prev; - timer->next->prev = timer->prev->next = timer; -} - -static void list_remove(grpc_timer *timer) { - timer->next->prev = timer->prev; - timer->prev->next = timer->next; -} - -static void swap_adjacent_shards_in_queue(uint32_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_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, - gpr_timespec deadline, grpc_iomgr_cb_func timer_cb, - void *timer_cb_arg, gpr_timespec now) { - int is_first_timer = 0; - shard_type *shard = &g_shards[shard_idx(timer)]; - GPR_ASSERT(deadline.clock_type == g_clock_type); - GPR_ASSERT(now.clock_type == g_clock_type); - grpc_closure_init(&timer->closure, timer_cb, timer_cb_arg); - timer->deadline = deadline; - timer->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_timer = grpc_timer_heap_add(&shard->heap, timer); - } else { - timer->heap_index = INVALID_HEAP_INDEX; - list_join(&shard->list, timer); - } - 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_timer_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_timer_check, which might beat us to the lock. In - that case, it is possible that the timer 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_timer_check that intervened failed to - trigger the new timer because the min_deadline hadn't yet been reduced. - In that case, the timer will simply have to wait for the next - grpc_timer_check. */ - if (is_first_timer) { - 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_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { - shard_type *shard = &g_shards[shard_idx(timer)]; - gpr_mu_lock(&shard->mu); - if (!timer->triggered) { - grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, false, NULL); - timer->triggered = 1; - if (timer->heap_index == INVALID_HEAP_INDEX) { - list_remove(timer); - } else { - grpc_timer_heap_remove(&shard->heap, timer); - } - } - gpr_mu_unlock(&shard->mu); -} - -/* 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 timers 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_timer *timer, *next; - - /* Compute the new cap and put all timers 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 (timer = shard->list.next; timer != &shard->list; timer = next) { - next = timer->next; - - if (gpr_time_cmp(timer->deadline, shard->queue_deadline_cap) < 0) { - list_remove(timer); - grpc_timer_heap_add(&shard->heap, timer); - } - } - return !grpc_timer_heap_is_empty(&shard->heap); -} - -/* This pops the next non-cancelled timer with deadline <= now from the queue, - or returns NULL if there isn't one. - REQUIRES: shard->mu locked */ -static grpc_timer *pop_one(shard_type *shard, gpr_timespec now) { - grpc_timer *timer; - for (;;) { - if (grpc_timer_heap_is_empty(&shard->heap)) { - if (gpr_time_cmp(now, shard->queue_deadline_cap) < 0) return NULL; - if (!refill_queue(shard, now)) return NULL; - } - timer = grpc_timer_heap_top(&shard->heap); - if (gpr_time_cmp(timer->deadline, now) > 0) return NULL; - timer->triggered = 1; - grpc_timer_heap_pop(&shard->heap); - return timer; - } -} - -/* REQUIRES: shard->mu unlocked */ -static size_t pop_timers(grpc_exec_ctx *exec_ctx, shard_type *shard, - gpr_timespec now, gpr_timespec *new_min_deadline, - int success) { - size_t n = 0; - grpc_timer *timer; - gpr_mu_lock(&shard->mu); - while ((timer = pop_one(shard, now))) { - grpc_exec_ctx_enqueue(exec_ctx, &timer->closure, success, NULL); - n++; - } - *new_min_deadline = compute_min_deadline(shard); - gpr_mu_unlock(&shard->mu); - return n; -} - -static int run_some_expired_timers(grpc_exec_ctx *exec_ctx, gpr_timespec now, - gpr_timespec *next, int success) { - size_t n = 0; - - /* TODO(ctiller): verify that there are any timers (atomically) here */ - - if (gpr_mu_trylock(&g_checker_mu)) { - gpr_mu_lock(&g_mu); - - while (gpr_time_cmp(g_shard_queue[0]->min_deadline, now) < 0) { - gpr_timespec new_min_deadline; - - /* For efficiency, we pop as many available timers as we can from the - shard. This may violate perfect timer deadline ordering, but that - shouldn't be a big deal because we don't make ordering guarantees. */ - n += pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, - success); - - /* An grpc_timer_init() on the shard could intervene here, adding a new - timer that is earlier than new_min_deadline. However, - grpc_timer_init() will block on the master_lock before it can call - set_min_deadline, so this one will complete first and then the Addtimer - 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); - } - - return (int)n; -} - -int grpc_timer_check(grpc_exec_ctx *exec_ctx, gpr_timespec now, - gpr_timespec *next) { - GPR_ASSERT(now.clock_type == g_clock_type); - return run_some_expired_timers( - exec_ctx, now, next, - gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0); -} diff --git a/third_party/grpc/src/core/iomgr/timer.h b/third_party/grpc/src/core/iomgr/timer.h deleted file mode 100644 index 720c9d5ab9444f..00000000000000 --- a/third_party/grpc/src/core/iomgr/timer.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_TIMER_H -#define GRPC_INTERNAL_CORE_IOMGR_TIMER_H - -#include "src/core/iomgr/iomgr.h" -#include "src/core/iomgr/exec_ctx.h" -#include -#include - -typedef struct grpc_timer { - gpr_timespec deadline; - uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ - int triggered; - struct grpc_timer *next; - struct grpc_timer *prev; - grpc_closure closure; -} grpc_timer; - -/* Initialize *timer. When expired or canceled, timer_cb will be called with - *timer_cb_arg and status to indicate if it expired (SUCCESS) or was - canceled (CANCELLED). timer_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_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, - gpr_timespec deadline, grpc_iomgr_cb_func timer_cb, - void *timer_cb_arg, gpr_timespec now); - -/* Note that there is no timer destroy function. This is because the - timer 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 timer event management state is destroyed just before - that callback is invoked. If the user has additional state associated with - the timer, the user is responsible for determining when it is safe to - destroy that state. */ - -/* Cancel an *timer. - There are three cases: - 1. We normally cancel the timer - 2. The timer has already run - 3. We can't cancel the timer because it is "in flight". - - In all of these cases, the cancellation is still considered successful. - They are essentially distinguished in that the timer_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_timer_cancel. It's expected that most timers 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 timer */ -void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_TIMER_H */ diff --git a/third_party/grpc/src/core/iomgr/timer_heap.c b/third_party/grpc/src/core/iomgr/timer_heap.c deleted file mode 100644 index 9d8be5c1fcb56b..00000000000000 --- a/third_party/grpc/src/core/iomgr/timer_heap.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/timer_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_timer **first, uint32_t i, grpc_timer *t) { - while (i > 0) { - uint32_t parent = (uint32_t)(((int)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_timer **first, uint32_t i, uint32_t length, - grpc_timer *t) { - for (;;) { - uint32_t left_child = 1u + 2u * i; - uint32_t right_child; - uint32_t 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_timer_heap *heap) { - if (heap->timer_count >= 8 && - heap->timer_count <= heap->timer_capacity / SHRINK_FULLNESS_FACTOR / 2) { - heap->timer_capacity = heap->timer_count * SHRINK_FULLNESS_FACTOR; - heap->timers = - gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); - } -} - -static void note_changed_priority(grpc_timer_heap *heap, grpc_timer *timer) { - uint32_t i = timer->heap_index; - uint32_t parent = (uint32_t)(((int)i - 1) / 2); - if (gpr_time_cmp(heap->timers[parent]->deadline, timer->deadline) < 0) { - adjust_upwards(heap->timers, i, timer); - } else { - adjust_downwards(heap->timers, i, heap->timer_count, timer); - } -} - -void grpc_timer_heap_init(grpc_timer_heap *heap) { - memset(heap, 0, sizeof(*heap)); -} - -void grpc_timer_heap_destroy(grpc_timer_heap *heap) { gpr_free(heap->timers); } - -int grpc_timer_heap_add(grpc_timer_heap *heap, grpc_timer *timer) { - if (heap->timer_count == heap->timer_capacity) { - heap->timer_capacity = - GPR_MAX(heap->timer_capacity + 1, heap->timer_capacity * 3 / 2); - heap->timers = - gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); - } - timer->heap_index = heap->timer_count; - adjust_upwards(heap->timers, heap->timer_count, timer); - heap->timer_count++; - return timer->heap_index == 0; -} - -void grpc_timer_heap_remove(grpc_timer_heap *heap, grpc_timer *timer) { - uint32_t i = timer->heap_index; - if (i == heap->timer_count - 1) { - heap->timer_count--; - maybe_shrink(heap); - return; - } - heap->timers[i] = heap->timers[heap->timer_count - 1]; - heap->timers[i]->heap_index = i; - heap->timer_count--; - maybe_shrink(heap); - note_changed_priority(heap, heap->timers[i]); -} - -int grpc_timer_heap_is_empty(grpc_timer_heap *heap) { - return heap->timer_count == 0; -} - -grpc_timer *grpc_timer_heap_top(grpc_timer_heap *heap) { - return heap->timers[0]; -} - -void grpc_timer_heap_pop(grpc_timer_heap *heap) { - grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); -} diff --git a/third_party/grpc/src/core/iomgr/timer_heap.h b/third_party/grpc/src/core/iomgr/timer_heap.h deleted file mode 100644 index 2d220f16774e7a..00000000000000 --- a/third_party/grpc/src/core/iomgr/timer_heap.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_TIMER_HEAP_H -#define GRPC_INTERNAL_CORE_IOMGR_TIMER_HEAP_H - -#include "src/core/iomgr/timer.h" - -typedef struct { - grpc_timer **timers; - uint32_t timer_count; - uint32_t timer_capacity; -} grpc_timer_heap; - -/* return 1 if the new timer is the first timer in the heap */ -int grpc_timer_heap_add(grpc_timer_heap *heap, grpc_timer *timer); - -void grpc_timer_heap_init(grpc_timer_heap *heap); -void grpc_timer_heap_destroy(grpc_timer_heap *heap); - -void grpc_timer_heap_remove(grpc_timer_heap *heap, grpc_timer *timer); -grpc_timer *grpc_timer_heap_top(grpc_timer_heap *heap); -void grpc_timer_heap_pop(grpc_timer_heap *heap); - -int grpc_timer_heap_is_empty(grpc_timer_heap *heap); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_TIMER_HEAP_H */ diff --git a/third_party/grpc/src/core/iomgr/timer_internal.h b/third_party/grpc/src/core/iomgr/timer_internal.h deleted file mode 100644 index f182e737646276..00000000000000 --- a/third_party/grpc/src/core/iomgr/timer_internal.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_TIMER_INTERNAL_H -#define GRPC_INTERNAL_CORE_IOMGR_TIMER_INTERNAL_H - -#include "src/core/iomgr/exec_ctx.h" -#include -#include - -/* iomgr internal api for dealing with timers */ - -/* Check for timers to be run, and run them. - Return non zero if timer 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 timer - IF that timer 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_timer_check(grpc_exec_ctx* exec_ctx, gpr_timespec now, - gpr_timespec* next); -void grpc_timer_list_init(gpr_timespec now); -void grpc_timer_list_shutdown(grpc_exec_ctx* exec_ctx); - -/* the following must be implemented by each iomgr implementation */ - -void grpc_kick_poller(void); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_TIMER_INTERNAL_H */ diff --git a/third_party/grpc/src/core/iomgr/udp_server.c b/third_party/grpc/src/core/iomgr/udp_server.c deleted file mode 100644 index fe006c603ca63a..00000000000000 --- a/third_party/grpc/src/core/iomgr/udp_server.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include - -#ifdef GRPC_NEED_UDP -#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 { - uint8_t untyped[GRPC_MAX_SOCKADDR_SIZE]; - struct sockaddr sockaddr; - struct sockaddr_un un; - } addr; - size_t addr_len; - grpc_closure read_closure; - grpc_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 { - 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 */ - grpc_closure *shutdown_complete; - - /* all pollsets interested in new connections */ - grpc_pollset **pollsets; - /* number of pollsets in the pollsets array */ - size_t pollset_count; - /* The parent grpc server */ - grpc_server *grpc_server; -}; - -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->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_exec_ctx *exec_ctx, grpc_udp_server *s) { - grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1); - - gpr_mu_destroy(&s->mu); - gpr_cv_destroy(&s->cv); - - gpr_free(s->ports); - gpr_free(s); -} - -static void destroyed_port(grpc_exec_ctx *exec_ctx, 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(exec_ctx, s); - } else { - gpr_mu_unlock(&s->mu); - } -} - -/* 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_exec_ctx *exec_ctx, 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(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL, - "udp_listener_shutdown"); - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - finish_shutdown(exec_ctx, s); - } -} - -void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, - grpc_closure *on_done) { - size_t i; - gpr_mu_lock(&s->mu); - - GPR_ASSERT(!s->shutdown); - s->shutdown = 1; - - s->shutdown_complete = on_done; - - /* shutdown all fd's */ - if (s->active_ports) { - for (i = 0; i < s->nports; i++) { - grpc_fd_shutdown(exec_ctx, s->ports[i].emfd); - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - deactivated_all_ports(exec_ctx, s); - } -} - -/* Prepare a recently-created socket for listening. */ -static int prepare_socket(int fd, const struct sockaddr *addr, - size_t addr_len) { - struct sockaddr_storage sockname_temp; - socklen_t sockname_len; - int get_local_ip; - int rc; - - if (fd < 0) { - goto error; - } - - if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1)) { - gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd, - strerror(errno)); - } - - 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 !defined(__APPLE__) - rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, - sizeof(get_local_ip)); -#endif - } - - GPR_ASSERT(addr_len < ~(socklen_t)0); - if (bind(fd, addr, (socklen_t)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(grpc_exec_ctx *exec_ctx, 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(exec_ctx, 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(exec_ctx, sp->emfd, sp->server->grpc_server); - - /* Re-arm the notification event so we get another chance to read. */ - grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); -} - -static int add_socket_to_server(grpc_udp_server *s, int fd, - const struct sockaddr *addr, size_t 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_free(addr_str); - gpr_mu_lock(&s->mu); - /* 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); - gpr_free(name); - } - - return port; -} - -int grpc_udp_server_add_port(grpc_udp_server *s, const void *addr, - size_t 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 port_index) { - return (port_index < s->nports) ? s->ports[port_index].fd : -1; -} - -void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, - grpc_pollset **pollsets, size_t pollset_count, - grpc_server *server) { - size_t i, j; - gpr_mu_lock(&s->mu); - GPR_ASSERT(s->active_ports == 0); - s->pollsets = pollsets; - s->grpc_server = server; - for (i = 0; i < s->nports; i++) { - for (j = 0; j < pollset_count; j++) { - grpc_pollset_add_fd(exec_ctx, 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(exec_ctx, s->ports[i].emfd, - &s->ports[i].read_closure); - s->active_ports++; - } - gpr_mu_unlock(&s->mu); -} - -#endif -#endif diff --git a/third_party/grpc/src/core/iomgr/udp_server.h b/third_party/grpc/src/core/iomgr/udp_server.h deleted file mode 100644 index 73a21c80ab46d9..00000000000000 --- a/third_party/grpc/src/core/iomgr/udp_server.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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_server */ -typedef struct grpc_server grpc_server; - -/* Forward decl of grpc_udp_server */ -typedef struct grpc_udp_server grpc_udp_server; - -/* Called when data is available to read from the socket. */ -typedef void (*grpc_udp_server_read_cb)(grpc_exec_ctx *exec_ctx, grpc_fd *emfd, - grpc_server *server); - -/* 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_exec_ctx *exec_ctx, grpc_udp_server *udp_server, - grpc_pollset **pollsets, size_t pollset_count, - grpc_server *server); - -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, - size_t addr_len, grpc_udp_server_read_cb read_cb); - -void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *server, - grpc_closure *on_done); - -#endif /* GRPC_INTERNAL_CORE_IOMGR_UDP_SERVER_H */ diff --git a/third_party/grpc/src/core/iomgr/wakeup_fd_eventfd.c b/third_party/grpc/src/core/iomgr/wakeup_fd_eventfd.c deleted file mode 100644 index f67379e4fcfaf7..00000000000000 --- a/third_party/grpc/src/core/iomgr/wakeup_fd_eventfd.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -#include "src/core/iomgr/wakeup_fd_posix.h" -#include "src/core/profiling/timers.h" - -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; - GPR_TIMER_BEGIN("eventfd_wakeup", 0); - do { - err = eventfd_write(fd_info->read_fd, 1); - } while (err < 0 && errno == EINTR); - GPR_TIMER_END("eventfd_wakeup", 0); -} - -static void eventfd_destroy(grpc_wakeup_fd* fd_info) { - if (fd_info->read_fd != 0) 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/third_party/grpc/src/core/iomgr/wakeup_fd_nospecial.c b/third_party/grpc/src/core/iomgr/wakeup_fd_nospecial.c deleted file mode 100644 index 78d763c103ad03..00000000000000 --- a/third_party/grpc/src/core/iomgr/wakeup_fd_nospecial.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/wakeup_fd_pipe.c b/third_party/grpc/src/core/iomgr/wakeup_fd_pipe.c deleted file mode 100644 index 80de181d9d1966..00000000000000 --- a/third_party/grpc/src/core/iomgr/wakeup_fd_pipe.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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]; - ssize_t 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) { - if (fd_info->read_fd != 0) close(fd_info->read_fd); - if (fd_info->write_fd != 0) 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/third_party/grpc/src/core/iomgr/wakeup_fd_pipe.h b/third_party/grpc/src/core/iomgr/wakeup_fd_pipe.h deleted file mode 100644 index 01a13a97c036e3..00000000000000 --- a/third_party/grpc/src/core/iomgr/wakeup_fd_pipe.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/iomgr/wakeup_fd_posix.c b/third_party/grpc/src/core/iomgr/wakeup_fd_posix.c deleted file mode 100644 index f40be081b05678..00000000000000 --- a/third_party/grpc/src/core/iomgr/wakeup_fd_posix.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; -int grpc_allow_specialized_wakeup_fd = 1; - -void grpc_wakeup_fd_global_init(void) { - if (grpc_allow_specialized_wakeup_fd && - 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_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/third_party/grpc/src/core/iomgr/workqueue.h b/third_party/grpc/src/core/iomgr/workqueue.h deleted file mode 100644 index 36dd133468328e..00000000000000 --- a/third_party/grpc/src/core/iomgr/workqueue.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_H -#define GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_H - -#include "src/core/iomgr/iomgr.h" -#include "src/core/iomgr/pollset.h" -#include "src/core/iomgr/closure.h" -#include "src/core/iomgr/exec_ctx.h" - -#ifdef GPR_POSIX_SOCKET -#include "src/core/iomgr/workqueue_posix.h" -#endif - -#ifdef GPR_WIN32 -#include "src/core/iomgr/workqueue_windows.h" -#endif - -/* grpc_workqueue is forward declared in exec_ctx.h */ - -/** Create a work queue */ -grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx); - -void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue); - -#define GRPC_WORKQUEUE_REFCOUNT_DEBUG -#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG -#define GRPC_WORKQUEUE_REF(p, r) \ - grpc_workqueue_ref((p), __FILE__, __LINE__, (r)) -#define GRPC_WORKQUEUE_UNREF(cl, p, r) \ - grpc_workqueue_unref((cl), (p), __FILE__, __LINE__, (r)) -void grpc_workqueue_ref(grpc_workqueue *workqueue, const char *file, int line, - const char *reason); -void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue, - const char *file, int line, const char *reason); -#else -#define GRPC_WORKQUEUE_REF(p, r) grpc_workqueue_ref((p)) -#define GRPC_WORKQUEUE_UNREF(cl, p, r) grpc_workqueue_unref((cl), (p)) -void grpc_workqueue_ref(grpc_workqueue *workqueue); -void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue); -#endif - -/** Bind this workqueue to a pollset */ -void grpc_workqueue_add_to_pollset(grpc_exec_ctx *exec_ctx, - grpc_workqueue *workqueue, - grpc_pollset *pollset); - -/** Add a work item to a workqueue */ -void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure, - int success); - -#endif diff --git a/third_party/grpc/src/core/iomgr/workqueue_posix.c b/third_party/grpc/src/core/iomgr/workqueue_posix.c deleted file mode 100644 index da11df67efba9e..00000000000000 --- a/third_party/grpc/src/core/iomgr/workqueue_posix.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_POSIX_SOCKET - -#include "src/core/iomgr/workqueue.h" - -#include - -#include -#include -#include - -#include "src/core/iomgr/fd_posix.h" - -static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, bool success); - -grpc_workqueue *grpc_workqueue_create(grpc_exec_ctx *exec_ctx) { - char name[32]; - grpc_workqueue *workqueue = gpr_malloc(sizeof(grpc_workqueue)); - gpr_ref_init(&workqueue->refs, 1); - gpr_mu_init(&workqueue->mu); - workqueue->closure_list.head = workqueue->closure_list.tail = NULL; - grpc_wakeup_fd_init(&workqueue->wakeup_fd); - sprintf(name, "workqueue:%p", (void *)workqueue); - workqueue->wakeup_read_fd = - grpc_fd_create(GRPC_WAKEUP_FD_GET_READ_FD(&workqueue->wakeup_fd), name); - grpc_closure_init(&workqueue->read_closure, on_readable, workqueue); - grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd, - &workqueue->read_closure); - return workqueue; -} - -static void workqueue_destroy(grpc_exec_ctx *exec_ctx, - grpc_workqueue *workqueue) { - GPR_ASSERT(grpc_closure_list_empty(workqueue->closure_list)); - grpc_fd_shutdown(exec_ctx, workqueue->wakeup_read_fd); -} - -#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG -void grpc_workqueue_ref(grpc_workqueue *workqueue, const char *file, int line, - const char *reason) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "WORKQUEUE:%p ref %d -> %d %s", - workqueue, (int)workqueue->refs.count, (int)workqueue->refs.count + 1, - reason); -#else -void grpc_workqueue_ref(grpc_workqueue *workqueue) { -#endif - gpr_ref(&workqueue->refs); -} - -#ifdef GRPC_WORKQUEUE_REFCOUNT_DEBUG -void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue, - const char *file, int line, const char *reason) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "WORKQUEUE:%p unref %d -> %d %s", - workqueue, (int)workqueue->refs.count, (int)workqueue->refs.count - 1, - reason); -#else -void grpc_workqueue_unref(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) { -#endif - if (gpr_unref(&workqueue->refs)) { - workqueue_destroy(exec_ctx, workqueue); - } -} - -void grpc_workqueue_add_to_pollset(grpc_exec_ctx *exec_ctx, - grpc_workqueue *workqueue, - grpc_pollset *pollset) { - grpc_pollset_add_fd(exec_ctx, pollset, workqueue->wakeup_read_fd); -} - -void grpc_workqueue_flush(grpc_exec_ctx *exec_ctx, grpc_workqueue *workqueue) { - gpr_mu_lock(&workqueue->mu); - if (grpc_closure_list_empty(workqueue->closure_list)) { - grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd); - } - grpc_closure_list_move(&exec_ctx->closure_list, &workqueue->closure_list); - gpr_mu_unlock(&workqueue->mu); -} - -static void on_readable(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - grpc_workqueue *workqueue = arg; - - if (!success) { - gpr_mu_destroy(&workqueue->mu); - /* HACK: let wakeup_fd code know that we stole the fd */ - workqueue->wakeup_fd.read_fd = 0; - grpc_wakeup_fd_destroy(&workqueue->wakeup_fd); - grpc_fd_orphan(exec_ctx, workqueue->wakeup_read_fd, NULL, NULL, "destroy"); - gpr_free(workqueue); - } else { - gpr_mu_lock(&workqueue->mu); - grpc_closure_list_move(&workqueue->closure_list, &exec_ctx->closure_list); - grpc_wakeup_fd_consume_wakeup(&workqueue->wakeup_fd); - gpr_mu_unlock(&workqueue->mu); - grpc_fd_notify_on_read(exec_ctx, workqueue->wakeup_read_fd, - &workqueue->read_closure); - } -} - -void grpc_workqueue_push(grpc_workqueue *workqueue, grpc_closure *closure, - int success) { - gpr_mu_lock(&workqueue->mu); - if (grpc_closure_list_empty(workqueue->closure_list)) { - grpc_wakeup_fd_wakeup(&workqueue->wakeup_fd); - } - grpc_closure_list_add(&workqueue->closure_list, closure, success); - gpr_mu_unlock(&workqueue->mu); -} - -#endif /* GPR_POSIX_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/workqueue_posix.h b/third_party/grpc/src/core/iomgr/workqueue_posix.h deleted file mode 100644 index 589034fe1bb502..00000000000000 --- a/third_party/grpc/src/core/iomgr/workqueue_posix.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_WORKQUEUE_POSIX_H -#define GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_POSIX_H - -struct grpc_fd; - -struct grpc_workqueue { - gpr_refcount refs; - - gpr_mu mu; - grpc_closure_list closure_list; - - grpc_wakeup_fd wakeup_fd; - struct grpc_fd *wakeup_read_fd; - - grpc_closure read_closure; -}; - -#endif /* GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_POSIX_H */ diff --git a/third_party/grpc/src/core/iomgr/workqueue_windows.c b/third_party/grpc/src/core/iomgr/workqueue_windows.c deleted file mode 100644 index f9ca57557b5523..00000000000000 --- a/third_party/grpc/src/core/iomgr/workqueue_windows.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/iomgr/workqueue.h" - -#endif /* GPR_WIN32 */ diff --git a/third_party/grpc/src/core/iomgr/workqueue_windows.h b/third_party/grpc/src/core/iomgr/workqueue_windows.h deleted file mode 100644 index 941f195f51403a..00000000000000 --- a/third_party/grpc/src/core/iomgr/workqueue_windows.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_WORKQUEUE_WINDOWS_H -#define GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_WINDOWS_H - -#endif /* GRPC_INTERNAL_CORE_IOMGR_WORKQUEUE_WINDOWS_H */ diff --git a/third_party/grpc/src/core/json/json.c b/third_party/grpc/src/core/json/json.c deleted file mode 100644 index 96e11eebb1116c..00000000000000 --- a/third_party/grpc/src/core/json/json.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/json/json.h b/third_party/grpc/src/core/json/json.h deleted file mode 100644 index c4df2998c3a9ea..00000000000000 --- a/third_party/grpc/src/core/json/json.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/json/json_common.h b/third_party/grpc/src/core/json/json_common.h deleted file mode 100644 index 481695b38b8412..00000000000000 --- a/third_party/grpc/src/core/json/json_common.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/json/json_writer.h b/third_party/grpc/src/core/json/json_writer.h deleted file mode 100644 index 9ef04aab012a78..00000000000000 --- a/third_party/grpc/src/core/json/json_writer.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/lib/README.md b/third_party/grpc/src/core/lib/README.md new file mode 100644 index 00000000000000..69b6bce2d9ff5d --- /dev/null +++ b/third_party/grpc/src/core/lib/README.md @@ -0,0 +1,6 @@ +Required elements of gRPC Core: Each module in this directory is required to +build gRPC. If it's possible to envisage a configuration where code is not +required, then that code belongs in ext/ instead. + +NOTE: The movement of code between lib and ext is an ongoing effort, so this +directory currently contains too much of the core library. diff --git a/third_party/grpc/src/core/lib/avl/avl.cc b/third_party/grpc/src/core/lib/avl/avl.cc new file mode 100644 index 00000000000000..ec106ddb1da48f --- /dev/null +++ b/third_party/grpc/src/core/lib/avl/avl.cc @@ -0,0 +1,306 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/avl/avl.h" + +#include +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" + +grpc_avl grpc_avl_create(const grpc_avl_vtable* vtable) { + grpc_avl out; + out.vtable = vtable; + out.root = nullptr; + return out; +} + +static grpc_avl_node* ref_node(grpc_avl_node* node) { + if (node) { + gpr_ref(&node->refs); + } + return node; +} + +static void unref_node(const grpc_avl_vtable* vtable, grpc_avl_node* node, + void* user_data) { + if (node == nullptr) { + return; + } + if (gpr_unref(&node->refs)) { + vtable->destroy_key(node->key, user_data); + vtable->destroy_value(node->value, user_data); + unref_node(vtable, node->left, user_data); + unref_node(vtable, node->right, user_data); + gpr_free(node); + } +} + +static long node_height(grpc_avl_node* node) { + return node == nullptr ? 0 : node->height; +} + +#ifndef NDEBUG +static long calculate_height(grpc_avl_node* node) { + return node == nullptr ? 0 + : 1 + GPR_MAX(calculate_height(node->left), + calculate_height(node->right)); +} + +static grpc_avl_node* assert_invariants(grpc_avl_node* n) { + if (n == nullptr) return nullptr; + assert_invariants(n->left); + assert_invariants(n->right); + assert(calculate_height(n) == n->height); + assert(labs(node_height(n->left) - node_height(n->right)) <= 1); + return n; +} +#else +static grpc_avl_node* assert_invariants(grpc_avl_node* n) { return n; } +#endif + +grpc_avl_node* new_node(void* key, void* value, grpc_avl_node* left, + grpc_avl_node* right) { + grpc_avl_node* node = static_cast(gpr_malloc(sizeof(*node))); + gpr_ref_init(&node->refs, 1); + node->key = key; + node->value = value; + node->left = assert_invariants(left); + node->right = assert_invariants(right); + node->height = 1 + GPR_MAX(node_height(left), node_height(right)); + return node; +} + +static grpc_avl_node* get(const grpc_avl_vtable* vtable, grpc_avl_node* node, + void* key, void* user_data) { + long cmp; + + if (node == nullptr) { + return nullptr; + } + + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + return node; + } else if (cmp > 0) { + return get(vtable, node->left, key, user_data); + } else { + return get(vtable, node->right, key, user_data); + } +} + +void* grpc_avl_get(grpc_avl avl, void* key, void* user_data) { + grpc_avl_node* node = get(avl.vtable, avl.root, key, user_data); + return node ? node->value : nullptr; +} + +int grpc_avl_maybe_get(grpc_avl avl, void* key, void** value, void* user_data) { + grpc_avl_node* node = get(avl.vtable, avl.root, key, user_data); + if (node != nullptr) { + *value = node->value; + return 1; + } + return 0; +} + +static grpc_avl_node* rotate_left(const grpc_avl_vtable* vtable, void* key, + void* value, grpc_avl_node* left, + grpc_avl_node* right, void* user_data) { + grpc_avl_node* n = new_node(vtable->copy_key(right->key, user_data), + vtable->copy_value(right->value, user_data), + new_node(key, value, left, ref_node(right->left)), + ref_node(right->right)); + unref_node(vtable, right, user_data); + return n; +} + +static grpc_avl_node* rotate_right(const grpc_avl_vtable* vtable, void* key, + void* value, grpc_avl_node* left, + grpc_avl_node* right, void* user_data) { + grpc_avl_node* n = + new_node(vtable->copy_key(left->key, user_data), + vtable->copy_value(left->value, user_data), ref_node(left->left), + new_node(key, value, ref_node(left->right), right)); + unref_node(vtable, left, user_data); + return n; +} + +static grpc_avl_node* rotate_left_right(const grpc_avl_vtable* vtable, + void* key, void* value, + grpc_avl_node* left, + grpc_avl_node* right, void* user_data) { + /* rotate_right(..., rotate_left(left), right) */ + grpc_avl_node* n = + new_node(vtable->copy_key(left->right->key, user_data), + vtable->copy_value(left->right->value, user_data), + new_node(vtable->copy_key(left->key, user_data), + vtable->copy_value(left->value, user_data), + ref_node(left->left), ref_node(left->right->left)), + new_node(key, value, ref_node(left->right->right), right)); + unref_node(vtable, left, user_data); + return n; +} + +static grpc_avl_node* rotate_right_left(const grpc_avl_vtable* vtable, + void* key, void* value, + grpc_avl_node* left, + grpc_avl_node* right, void* user_data) { + /* rotate_left(..., left, rotate_right(right)) */ + grpc_avl_node* n = + new_node(vtable->copy_key(right->left->key, user_data), + vtable->copy_value(right->left->value, user_data), + new_node(key, value, left, ref_node(right->left->left)), + new_node(vtable->copy_key(right->key, user_data), + vtable->copy_value(right->value, user_data), + ref_node(right->left->right), ref_node(right->right))); + unref_node(vtable, right, user_data); + return n; +} + +static grpc_avl_node* rebalance(const grpc_avl_vtable* vtable, void* key, + void* value, grpc_avl_node* left, + grpc_avl_node* right, void* user_data) { + switch (node_height(left) - node_height(right)) { + case 2: + if (node_height(left->left) - node_height(left->right) == -1) { + return assert_invariants( + rotate_left_right(vtable, key, value, left, right, user_data)); + } else { + return assert_invariants( + rotate_right(vtable, key, value, left, right, user_data)); + } + case -2: + if (node_height(right->left) - node_height(right->right) == 1) { + return assert_invariants( + rotate_right_left(vtable, key, value, left, right, user_data)); + } else { + return assert_invariants( + rotate_left(vtable, key, value, left, right, user_data)); + } + default: + return assert_invariants(new_node(key, value, left, right)); + } +} + +static grpc_avl_node* add_key(const grpc_avl_vtable* vtable, + grpc_avl_node* node, void* key, void* value, + void* user_data) { + long cmp; + if (node == nullptr) { + return new_node(key, value, nullptr, nullptr); + } + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + return new_node(key, value, ref_node(node->left), ref_node(node->right)); + } else if (cmp > 0) { + return rebalance(vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), + add_key(vtable, node->left, key, value, user_data), + ref_node(node->right), user_data); + } else { + return rebalance( + vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), ref_node(node->left), + add_key(vtable, node->right, key, value, user_data), user_data); + } +} + +grpc_avl grpc_avl_add(grpc_avl avl, void* key, void* value, void* user_data) { + grpc_avl_node* old_root = avl.root; + avl.root = add_key(avl.vtable, avl.root, key, value, user_data); + assert_invariants(avl.root); + unref_node(avl.vtable, old_root, user_data); + return avl; +} + +static grpc_avl_node* in_order_head(grpc_avl_node* node) { + while (node->left != nullptr) { + node = node->left; + } + return node; +} + +static grpc_avl_node* in_order_tail(grpc_avl_node* node) { + while (node->right != nullptr) { + node = node->right; + } + return node; +} + +static grpc_avl_node* remove_key(const grpc_avl_vtable* vtable, + grpc_avl_node* node, void* key, + void* user_data) { + long cmp; + if (node == nullptr) { + return nullptr; + } + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + if (node->left == nullptr) { + return ref_node(node->right); + } else if (node->right == nullptr) { + return ref_node(node->left); + } else if (node->left->height < node->right->height) { + grpc_avl_node* h = in_order_head(node->right); + return rebalance( + vtable, vtable->copy_key(h->key, user_data), + vtable->copy_value(h->value, user_data), ref_node(node->left), + remove_key(vtable, node->right, h->key, user_data), user_data); + } else { + grpc_avl_node* h = in_order_tail(node->left); + return rebalance(vtable, vtable->copy_key(h->key, user_data), + vtable->copy_value(h->value, user_data), + remove_key(vtable, node->left, h->key, user_data), + ref_node(node->right), user_data); + } + } else if (cmp > 0) { + return rebalance(vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), + remove_key(vtable, node->left, key, user_data), + ref_node(node->right), user_data); + } else { + return rebalance( + vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), ref_node(node->left), + remove_key(vtable, node->right, key, user_data), user_data); + } +} + +grpc_avl grpc_avl_remove(grpc_avl avl, void* key, void* user_data) { + grpc_avl_node* old_root = avl.root; + avl.root = remove_key(avl.vtable, avl.root, key, user_data); + assert_invariants(avl.root); + unref_node(avl.vtable, old_root, user_data); + return avl; +} + +grpc_avl grpc_avl_ref(grpc_avl avl, void* user_data) { + ref_node(avl.root); + return avl; +} + +void grpc_avl_unref(grpc_avl avl, void* user_data) { + unref_node(avl.vtable, avl.root, user_data); +} + +int grpc_avl_is_empty(grpc_avl avl) { return avl.root == nullptr; } diff --git a/third_party/grpc/src/core/lib/avl/avl.h b/third_party/grpc/src/core/lib/avl/avl.h new file mode 100644 index 00000000000000..15a9d569471cf0 --- /dev/null +++ b/third_party/grpc/src/core/lib/avl/avl.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_AVL_AVL_H +#define GRPC_CORE_LIB_AVL_AVL_H + +#include + +#include + +/** internal node of an AVL tree */ +typedef struct grpc_avl_node { + gpr_refcount refs; + void* key; + void* value; + struct grpc_avl_node* left; + struct grpc_avl_node* right; + long height; +} grpc_avl_node; + +/** vtable for the AVL tree + * The optional user_data is propagated from the top level grpc_avl_XXX API. + * From the same API call, multiple vtable functions may be called multiple + * times. + */ +typedef struct grpc_avl_vtable { + /** destroy a key */ + void (*destroy_key)(void* key, void* user_data); + /** copy a key, returning new value */ + void* (*copy_key)(void* key, void* user_data); + /** compare key1, key2; return <0 if key1 < key2, + >0 if key1 > key2, 0 if key1 == key2 */ + long (*compare_keys)(void* key1, void* key2, void* user_data); + /** destroy a value */ + void (*destroy_value)(void* value, void* user_data); + /** copy a value */ + void* (*copy_value)(void* value, void* user_data); +} grpc_avl_vtable; + +/** "pointer" to an AVL tree - this is a reference + counted object - use grpc_avl_ref to add a reference, + grpc_avl_unref when done with a reference */ +typedef struct grpc_avl { + const grpc_avl_vtable* vtable; + grpc_avl_node* root; +} grpc_avl; + +/** Create an immutable AVL tree. */ +grpc_avl grpc_avl_create(const grpc_avl_vtable* vtable); +/** Add a reference to an existing tree - returns + the tree as a convenience. The optional user_data will be passed to vtable + functions. */ +grpc_avl grpc_avl_ref(grpc_avl avl, void* user_data); +/** Remove a reference to a tree - destroying it if there + are no references left. The optional user_data will be passed to vtable + functions. */ +void grpc_avl_unref(grpc_avl avl, void* user_data); +/** Return a new tree with (key, value) added to avl. + implicitly unrefs avl to allow easy chaining. + if key exists in avl, the new tree's key entry updated + (i.e. a duplicate is not created). The optional user_data will be passed to + vtable functions. */ +grpc_avl grpc_avl_add(grpc_avl avl, void* key, void* value, void* user_data); +/** Return a new tree with key deleted + implicitly unrefs avl to allow easy chaining. The optional user_data will be + passed to vtable functions. */ +grpc_avl grpc_avl_remove(grpc_avl avl, void* key, void* user_data); +/** Lookup key, and return the associated value. + Does not mutate avl. + Returns NULL if key is not found. The optional user_data will be passed to + vtable functions.*/ +void* grpc_avl_get(grpc_avl avl, void* key, void* user_data); +/** Return 1 if avl contains key, 0 otherwise; if it has the key, sets *value to + its value. The optional user_data will be passed to vtable functions. */ +int grpc_avl_maybe_get(grpc_avl avl, void* key, void** value, void* user_data); +/** Return 1 if avl is empty, 0 otherwise */ +int grpc_avl_is_empty(grpc_avl avl); + +#endif /* GRPC_CORE_LIB_AVL_AVL_H */ diff --git a/third_party/grpc/src/core/lib/backoff/backoff.cc b/third_party/grpc/src/core/lib/backoff/backoff.cc new file mode 100644 index 00000000000000..e536abde0a28e2 --- /dev/null +++ b/third_party/grpc/src/core/lib/backoff/backoff.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/backoff/backoff.h" + +#include + +#include "src/core/lib/gpr/useful.h" + +namespace grpc_core { + +namespace { + +/* Generate a random number between 0 and 1. We roll our own RNG because seeding + * rand() modifies a global variable we have no control over. */ +double generate_uniform_random_number(uint32_t* rng_state) { + constexpr uint32_t two_raise_31 = uint32_t(1) << 31; + *rng_state = (1103515245 * *rng_state + 12345) % two_raise_31; + return *rng_state / static_cast(two_raise_31); +} + +double generate_uniform_random_number_between(uint32_t* rng_state, double a, + double b) { + if (a == b) return a; + if (a > b) GPR_SWAP(double, a, b); // make sure a < b + const double range = b - a; + return a + generate_uniform_random_number(rng_state) * range; +} + +} // namespace + +BackOff::BackOff(const Options& options) + : options_(options), + rng_state_(static_cast(gpr_now(GPR_CLOCK_REALTIME).tv_nsec)) { + Reset(); +} + +grpc_millis BackOff::NextAttemptTime() { + if (initial_) { + initial_ = false; + return current_backoff_ + grpc_core::ExecCtx::Get()->Now(); + } + current_backoff_ = static_cast( + std::min(current_backoff_ * options_.multiplier(), + static_cast(options_.max_backoff()))); + const double jitter = generate_uniform_random_number_between( + &rng_state_, -options_.jitter() * current_backoff_, + options_.jitter() * current_backoff_); + const grpc_millis next_timeout = + static_cast(current_backoff_ + jitter); + return next_timeout + grpc_core::ExecCtx::Get()->Now(); +} + +void BackOff::Reset() { + current_backoff_ = options_.initial_backoff(); + initial_ = true; +} + +void BackOff::SetRandomSeed(uint32_t seed) { rng_state_ = seed; } + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/backoff/backoff.h b/third_party/grpc/src/core/lib/backoff/backoff.h new file mode 100644 index 00000000000000..e769d150ef4331 --- /dev/null +++ b/third_party/grpc/src/core/lib/backoff/backoff.h @@ -0,0 +1,89 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_BACKOFF_BACKOFF_H +#define GRPC_CORE_LIB_BACKOFF_BACKOFF_H + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" + +namespace grpc_core { + +/// Implementation of the backoff mechanism described in +/// doc/connection-backoff.md +class BackOff { + public: + class Options; + + /// Initialize backoff machinery - does not need to be destroyed + explicit BackOff(const Options& options); + + /// Returns the time at which the next attempt should start. + grpc_millis NextAttemptTime(); + + /// Reset the backoff, so the next value returned by NextAttemptTime() + /// will be the time of the second attempt (rather than the Nth). + void Reset(); + + void SetRandomSeed(unsigned int seed); + + class Options { + public: + Options& set_initial_backoff(grpc_millis initial_backoff) { + initial_backoff_ = initial_backoff; + return *this; + } + Options& set_multiplier(double multiplier) { + multiplier_ = multiplier; + return *this; + } + Options& set_jitter(double jitter) { + jitter_ = jitter; + return *this; + } + Options& set_max_backoff(grpc_millis max_backoff) { + max_backoff_ = max_backoff; + return *this; + } + /// how long to wait after the first failure before retrying + grpc_millis initial_backoff() const { return initial_backoff_; } + /// factor with which to multiply backoff after a failed retry + double multiplier() const { return multiplier_; } + /// amount to randomize backoffs + double jitter() const { return jitter_; } + /// maximum time between retries + grpc_millis max_backoff() const { return max_backoff_; } + + private: + grpc_millis initial_backoff_; + double multiplier_; + double jitter_; + grpc_millis max_backoff_; + }; // class Options + + private: + const Options options_; + uint32_t rng_state_; + bool initial_; + /// current delay before retries + grpc_millis current_backoff_; +}; + +} // namespace grpc_core +#endif /* GRPC_CORE_LIB_BACKOFF_BACKOFF_H */ diff --git a/third_party/grpc/src/core/lib/channel/README.md b/third_party/grpc/src/core/lib/channel/README.md new file mode 100644 index 00000000000000..2dfcfe6e662322 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/README.md @@ -0,0 +1,4 @@ +# Channel + +Provides channel/call stack implementation, and implementation of common filters +for that implementation. diff --git a/third_party/grpc/src/core/lib/channel/channel_args.cc b/third_party/grpc/src/core/lib/channel/channel_args.cc new file mode 100644 index 00000000000000..e49d532e11dd40 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_args.cc @@ -0,0 +1,441 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" + +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.vtable->copy(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) { + return grpc_channel_args_copy_and_add_and_remove(src, nullptr, 0, to_add, + num_to_add); +} + +grpc_channel_args* grpc_channel_args_copy_and_remove( + const grpc_channel_args* src, const char** to_remove, + size_t num_to_remove) { + return grpc_channel_args_copy_and_add_and_remove(src, to_remove, + num_to_remove, nullptr, 0); +} + +static bool should_remove_arg(const grpc_arg* arg, const char** to_remove, + size_t num_to_remove) { + for (size_t i = 0; i < num_to_remove; ++i) { + if (strcmp(arg->key, to_remove[i]) == 0) return true; + } + return false; +} + +grpc_channel_args* grpc_channel_args_copy_and_add_and_remove( + const grpc_channel_args* src, const char** to_remove, size_t num_to_remove, + const grpc_arg* to_add, size_t num_to_add) { + // Figure out how many args we'll be copying. + size_t num_args_to_copy = 0; + if (src != nullptr) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + ++num_args_to_copy; + } + } + } + // Create result. + grpc_channel_args* dst = + static_cast(gpr_malloc(sizeof(grpc_channel_args))); + dst->num_args = num_args_to_copy + num_to_add; + if (dst->num_args == 0) { + dst->args = nullptr; + return dst; + } + dst->args = + static_cast(gpr_malloc(sizeof(grpc_arg) * dst->num_args)); + // Copy args from src that are not being removed. + size_t dst_idx = 0; + if (src != nullptr) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + dst->args[dst_idx++] = copy_arg(&src->args[i]); + } + } + } + // Add args from to_add. + for (size_t i = 0; i < num_to_add; ++i) { + dst->args[dst_idx++] = copy_arg(&to_add[i]); + } + GPR_ASSERT(dst_idx == dst->num_args); + return dst; +} + +grpc_channel_args* grpc_channel_args_copy(const grpc_channel_args* src) { + return grpc_channel_args_copy_and_add(src, nullptr, 0); +} + +grpc_channel_args* grpc_channel_args_union(const grpc_channel_args* a, + const grpc_channel_args* b) { + const size_t max_out = (a->num_args + b->num_args); + grpc_arg* uniques = + static_cast(gpr_malloc(sizeof(*uniques) * max_out)); + for (size_t i = 0; i < a->num_args; ++i) uniques[i] = a->args[i]; + + size_t uniques_idx = a->num_args; + for (size_t i = 0; i < b->num_args; ++i) { + const char* b_key = b->args[i].key; + if (grpc_channel_args_find(a, b_key) == nullptr) { // not found + uniques[uniques_idx++] = b->args[i]; + } + } + grpc_channel_args* result = + grpc_channel_args_copy_and_add(nullptr, uniques, uniques_idx); + gpr_free(uniques); + return result; +} + +static int cmp_arg(const grpc_arg* a, const grpc_arg* b) { + int c = GPR_ICMP(a->type, b->type); + if (c != 0) return c; + c = strcmp(a->key, b->key); + if (c != 0) return c; + switch (a->type) { + case GRPC_ARG_STRING: + return strcmp(a->value.string, b->value.string); + case GRPC_ARG_INTEGER: + return GPR_ICMP(a->value.integer, b->value.integer); + case GRPC_ARG_POINTER: + c = GPR_ICMP(a->value.pointer.p, b->value.pointer.p); + if (c != 0) { + c = GPR_ICMP(a->value.pointer.vtable, b->value.pointer.vtable); + if (c == 0) { + c = a->value.pointer.vtable->cmp(a->value.pointer.p, + b->value.pointer.p); + } + } + return c; + } + GPR_UNREACHABLE_CODE(return 0); +} + +/* stabilizing comparison function: since channel_args ordering matters for + * keys with the same name, we need to preserve that ordering */ +static int cmp_key_stable(const void* ap, const void* bp) { + const grpc_arg* const* a = static_cast(ap); + const grpc_arg* const* b = static_cast(bp); + int c = strcmp((*a)->key, (*b)->key); + if (c == 0) c = GPR_ICMP(*a, *b); + return c; +} + +grpc_channel_args* grpc_channel_args_normalize(const grpc_channel_args* a) { + grpc_arg** args = + static_cast(gpr_malloc(sizeof(grpc_arg*) * a->num_args)); + for (size_t i = 0; i < a->num_args; i++) { + args[i] = &a->args[i]; + } + if (a->num_args > 1) + qsort(args, a->num_args, sizeof(grpc_arg*), cmp_key_stable); + + grpc_channel_args* b = + static_cast(gpr_malloc(sizeof(grpc_channel_args))); + b->num_args = a->num_args; + b->args = static_cast(gpr_malloc(sizeof(grpc_arg) * b->num_args)); + for (size_t i = 0; i < a->num_args; i++) { + b->args[i] = copy_arg(args[i]); + } + + gpr_free(args); + return b; +} + +void grpc_channel_args_destroy(grpc_channel_args* a) { + size_t i; + if (!a) return; + 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: + a->args[i].value.pointer.vtable->destroy(a->args[i].value.pointer.p); + break; + } + gpr_free(a->args[i].key); + } + gpr_free(a->args); + gpr_free(a); +} + +grpc_compression_algorithm grpc_channel_args_get_compression_algorithm( + const grpc_channel_args* a) { + size_t i; + if (a == nullptr) return GRPC_COMPRESS_NONE; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, a->args[i].key)) { + return static_cast(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) { + GPR_ASSERT(algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT); + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM; + 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 != nullptr) { + size_t i; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, + a->args[i].key)) { + *states_arg = &a->args[i].value.integer; + **states_arg |= 0x1; /* forcefully enable support for no compression */ + return 1; + } + } + } + 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 = nullptr; + grpc_channel_args* result = *a; + const int states_arg_found = + find_compression_algorithm_states_bitset(*a, &states_arg); + + if (grpc_channel_args_get_compression_algorithm(*a) == algorithm && + state == 0) { + const char* algo_name = nullptr; + GPR_ASSERT(grpc_compression_algorithm_name(algorithm, &algo_name) != 0); + gpr_log(GPR_ERROR, + "Tried to disable default compression algorithm '%s'. The " + "operation has been ignored.", + algo_name); + } else if (states_arg_found) { + if (state != 0) { + GPR_BITSET((unsigned*)states_arg, algorithm); + } else if (algorithm != GRPC_COMPRESS_NONE) { + GPR_BITCLEAR((unsigned*)states_arg, algorithm); + } + } else { + /* create a new arg */ + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = (char*)GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET; + /* all enabled by default */ + tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; + if (state != 0) { + GPR_BITSET((unsigned*)&tmp.value.integer, algorithm); + } else if (algorithm != GRPC_COMPRESS_NONE) { + GPR_BITCLEAR((unsigned*)&tmp.value.integer, algorithm); + } + result = grpc_channel_args_copy_and_add(*a, &tmp, 1); + grpc_channel_args_destroy(*a); + *a = result; + } + return result; +} + +uint32_t 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 static_cast(*states_arg); + } else { + return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */ + } +} + +grpc_channel_args* grpc_channel_args_set_socket_mutator( + grpc_channel_args* a, grpc_socket_mutator* mutator) { + grpc_arg tmp = grpc_socket_mutator_to_arg(mutator); + return grpc_channel_args_copy_and_add(a, &tmp, 1); +} + +int grpc_channel_args_compare(const grpc_channel_args* a, + const grpc_channel_args* b) { + int c = GPR_ICMP(a->num_args, b->num_args); + if (c != 0) return c; + for (size_t i = 0; i < a->num_args; i++) { + c = cmp_arg(&a->args[i], &b->args[i]); + if (c != 0) return c; + } + return 0; +} + +const grpc_arg* grpc_channel_args_find(const grpc_channel_args* args, + const char* name) { + if (args != nullptr) { + for (size_t i = 0; i < args->num_args; ++i) { + if (strcmp(args->args[i].key, name) == 0) { + return &args->args[i]; + } + } + } + return nullptr; +} + +int grpc_channel_arg_get_integer(const grpc_arg* arg, + const grpc_integer_options options) { + if (arg == nullptr) return options.default_value; + if (arg->type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key); + return options.default_value; + } + if (arg->value.integer < options.min_value) { + gpr_log(GPR_ERROR, "%s ignored: it must be >= %d", arg->key, + options.min_value); + return options.default_value; + } + if (arg->value.integer > options.max_value) { + gpr_log(GPR_ERROR, "%s ignored: it must be <= %d", arg->key, + options.max_value); + return options.default_value; + } + return arg->value.integer; +} + +char* grpc_channel_arg_get_string(const grpc_arg* arg) { + if (arg == nullptr) return nullptr; + if (arg->type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "%s ignored: it must be an string", arg->key); + return nullptr; + } + return arg->value.string; +} + +bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value) { + if (arg == nullptr) return default_value; + if (arg->type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key); + return default_value; + } + switch (arg->value.integer) { + case 0: + return false; + case 1: + return true; + default: + gpr_log(GPR_ERROR, "%s treated as bool but set to %d (assuming true)", + arg->key, arg->value.integer); + return true; + } +} + +bool grpc_channel_args_want_minimal_stack(const grpc_channel_args* args) { + return grpc_channel_arg_get_bool( + grpc_channel_args_find(args, GRPC_ARG_MINIMAL_STACK), false); +} + +grpc_arg grpc_channel_arg_string_create(char* name, char* value) { + grpc_arg arg; + arg.type = GRPC_ARG_STRING; + arg.key = name; + arg.value.string = value; + return arg; +} + +grpc_arg grpc_channel_arg_integer_create(char* name, int value) { + grpc_arg arg; + arg.type = GRPC_ARG_INTEGER; + arg.key = name; + arg.value.integer = value; + return arg; +} + +grpc_arg grpc_channel_arg_pointer_create( + char* name, void* value, const grpc_arg_pointer_vtable* vtable) { + grpc_arg arg; + arg.type = GRPC_ARG_POINTER; + arg.key = name; + arg.value.pointer.p = value; + arg.value.pointer.vtable = vtable; + return arg; +} + +char* grpc_channel_args_string(const grpc_channel_args* args) { + if (args == nullptr) return nullptr; + gpr_strvec v; + gpr_strvec_init(&v); + for (size_t i = 0; i < args->num_args; ++i) { + const grpc_arg& arg = args->args[i]; + char* s; + switch (arg.type) { + case GRPC_ARG_INTEGER: + gpr_asprintf(&s, "%s=%d", arg.key, arg.value.integer); + break; + case GRPC_ARG_STRING: + gpr_asprintf(&s, "%s=%s", arg.key, arg.value.string); + break; + case GRPC_ARG_POINTER: + gpr_asprintf(&s, "%s=%p", arg.key, arg.value.pointer.p); + break; + default: + gpr_asprintf(&s, "arg with unknown type"); + } + gpr_strvec_add(&v, s); + } + char* result = + gpr_strjoin_sep(const_cast(v.strs), v.count, ", ", nullptr); + gpr_strvec_destroy(&v); + return result; +} diff --git a/third_party/grpc/src/core/lib/channel/channel_args.h b/third_party/grpc/src/core/lib/channel/channel_args.h new file mode 100644 index 00000000000000..5ff303a9dc6ac3 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_args.h @@ -0,0 +1,131 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H +#define GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H + +#include + +#include +#include +#include "src/core/lib/iomgr/socket_mutator.h" + +// Channel args are intentionally immutable, to avoid the need for locking. + +/** Copy the arguments in \a src into a new instance */ +grpc_channel_args* grpc_channel_args_copy(const grpc_channel_args* src); + +/** Copy the arguments in \a src into a new instance, stably sorting keys */ +grpc_channel_args* grpc_channel_args_normalize(const grpc_channel_args* src); + +/** Copy the arguments in \a src and append \a to_add. If \a to_add is NULL, it + * is equivalent to calling \a 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); + +/** Copies the arguments in \a src except for those whose keys are in + \a to_remove. */ +grpc_channel_args* grpc_channel_args_copy_and_remove( + const grpc_channel_args* src, const char** to_remove, size_t num_to_remove); + +/** Copies the arguments from \a src except for those whose keys are in + \a to_remove and appends the arguments in \a to_add. */ +grpc_channel_args* grpc_channel_args_copy_and_add_and_remove( + const grpc_channel_args* src, const char** to_remove, size_t num_to_remove, + const grpc_arg* to_add, size_t num_to_add); + +/** Perform the union of \a a and \a b, prioritizing \a a entries */ +grpc_channel_args* grpc_channel_args_union(const grpc_channel_args* a, + const grpc_channel_args* b); + +/** Destroy arguments created by \a grpc_channel_args_copy */ +void grpc_channel_args_destroy(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 with 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. */ +uint32_t grpc_channel_args_compression_algorithm_get_states( + const grpc_channel_args* a); + +int grpc_channel_args_compare(const grpc_channel_args* a, + const grpc_channel_args* b); + +/** Returns a channel arg instance with socket mutator added. The socket mutator + * will perform its mutate_fd method on all file descriptors used by the + * channel. + * If \a a is non-MULL, its args are copied. */ +grpc_channel_args* grpc_channel_args_set_socket_mutator( + grpc_channel_args* a, grpc_socket_mutator* mutator); + +/** Returns the value of argument \a name from \a args, or NULL if not found. */ +const grpc_arg* grpc_channel_args_find(const grpc_channel_args* args, + const char* name); + +bool grpc_channel_args_want_minimal_stack(const grpc_channel_args* args); + +typedef struct grpc_integer_options { + int default_value; // Return this if value is outside of expected bounds. + int min_value; + int max_value; +} grpc_integer_options; + +/** Returns the value of \a arg, subject to the contraints in \a options. */ +int grpc_channel_arg_get_integer(const grpc_arg* arg, + const grpc_integer_options options); + +/** Returns the value of \a arg if \a arg is of type GRPC_ARG_STRING. + Otherwise, emits a warning log, and returns nullptr. + If arg is nullptr, returns nullptr, and does not emit a warning. */ +char* grpc_channel_arg_get_string(const grpc_arg* arg); + +bool grpc_channel_arg_get_bool(const grpc_arg* arg, bool default_value); + +// Helpers for creating channel args. +grpc_arg grpc_channel_arg_string_create(char* name, char* value); +grpc_arg grpc_channel_arg_integer_create(char* name, int value); +grpc_arg grpc_channel_arg_pointer_create(char* name, void* value, + const grpc_arg_pointer_vtable* vtable); + +// Returns a string representing channel args in human-readable form. +// Callers takes ownership of result. +char* grpc_channel_args_string(const grpc_channel_args* args); + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */ diff --git a/third_party/grpc/src/core/lib/channel/channel_stack.cc b/third_party/grpc/src/core/lib/channel/channel_stack.cc new file mode 100644 index 00000000000000..df956c7176c63c --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_stack.cc @@ -0,0 +1,252 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/gpr/alloc.h" + +#include +#include + +grpc_core::TraceFlag grpc_trace_channel(false, "channel"); + +/* 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 + } */ + +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 = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)) + + GPR_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 += GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); + } + + return size; +} + +#define CHANNEL_ELEMS_FROM_STACK(stk) \ + ((grpc_channel_element*)((char*)(stk) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE( \ + sizeof(grpc_channel_stack)))) + +#define CALL_ELEMS_FROM_STACK(stk) \ + ((grpc_call_element*)((char*)(stk) + GPR_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; +} + +grpc_error* grpc_channel_stack_init( + int initial_refs, grpc_iomgr_cb_func destroy, void* destroy_arg, + const grpc_channel_filter** filters, size_t filter_count, + const grpc_channel_args* channel_args, grpc_transport* optional_transport, + const char* name, grpc_channel_stack* stack) { + size_t call_size = + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) + + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element)); + grpc_channel_element* elems; + grpc_channel_element_args args; + char* user_data; + size_t i; + + stack->count = filter_count; + GRPC_STREAM_REF_INIT(&stack->refcount, initial_refs, destroy, destroy_arg, + name); + elems = CHANNEL_ELEMS_FROM_STACK(stack); + user_data = (reinterpret_cast(elems)) + + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * + sizeof(grpc_channel_element)); + + /* init per-filter data */ + grpc_error* first_error = GRPC_ERROR_NONE; + for (i = 0; i < filter_count; i++) { + args.channel_stack = stack; + args.channel_args = channel_args; + args.optional_transport = optional_transport; + args.is_first = i == 0; + args.is_last = i == (filter_count - 1); + elems[i].filter = filters[i]; + elems[i].channel_data = user_data; + grpc_error* error = elems[i].filter->init_channel_elem(&elems[i], &args); + if (error != GRPC_ERROR_NONE) { + if (first_error == GRPC_ERROR_NONE) { + first_error = error; + } else { + GRPC_ERROR_UNREF(error); + } + } + user_data += + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); + call_size += GPR_ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data); + } + + GPR_ASSERT(user_data > (char*)stack); + GPR_ASSERT((uintptr_t)(user_data - (char*)stack) == + grpc_channel_stack_size(filters, filter_count)); + + stack->call_stack_size = call_size; + return first_error; +} + +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]); + } +} + +grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack, + int initial_refs, grpc_iomgr_cb_func destroy, + void* destroy_arg, + const grpc_call_element_args* elem_args) { + 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; + + elem_args->call_stack->count = count; + GRPC_STREAM_REF_INIT(&elem_args->call_stack->refcount, initial_refs, destroy, + destroy_arg, "CALL_STACK"); + call_elems = CALL_ELEMS_FROM_STACK(elem_args->call_stack); + user_data = (reinterpret_cast(call_elems)) + + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element)); + + /* init per-filter data */ + grpc_error* first_error = GRPC_ERROR_NONE; + for (size_t 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; + user_data += + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); + } + for (size_t i = 0; i < count; i++) { + grpc_error* error = + call_elems[i].filter->init_call_elem(&call_elems[i], elem_args); + if (error != GRPC_ERROR_NONE) { + if (first_error == GRPC_ERROR_NONE) { + first_error = error; + } else { + GRPC_ERROR_UNREF(error); + } + } + } + return first_error; +} + +void grpc_call_stack_set_pollset_or_pollset_set(grpc_call_stack* call_stack, + grpc_polling_entity* pollent) { + size_t count = call_stack->count; + grpc_call_element* call_elems; + size_t i; + + call_elems = CALL_ELEMS_FROM_STACK(call_stack); + + /* init per-filter data */ + for (i = 0; i < count; i++) { + call_elems[i].filter->set_pollset_or_pollset_set(&call_elems[i], pollent); + } +} + +void grpc_call_stack_ignore_set_pollset_or_pollset_set( + grpc_call_element* elem, grpc_polling_entity* pollent) {} + +void grpc_call_stack_destroy(grpc_call_stack* stack, + const grpc_call_final_info* final_info, + grpc_closure* then_schedule_closure) { + 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], final_info, + i == count - 1 ? then_schedule_closure : nullptr); + } +} + +void grpc_call_next_op(grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + grpc_call_element* next_elem = elem + 1; + GRPC_CALL_LOG_OP(GPR_INFO, next_elem, op); + next_elem->filter->start_transport_stream_op_batch(next_elem, op); +} + +void grpc_channel_next_get_info(grpc_channel_element* elem, + const grpc_channel_info* channel_info) { + grpc_channel_element* next_elem = elem + 1; + next_elem->filter->get_channel_info(next_elem, channel_info); +} + +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 reinterpret_cast( + reinterpret_cast(elem) - + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack))); +} + +grpc_call_stack* grpc_call_stack_from_top_element(grpc_call_element* elem) { + return reinterpret_cast( + reinterpret_cast(elem) - + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack))); +} diff --git a/third_party/grpc/src/core/lib/channel/channel_stack.h b/third_party/grpc/src/core/lib/channel/channel_stack.h new file mode 100644 index 00000000000000..0de8c67079005d --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_stack.h @@ -0,0 +1,280 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H +#define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H + +////////////////////////////////////////////////////////////////////////////// +// IMPORTANT NOTE: +// +// When you update this API, please make the corresponding changes to +// the C++ API in src/cpp/common/channel_filter.{h,cc} +////////////////////////////////////////////////////////////////////////////// + +/* 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 +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/arena.h" +#include "src/core/lib/iomgr/call_combiner.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/transport/transport.h" + +typedef struct grpc_channel_element grpc_channel_element; +typedef struct grpc_call_element grpc_call_element; + +typedef struct grpc_channel_stack grpc_channel_stack; +typedef struct grpc_call_stack grpc_call_stack; + +typedef struct { + grpc_channel_stack* channel_stack; + const grpc_channel_args* channel_args; + /** Transport, iff it is known */ + grpc_transport* optional_transport; + int is_first; + int is_last; +} grpc_channel_element_args; + +typedef struct { + grpc_call_stack* call_stack; + const void* server_transport_data; + grpc_call_context_element* context; + grpc_slice path; + gpr_timespec start_time; + grpc_millis deadline; + gpr_arena* arena; + grpc_call_combiner* call_combiner; +} grpc_call_element_args; + +typedef struct { + grpc_transport_stream_stats transport_stream_stats; + gpr_timespec latency; /* From call creating to enqueing of received status */ +} grpc_call_stats; + +/** Information about the call upon completion. */ +struct grpc_call_final_info { + grpc_call_stats stats; + grpc_status_code final_status = GRPC_STATUS_OK; + const char* error_string = nullptr; +}; + +/* 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_batch)(grpc_call_element* elem, + grpc_transport_stream_op_batch* 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. + Implementations may assume that elem->call_data is all zeros. */ + grpc_error* (*init_call_elem)(grpc_call_element* elem, + const grpc_call_element_args* args); + void (*set_pollset_or_pollset_set)(grpc_call_element* elem, + grpc_polling_entity* pollent); + /* Destroy per call data. + The filter does not need to do any chaining. + The bottom filter of a stack will be passed a non-NULL pointer to + \a then_schedule_closure that should be passed to GRPC_CLOSURE_SCHED when + destruction is complete. \a final_info contains data about the completed + call, mainly for reporting purposes. */ + void (*destroy_call_elem)(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_schedule_closure); + + /* sizeof(per channel data) */ + size_t sizeof_channel_data; + /* Initialize per-channel data. + elem is initialized at the creating of the channel, 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. + Implementations may assume that elem->channel_data is all zeros. */ + grpc_error* (*init_channel_elem)(grpc_channel_element* elem, + grpc_channel_element_args* args); + /* Destroy per channel data. + The filter does not need to do any chaining */ + void (*destroy_channel_elem)(grpc_channel_element* elem); + + /* Implement grpc_channel_get_info() */ + void (*get_channel_info)(grpc_channel_element* elem, + const grpc_channel_info* channel_info); + + /* 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 */ +struct grpc_channel_stack { + grpc_stream_refcount refcount; + size_t count; + /* Memory required for a call stack (computed at channel stack + initialization) */ + size_t call_stack_size; +}; + +/* A call stack tracks a set of related filters for one call, and guarantees + they live within a single malloc() allocation */ +struct grpc_call_stack { + /* shared refcount for this channel stack. + MUST be the first element: the underlying code calls destroy + with the address of the refcount, but higher layers prefer to think + about the address of the call stack itself. */ + grpc_stream_refcount refcount; + size_t count; +}; + +/* 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 */ +grpc_error* grpc_channel_stack_init( + int initial_refs, grpc_iomgr_cb_func destroy, void* destroy_arg, + const grpc_channel_filter** filters, size_t filter_count, + const grpc_channel_args* args, grpc_transport* optional_transport, + const char* name, 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. */ +grpc_error* grpc_call_stack_init(grpc_channel_stack* channel_stack, + int initial_refs, grpc_iomgr_cb_func destroy, + void* destroy_arg, + const grpc_call_element_args* elem_args); +/* Set a pollset or a pollset_set for a call stack: must occur before the first + * op is started */ +void grpc_call_stack_set_pollset_or_pollset_set(grpc_call_stack* call_stack, + grpc_polling_entity* pollent); + +#ifndef NDEBUG +#define GRPC_CALL_STACK_REF(call_stack, reason) \ + grpc_stream_ref(&(call_stack)->refcount, reason) +#define GRPC_CALL_STACK_UNREF(call_stack, reason) \ + grpc_stream_unref(&(call_stack)->refcount, reason) +#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \ + grpc_stream_ref(&(channel_stack)->refcount, reason) +#define GRPC_CHANNEL_STACK_UNREF(channel_stack, reason) \ + grpc_stream_unref(&(channel_stack)->refcount, reason) +#else +#define GRPC_CALL_STACK_REF(call_stack, reason) \ + grpc_stream_ref(&(call_stack)->refcount) +#define GRPC_CALL_STACK_UNREF(call_stack, reason) \ + grpc_stream_unref(&(call_stack)->refcount) +#define GRPC_CHANNEL_STACK_REF(channel_stack, reason) \ + grpc_stream_ref(&(channel_stack)->refcount) +#define GRPC_CHANNEL_STACK_UNREF(channel_stack, reason) \ + grpc_stream_unref(&(channel_stack)->refcount) +#endif + +/* Destroy a call stack */ +void grpc_call_stack_destroy(grpc_call_stack* stack, + const grpc_call_final_info* final_info, + grpc_closure* then_schedule_closure); + +/* Ignore set pollset{_set} - used by filters if they don't care about pollsets + * at all. Does nothing. */ +void grpc_call_stack_ignore_set_pollset_or_pollset_set( + grpc_call_element* elem, grpc_polling_entity* pollent); +/* Call the next operation in a call stack */ +void grpc_call_next_op(grpc_call_element* elem, + grpc_transport_stream_op_batch* 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_channel_info() to the next child element */ +void grpc_channel_next_get_info(grpc_channel_element* elem, + const grpc_channel_info* channel_info); + +/* 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(const char* file, int line, gpr_log_severity severity, + grpc_call_element* elem, + grpc_transport_stream_op_batch* op); + +extern grpc_core::TraceFlag grpc_trace_channel; + +#define GRPC_CALL_LOG_OP(sev, elem, op) \ + if (grpc_trace_channel.enabled()) grpc_call_log_op(sev, elem, op) + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_H */ diff --git a/third_party/grpc/src/core/lib/channel/channel_stack_builder.cc b/third_party/grpc/src/core/lib/channel/channel_stack_builder.cc new file mode 100644 index 00000000000000..8b3008f2217f5b --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_stack_builder.cc @@ -0,0 +1,323 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channel_stack_builder.h" + +#include + +#include +#include + +typedef struct filter_node { + struct filter_node* next; + struct filter_node* prev; + const grpc_channel_filter* filter; + grpc_post_filter_create_init_func init; + void* init_arg; +} filter_node; + +struct grpc_channel_stack_builder { + // sentinel nodes for filters that have been added + filter_node begin; + filter_node end; + // various set/get-able parameters + grpc_channel_args* args; + grpc_transport* transport; + grpc_resource_user* resource_user; + char* target; + const char* name; +}; + +struct grpc_channel_stack_builder_iterator { + grpc_channel_stack_builder* builder; + filter_node* node; +}; + +grpc_channel_stack_builder* grpc_channel_stack_builder_create(void) { + grpc_channel_stack_builder* b = + static_cast(gpr_zalloc(sizeof(*b))); + + b->begin.filter = nullptr; + b->end.filter = nullptr; + b->begin.next = &b->end; + b->begin.prev = &b->end; + b->end.next = &b->begin; + b->end.prev = &b->begin; + + return b; +} + +void grpc_channel_stack_builder_set_target(grpc_channel_stack_builder* b, + const char* target) { + gpr_free(b->target); + b->target = gpr_strdup(target); +} + +const char* grpc_channel_stack_builder_get_target( + grpc_channel_stack_builder* b) { + return b->target; +} + +static grpc_channel_stack_builder_iterator* create_iterator_at_filter_node( + grpc_channel_stack_builder* builder, filter_node* node) { + grpc_channel_stack_builder_iterator* it = + static_cast( + gpr_malloc(sizeof(*it))); + it->builder = builder; + it->node = node; + return it; +} + +void grpc_channel_stack_builder_iterator_destroy( + grpc_channel_stack_builder_iterator* it) { + gpr_free(it); +} + +grpc_channel_stack_builder_iterator* +grpc_channel_stack_builder_create_iterator_at_first( + grpc_channel_stack_builder* builder) { + return create_iterator_at_filter_node(builder, &builder->begin); +} + +grpc_channel_stack_builder_iterator* +grpc_channel_stack_builder_create_iterator_at_last( + grpc_channel_stack_builder* builder) { + return create_iterator_at_filter_node(builder, &builder->end); +} + +bool grpc_channel_stack_builder_iterator_is_end( + grpc_channel_stack_builder_iterator* iterator) { + return iterator->node == &iterator->builder->end; +} + +const char* grpc_channel_stack_builder_iterator_filter_name( + grpc_channel_stack_builder_iterator* iterator) { + if (iterator->node->filter == nullptr) return nullptr; + return iterator->node->filter->name; +} + +bool grpc_channel_stack_builder_move_next( + grpc_channel_stack_builder_iterator* iterator) { + if (iterator->node == &iterator->builder->end) return false; + iterator->node = iterator->node->next; + return true; +} + +bool grpc_channel_stack_builder_move_prev( + grpc_channel_stack_builder_iterator* iterator) { + if (iterator->node == &iterator->builder->begin) return false; + iterator->node = iterator->node->prev; + return true; +} + +grpc_channel_stack_builder_iterator* grpc_channel_stack_builder_iterator_find( + grpc_channel_stack_builder* builder, const char* filter_name) { + GPR_ASSERT(filter_name != nullptr); + grpc_channel_stack_builder_iterator* it = + grpc_channel_stack_builder_create_iterator_at_first(builder); + while (grpc_channel_stack_builder_move_next(it)) { + if (grpc_channel_stack_builder_iterator_is_end(it)) break; + const char* filter_name_at_it = + grpc_channel_stack_builder_iterator_filter_name(it); + if (strcmp(filter_name, filter_name_at_it) == 0) break; + } + return it; +} + +bool grpc_channel_stack_builder_move_prev( + grpc_channel_stack_builder_iterator* iterator); + +void grpc_channel_stack_builder_set_name(grpc_channel_stack_builder* builder, + const char* name) { + GPR_ASSERT(builder->name == nullptr); + builder->name = name; +} + +void grpc_channel_stack_builder_set_channel_arguments( + grpc_channel_stack_builder* builder, const grpc_channel_args* args) { + if (builder->args != nullptr) { + grpc_channel_args_destroy(builder->args); + } + builder->args = grpc_channel_args_copy(args); +} + +const grpc_channel_args* grpc_channel_stack_builder_get_channel_arguments( + grpc_channel_stack_builder* builder) { + return builder->args; +} + +void grpc_channel_stack_builder_set_transport( + grpc_channel_stack_builder* builder, grpc_transport* transport) { + GPR_ASSERT(builder->transport == nullptr); + builder->transport = transport; +} + +grpc_transport* grpc_channel_stack_builder_get_transport( + grpc_channel_stack_builder* builder) { + return builder->transport; +} + +void grpc_channel_stack_builder_set_resource_user( + grpc_channel_stack_builder* builder, grpc_resource_user* resource_user) { + GPR_ASSERT(builder->resource_user == nullptr); + builder->resource_user = resource_user; +} + +grpc_resource_user* grpc_channel_stack_builder_get_resource_user( + grpc_channel_stack_builder* builder) { + return builder->resource_user; +} + +bool grpc_channel_stack_builder_append_filter( + grpc_channel_stack_builder* builder, const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, void* user_data) { + grpc_channel_stack_builder_iterator* it = + grpc_channel_stack_builder_create_iterator_at_last(builder); + bool ok = grpc_channel_stack_builder_add_filter_before( + it, filter, post_init_func, user_data); + grpc_channel_stack_builder_iterator_destroy(it); + return ok; +} + +bool grpc_channel_stack_builder_remove_filter( + grpc_channel_stack_builder* builder, const char* filter_name) { + grpc_channel_stack_builder_iterator* it = + grpc_channel_stack_builder_iterator_find(builder, filter_name); + if (grpc_channel_stack_builder_iterator_is_end(it)) { + grpc_channel_stack_builder_iterator_destroy(it); + return false; + } + it->node->prev->next = it->node->next; + it->node->next->prev = it->node->prev; + gpr_free(it->node); + grpc_channel_stack_builder_iterator_destroy(it); + return true; +} + +bool grpc_channel_stack_builder_prepend_filter( + grpc_channel_stack_builder* builder, const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, void* user_data) { + grpc_channel_stack_builder_iterator* it = + grpc_channel_stack_builder_create_iterator_at_first(builder); + bool ok = grpc_channel_stack_builder_add_filter_after( + it, filter, post_init_func, user_data); + grpc_channel_stack_builder_iterator_destroy(it); + return ok; +} + +static void add_after(filter_node* before, const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, + void* user_data) { + filter_node* new_node = + static_cast(gpr_malloc(sizeof(*new_node))); + new_node->next = before->next; + new_node->prev = before; + new_node->next->prev = new_node->prev->next = new_node; + new_node->filter = filter; + new_node->init = post_init_func; + new_node->init_arg = user_data; +} + +bool grpc_channel_stack_builder_add_filter_before( + grpc_channel_stack_builder_iterator* iterator, + const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, void* user_data) { + if (iterator->node == &iterator->builder->begin) return false; + add_after(iterator->node->prev, filter, post_init_func, user_data); + return true; +} + +bool grpc_channel_stack_builder_add_filter_after( + grpc_channel_stack_builder_iterator* iterator, + const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, void* user_data) { + if (iterator->node == &iterator->builder->end) return false; + add_after(iterator->node, filter, post_init_func, user_data); + return true; +} + +void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder* builder) { + filter_node* p = builder->begin.next; + while (p != &builder->end) { + filter_node* next = p->next; + gpr_free(p); + p = next; + } + if (builder->args != nullptr) { + grpc_channel_args_destroy(builder->args); + } + gpr_free(builder->target); + gpr_free(builder); +} + +grpc_error* grpc_channel_stack_builder_finish( + grpc_channel_stack_builder* builder, size_t prefix_bytes, int initial_refs, + grpc_iomgr_cb_func destroy, void* destroy_arg, void** result) { + // count the number of filters + size_t num_filters = 0; + for (filter_node* p = builder->begin.next; p != &builder->end; p = p->next) { + num_filters++; + } + + // create an array of filters + const grpc_channel_filter** filters = + static_cast( + gpr_malloc(sizeof(*filters) * num_filters)); + size_t i = 0; + for (filter_node* p = builder->begin.next; p != &builder->end; p = p->next) { + filters[i++] = p->filter; + } + + // calculate the size of the channel stack + size_t channel_stack_size = grpc_channel_stack_size(filters, num_filters); + + // allocate memory, with prefix_bytes followed by channel_stack_size + *result = gpr_zalloc(prefix_bytes + channel_stack_size); + // fetch a pointer to the channel stack + grpc_channel_stack* channel_stack = reinterpret_cast( + static_cast(*result) + prefix_bytes); + // and initialize it + grpc_error* error = grpc_channel_stack_init( + initial_refs, destroy, destroy_arg == nullptr ? *result : destroy_arg, + filters, num_filters, builder->args, builder->transport, builder->name, + channel_stack); + + if (error != GRPC_ERROR_NONE) { + grpc_channel_stack_destroy(channel_stack); + gpr_free(*result); + *result = nullptr; + } else { + // run post-initialization functions + i = 0; + for (filter_node* p = builder->begin.next; p != &builder->end; + p = p->next) { + if (p->init != nullptr) { + p->init(channel_stack, grpc_channel_stack_element(channel_stack, i), + p->init_arg); + } + i++; + } + } + + grpc_channel_stack_builder_destroy(builder); + gpr_free(const_cast(filters)); + + return error; +} diff --git a/third_party/grpc/src/core/lib/channel/channel_stack_builder.h b/third_party/grpc/src/core/lib/channel/channel_stack_builder.h new file mode 100644 index 00000000000000..89c30e0c5ea0ef --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_stack_builder.h @@ -0,0 +1,166 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H +#define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H + +#include + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack.h" + +/// grpc_channel_stack_builder offers a programmatic interface to selected +/// and order channel filters +typedef struct grpc_channel_stack_builder grpc_channel_stack_builder; +typedef struct grpc_channel_stack_builder_iterator + grpc_channel_stack_builder_iterator; + +/// Create a new channel stack builder +grpc_channel_stack_builder* grpc_channel_stack_builder_create(void); + +/// Assign a name to the channel stack: \a name must be statically allocated +void grpc_channel_stack_builder_set_name(grpc_channel_stack_builder* builder, + const char* name); + +/// Set the target uri +void grpc_channel_stack_builder_set_target(grpc_channel_stack_builder* b, + const char* target); + +const char* grpc_channel_stack_builder_get_target( + grpc_channel_stack_builder* b); + +/// Attach \a transport to the builder (does not take ownership) +void grpc_channel_stack_builder_set_transport( + grpc_channel_stack_builder* builder, grpc_transport* transport); + +/// Fetch attached transport +grpc_transport* grpc_channel_stack_builder_get_transport( + grpc_channel_stack_builder* builder); + +/// Attach \a resource_user to the builder (does not take ownership) +void grpc_channel_stack_builder_set_resource_user( + grpc_channel_stack_builder* builder, grpc_resource_user* resource_user); + +/// Fetch attached resource user +grpc_resource_user* grpc_channel_stack_builder_get_resource_user( + grpc_channel_stack_builder* builder); + +/// Set channel arguments: copies args +void grpc_channel_stack_builder_set_channel_arguments( + grpc_channel_stack_builder* builder, const grpc_channel_args* args); + +/// Return a borrowed pointer to the channel arguments +const grpc_channel_args* grpc_channel_stack_builder_get_channel_arguments( + grpc_channel_stack_builder* builder); + +/// Begin iterating over already defined filters in the builder at the beginning +grpc_channel_stack_builder_iterator* +grpc_channel_stack_builder_create_iterator_at_first( + grpc_channel_stack_builder* builder); + +/// Begin iterating over already defined filters in the builder at the end +grpc_channel_stack_builder_iterator* +grpc_channel_stack_builder_create_iterator_at_last( + grpc_channel_stack_builder* builder); + +/// Is an iterator at the first element? +bool grpc_channel_stack_builder_iterator_is_first( + grpc_channel_stack_builder_iterator* iterator); + +/// Is an iterator at the end? +bool grpc_channel_stack_builder_iterator_is_end( + grpc_channel_stack_builder_iterator* iterator); + +/// What is the name of the filter at this iterator position? +const char* grpc_channel_stack_builder_iterator_filter_name( + grpc_channel_stack_builder_iterator* iterator); + +/// Move an iterator to the next item +bool grpc_channel_stack_builder_move_next( + grpc_channel_stack_builder_iterator* iterator); + +/// Move an iterator to the previous item +bool grpc_channel_stack_builder_move_prev( + grpc_channel_stack_builder_iterator* iterator); + +/// Return an iterator at \a filter_name, or at the end of the list if not +/// found. +grpc_channel_stack_builder_iterator* grpc_channel_stack_builder_iterator_find( + grpc_channel_stack_builder* builder, const char* filter_name); + +typedef void (*grpc_post_filter_create_init_func)( + grpc_channel_stack* channel_stack, grpc_channel_element* elem, void* arg); + +/// Add \a filter to the stack, after \a iterator. +/// Call \a post_init_func(..., \a user_data) once the channel stack is +/// created. +bool grpc_channel_stack_builder_add_filter_after( + grpc_channel_stack_builder_iterator* iterator, + const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, + void* user_data) GRPC_MUST_USE_RESULT; + +/// Add \a filter to the stack, before \a iterator. +/// Call \a post_init_func(..., \a user_data) once the channel stack is +/// created. +bool grpc_channel_stack_builder_add_filter_before( + grpc_channel_stack_builder_iterator* iterator, + const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, + void* user_data) GRPC_MUST_USE_RESULT; + +/// Add \a filter to the beginning of the filter list. +/// Call \a post_init_func(..., \a user_data) once the channel stack is +/// created. +bool grpc_channel_stack_builder_prepend_filter( + grpc_channel_stack_builder* builder, const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, + void* user_data) GRPC_MUST_USE_RESULT; + +/// Add \a filter to the end of the filter list. +/// Call \a post_init_func(..., \a user_data) once the channel stack is +/// created. +bool grpc_channel_stack_builder_append_filter( + grpc_channel_stack_builder* builder, const grpc_channel_filter* filter, + grpc_post_filter_create_init_func post_init_func, + void* user_data) GRPC_MUST_USE_RESULT; + +/// Remove any filter whose name is \a filter_name from \a builder. Returns true +/// if \a filter_name was not found. +bool grpc_channel_stack_builder_remove_filter( + grpc_channel_stack_builder* builder, const char* filter_name); + +/// Terminate iteration and destroy \a iterator +void grpc_channel_stack_builder_iterator_destroy( + grpc_channel_stack_builder_iterator* iterator); + +/// Destroy the builder, return the freshly minted channel stack in \a result. +/// Allocates \a prefix_bytes bytes before the channel stack +/// Returns the base pointer of the allocated block +/// \a initial_refs, \a destroy, \a destroy_arg are as per +/// grpc_channel_stack_init +grpc_error* grpc_channel_stack_builder_finish( + grpc_channel_stack_builder* builder, size_t prefix_bytes, int initial_refs, + grpc_iomgr_cb_func destroy, void* destroy_arg, void** result); + +/// Destroy the builder without creating a channel stack +void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder* builder); + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H */ diff --git a/third_party/grpc/src/core/lib/channel/channel_trace.cc b/third_party/grpc/src/core/lib/channel/channel_trace.cc new file mode 100644 index 00000000000000..f0d21db32a896a --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_trace.cc @@ -0,0 +1,203 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channel_trace.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/status_util.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/error_utils.h" + +namespace grpc_core { +namespace channelz { + +ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data, + RefCountedPtr referenced_entity) + : severity_(severity), + data_(data), + timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(), + GPR_CLOCK_REALTIME)), + next_(nullptr), + referenced_entity_(std::move(referenced_entity)), + memory_usage_(sizeof(TraceEvent) + grpc_slice_memory_usage(data)) {} + +ChannelTrace::TraceEvent::TraceEvent(Severity severity, grpc_slice data) + : severity_(severity), + data_(data), + timestamp_(grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(), + GPR_CLOCK_REALTIME)), + next_(nullptr), + memory_usage_(sizeof(TraceEvent) + grpc_slice_memory_usage(data)) {} + +ChannelTrace::TraceEvent::~TraceEvent() { grpc_slice_unref_internal(data_); } + +ChannelTrace::ChannelTrace(size_t max_event_memory) + : num_events_logged_(0), + event_list_memory_usage_(0), + max_event_memory_(max_event_memory), + head_trace_(nullptr), + tail_trace_(nullptr) { + if (max_event_memory_ == 0) + return; // tracing is disabled if max_event_memory_ == 0 + gpr_mu_init(&tracer_mu_); + time_created_ = grpc_millis_to_timespec(grpc_core::ExecCtx::Get()->Now(), + GPR_CLOCK_REALTIME); +} + +ChannelTrace::~ChannelTrace() { + if (max_event_memory_ == 0) + return; // tracing is disabled if max_event_memory_ == 0 + TraceEvent* it = head_trace_; + while (it != nullptr) { + TraceEvent* to_free = it; + it = it->next(); + Delete(to_free); + } + gpr_mu_destroy(&tracer_mu_); +} + +void ChannelTrace::AddTraceEventHelper(TraceEvent* new_trace_event) { + ++num_events_logged_; + // first event case + if (head_trace_ == nullptr) { + head_trace_ = tail_trace_ = new_trace_event; + } + // regular event add case + else { + tail_trace_->set_next(new_trace_event); + tail_trace_ = tail_trace_->next(); + } + event_list_memory_usage_ += new_trace_event->memory_usage(); + // maybe garbage collect the tail until we are under the memory limit. + while (event_list_memory_usage_ > max_event_memory_) { + TraceEvent* to_free = head_trace_; + event_list_memory_usage_ -= to_free->memory_usage(); + head_trace_ = head_trace_->next(); + Delete(to_free); + } +} + +void ChannelTrace::AddTraceEvent(Severity severity, grpc_slice data) { + if (max_event_memory_ == 0) { + grpc_slice_unref_internal(data); + return; // tracing is disabled if max_event_memory_ == 0 + } + AddTraceEventHelper(New(severity, data)); +} + +void ChannelTrace::AddTraceEventWithReference( + Severity severity, grpc_slice data, + RefCountedPtr referenced_entity) { + if (max_event_memory_ == 0) { + grpc_slice_unref_internal(data); + return; // tracing is disabled if max_event_memory_ == 0 + } + // create and fill up the new event + AddTraceEventHelper( + New(severity, data, std::move(referenced_entity))); +} + +namespace { + +const char* severity_string(ChannelTrace::Severity severity) { + switch (severity) { + case ChannelTrace::Severity::Info: + return "CT_INFO"; + case ChannelTrace::Severity::Warning: + return "CT_WARNING"; + case ChannelTrace::Severity::Error: + return "CT_ERROR"; + default: + GPR_UNREACHABLE_CODE(return "CT_UNKNOWN"); + } +} + +} // anonymous namespace + +void ChannelTrace::TraceEvent::RenderTraceEvent(grpc_json* json) const { + grpc_json* json_iterator = nullptr; + json_iterator = grpc_json_create_child(json_iterator, json, "description", + grpc_slice_to_c_string(data_), + GRPC_JSON_STRING, true); + json_iterator = grpc_json_create_child(json_iterator, json, "severity", + severity_string(severity_), + GRPC_JSON_STRING, false); + json_iterator = grpc_json_create_child(json_iterator, json, "timestamp", + gpr_format_timespec(timestamp_), + GRPC_JSON_STRING, true); + if (referenced_entity_ != nullptr) { + const bool is_channel = + (referenced_entity_->type() == BaseNode::EntityType::kTopLevelChannel || + referenced_entity_->type() == BaseNode::EntityType::kInternalChannel); + char* uuid_str; + gpr_asprintf(&uuid_str, "%" PRIdPTR, referenced_entity_->uuid()); + grpc_json* child_ref = grpc_json_create_child( + json_iterator, json, is_channel ? "channelRef" : "subchannelRef", + nullptr, GRPC_JSON_OBJECT, false); + json_iterator = grpc_json_create_child( + nullptr, child_ref, is_channel ? "channelId" : "subchannelId", uuid_str, + GRPC_JSON_STRING, true); + json_iterator = child_ref; + } +} + +grpc_json* ChannelTrace::RenderJson() const { + if (max_event_memory_ == 0) + return nullptr; // tracing is disabled if max_event_memory_ == 0 + grpc_json* json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json_iterator = nullptr; + if (num_events_logged_ > 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "numEventsLogged", num_events_logged_); + } + json_iterator = grpc_json_create_child( + json_iterator, json, "creationTimestamp", + gpr_format_timespec(time_created_), GRPC_JSON_STRING, true); + // only add in the event list if it is non-empty. + if (head_trace_ != nullptr) { + grpc_json* events = grpc_json_create_child(json_iterator, json, "events", + nullptr, GRPC_JSON_ARRAY, false); + json_iterator = nullptr; + TraceEvent* it = head_trace_; + while (it != nullptr) { + json_iterator = grpc_json_create_child(json_iterator, events, nullptr, + nullptr, GRPC_JSON_OBJECT, false); + it->RenderTraceEvent(json_iterator); + it = it->next(); + } + } + return json; +} + +} // namespace channelz +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/channel/channel_trace.h b/third_party/grpc/src/core/lib/channel/channel_trace.h new file mode 100644 index 00000000000000..8ff91ee8c81df8 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channel_trace.h @@ -0,0 +1,134 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H +#define GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H + +#include + +#include +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/json/json.h" + +namespace grpc_core { +namespace channelz { + +namespace testing { +size_t GetSizeofTraceEvent(void); +} + +class BaseNode; + +// Object used to hold live data for a channel. This data is exposed via the +// channelz service: +// https://github.com/grpc/proposal/blob/master/A14-channelz.md +class ChannelTrace { + public: + ChannelTrace(size_t max_event_memory); + ~ChannelTrace(); + + enum Severity { + Unset = 0, // never to be used + Info, // we start at 1 to avoid using proto default values + Warning, + Error + }; + + // Adds a new trace event to the tracing object + // + // NOTE: each ChannelTrace tracks the memory used by its list of trace + // events, so adding an event with a large amount of data could cause other + // trace event to be evicted. If a single trace is larger than the limit, it + // will cause all events to be evicted. The limit is set with the arg: + // GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE. + // + // TODO(ncteisen): as this call is used more and more throughout the gRPC + // stack, determine if it makes more sense to accept a char* instead of a + // slice. + void AddTraceEvent(Severity severity, grpc_slice data); + + // Adds a new trace event to the tracing object. This trace event refers to a + // an event that concerns a different channelz entity. For example, if this + // channel has created a new subchannel, then it would record that with + // a TraceEvent referencing the new subchannel. + // + // NOTE: see the note in the method above. + // + // TODO(ncteisen): see the todo in the method above. + void AddTraceEventWithReference(Severity severity, grpc_slice data, + RefCountedPtr referenced_entity); + + // Creates and returns the raw grpc_json object, so a parent channelz + // object may incorporate the json before rendering. + grpc_json* RenderJson() const; + + private: + friend size_t testing::GetSizeofTraceEvent(void); + + // Private class to encapsulate all the data and bookkeeping needed for a + // a trace event. + class TraceEvent { + public: + // Constructor for a TraceEvent that references a channel. + TraceEvent(Severity severity, grpc_slice data, + RefCountedPtr referenced_entity_); + + // Constructor for a TraceEvent that does not reverence a different + // channel. + TraceEvent(Severity severity, grpc_slice data); + + ~TraceEvent(); + + // Renders the data inside of this TraceEvent into a json object. This is + // used by the ChannelTrace, when it is rendering itself. + void RenderTraceEvent(grpc_json* json) const; + + // set and get for the next_ pointer. + TraceEvent* next() const { return next_; } + void set_next(TraceEvent* next) { next_ = next; } + + size_t memory_usage() const { return memory_usage_; } + + private: + Severity severity_; + grpc_slice data_; + gpr_timespec timestamp_; + TraceEvent* next_; + // the tracer object for the (sub)channel that this trace event refers to. + RefCountedPtr referenced_entity_; + size_t memory_usage_; + }; // TraceEvent + + // Internal helper to add and link in a trace event + void AddTraceEventHelper(TraceEvent* new_trace_event); + + gpr_mu tracer_mu_; + uint64_t num_events_logged_; + size_t event_list_memory_usage_; + size_t max_event_memory_; + TraceEvent* head_trace_; + TraceEvent* tail_trace_; + gpr_timespec time_created_; +}; + +} // namespace channelz +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_TRACE_H */ diff --git a/third_party/grpc/src/core/lib/channel/channelz.cc b/third_party/grpc/src/core/lib/channel/channelz.cc new file mode 100644 index 00000000000000..8a596ad46052b2 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channelz.cc @@ -0,0 +1,460 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channelz.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channelz_registry.h" +#include "src/core/lib/channel/status_util.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/server.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/uri/uri_parser.h" + +namespace grpc_core { +namespace channelz { + +BaseNode::BaseNode(EntityType type) : type_(type), uuid_(-1) { + // The registry will set uuid_ under its lock. + ChannelzRegistry::Register(this); +} + +BaseNode::~BaseNode() { ChannelzRegistry::Unregister(uuid_); } + +char* BaseNode::RenderJsonString() { + grpc_json* json = RenderJson(); + GPR_ASSERT(json != nullptr); + char* json_str = grpc_json_dump_to_string(json, 0); + grpc_json_destroy(json); + return json_str; +} + +CallCountingHelper::CallCountingHelper() { + num_cores_ = GPR_MAX(1, gpr_cpu_num_cores()); + per_cpu_counter_data_storage_ = static_cast( + gpr_zalloc(sizeof(AtomicCounterData) * num_cores_)); +} + +CallCountingHelper::~CallCountingHelper() { + gpr_free(per_cpu_counter_data_storage_); +} + +void CallCountingHelper::RecordCallStarted() { + gpr_atm_no_barrier_fetch_add( + &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] + .calls_started, + static_cast(1)); + gpr_atm_no_barrier_store( + &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] + .last_call_started_millis, + (gpr_atm)ExecCtx::Get()->Now()); +} + +void CallCountingHelper::RecordCallFailed() { + gpr_atm_no_barrier_fetch_add( + &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] + .calls_failed, + static_cast(1)); +} + +void CallCountingHelper::RecordCallSucceeded() { + gpr_atm_no_barrier_fetch_add( + &per_cpu_counter_data_storage_[grpc_core::ExecCtx::Get()->starting_cpu()] + .calls_succeeded, + static_cast(1)); +} + +void CallCountingHelper::CollectData(CounterData* out) { + for (size_t core = 0; core < num_cores_; ++core) { + out->calls_started += gpr_atm_no_barrier_load( + &per_cpu_counter_data_storage_[core].calls_started); + out->calls_succeeded += gpr_atm_no_barrier_load( + &per_cpu_counter_data_storage_[core].calls_succeeded); + out->calls_failed += gpr_atm_no_barrier_load( + &per_cpu_counter_data_storage_[core].calls_failed); + gpr_atm last_call = gpr_atm_no_barrier_load( + &per_cpu_counter_data_storage_[core].last_call_started_millis); + if (last_call > out->last_call_started_millis) { + out->last_call_started_millis = last_call; + } + } +} + +void CallCountingHelper::PopulateCallCounts(grpc_json* json) { + grpc_json* json_iterator = nullptr; + CounterData data; + CollectData(&data); + if (data.calls_started != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "callsStarted", data.calls_started); + } + if (data.calls_succeeded != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "callsSucceeded", data.calls_succeeded); + } + if (data.calls_failed) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "callsFailed", data.calls_failed); + } + if (data.calls_started != 0) { + gpr_timespec ts = grpc_millis_to_timespec(data.last_call_started_millis, + GPR_CLOCK_REALTIME); + json_iterator = + grpc_json_create_child(json_iterator, json, "lastCallStartedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } +} + +ChannelNode::ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes, + bool is_top_level_channel) + : BaseNode(is_top_level_channel ? EntityType::kTopLevelChannel + : EntityType::kInternalChannel), + channel_(channel), + target_(UniquePtr(grpc_channel_get_target(channel_))), + trace_(channel_tracer_max_nodes) {} + +ChannelNode::~ChannelNode() {} + +grpc_json* ChannelNode::RenderJson() { + // We need to track these three json objects to build our object + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + // create and fill the ref child + json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_add_number_string_child(json, json_iterator, + "channelId", uuid()); + // reset json iterators to top level object + json = top_level_json; + json_iterator = nullptr; + // create and fill the data child. + grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, + GRPC_JSON_OBJECT, false); + json = data; + json_iterator = nullptr; + // template method. Child classes may override this to add their specific + // functionality. + PopulateConnectivityState(json); + // populate the target. + GPR_ASSERT(target_.get() != nullptr); + grpc_json_create_child(nullptr, json, "target", target_.get(), + GRPC_JSON_STRING, false); + // fill in the channel trace if applicable + grpc_json* trace_json = trace_.RenderJson(); + if (trace_json != nullptr) { + trace_json->key = "trace"; // this object is named trace in channelz.proto + grpc_json_link_child(json, trace_json, nullptr); + } + // ask CallCountingHelper to populate trace and call count data. + call_counter_.PopulateCallCounts(json); + json = top_level_json; + // template method. Child classes may override this to add their specific + // functionality. + PopulateChildRefs(json); + return top_level_json; +} + +RefCountedPtr ChannelNode::MakeChannelNode( + grpc_channel* channel, size_t channel_tracer_max_nodes, + bool is_top_level_channel) { + return MakeRefCounted( + channel, channel_tracer_max_nodes, is_top_level_channel); +} + +ServerNode::ServerNode(grpc_server* server, size_t channel_tracer_max_nodes) + : BaseNode(EntityType::kServer), + server_(server), + trace_(channel_tracer_max_nodes) {} + +ServerNode::~ServerNode() {} + +char* ServerNode::RenderServerSockets(intptr_t start_socket_id, + intptr_t max_results) { + // if user does not set max_results, we choose 500. + size_t pagination_limit = max_results == 0 ? 500 : max_results; + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + ChildSocketsList socket_refs; + grpc_server_populate_server_sockets(server_, &socket_refs, start_socket_id); + // declared early so it can be used outside of the loop. + size_t i = 0; + if (!socket_refs.empty()) { + // create list of socket refs + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "socketRef", nullptr, GRPC_JSON_ARRAY, false); + for (i = 0; i < GPR_MIN(socket_refs.size(), pagination_limit); ++i) { + grpc_json* socket_ref_json = grpc_json_create_child( + nullptr, array_parent, nullptr, nullptr, GRPC_JSON_OBJECT, false); + json_iterator = grpc_json_add_number_string_child( + socket_ref_json, nullptr, "socketId", socket_refs[i]->uuid()); + grpc_json_create_child(json_iterator, socket_ref_json, "name", + socket_refs[i]->remote(), GRPC_JSON_STRING, false); + } + } + if (i == socket_refs.size()) { + json_iterator = grpc_json_create_child(nullptr, json, "end", nullptr, + GRPC_JSON_TRUE, false); + } + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +grpc_json* ServerNode::RenderJson() { + // We need to track these three json objects to build our object + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + // create and fill the ref child + json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_add_number_string_child(json, json_iterator, + "serverId", uuid()); + // reset json iterators to top level object + json = top_level_json; + json_iterator = nullptr; + // create and fill the data child. + grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, + GRPC_JSON_OBJECT, false); + json = data; + json_iterator = nullptr; + // fill in the channel trace if applicable + grpc_json* trace_json = trace_.RenderJson(); + if (trace_json != nullptr) { + trace_json->key = "trace"; // this object is named trace in channelz.proto + grpc_json_link_child(json, trace_json, nullptr); + } + // ask CallCountingHelper to populate trace and call count data. + call_counter_.PopulateCallCounts(json); + json = top_level_json; + ChildRefsList listen_sockets; + grpc_server_populate_listen_sockets(server_, &listen_sockets); + if (!listen_sockets.empty()) { + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "listenSocket", nullptr, GRPC_JSON_ARRAY, false); + for (size_t i = 0; i < listen_sockets.size(); ++i) { + json_iterator = + grpc_json_create_child(json_iterator, array_parent, nullptr, nullptr, + GRPC_JSON_OBJECT, false); + grpc_json_add_number_string_child(json_iterator, nullptr, "socketId", + listen_sockets[i]); + } + } + return top_level_json; +} + +static void PopulateSocketAddressJson(grpc_json* json, const char* name, + const char* addr_str) { + if (addr_str == nullptr) return; + grpc_json* json_iterator = nullptr; + json_iterator = grpc_json_create_child(json_iterator, json, name, nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + grpc_uri* uri = grpc_uri_parse(addr_str, true); + if ((uri != nullptr) && ((strcmp(uri->scheme, "ipv4") == 0) || + (strcmp(uri->scheme, "ipv6") == 0))) { + const char* host_port = uri->path; + if (*host_port == '/') ++host_port; + char* host = nullptr; + char* port = nullptr; + GPR_ASSERT(gpr_split_host_port(host_port, &host, &port)); + int port_num = -1; + if (port != nullptr) { + port_num = atoi(port); + } + char* b64_host = grpc_base64_encode(host, strlen(host), false, false); + json_iterator = grpc_json_create_child(json_iterator, json, "tcpip_address", + nullptr, GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_add_number_string_child(json, json_iterator, + "port", port_num); + json_iterator = grpc_json_create_child(json_iterator, json, "ip_address", + b64_host, GRPC_JSON_STRING, true); + gpr_free(host); + gpr_free(port); + + } else if (uri != nullptr && strcmp(uri->scheme, "unix") == 0) { + json_iterator = grpc_json_create_child(json_iterator, json, "uds_address", + nullptr, GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = + grpc_json_create_child(json_iterator, json, "filename", + gpr_strdup(uri->path), GRPC_JSON_STRING, true); + } else { + json_iterator = grpc_json_create_child(json_iterator, json, "other_address", + nullptr, GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_create_child(json_iterator, json, "name", + addr_str, GRPC_JSON_STRING, false); + } + grpc_uri_destroy(uri); +} + +SocketNode::SocketNode(UniquePtr local, UniquePtr remote) + : BaseNode(EntityType::kSocket), + local_(std::move(local)), + remote_(std::move(remote)) {} + +void SocketNode::RecordStreamStartedFromLocal() { + gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast(1)); + gpr_atm_no_barrier_store(&last_local_stream_created_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +void SocketNode::RecordStreamStartedFromRemote() { + gpr_atm_no_barrier_fetch_add(&streams_started_, static_cast(1)); + gpr_atm_no_barrier_store(&last_remote_stream_created_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +void SocketNode::RecordMessagesSent(uint32_t num_sent) { + gpr_atm_no_barrier_fetch_add(&messages_sent_, static_cast(num_sent)); + gpr_atm_no_barrier_store(&last_message_sent_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +void SocketNode::RecordMessageReceived() { + gpr_atm_no_barrier_fetch_add(&messages_received_, static_cast(1)); + gpr_atm_no_barrier_store(&last_message_received_millis_, + (gpr_atm)ExecCtx::Get()->Now()); +} + +grpc_json* SocketNode::RenderJson() { + // We need to track these three json objects to build our object + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + // create and fill the ref child + json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_add_number_string_child(json, json_iterator, + "socketId", uuid()); + json = top_level_json; + PopulateSocketAddressJson(json, "remote", remote_.get()); + PopulateSocketAddressJson(json, "local", local_.get()); + // reset json iterators to top level object + json = top_level_json; + json_iterator = nullptr; + // create and fill the data child. + grpc_json* data = grpc_json_create_child(json_iterator, json, "data", nullptr, + GRPC_JSON_OBJECT, false); + json = data; + json_iterator = nullptr; + gpr_timespec ts; + if (streams_started_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "streamsStarted", streams_started_); + if (last_local_stream_created_millis_ != 0) { + ts = grpc_millis_to_timespec(last_local_stream_created_millis_, + GPR_CLOCK_REALTIME); + json_iterator = grpc_json_create_child( + json_iterator, json, "lastLocalStreamCreatedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + if (last_remote_stream_created_millis_ != 0) { + ts = grpc_millis_to_timespec(last_remote_stream_created_millis_, + GPR_CLOCK_REALTIME); + json_iterator = grpc_json_create_child( + json_iterator, json, "lastRemoteStreamCreatedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + } + if (streams_succeeded_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "streamsSucceeded", streams_succeeded_); + } + if (streams_failed_) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "streamsFailed", streams_failed_); + } + if (messages_sent_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "messagesSent", messages_sent_); + ts = grpc_millis_to_timespec(last_message_sent_millis_, GPR_CLOCK_REALTIME); + json_iterator = + grpc_json_create_child(json_iterator, json, "lastMessageSentTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + if (messages_received_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "messagesReceived", messages_received_); + ts = grpc_millis_to_timespec(last_message_received_millis_, + GPR_CLOCK_REALTIME); + json_iterator = grpc_json_create_child( + json_iterator, json, "lastMessageReceivedTimestamp", + gpr_format_timespec(ts), GRPC_JSON_STRING, true); + } + if (keepalives_sent_ != 0) { + json_iterator = grpc_json_add_number_string_child( + json, json_iterator, "keepAlivesSent", keepalives_sent_); + } + return top_level_json; +} + +ListenSocketNode::ListenSocketNode(UniquePtr local_addr) + : BaseNode(EntityType::kSocket), local_addr_(std::move(local_addr)) {} + +grpc_json* ListenSocketNode::RenderJson() { + // We need to track these three json objects to build our object + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + // create and fill the ref child + json_iterator = grpc_json_create_child(json_iterator, json, "ref", nullptr, + GRPC_JSON_OBJECT, false); + json = json_iterator; + json_iterator = nullptr; + json_iterator = grpc_json_add_number_string_child(json, json_iterator, + "socketId", uuid()); + json = top_level_json; + PopulateSocketAddressJson(json, "local", local_addr_.get()); + + return top_level_json; +} + +} // namespace channelz +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/channel/channelz.h b/third_party/grpc/src/core/lib/channel/channelz.h new file mode 100644 index 00000000000000..e43792126f089a --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channelz.h @@ -0,0 +1,296 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CHANNELZ_H +#define GRPC_CORE_LIB_CHANNEL_CHANNELZ_H + +#include + +#include + +#include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/json/json.h" + +// Channel arg key for client channel factory. +#define GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC \ + "grpc.channelz_channel_node_creation_func" + +// Channel arg key to signal that the channel is an internal channel. +#define GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL \ + "grpc.channelz_channel_is_internal_channel" + +/** This is the default value for whether or not to enable channelz. If + * GRPC_ARG_ENABLE_CHANNELZ is set, it will override this default value. */ +#define GRPC_ENABLE_CHANNELZ_DEFAULT true + +/** This is the default value for the maximum amount of memory used by trace + * events per channel trace node. If + * GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE is set, it will override + * this default value. */ +#define GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT 1024 * 4 + +namespace grpc_core { + +namespace channelz { + +// TODO(ncteisen), this only contains the uuids of the children for now, +// since that is all that is strictly needed. In a future enhancement we will +// add human readable names as in the channelz.proto +typedef InlinedVector ChildRefsList; + +class SocketNode; +typedef InlinedVector ChildSocketsList; + +namespace testing { +class CallCountingHelperPeer; +class ChannelNodePeer; +} // namespace testing + +// base class for all channelz entities +class BaseNode : public RefCounted { + public: + // There are only four high level channelz entities. However, to support + // GetTopChannelsRequest, we split the Channel entity into two different + // types. All children of BaseNode must be one of these types. + enum class EntityType { + kTopLevelChannel, + kInternalChannel, + kSubchannel, + kServer, + kSocket, + }; + + explicit BaseNode(EntityType type); + virtual ~BaseNode(); + + // All children must implement this function. + virtual grpc_json* RenderJson() GRPC_ABSTRACT; + + // Renders the json and returns allocated string that must be freed by the + // caller. + char* RenderJsonString(); + + EntityType type() const { return type_; } + intptr_t uuid() const { return uuid_; } + + private: + // to allow the ChannelzRegistry to set uuid_ under its lock. + friend class ChannelzRegistry; + const EntityType type_; + intptr_t uuid_; +}; + +// This class is a helper class for channelz entities that deal with Channels, +// Subchannels, and Servers, since those have similar proto definitions. +// This class has the ability to: +// - track calls_{started,succeeded,failed} +// - track last_call_started_timestamp +// - perform rendering of the above items +class CallCountingHelper { + public: + CallCountingHelper(); + ~CallCountingHelper(); + + void RecordCallStarted(); + void RecordCallFailed(); + void RecordCallSucceeded(); + + // Common rendering of the call count data and last_call_started_timestamp. + void PopulateCallCounts(grpc_json* json); + + private: + // testing peer friend. + friend class testing::CallCountingHelperPeer; + + struct AtomicCounterData { + gpr_atm calls_started = 0; + gpr_atm calls_succeeded = 0; + gpr_atm calls_failed = 0; + gpr_atm last_call_started_millis = 0; + }; + + struct CounterData { + intptr_t calls_started = 0; + intptr_t calls_succeeded = 0; + intptr_t calls_failed = 0; + intptr_t last_call_started_millis = 0; + }; + + // collects the sharded data into one CounterData struct. + void CollectData(CounterData* out); + + AtomicCounterData* per_cpu_counter_data_storage_ = nullptr; + size_t num_cores_ = 0; +}; + +// Handles channelz bookkeeping for channels +class ChannelNode : public BaseNode { + public: + static RefCountedPtr MakeChannelNode( + grpc_channel* channel, size_t channel_tracer_max_nodes, + bool is_top_level_channel); + + ChannelNode(grpc_channel* channel, size_t channel_tracer_max_nodes, + bool is_top_level_channel); + ~ChannelNode() override; + + grpc_json* RenderJson() override; + + // template methods. RenderJSON uses these methods to render its JSON + // representation. These are virtual so that children classes may provide + // their specific mechanism for populating these parts of the channelz + // object. + // + // ChannelNode does not have a notion of connectivity state or child refs, + // so it leaves these implementations blank. + // + // This is utilizing the template method design pattern. + // + // TODO(ncteisen): remove these template methods in favor of manual traversal + // and mutation of the grpc_json object. + virtual void PopulateConnectivityState(grpc_json* json) {} + virtual void PopulateChildRefs(grpc_json* json) {} + + void MarkChannelDestroyed() { + GPR_ASSERT(channel_ != nullptr); + channel_ = nullptr; + } + + bool ChannelIsDestroyed() { return channel_ == nullptr; } + + // proxy methods to composed classes. + void AddTraceEvent(ChannelTrace::Severity severity, grpc_slice data) { + trace_.AddTraceEvent(severity, data); + } + void AddTraceEventWithReference(ChannelTrace::Severity severity, + grpc_slice data, + RefCountedPtr referenced_channel) { + trace_.AddTraceEventWithReference(severity, data, + std::move(referenced_channel)); + } + void RecordCallStarted() { call_counter_.RecordCallStarted(); } + void RecordCallFailed() { call_counter_.RecordCallFailed(); } + void RecordCallSucceeded() { call_counter_.RecordCallSucceeded(); } + + private: + // to allow the channel trace test to access trace_. + friend class testing::ChannelNodePeer; + grpc_channel* channel_ = nullptr; + UniquePtr target_; + CallCountingHelper call_counter_; + ChannelTrace trace_; +}; + +// Handles channelz bookkeeping for servers +class ServerNode : public BaseNode { + public: + ServerNode(grpc_server* server, size_t channel_tracer_max_nodes); + ~ServerNode() override; + + grpc_json* RenderJson() override; + + char* RenderServerSockets(intptr_t start_socket_id, + intptr_t pagination_limit); + + // proxy methods to composed classes. + void AddTraceEvent(ChannelTrace::Severity severity, grpc_slice data) { + trace_.AddTraceEvent(severity, data); + } + void AddTraceEventWithReference(ChannelTrace::Severity severity, + grpc_slice data, + RefCountedPtr referenced_channel) { + trace_.AddTraceEventWithReference(severity, data, + std::move(referenced_channel)); + } + void RecordCallStarted() { call_counter_.RecordCallStarted(); } + void RecordCallFailed() { call_counter_.RecordCallFailed(); } + void RecordCallSucceeded() { call_counter_.RecordCallSucceeded(); } + + private: + grpc_server* server_; + CallCountingHelper call_counter_; + ChannelTrace trace_; +}; + +// Handles channelz bookkeeping for sockets +class SocketNode : public BaseNode { + public: + SocketNode(UniquePtr local, UniquePtr remote); + ~SocketNode() override {} + + grpc_json* RenderJson() override; + + void RecordStreamStartedFromLocal(); + void RecordStreamStartedFromRemote(); + void RecordStreamSucceeded() { + gpr_atm_no_barrier_fetch_add(&streams_succeeded_, static_cast(1)); + } + void RecordStreamFailed() { + gpr_atm_no_barrier_fetch_add(&streams_failed_, static_cast(1)); + } + void RecordMessagesSent(uint32_t num_sent); + void RecordMessageReceived(); + void RecordKeepaliveSent() { + gpr_atm_no_barrier_fetch_add(&keepalives_sent_, static_cast(1)); + } + + const char* remote() { return remote_.get(); } + + private: + gpr_atm streams_started_ = 0; + gpr_atm streams_succeeded_ = 0; + gpr_atm streams_failed_ = 0; + gpr_atm messages_sent_ = 0; + gpr_atm messages_received_ = 0; + gpr_atm keepalives_sent_ = 0; + gpr_atm last_local_stream_created_millis_ = 0; + gpr_atm last_remote_stream_created_millis_ = 0; + gpr_atm last_message_sent_millis_ = 0; + gpr_atm last_message_received_millis_ = 0; + UniquePtr local_; + UniquePtr remote_; +}; + +// Handles channelz bookkeeping for listen sockets +class ListenSocketNode : public BaseNode { + public: + // ListenSocketNode takes ownership of host. + explicit ListenSocketNode(UniquePtr local_addr); + ~ListenSocketNode() override {} + + grpc_json* RenderJson() override; + + private: + UniquePtr local_addr_; +}; + +// Creation functions + +typedef RefCountedPtr (*ChannelNodeCreationFunc)(grpc_channel*, + size_t, bool); + +} // namespace channelz +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNELZ_H */ diff --git a/third_party/grpc/src/core/lib/channel/channelz_registry.cc b/third_party/grpc/src/core/lib/channel/channelz_registry.cc new file mode 100644 index 00000000000000..7cca247d64df18 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channelz_registry.cc @@ -0,0 +1,324 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/channel/channelz.h" +#include "src/core/lib/channel/channelz_registry.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/mutex_lock.h" + +#include +#include +#include + +#include + +namespace grpc_core { +namespace channelz { +namespace { + +// singleton instance of the registry. +ChannelzRegistry* g_channelz_registry = nullptr; + +const int kPaginationLimit = 100; + +} // anonymous namespace + +void ChannelzRegistry::Init() { g_channelz_registry = New(); } + +void ChannelzRegistry::Shutdown() { Delete(g_channelz_registry); } + +ChannelzRegistry* ChannelzRegistry::Default() { + GPR_DEBUG_ASSERT(g_channelz_registry != nullptr); + return g_channelz_registry; +} + +ChannelzRegistry::ChannelzRegistry() { gpr_mu_init(&mu_); } + +ChannelzRegistry::~ChannelzRegistry() { gpr_mu_destroy(&mu_); } + +void ChannelzRegistry::InternalRegister(BaseNode* node) { + MutexLock lock(&mu_); + entities_.push_back(node); + node->uuid_ = ++uuid_generator_; +} + +void ChannelzRegistry::MaybePerformCompactionLocked() { + constexpr double kEmptinessTheshold = 1 / 3; + double emptiness_ratio = + double(num_empty_slots_) / double(entities_.capacity()); + if (emptiness_ratio > kEmptinessTheshold) { + int front = 0; + for (size_t i = 0; i < entities_.size(); ++i) { + if (entities_[i] != nullptr) { + entities_[front++] = entities_[i]; + } + } + for (int i = 0; i < num_empty_slots_; ++i) { + entities_.pop_back(); + } + num_empty_slots_ = 0; + } +} + +int ChannelzRegistry::FindByUuidLocked(intptr_t target_uuid, + bool direct_hit_needed) { + int left = 0; + int right = int(entities_.size() - 1); + while (left <= right) { + int true_middle = left + (right - left) / 2; + int first_non_null = true_middle; + while (first_non_null < right && entities_[first_non_null] == nullptr) { + first_non_null++; + } + if (entities_[first_non_null] == nullptr) { + right = true_middle - 1; + continue; + } + intptr_t uuid = entities_[first_non_null]->uuid(); + if (uuid == target_uuid) { + return int(first_non_null); + } + if (uuid < target_uuid) { + left = first_non_null + 1; + } else { + right = true_middle - 1; + } + } + return direct_hit_needed ? -1 : left; +} + +void ChannelzRegistry::InternalUnregister(intptr_t uuid) { + GPR_ASSERT(uuid >= 1); + MutexLock lock(&mu_); + GPR_ASSERT(uuid <= uuid_generator_); + int idx = FindByUuidLocked(uuid, true); + GPR_ASSERT(idx >= 0); + entities_[idx] = nullptr; + num_empty_slots_++; + MaybePerformCompactionLocked(); +} + +BaseNode* ChannelzRegistry::InternalGet(intptr_t uuid) { + MutexLock lock(&mu_); + if (uuid < 1 || uuid > uuid_generator_) { + return nullptr; + } + int idx = FindByUuidLocked(uuid, true); + return idx < 0 ? nullptr : entities_[idx]; +} + +char* ChannelzRegistry::InternalGetTopChannels(intptr_t start_channel_id) { + MutexLock lock(&mu_); + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + InlinedVector top_level_channels; + bool reached_pagination_limit = false; + int start_idx = GPR_MAX(FindByUuidLocked(start_channel_id, false), 0); + for (size_t i = start_idx; i < entities_.size(); ++i) { + if (entities_[i] != nullptr && + entities_[i]->type() == + grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel && + entities_[i]->uuid() >= start_channel_id) { + // check if we are over pagination limit to determine if we need to set + // the "end" element. If we don't go through this block, we know that + // when the loop terminates, we have <= to kPaginationLimit. + if (top_level_channels.size() == kPaginationLimit) { + reached_pagination_limit = true; + break; + } + top_level_channels.push_back(entities_[i]); + } + } + if (!top_level_channels.empty()) { + // create list of channels + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "channel", nullptr, GRPC_JSON_ARRAY, false); + for (size_t i = 0; i < top_level_channels.size(); ++i) { + grpc_json* channel_json = top_level_channels[i]->RenderJson(); + json_iterator = + grpc_json_link_child(array_parent, channel_json, json_iterator); + } + } + if (!reached_pagination_limit) { + grpc_json_create_child(nullptr, json, "end", nullptr, GRPC_JSON_TRUE, + false); + } + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +char* ChannelzRegistry::InternalGetServers(intptr_t start_server_id) { + MutexLock lock(&mu_); + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* json_iterator = nullptr; + InlinedVector servers; + bool reached_pagination_limit = false; + int start_idx = GPR_MAX(FindByUuidLocked(start_server_id, false), 0); + for (size_t i = start_idx; i < entities_.size(); ++i) { + if (entities_[i] != nullptr && + entities_[i]->type() == + grpc_core::channelz::BaseNode::EntityType::kServer && + entities_[i]->uuid() >= start_server_id) { + // check if we are over pagination limit to determine if we need to set + // the "end" element. If we don't go through this block, we know that + // when the loop terminates, we have <= to kPaginationLimit. + if (servers.size() == kPaginationLimit) { + reached_pagination_limit = true; + break; + } + servers.push_back(entities_[i]); + } + } + if (!servers.empty()) { + // create list of servers + grpc_json* array_parent = grpc_json_create_child( + nullptr, json, "server", nullptr, GRPC_JSON_ARRAY, false); + for (size_t i = 0; i < servers.size(); ++i) { + grpc_json* server_json = servers[i]->RenderJson(); + json_iterator = + grpc_json_link_child(array_parent, server_json, json_iterator); + } + } + if (!reached_pagination_limit) { + grpc_json_create_child(nullptr, json, "end", nullptr, GRPC_JSON_TRUE, + false); + } + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +void ChannelzRegistry::InternalLogAllEntities() { + MutexLock lock(&mu_); + for (size_t i = 0; i < entities_.size(); ++i) { + if (entities_[i] != nullptr) { + char* json = entities_[i]->RenderJsonString(); + gpr_log(GPR_INFO, "%s", json); + gpr_free(json); + } + } +} + +} // namespace channelz +} // namespace grpc_core + +char* grpc_channelz_get_top_channels(intptr_t start_channel_id) { + return grpc_core::channelz::ChannelzRegistry::GetTopChannels( + start_channel_id); +} + +char* grpc_channelz_get_servers(intptr_t start_server_id) { + return grpc_core::channelz::ChannelzRegistry::GetServers(start_server_id); +} + +char* grpc_channelz_get_server(intptr_t server_id) { + grpc_core::channelz::BaseNode* server_node = + grpc_core::channelz::ChannelzRegistry::Get(server_id); + if (server_node == nullptr || + server_node->type() != + grpc_core::channelz::BaseNode::EntityType::kServer) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* channel_json = server_node->RenderJson(); + channel_json->key = "server"; + grpc_json_link_child(json, channel_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +char* grpc_channelz_get_server_sockets(intptr_t server_id, + intptr_t start_socket_id, + intptr_t max_results) { + grpc_core::channelz::BaseNode* base_node = + grpc_core::channelz::ChannelzRegistry::Get(server_id); + if (base_node == nullptr || + base_node->type() != grpc_core::channelz::BaseNode::EntityType::kServer) { + return nullptr; + } + // This cast is ok since we have just checked to make sure base_node is + // actually a server node + grpc_core::channelz::ServerNode* server_node = + static_cast(base_node); + return server_node->RenderServerSockets(start_socket_id, max_results); +} + +char* grpc_channelz_get_channel(intptr_t channel_id) { + grpc_core::channelz::BaseNode* channel_node = + grpc_core::channelz::ChannelzRegistry::Get(channel_id); + if (channel_node == nullptr || + (channel_node->type() != + grpc_core::channelz::BaseNode::EntityType::kTopLevelChannel && + channel_node->type() != + grpc_core::channelz::BaseNode::EntityType::kInternalChannel)) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* channel_json = channel_node->RenderJson(); + channel_json->key = "channel"; + grpc_json_link_child(json, channel_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +char* grpc_channelz_get_subchannel(intptr_t subchannel_id) { + grpc_core::channelz::BaseNode* subchannel_node = + grpc_core::channelz::ChannelzRegistry::Get(subchannel_id); + if (subchannel_node == nullptr || + subchannel_node->type() != + grpc_core::channelz::BaseNode::EntityType::kSubchannel) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* subchannel_json = subchannel_node->RenderJson(); + subchannel_json->key = "subchannel"; + grpc_json_link_child(json, subchannel_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} + +char* grpc_channelz_get_socket(intptr_t socket_id) { + grpc_core::channelz::BaseNode* socket_node = + grpc_core::channelz::ChannelzRegistry::Get(socket_id); + if (socket_node == nullptr || + socket_node->type() != + grpc_core::channelz::BaseNode::EntityType::kSocket) { + return nullptr; + } + grpc_json* top_level_json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json* json = top_level_json; + grpc_json* socket_json = socket_node->RenderJson(); + socket_json->key = "socket"; + grpc_json_link_child(json, socket_json, nullptr); + char* json_str = grpc_json_dump_to_string(top_level_json, 0); + grpc_json_destroy(top_level_json); + return json_str; +} diff --git a/third_party/grpc/src/core/lib/channel/channelz_registry.h b/third_party/grpc/src/core/lib/channel/channelz_registry.h new file mode 100644 index 00000000000000..73b330785d234e --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/channelz_registry.h @@ -0,0 +1,115 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CHANNELZ_REGISTRY_H +#define GRPC_CORE_LIB_CHANNEL_CHANNELZ_REGISTRY_H + +#include + +#include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/channel/channelz.h" +#include "src/core/lib/gprpp/inlined_vector.h" + +#include + +namespace grpc_core { +namespace channelz { + +namespace testing { +class ChannelzRegistryPeer; +} + +// singleton registry object to track all objects that are needed to support +// channelz bookkeeping. All objects share globally distributed uuids. +class ChannelzRegistry { + public: + // To be called in grpc_init() + static void Init(); + + // To be called in grpc_shutdown(); + static void Shutdown(); + + static void Register(BaseNode* node) { + return Default()->InternalRegister(node); + } + static void Unregister(intptr_t uuid) { Default()->InternalUnregister(uuid); } + static BaseNode* Get(intptr_t uuid) { return Default()->InternalGet(uuid); } + + // Returns the allocated JSON string that represents the proto + // GetTopChannelsResponse as per channelz.proto. + static char* GetTopChannels(intptr_t start_channel_id) { + return Default()->InternalGetTopChannels(start_channel_id); + } + + // Returns the allocated JSON string that represents the proto + // GetServersResponse as per channelz.proto. + static char* GetServers(intptr_t start_server_id) { + return Default()->InternalGetServers(start_server_id); + } + + // Test only helper function to dump the JSON representation to std out. + // This can aid in debugging channelz code. + static void LogAllEntities() { Default()->InternalLogAllEntities(); } + + private: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE + friend class testing::ChannelzRegistryPeer; + + ChannelzRegistry(); + ~ChannelzRegistry(); + + // Returned the singleton instance of ChannelzRegistry; + static ChannelzRegistry* Default(); + + // globally registers an Entry. Returns its unique uuid + void InternalRegister(BaseNode* node); + + // globally unregisters the object that is associated to uuid. Also does + // sanity check that an object doesn't try to unregister the wrong type. + void InternalUnregister(intptr_t uuid); + + // if object with uuid has previously been registered as the correct type, + // returns the void* associated with that uuid. Else returns nullptr. + BaseNode* InternalGet(intptr_t uuid); + + char* InternalGetTopChannels(intptr_t start_channel_id); + char* InternalGetServers(intptr_t start_server_id); + + // If entities_ has over a certain threshold of empty slots, it will + // compact the vector and move all used slots to the front. + void MaybePerformCompactionLocked(); + + // Performs binary search on entities_ to find the index with that uuid. + // If direct_hit_needed, then will return -1 in case of absence. + // Else, will return idx of the first uuid higher than the target. + int FindByUuidLocked(intptr_t uuid, bool direct_hit_needed); + + void InternalLogAllEntities(); + + // protects members + gpr_mu mu_; + InlinedVector entities_; + intptr_t uuid_generator_ = 0; + int num_empty_slots_ = 0; +}; + +} // namespace channelz +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNELZ_REGISTRY_H */ diff --git a/third_party/grpc/src/core/lib/channel/connected_channel.cc b/third_party/grpc/src/core/lib/channel/connected_channel.cc new file mode 100644 index 00000000000000..e2ea334dedf25b --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/connected_channel.cc @@ -0,0 +1,243 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/connected_channel.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/transport/transport.h" + +#define MAX_BUFFER_LENGTH 8192 + +typedef struct connected_channel_channel_data { + grpc_transport* transport; +} channel_data; + +typedef struct { + grpc_closure closure; + grpc_closure* original_closure; + grpc_call_combiner* call_combiner; + const char* reason; +} callback_state; + +typedef struct connected_channel_call_data { + grpc_call_combiner* call_combiner; + // Closures used for returning results on the call combiner. + callback_state on_complete[6]; // Max number of pending batches. + callback_state recv_initial_metadata_ready; + callback_state recv_message_ready; + callback_state recv_trailing_metadata_ready; +} call_data; + +static void run_in_call_combiner(void* arg, grpc_error* error) { + callback_state* state = static_cast(arg); + GRPC_CALL_COMBINER_START(state->call_combiner, state->original_closure, + GRPC_ERROR_REF(error), state->reason); +} + +static void run_cancel_in_call_combiner(void* arg, grpc_error* error) { + run_in_call_combiner(arg, error); + gpr_free(arg); +} + +static void intercept_callback(call_data* calld, callback_state* state, + bool free_when_done, const char* reason, + grpc_closure** original_closure) { + state->original_closure = *original_closure; + state->call_combiner = calld->call_combiner; + state->reason = reason; + *original_closure = GRPC_CLOSURE_INIT( + &state->closure, + free_when_done ? run_cancel_in_call_combiner : run_in_call_combiner, + state, grpc_schedule_on_exec_ctx); +} + +static callback_state* get_state_for_batch( + call_data* calld, grpc_transport_stream_op_batch* batch) { + if (batch->send_initial_metadata) return &calld->on_complete[0]; + if (batch->send_message) return &calld->on_complete[1]; + if (batch->send_trailing_metadata) return &calld->on_complete[2]; + if (batch->recv_initial_metadata) return &calld->on_complete[3]; + if (batch->recv_message) return &calld->on_complete[4]; + if (batch->recv_trailing_metadata) return &calld->on_complete[5]; + GPR_UNREACHABLE_CODE(return nullptr); +} + +/* 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_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + if (batch->recv_initial_metadata) { + callback_state* state = &calld->recv_initial_metadata_ready; + intercept_callback( + calld, state, false, "recv_initial_metadata_ready", + &batch->payload->recv_initial_metadata.recv_initial_metadata_ready); + } + if (batch->recv_message) { + callback_state* state = &calld->recv_message_ready; + intercept_callback(calld, state, false, "recv_message_ready", + &batch->payload->recv_message.recv_message_ready); + } + if (batch->recv_trailing_metadata) { + callback_state* state = &calld->recv_trailing_metadata_ready; + intercept_callback( + calld, state, false, "recv_trailing_metadata_ready", + &batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready); + } + if (batch->cancel_stream) { + // There can be more than one cancellation batch in flight at any + // given time, so we can't just pick out a fixed index into + // calld->on_complete like we can for the other ops. However, + // cancellation isn't in the fast path, so we just allocate a new + // closure for each one. + callback_state* state = + static_cast(gpr_malloc(sizeof(*state))); + intercept_callback(calld, state, true, "on_complete (cancel_stream)", + &batch->on_complete); + } else if (batch->on_complete != nullptr) { + callback_state* state = get_state_for_batch(calld, batch); + intercept_callback(calld, state, false, "on_complete", &batch->on_complete); + } + grpc_transport_perform_stream_op( + chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), batch); + GRPC_CALL_COMBINER_STOP(calld->call_combiner, "passed batch to transport"); +} + +static void con_start_transport_op(grpc_channel_element* elem, + grpc_transport_op* op) { + channel_data* chand = static_cast(elem->channel_data); + grpc_transport_perform_op(chand->transport, op); +} + +/* Constructor for call_data */ +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + calld->call_combiner = args->call_combiner; + int r = grpc_transport_init_stream( + chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), + &args->call_stack->refcount, args->server_transport_data, args->arena); + return r == 0 ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "transport stream initialization failed"); +} + +static void set_pollset_or_pollset_set(grpc_call_element* elem, + grpc_polling_entity* pollent) { + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + grpc_transport_set_pops(chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollent); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_schedule_closure) { + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + grpc_transport_destroy_stream(chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), + then_schedule_closure); +} + +/* Constructor for channel_data */ +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* cd = static_cast(elem->channel_data); + GPR_ASSERT(args->is_last); + cd->transport = nullptr; + return GRPC_ERROR_NONE; +} + +/* Destructor for channel_data */ +static void destroy_channel_elem(grpc_channel_element* elem) { + channel_data* cd = static_cast(elem->channel_data); + if (cd->transport) { + grpc_transport_destroy(cd->transport); + } +} + +/* No-op. */ +static void con_get_channel_info(grpc_channel_element* elem, + const grpc_channel_info* channel_info) {} + +const grpc_channel_filter grpc_connected_filter = { + con_start_transport_stream_op_batch, + con_start_transport_op, + sizeof(call_data), + init_call_elem, + set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + con_get_channel_info, + "connected", +}; + +static void bind_transport(grpc_channel_stack* channel_stack, + grpc_channel_element* elem, void* t) { + channel_data* cd = static_cast(elem->channel_data); + GPR_ASSERT(elem->filter == &grpc_connected_filter); + GPR_ASSERT(cd->transport == nullptr); + cd->transport = static_cast(t); + + /* 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(static_cast(t)); +} + +bool grpc_add_connected_filter(grpc_channel_stack_builder* builder, + void* arg_must_be_null) { + GPR_ASSERT(arg_must_be_null == nullptr); + grpc_transport* t = grpc_channel_stack_builder_get_transport(builder); + GPR_ASSERT(t != nullptr); + return grpc_channel_stack_builder_append_filter( + builder, &grpc_connected_filter, bind_transport, t); +} + +grpc_stream* grpc_connected_channel_get_stream(grpc_call_element* elem) { + call_data* calld = static_cast(elem->call_data); + return TRANSPORT_STREAM_FROM_CALL_DATA(calld); +} diff --git a/third_party/grpc/src/core/lib/channel/connected_channel.h b/third_party/grpc/src/core/lib/channel/connected_channel.h new file mode 100644 index 00000000000000..faa1c73a21f13d --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/connected_channel.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H +#define GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H + +#include + +#include "src/core/lib/channel/channel_stack_builder.h" + +extern const grpc_channel_filter grpc_connected_filter; + +bool grpc_add_connected_filter(grpc_channel_stack_builder* builder, + void* arg_must_be_null); + +/* Debug helper to dig the transport stream out of a call element */ +grpc_stream* grpc_connected_channel_get_stream(grpc_call_element* elem); + +#endif /* GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H */ diff --git a/third_party/grpc/src/core/lib/channel/context.h b/third_party/grpc/src/core/lib/channel/context.h new file mode 100644 index 00000000000000..763e4ffc9fef4a --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/context.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_CONTEXT_H +#define GRPC_CORE_LIB_CHANNEL_CONTEXT_H + +/// Call object context pointers. + +/// Call context is represented as an array of \a grpc_call_context_elements. +/// This enum represents the indexes into the array, where each index +/// contains a different type of value. +typedef enum { + /// Value is either a \a grpc_client_security_context or a + /// \a grpc_server_security_context. + GRPC_CONTEXT_SECURITY = 0, + + /// Value is a \a census_context. + GRPC_CONTEXT_TRACING, + + /// Reserved for traffic_class_context. + GRPC_CONTEXT_TRAFFIC, + + /// Value is a \a grpc_grpclb_client_stats. + GRPC_GRPCLB_CLIENT_STATS, + + GRPC_CONTEXT_COUNT +} grpc_context_index; + +struct grpc_call_context_element { + void* value = nullptr; + void (*destroy)(void*) = nullptr; +}; + +#endif /* GRPC_CORE_LIB_CHANNEL_CONTEXT_H */ diff --git a/third_party/grpc/src/core/lib/channel/handshaker.cc b/third_party/grpc/src/core/lib/channel/handshaker.cc new file mode 100644 index 00000000000000..e516b56b7431e6 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/handshaker.cc @@ -0,0 +1,331 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_internal.h" + +grpc_core::TraceFlag grpc_handshaker_trace(false, "handshaker"); + +// +// grpc_handshaker +// + +void grpc_handshaker_init(const grpc_handshaker_vtable* vtable, + grpc_handshaker* handshaker) { + handshaker->vtable = vtable; +} + +void grpc_handshaker_destroy(grpc_handshaker* handshaker) { + handshaker->vtable->destroy(handshaker); +} + +void grpc_handshaker_shutdown(grpc_handshaker* handshaker, grpc_error* why) { + handshaker->vtable->shutdown(handshaker, why); +} + +void grpc_handshaker_do_handshake(grpc_handshaker* handshaker, + grpc_tcp_server_acceptor* acceptor, + grpc_closure* on_handshake_done, + grpc_handshaker_args* args) { + handshaker->vtable->do_handshake(handshaker, acceptor, on_handshake_done, + args); +} + +const char* grpc_handshaker_name(grpc_handshaker* handshaker) { + return handshaker->vtable->name; +} + +// +// grpc_handshake_manager +// + +struct grpc_handshake_manager { + gpr_mu mu; + gpr_refcount refs; + bool shutdown; + // An array of handshakers added via grpc_handshake_manager_add(). + size_t count; + grpc_handshaker** handshakers; + // The index of the handshaker to invoke next and closure to invoke it. + size_t index; + grpc_closure call_next_handshaker; + // The acceptor to call the handshakers with. + grpc_tcp_server_acceptor* acceptor; + // Deadline timer across all handshakers. + grpc_timer deadline_timer; + grpc_closure on_timeout; + // The final callback and user_data to invoke after the last handshaker. + grpc_closure on_handshake_done; + void* user_data; + // Handshaker args. + grpc_handshaker_args args; + // Links to the previous and next managers in a list of all pending handshakes + // Used at server side only. + grpc_handshake_manager* prev; + grpc_handshake_manager* next; +}; + +grpc_handshake_manager* grpc_handshake_manager_create() { + grpc_handshake_manager* mgr = static_cast( + gpr_zalloc(sizeof(grpc_handshake_manager))); + gpr_mu_init(&mgr->mu); + gpr_ref_init(&mgr->refs, 1); + return mgr; +} + +void grpc_handshake_manager_pending_list_add(grpc_handshake_manager** head, + grpc_handshake_manager* mgr) { + GPR_ASSERT(mgr->prev == nullptr); + GPR_ASSERT(mgr->next == nullptr); + mgr->next = *head; + if (*head) { + (*head)->prev = mgr; + } + *head = mgr; +} + +void grpc_handshake_manager_pending_list_remove(grpc_handshake_manager** head, + grpc_handshake_manager* mgr) { + if (mgr->next != nullptr) { + mgr->next->prev = mgr->prev; + } + if (mgr->prev != nullptr) { + mgr->prev->next = mgr->next; + } else { + GPR_ASSERT(*head == mgr); + *head = mgr->next; + } +} + +void grpc_handshake_manager_pending_list_shutdown_all( + grpc_handshake_manager* head, grpc_error* why) { + while (head != nullptr) { + grpc_handshake_manager_shutdown(head, GRPC_ERROR_REF(why)); + head = head->next; + } + GRPC_ERROR_UNREF(why); +} + +static bool is_power_of_2(size_t n) { return (n & (n - 1)) == 0; } + +void grpc_handshake_manager_add(grpc_handshake_manager* mgr, + grpc_handshaker* handshaker) { + if (grpc_handshaker_trace.enabled()) { + gpr_log( + GPR_INFO, + "handshake_manager %p: adding handshaker %s [%p] at index %" PRIuPTR, + mgr, grpc_handshaker_name(handshaker), handshaker, mgr->count); + } + gpr_mu_lock(&mgr->mu); + // To avoid allocating memory for each handshaker we add, we double + // the number of elements every time we need more. + size_t realloc_count = 0; + if (mgr->count == 0) { + realloc_count = 2; + } else if (mgr->count >= 2 && is_power_of_2(mgr->count)) { + realloc_count = mgr->count * 2; + } + if (realloc_count > 0) { + mgr->handshakers = static_cast(gpr_realloc( + mgr->handshakers, realloc_count * sizeof(grpc_handshaker*))); + } + mgr->handshakers[mgr->count++] = handshaker; + gpr_mu_unlock(&mgr->mu); +} + +static void grpc_handshake_manager_unref(grpc_handshake_manager* mgr) { + if (gpr_unref(&mgr->refs)) { + for (size_t i = 0; i < mgr->count; ++i) { + grpc_handshaker_destroy(mgr->handshakers[i]); + } + gpr_free(mgr->handshakers); + gpr_mu_destroy(&mgr->mu); + gpr_free(mgr); + } +} + +void grpc_handshake_manager_destroy(grpc_handshake_manager* mgr) { + grpc_handshake_manager_unref(mgr); +} + +void grpc_handshake_manager_shutdown(grpc_handshake_manager* mgr, + grpc_error* why) { + gpr_mu_lock(&mgr->mu); + // Shutdown the handshaker that's currently in progress, if any. + if (!mgr->shutdown && mgr->index > 0) { + mgr->shutdown = true; + grpc_handshaker_shutdown(mgr->handshakers[mgr->index - 1], + GRPC_ERROR_REF(why)); + } + gpr_mu_unlock(&mgr->mu); + GRPC_ERROR_UNREF(why); +} + +static char* handshaker_args_string(grpc_handshaker_args* args) { + char* args_str = grpc_channel_args_string(args->args); + size_t num_args = args->args != nullptr ? args->args->num_args : 0; + size_t read_buffer_length = + args->read_buffer != nullptr ? args->read_buffer->length : 0; + char* str; + gpr_asprintf(&str, + "{endpoint=%p, args=%p {size=%" PRIuPTR + ": %s}, read_buffer=%p (length=%" PRIuPTR "), exit_early=%d}", + args->endpoint, args->args, num_args, args_str, + args->read_buffer, read_buffer_length, args->exit_early); + gpr_free(args_str); + return str; +} + +// Helper function to call either the next handshaker or the +// on_handshake_done callback. +// Returns true if we've scheduled the on_handshake_done callback. +static bool call_next_handshaker_locked(grpc_handshake_manager* mgr, + grpc_error* error) { + if (grpc_handshaker_trace.enabled()) { + char* args_str = handshaker_args_string(&mgr->args); + gpr_log(GPR_INFO, + "handshake_manager %p: error=%s shutdown=%d index=%" PRIuPTR + ", args=%s", + mgr, grpc_error_string(error), mgr->shutdown, mgr->index, args_str); + gpr_free(args_str); + } + GPR_ASSERT(mgr->index <= mgr->count); + // If we got an error or we've been shut down or we're exiting early or + // we've finished the last handshaker, invoke the on_handshake_done + // callback. Otherwise, call the next handshaker. + if (error != GRPC_ERROR_NONE || mgr->shutdown || mgr->args.exit_early || + mgr->index == mgr->count) { + if (error == GRPC_ERROR_NONE && mgr->shutdown) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("handshaker shutdown"); + // It is possible that the endpoint has already been destroyed by + // a shutdown call while this callback was sitting on the ExecCtx + // with no error. + if (mgr->args.endpoint != nullptr) { + // TODO(roth): It is currently necessary to shutdown endpoints + // before destroying then, even when we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(mgr->args.endpoint, GRPC_ERROR_REF(error)); + grpc_endpoint_destroy(mgr->args.endpoint); + mgr->args.endpoint = nullptr; + grpc_channel_args_destroy(mgr->args.args); + mgr->args.args = nullptr; + grpc_slice_buffer_destroy_internal(mgr->args.read_buffer); + gpr_free(mgr->args.read_buffer); + mgr->args.read_buffer = nullptr; + } + } + if (grpc_handshaker_trace.enabled()) { + gpr_log(GPR_INFO, + "handshake_manager %p: handshaking complete -- scheduling " + "on_handshake_done with error=%s", + mgr, grpc_error_string(error)); + } + // Cancel deadline timer, since we're invoking the on_handshake_done + // callback now. + grpc_timer_cancel(&mgr->deadline_timer); + GRPC_CLOSURE_SCHED(&mgr->on_handshake_done, error); + mgr->shutdown = true; + } else { + if (grpc_handshaker_trace.enabled()) { + gpr_log( + GPR_INFO, + "handshake_manager %p: calling handshaker %s [%p] at index %" PRIuPTR, + mgr, grpc_handshaker_name(mgr->handshakers[mgr->index]), + mgr->handshakers[mgr->index], mgr->index); + } + grpc_handshaker_do_handshake(mgr->handshakers[mgr->index], mgr->acceptor, + &mgr->call_next_handshaker, &mgr->args); + } + ++mgr->index; + return mgr->shutdown; +} + +// A function used as the handshaker-done callback when chaining +// handshakers together. +static void call_next_handshaker(void* arg, grpc_error* error) { + grpc_handshake_manager* mgr = static_cast(arg); + gpr_mu_lock(&mgr->mu); + bool done = call_next_handshaker_locked(mgr, GRPC_ERROR_REF(error)); + gpr_mu_unlock(&mgr->mu); + // If we're invoked the final callback, we won't be coming back + // to this function, so we can release our reference to the + // handshake manager. + if (done) { + grpc_handshake_manager_unref(mgr); + } +} + +// Callback invoked when deadline is exceeded. +static void on_timeout(void* arg, grpc_error* error) { + grpc_handshake_manager* mgr = static_cast(arg); + if (error == GRPC_ERROR_NONE) { // Timer fired, rather than being cancelled. + grpc_handshake_manager_shutdown( + mgr, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake timed out")); + } + grpc_handshake_manager_unref(mgr); +} + +void grpc_handshake_manager_do_handshake(grpc_handshake_manager* mgr, + grpc_endpoint* endpoint, + const grpc_channel_args* channel_args, + grpc_millis deadline, + grpc_tcp_server_acceptor* acceptor, + grpc_iomgr_cb_func on_handshake_done, + void* user_data) { + gpr_mu_lock(&mgr->mu); + GPR_ASSERT(mgr->index == 0); + GPR_ASSERT(!mgr->shutdown); + // Construct handshaker args. These will be passed through all + // handshakers and eventually be freed by the on_handshake_done callback. + mgr->args.endpoint = endpoint; + mgr->args.args = grpc_channel_args_copy(channel_args); + mgr->args.user_data = user_data; + mgr->args.read_buffer = static_cast( + gpr_malloc(sizeof(*mgr->args.read_buffer))); + grpc_slice_buffer_init(mgr->args.read_buffer); + // Initialize state needed for calling handshakers. + mgr->acceptor = acceptor; + GRPC_CLOSURE_INIT(&mgr->call_next_handshaker, call_next_handshaker, mgr, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&mgr->on_handshake_done, on_handshake_done, &mgr->args, + grpc_schedule_on_exec_ctx); + // Start deadline timer, which owns a ref. + gpr_ref(&mgr->refs); + GRPC_CLOSURE_INIT(&mgr->on_timeout, on_timeout, mgr, + grpc_schedule_on_exec_ctx); + grpc_timer_init(&mgr->deadline_timer, deadline, &mgr->on_timeout); + // Start first handshaker, which also owns a ref. + gpr_ref(&mgr->refs); + bool done = call_next_handshaker_locked(mgr, GRPC_ERROR_NONE); + gpr_mu_unlock(&mgr->mu); + if (done) { + grpc_handshake_manager_unref(mgr); + } +} diff --git a/third_party/grpc/src/core/lib/channel/handshaker.h b/third_party/grpc/src/core/lib/channel/handshaker.h new file mode 100644 index 00000000000000..a65990fceb4b39 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/handshaker.h @@ -0,0 +1,169 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H +#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H + +#include + +#include + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/tcp_server.h" + +/// Handshakers are used to perform initial handshakes on a connection +/// before the client sends the initial request. Some examples of what +/// a handshaker can be used for includes support for HTTP CONNECT on +/// the client side and various types of security initialization. +/// +/// In general, handshakers should be used via a handshake manager. + +/// +/// grpc_handshaker +/// + +typedef struct grpc_handshaker grpc_handshaker; + +/// Arguments passed through handshakers and to the on_handshake_done callback. +/// +/// For handshakers, all members are input/output parameters; for +/// example, a handshaker may read from or write to \a endpoint and +/// then later replace it with a wrapped endpoint. Similarly, a +/// handshaker may modify \a args. +/// +/// A handshaker takes ownership of the members while a handshake is in +/// progress. Upon failure or shutdown of an in-progress handshaker, +/// the handshaker is responsible for destroying the members and setting +/// them to NULL before invoking the on_handshake_done callback. +/// +/// For the on_handshake_done callback, all members are input arguments, +/// which the callback takes ownership of. +typedef struct { + grpc_endpoint* endpoint; + grpc_channel_args* args; + grpc_slice_buffer* read_buffer; + // A handshaker may set this to true before invoking on_handshake_done + // to indicate that subsequent handshakers should be skipped. + bool exit_early; + // User data passed through the handshake manager. Not used by + // individual handshakers. + void* user_data; +} grpc_handshaker_args; + +typedef struct { + /// Destroys the handshaker. + void (*destroy)(grpc_handshaker* handshaker); + + /// Shuts down the handshaker (e.g., to clean up when the operation is + /// aborted in the middle). + void (*shutdown)(grpc_handshaker* handshaker, grpc_error* why); + + /// Performs handshaking, modifying \a args as needed (e.g., to + /// replace \a endpoint with a wrapped endpoint). + /// When finished, invokes \a on_handshake_done. + /// \a acceptor will be NULL for client-side handshakers. + void (*do_handshake)(grpc_handshaker* handshaker, + grpc_tcp_server_acceptor* acceptor, + grpc_closure* on_handshake_done, + grpc_handshaker_args* args); + + /// The name of the handshaker, for debugging purposes. + const char* name; +} grpc_handshaker_vtable; + +/// Base struct. To subclass, make this the first member of the +/// implementation struct. +struct grpc_handshaker { + const grpc_handshaker_vtable* vtable; +}; + +/// Called by concrete implementations to initialize the base struct. +void grpc_handshaker_init(const grpc_handshaker_vtable* vtable, + grpc_handshaker* handshaker); + +void grpc_handshaker_destroy(grpc_handshaker* handshaker); +void grpc_handshaker_shutdown(grpc_handshaker* handshaker, grpc_error* why); +void grpc_handshaker_do_handshake(grpc_handshaker* handshaker, + grpc_tcp_server_acceptor* acceptor, + grpc_closure* on_handshake_done, + grpc_handshaker_args* args); +const char* grpc_handshaker_name(grpc_handshaker* handshaker); + +/// +/// grpc_handshake_manager +/// + +typedef struct grpc_handshake_manager grpc_handshake_manager; + +/// Creates a new handshake manager. Caller takes ownership. +grpc_handshake_manager* grpc_handshake_manager_create(); + +/// Adds a handshaker to the handshake manager. +/// Takes ownership of \a handshaker. +void grpc_handshake_manager_add(grpc_handshake_manager* mgr, + grpc_handshaker* handshaker); + +/// Destroys the handshake manager. +void grpc_handshake_manager_destroy(grpc_handshake_manager* mgr); + +/// Shuts down the handshake manager (e.g., to clean up when the operation is +/// aborted in the middle). +/// The caller must still call grpc_handshake_manager_destroy() after +/// calling this function. +void grpc_handshake_manager_shutdown(grpc_handshake_manager* mgr, + grpc_error* why); + +/// Invokes handshakers in the order they were added. +/// Takes ownership of \a endpoint, and then passes that ownership to +/// the \a on_handshake_done callback. +/// Does NOT take ownership of \a channel_args. Instead, makes a copy before +/// invoking the first handshaker. +/// \a acceptor will be nullptr for client-side handshakers. +/// +/// When done, invokes \a on_handshake_done with a grpc_handshaker_args +/// object as its argument. If the callback is invoked with error != +/// GRPC_ERROR_NONE, then handshaking failed and the handshaker has done +/// the necessary clean-up. Otherwise, the callback takes ownership of +/// the arguments. +void grpc_handshake_manager_do_handshake(grpc_handshake_manager* mgr, + grpc_endpoint* endpoint, + const grpc_channel_args* channel_args, + grpc_millis deadline, + grpc_tcp_server_acceptor* acceptor, + grpc_iomgr_cb_func on_handshake_done, + void* user_data); + +/// Add \a mgr to the server side list of all pending handshake managers, the +/// list starts with \a *head. +// Not thread-safe. Caller needs to synchronize. +void grpc_handshake_manager_pending_list_add(grpc_handshake_manager** head, + grpc_handshake_manager* mgr); + +/// Remove \a mgr from the server side list of all pending handshake managers. +// Not thread-safe. Caller needs to synchronize. +void grpc_handshake_manager_pending_list_remove(grpc_handshake_manager** head, + grpc_handshake_manager* mgr); + +/// Shutdown all pending handshake managers on the server side. +// Not thread-safe. Caller needs to synchronize. +void grpc_handshake_manager_pending_list_shutdown_all( + grpc_handshake_manager* head, grpc_error* why); + +#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H */ diff --git a/third_party/grpc/src/core/lib/channel/handshaker_factory.cc b/third_party/grpc/src/core/lib/channel/handshaker_factory.cc new file mode 100644 index 00000000000000..8ade8fe4e23b8d --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/handshaker_factory.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/handshaker_factory.h" + +#include + +void grpc_handshaker_factory_add_handshakers( + grpc_handshaker_factory* handshaker_factory, const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) { + if (handshaker_factory != nullptr) { + GPR_ASSERT(handshaker_factory->vtable != nullptr); + handshaker_factory->vtable->add_handshakers( + handshaker_factory, args, interested_parties, handshake_mgr); + } +} + +void grpc_handshaker_factory_destroy( + grpc_handshaker_factory* handshaker_factory) { + if (handshaker_factory != nullptr) { + GPR_ASSERT(handshaker_factory->vtable != nullptr); + handshaker_factory->vtable->destroy(handshaker_factory); + } +} diff --git a/third_party/grpc/src/core/lib/channel/handshaker_factory.h b/third_party/grpc/src/core/lib/channel/handshaker_factory.h new file mode 100644 index 00000000000000..e17a6781798c98 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/handshaker_factory.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H +#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H + +#include + +#include + +#include "src/core/lib/channel/handshaker.h" + +// A handshaker factory is used to create handshakers. + +typedef struct grpc_handshaker_factory grpc_handshaker_factory; + +typedef struct { + void (*add_handshakers)(grpc_handshaker_factory* handshaker_factory, + const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr); + void (*destroy)(grpc_handshaker_factory* handshaker_factory); +} grpc_handshaker_factory_vtable; + +struct grpc_handshaker_factory { + const grpc_handshaker_factory_vtable* vtable; +}; + +void grpc_handshaker_factory_add_handshakers( + grpc_handshaker_factory* handshaker_factory, const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr); + +void grpc_handshaker_factory_destroy( + grpc_handshaker_factory* handshaker_factory); + +#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H */ diff --git a/third_party/grpc/src/core/lib/channel/handshaker_registry.cc b/third_party/grpc/src/core/lib/channel/handshaker_registry.cc new file mode 100644 index 00000000000000..fbafc43e795fb4 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/handshaker_registry.cc @@ -0,0 +1,101 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/handshaker_registry.h" + +#include + +#include + +// +// grpc_handshaker_factory_list +// + +typedef struct { + grpc_handshaker_factory** list; + size_t num_factories; +} grpc_handshaker_factory_list; + +static void grpc_handshaker_factory_list_register( + grpc_handshaker_factory_list* list, bool at_start, + grpc_handshaker_factory* factory) { + list->list = static_cast(gpr_realloc( + list->list, + (list->num_factories + 1) * sizeof(grpc_handshaker_factory*))); + if (at_start) { + memmove(list->list + 1, list->list, + sizeof(grpc_handshaker_factory*) * list->num_factories); + list->list[0] = factory; + } else { + list->list[list->num_factories] = factory; + } + ++list->num_factories; +} + +static void grpc_handshaker_factory_list_add_handshakers( + grpc_handshaker_factory_list* list, const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) { + for (size_t i = 0; i < list->num_factories; ++i) { + grpc_handshaker_factory_add_handshakers(list->list[i], args, + interested_parties, handshake_mgr); + } +} + +static void grpc_handshaker_factory_list_destroy( + grpc_handshaker_factory_list* list) { + for (size_t i = 0; i < list->num_factories; ++i) { + grpc_handshaker_factory_destroy(list->list[i]); + } + gpr_free(list->list); +} + +// +// plugin +// + +static grpc_handshaker_factory_list + g_handshaker_factory_lists[NUM_HANDSHAKER_TYPES]; + +void grpc_handshaker_factory_registry_init() { + memset(g_handshaker_factory_lists, 0, sizeof(g_handshaker_factory_lists)); +} + +void grpc_handshaker_factory_registry_shutdown() { + for (size_t i = 0; i < NUM_HANDSHAKER_TYPES; ++i) { + grpc_handshaker_factory_list_destroy(&g_handshaker_factory_lists[i]); + } +} + +void grpc_handshaker_factory_register(bool at_start, + grpc_handshaker_type handshaker_type, + grpc_handshaker_factory* factory) { + grpc_handshaker_factory_list_register( + &g_handshaker_factory_lists[handshaker_type], at_start, factory); +} + +void grpc_handshakers_add(grpc_handshaker_type handshaker_type, + const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) { + grpc_handshaker_factory_list_add_handshakers( + &g_handshaker_factory_lists[handshaker_type], args, interested_parties, + handshake_mgr); +} diff --git a/third_party/grpc/src/core/lib/channel/handshaker_registry.h b/third_party/grpc/src/core/lib/channel/handshaker_registry.h new file mode 100644 index 00000000000000..3dd4316de67a3f --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/handshaker_registry.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H +#define GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H + +#include + +#include + +#include "src/core/lib/channel/handshaker_factory.h" + +typedef enum { + HANDSHAKER_CLIENT = 0, + HANDSHAKER_SERVER, + NUM_HANDSHAKER_TYPES, // Must be last. +} grpc_handshaker_type; + +void grpc_handshaker_factory_registry_init(); +void grpc_handshaker_factory_registry_shutdown(); + +/// Registers a new handshaker factory. Takes ownership. +/// If \a at_start is true, the new handshaker will be at the beginning of +/// the list. Otherwise, it will be added to the end. +void grpc_handshaker_factory_register(bool at_start, + grpc_handshaker_type handshaker_type, + grpc_handshaker_factory* factory); + +void grpc_handshakers_add(grpc_handshaker_type handshaker_type, + const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr); + +#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H */ diff --git a/third_party/grpc/src/core/lib/channel/status_util.cc b/third_party/grpc/src/core/lib/channel/status_util.cc new file mode 100644 index 00000000000000..563db408464248 --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/status_util.cc @@ -0,0 +1,100 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/status_util.h" + +#include "src/core/lib/gpr/useful.h" + +typedef struct { + const char* str; + grpc_status_code status; +} status_string_entry; + +static const status_string_entry g_status_string_entries[] = { + {"OK", GRPC_STATUS_OK}, + {"CANCELLED", GRPC_STATUS_CANCELLED}, + {"UNKNOWN", GRPC_STATUS_UNKNOWN}, + {"INVALID_ARGUMENT", GRPC_STATUS_INVALID_ARGUMENT}, + {"DEADLINE_EXCEEDED", GRPC_STATUS_DEADLINE_EXCEEDED}, + {"NOT_FOUND", GRPC_STATUS_NOT_FOUND}, + {"ALREADY_EXISTS", GRPC_STATUS_ALREADY_EXISTS}, + {"PERMISSION_DENIED", GRPC_STATUS_PERMISSION_DENIED}, + {"UNAUTHENTICATED", GRPC_STATUS_UNAUTHENTICATED}, + {"RESOURCE_EXHAUSTED", GRPC_STATUS_RESOURCE_EXHAUSTED}, + {"FAILED_PRECONDITION", GRPC_STATUS_FAILED_PRECONDITION}, + {"ABORTED", GRPC_STATUS_ABORTED}, + {"OUT_OF_RANGE", GRPC_STATUS_OUT_OF_RANGE}, + {"UNIMPLEMENTED", GRPC_STATUS_UNIMPLEMENTED}, + {"INTERNAL", GRPC_STATUS_INTERNAL}, + {"UNAVAILABLE", GRPC_STATUS_UNAVAILABLE}, + {"DATA_LOSS", GRPC_STATUS_DATA_LOSS}, +}; + +bool grpc_status_code_from_string(const char* status_str, + grpc_status_code* status) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(g_status_string_entries); ++i) { + if (strcmp(status_str, g_status_string_entries[i].str) == 0) { + *status = g_status_string_entries[i].status; + return true; + } + } + return false; +} + +const char* grpc_status_code_to_string(grpc_status_code status) { + switch (status) { + case GRPC_STATUS_OK: + return "OK"; + case GRPC_STATUS_CANCELLED: + return "CANCELLED"; + case GRPC_STATUS_UNKNOWN: + return "UNKNOWN"; + case GRPC_STATUS_INVALID_ARGUMENT: + return "INVALID_ARGUMENT"; + case GRPC_STATUS_DEADLINE_EXCEEDED: + return "DEADLINE_EXCEEDED"; + case GRPC_STATUS_NOT_FOUND: + return "NOT_FOUND"; + case GRPC_STATUS_ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case GRPC_STATUS_PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case GRPC_STATUS_UNAUTHENTICATED: + return "UNAUTHENTICATED"; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return "RESOURCE_EXHAUSTED"; + case GRPC_STATUS_FAILED_PRECONDITION: + return "FAILED_PRECONDITION"; + case GRPC_STATUS_ABORTED: + return "ABORTED"; + case GRPC_STATUS_OUT_OF_RANGE: + return "OUT_OF_RANGE"; + case GRPC_STATUS_UNIMPLEMENTED: + return "UNIMPLEMENTED"; + case GRPC_STATUS_INTERNAL: + return "INTERNAL"; + case GRPC_STATUS_UNAVAILABLE: + return "UNAVAILABLE"; + case GRPC_STATUS_DATA_LOSS: + return "DATA_LOSS"; + default: + return "UNKNOWN"; + } +} diff --git a/third_party/grpc/src/core/lib/channel/status_util.h b/third_party/grpc/src/core/lib/channel/status_util.h new file mode 100644 index 00000000000000..5409de6b3c96ce --- /dev/null +++ b/third_party/grpc/src/core/lib/channel/status_util.h @@ -0,0 +1,58 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_CHANNEL_STATUS_UTIL_H +#define GRPC_CORE_LIB_CHANNEL_STATUS_UTIL_H + +#include + +#include + +#include +#include + +/// If \a status_str is a valid status string, sets \a status to the +/// corresponding status value and returns true. +bool grpc_status_code_from_string(const char* status_str, + grpc_status_code* status); + +/// Returns the string form of \a status, or "UNKNOWN" if invalid. +const char* grpc_status_code_to_string(grpc_status_code status); + +namespace grpc_core { +namespace internal { + +/// A set of grpc_status_code values. +class StatusCodeSet { + public: + bool Empty() const { return status_code_mask_ == 0; } + + void Add(grpc_status_code status) { status_code_mask_ |= (1 << status); } + + bool Contains(grpc_status_code status) const { + return status_code_mask_ & (1 << status); + } + + private: + int status_code_mask_ = 0; // A bitfield of status codes in the set. +}; + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_CHANNEL_STATUS_UTIL_H */ diff --git a/third_party/grpc/src/core/lib/compression/algorithm_metadata.h b/third_party/grpc/src/core/lib/compression/algorithm_metadata.h new file mode 100644 index 00000000000000..1be79e59c0068f --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/algorithm_metadata.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H +#define GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H + +#include + +#include +#include "src/core/lib/compression/compression_internal.h" +#include "src/core/lib/transport/metadata.h" + +/** Return compression algorithm based metadata value */ +grpc_slice grpc_compression_algorithm_slice( + grpc_compression_algorithm algorithm); + +/** Find compression algorithm based on passed in mdstr - returns + * GRPC_COMPRESS_ALGORITHM_COUNT on failure */ +grpc_compression_algorithm grpc_compression_algorithm_from_slice( + grpc_slice str); + +/** Return compression algorithm based metadata element */ +grpc_mdelem grpc_compression_encoding_mdelem( + grpc_compression_algorithm algorithm); + +/** Return message compression algorithm based metadata element (grpc-encoding: + * xxx) */ +grpc_mdelem grpc_message_compression_encoding_mdelem( + grpc_message_compression_algorithm algorithm); + +/** Return stream compression algorithm based metadata element + * (content-encoding: xxx) */ +grpc_mdelem grpc_stream_compression_encoding_mdelem( + grpc_stream_compression_algorithm algorithm); + +/** Find compression algorithm based on passed in mdstr - returns + * GRPC_COMPRESS_ALGORITHM_COUNT on failure */ +grpc_message_compression_algorithm +grpc_message_compression_algorithm_from_slice(grpc_slice str); + +/** Find stream compression algorithm based on passed in mdstr - returns + * GRPC_STREAM_COMPRESS_ALGORITHM_COUNT on failure */ +grpc_stream_compression_algorithm grpc_stream_compression_algorithm_from_slice( + grpc_slice str); + +#endif /* GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H */ diff --git a/third_party/grpc/src/core/lib/compression/compression.cc b/third_party/grpc/src/core/lib/compression/compression.cc new file mode 100644 index 00000000000000..48717541a764fe --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/compression.cc @@ -0,0 +1,174 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +#include "src/core/lib/compression/algorithm_metadata.h" +#include "src/core/lib/compression/compression_internal.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/transport/static_metadata.h" + +int grpc_compression_algorithm_is_message( + grpc_compression_algorithm algorithm) { + return (algorithm >= GRPC_COMPRESS_DEFLATE && algorithm <= GRPC_COMPRESS_GZIP) + ? 1 + : 0; +} + +int grpc_compression_algorithm_is_stream(grpc_compression_algorithm algorithm) { + return (algorithm == GRPC_COMPRESS_STREAM_GZIP) ? 1 : 0; +} + +int grpc_compression_algorithm_parse(grpc_slice name, + grpc_compression_algorithm* algorithm) { + if (grpc_slice_eq(name, GRPC_MDSTR_IDENTITY)) { + *algorithm = GRPC_COMPRESS_NONE; + return 1; + } else if (grpc_slice_eq(name, GRPC_MDSTR_DEFLATE)) { + *algorithm = GRPC_COMPRESS_DEFLATE; + return 1; + } else if (grpc_slice_eq(name, GRPC_MDSTR_GZIP)) { + *algorithm = GRPC_COMPRESS_GZIP; + return 1; + } else if (grpc_slice_eq(name, GRPC_MDSTR_STREAM_SLASH_GZIP)) { + *algorithm = GRPC_COMPRESS_STREAM_GZIP; + return 1; + } else { + return 0; + } + return 0; +} + +int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm, + const char** name) { + GRPC_API_TRACE("grpc_compression_algorithm_parse(algorithm=%d, name=%p)", 2, + ((int)algorithm, name)); + switch (algorithm) { + case GRPC_COMPRESS_NONE: + *name = "identity"; + return 1; + case GRPC_COMPRESS_DEFLATE: + *name = "deflate"; + return 1; + case GRPC_COMPRESS_GZIP: + *name = "gzip"; + return 1; + case GRPC_COMPRESS_STREAM_GZIP: + *name = "stream/gzip"; + return 1; + case GRPC_COMPRESS_ALGORITHMS_COUNT: + return 0; + } + return 0; +} + +grpc_compression_algorithm grpc_compression_algorithm_for_level( + grpc_compression_level level, uint32_t accepted_encodings) { + grpc_compression_algorithm algo; + if (level == GRPC_COMPRESS_LEVEL_NONE) { + return GRPC_COMPRESS_NONE; + } else if (level <= GRPC_COMPRESS_LEVEL_HIGH) { + // TODO(mxyan): Design algorithm to select from all algorithms, including + // stream compression algorithm + if (!grpc_compression_algorithm_from_message_stream_compression_algorithm( + &algo, + grpc_message_compression_algorithm_for_level( + level, + grpc_compression_bitset_to_message_bitset(accepted_encodings)), + static_cast(0))) { + gpr_log(GPR_ERROR, "Parse compression level error"); + return GRPC_COMPRESS_NONE; + } + return algo; + } else { + gpr_log(GPR_ERROR, "Unknown compression level: %d", level); + return GRPC_COMPRESS_NONE; + } +} + +void grpc_compression_options_init(grpc_compression_options* opts) { + memset(opts, 0, sizeof(*opts)); + /* all enabled by default */ + opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; +} + +void grpc_compression_options_enable_algorithm( + grpc_compression_options* opts, grpc_compression_algorithm algorithm) { + GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm); +} + +void grpc_compression_options_disable_algorithm( + grpc_compression_options* opts, grpc_compression_algorithm algorithm) { + GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm); +} + +int grpc_compression_options_is_algorithm_enabled( + const grpc_compression_options* opts, + grpc_compression_algorithm algorithm) { + return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm); +} + +grpc_slice grpc_compression_algorithm_slice( + grpc_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + return GRPC_MDSTR_IDENTITY; + case GRPC_COMPRESS_DEFLATE: + return GRPC_MDSTR_DEFLATE; + case GRPC_COMPRESS_GZIP: + return GRPC_MDSTR_GZIP; + case GRPC_COMPRESS_STREAM_GZIP: + return GRPC_MDSTR_STREAM_SLASH_GZIP; + case GRPC_COMPRESS_ALGORITHMS_COUNT: + return grpc_empty_slice(); + } + return grpc_empty_slice(); +} + +grpc_compression_algorithm grpc_compression_algorithm_from_slice( + grpc_slice str) { + if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_COMPRESS_NONE; + if (grpc_slice_eq(str, GRPC_MDSTR_DEFLATE)) return GRPC_COMPRESS_DEFLATE; + if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_COMPRESS_GZIP; + if (grpc_slice_eq(str, GRPC_MDSTR_STREAM_SLASH_GZIP)) + return GRPC_COMPRESS_STREAM_GZIP; + return GRPC_COMPRESS_ALGORITHMS_COUNT; +} + +grpc_mdelem grpc_compression_encoding_mdelem( + grpc_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + return GRPC_MDELEM_GRPC_ENCODING_IDENTITY; + case GRPC_COMPRESS_DEFLATE: + return GRPC_MDELEM_GRPC_ENCODING_DEFLATE; + case GRPC_COMPRESS_GZIP: + return GRPC_MDELEM_GRPC_ENCODING_GZIP; + case GRPC_COMPRESS_STREAM_GZIP: + return GRPC_MDELEM_GRPC_ENCODING_GZIP; + default: + break; + } + return GRPC_MDNULL; +} diff --git a/third_party/grpc/src/core/lib/compression/compression_internal.cc b/third_party/grpc/src/core/lib/compression/compression_internal.cc new file mode 100644 index 00000000000000..538514caf373b8 --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/compression_internal.cc @@ -0,0 +1,276 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +#include "src/core/lib/compression/algorithm_metadata.h" +#include "src/core/lib/compression/compression_internal.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/transport/static_metadata.h" + +/* Interfaces related to MD */ + +grpc_message_compression_algorithm +grpc_message_compression_algorithm_from_slice(grpc_slice str) { + if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) + return GRPC_MESSAGE_COMPRESS_NONE; + if (grpc_slice_eq(str, GRPC_MDSTR_DEFLATE)) + return GRPC_MESSAGE_COMPRESS_DEFLATE; + if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_MESSAGE_COMPRESS_GZIP; + return GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT; +} + +grpc_stream_compression_algorithm grpc_stream_compression_algorithm_from_slice( + grpc_slice str) { + if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_STREAM_COMPRESS_NONE; + if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_STREAM_COMPRESS_GZIP; + return GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT; +} + +grpc_mdelem grpc_message_compression_encoding_mdelem( + grpc_message_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_MESSAGE_COMPRESS_NONE: + return GRPC_MDELEM_GRPC_ENCODING_IDENTITY; + case GRPC_MESSAGE_COMPRESS_DEFLATE: + return GRPC_MDELEM_GRPC_ENCODING_DEFLATE; + case GRPC_MESSAGE_COMPRESS_GZIP: + return GRPC_MDELEM_GRPC_ENCODING_GZIP; + default: + break; + } + return GRPC_MDNULL; +} + +grpc_mdelem grpc_stream_compression_encoding_mdelem( + grpc_stream_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_STREAM_COMPRESS_NONE: + return GRPC_MDELEM_CONTENT_ENCODING_IDENTITY; + case GRPC_STREAM_COMPRESS_GZIP: + return GRPC_MDELEM_CONTENT_ENCODING_GZIP; + default: + break; + } + return GRPC_MDNULL; +} + +/* Interfaces performing transformation between compression algorithms and + * levels. */ +grpc_message_compression_algorithm +grpc_compression_algorithm_to_message_compression_algorithm( + grpc_compression_algorithm algo) { + switch (algo) { + case GRPC_COMPRESS_DEFLATE: + return GRPC_MESSAGE_COMPRESS_DEFLATE; + case GRPC_COMPRESS_GZIP: + return GRPC_MESSAGE_COMPRESS_GZIP; + default: + return GRPC_MESSAGE_COMPRESS_NONE; + } +} + +grpc_stream_compression_algorithm +grpc_compression_algorithm_to_stream_compression_algorithm( + grpc_compression_algorithm algo) { + switch (algo) { + case GRPC_COMPRESS_STREAM_GZIP: + return GRPC_STREAM_COMPRESS_GZIP; + default: + return GRPC_STREAM_COMPRESS_NONE; + } +} + +uint32_t grpc_compression_bitset_to_message_bitset(uint32_t bitset) { + return bitset & ((1u << GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT) - 1); +} + +uint32_t grpc_compression_bitset_to_stream_bitset(uint32_t bitset) { + uint32_t identity = (bitset & 1u); + uint32_t other_bits = + (bitset >> (GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT - 1)) & + ((1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 2); + return identity | other_bits; +} + +uint32_t grpc_compression_bitset_from_message_stream_compression_bitset( + uint32_t message_bitset, uint32_t stream_bitset) { + uint32_t offset_stream_bitset = + (stream_bitset & 1u) | + ((stream_bitset & (~1u)) << (GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT - 1)); + return message_bitset | offset_stream_bitset; +} + +int grpc_compression_algorithm_from_message_stream_compression_algorithm( + grpc_compression_algorithm* algorithm, + grpc_message_compression_algorithm message_algorithm, + grpc_stream_compression_algorithm stream_algorithm) { + if (message_algorithm != GRPC_MESSAGE_COMPRESS_NONE && + stream_algorithm != GRPC_STREAM_COMPRESS_NONE) { + *algorithm = GRPC_COMPRESS_NONE; + return 0; + } + if (message_algorithm == GRPC_MESSAGE_COMPRESS_NONE) { + switch (stream_algorithm) { + case GRPC_STREAM_COMPRESS_NONE: + *algorithm = GRPC_COMPRESS_NONE; + return 1; + case GRPC_STREAM_COMPRESS_GZIP: + *algorithm = GRPC_COMPRESS_STREAM_GZIP; + return 1; + default: + *algorithm = GRPC_COMPRESS_NONE; + return 0; + } + } else { + switch (message_algorithm) { + case GRPC_MESSAGE_COMPRESS_NONE: + *algorithm = GRPC_COMPRESS_NONE; + return 1; + case GRPC_MESSAGE_COMPRESS_DEFLATE: + *algorithm = GRPC_COMPRESS_DEFLATE; + return 1; + case GRPC_MESSAGE_COMPRESS_GZIP: + *algorithm = GRPC_COMPRESS_GZIP; + return 1; + default: + *algorithm = GRPC_COMPRESS_NONE; + return 0; + } + } + return 0; +} + +/* Interfaces for message compression. */ + +int grpc_message_compression_algorithm_name( + grpc_message_compression_algorithm algorithm, const char** name) { + GRPC_API_TRACE( + "grpc_message_compression_algorithm_parse(algorithm=%d, name=%p)", 2, + ((int)algorithm, name)); + switch (algorithm) { + case GRPC_MESSAGE_COMPRESS_NONE: + *name = "identity"; + return 1; + case GRPC_MESSAGE_COMPRESS_DEFLATE: + *name = "deflate"; + return 1; + case GRPC_MESSAGE_COMPRESS_GZIP: + *name = "gzip"; + return 1; + case GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT: + return 0; + } + return 0; +} + +/* TODO(dgq): Add the ability to specify parameters to the individual + * compression algorithms */ +grpc_message_compression_algorithm grpc_message_compression_algorithm_for_level( + grpc_compression_level level, uint32_t accepted_encodings) { + GRPC_API_TRACE("grpc_message_compression_algorithm_for_level(level=%d)", 1, + ((int)level)); + if (level > GRPC_COMPRESS_LEVEL_HIGH) { + gpr_log(GPR_ERROR, "Unknown message compression level %d.", + static_cast(level)); + abort(); + } + + const size_t num_supported = + GPR_BITCOUNT(accepted_encodings) - 1; /* discard NONE */ + if (level == GRPC_COMPRESS_LEVEL_NONE || num_supported == 0) { + return GRPC_MESSAGE_COMPRESS_NONE; + } + + GPR_ASSERT(level > 0); + + /* Establish a "ranking" or compression algorithms in increasing order of + * compression. + * This is simplistic and we will probably want to introduce other dimensions + * in the future (cpu/memory cost, etc). */ + const grpc_message_compression_algorithm algos_ranking[] = { + GRPC_MESSAGE_COMPRESS_GZIP, GRPC_MESSAGE_COMPRESS_DEFLATE}; + + /* intersect algos_ranking with the supported ones keeping the ranked order */ + grpc_message_compression_algorithm + sorted_supported_algos[GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT]; + size_t algos_supported_idx = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(algos_ranking); i++) { + const grpc_message_compression_algorithm alg = algos_ranking[i]; + for (size_t j = 0; j < num_supported; j++) { + if (GPR_BITGET(accepted_encodings, alg) == 1) { + /* if \a alg in supported */ + sorted_supported_algos[algos_supported_idx++] = alg; + break; + } + } + if (algos_supported_idx == num_supported) break; + } + + switch (level) { + case GRPC_COMPRESS_LEVEL_NONE: + abort(); /* should have been handled already */ + case GRPC_COMPRESS_LEVEL_LOW: + return sorted_supported_algos[0]; + case GRPC_COMPRESS_LEVEL_MED: + return sorted_supported_algos[num_supported / 2]; + case GRPC_COMPRESS_LEVEL_HIGH: + return sorted_supported_algos[num_supported - 1]; + default: + abort(); + }; +} + +int grpc_message_compression_algorithm_parse( + grpc_slice value, grpc_message_compression_algorithm* algorithm) { + if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) { + *algorithm = GRPC_MESSAGE_COMPRESS_NONE; + return 1; + } else if (grpc_slice_eq(value, GRPC_MDSTR_DEFLATE)) { + *algorithm = GRPC_MESSAGE_COMPRESS_DEFLATE; + return 1; + } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) { + *algorithm = GRPC_MESSAGE_COMPRESS_GZIP; + return 1; + } else { + return 0; + } + return 0; +} + +/* Interfaces for stream compression. */ + +int grpc_stream_compression_algorithm_parse( + grpc_slice value, grpc_stream_compression_algorithm* algorithm) { + if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) { + *algorithm = GRPC_STREAM_COMPRESS_NONE; + return 1; + } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) { + *algorithm = GRPC_STREAM_COMPRESS_GZIP; + return 1; + } else { + return 0; + } + return 0; +} diff --git a/third_party/grpc/src/core/lib/compression/compression_internal.h b/third_party/grpc/src/core/lib/compression/compression_internal.h new file mode 100644 index 00000000000000..da007368b01517 --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/compression_internal.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H +#define GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + GRPC_MESSAGE_COMPRESS_NONE = 0, + GRPC_MESSAGE_COMPRESS_DEFLATE, + GRPC_MESSAGE_COMPRESS_GZIP, + /* TODO(ctiller): snappy */ + GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT +} grpc_message_compression_algorithm; + +/** Stream compresssion algorithms supported by gRPC */ +typedef enum { + GRPC_STREAM_COMPRESS_NONE = 0, + GRPC_STREAM_COMPRESS_GZIP, + GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT +} grpc_stream_compression_algorithm; + +/* Interfaces performing transformation between compression algorithms and + * levels. */ + +grpc_message_compression_algorithm +grpc_compression_algorithm_to_message_compression_algorithm( + grpc_compression_algorithm algo); + +grpc_stream_compression_algorithm +grpc_compression_algorithm_to_stream_compression_algorithm( + grpc_compression_algorithm algo); + +uint32_t grpc_compression_bitset_to_message_bitset(uint32_t bitset); + +uint32_t grpc_compression_bitset_to_stream_bitset(uint32_t bitset); + +uint32_t grpc_compression_bitset_from_message_stream_compression_bitset( + uint32_t message_bitset, uint32_t stream_bitset); + +int grpc_compression_algorithm_from_message_stream_compression_algorithm( + grpc_compression_algorithm* algorithm, + grpc_message_compression_algorithm message_algorithm, + grpc_stream_compression_algorithm stream_algorithm); + +/* Interfaces for message compression. */ + +int grpc_message_compression_algorithm_name( + grpc_message_compression_algorithm algorithm, const char** name); + +grpc_message_compression_algorithm grpc_message_compression_algorithm_for_level( + grpc_compression_level level, uint32_t accepted_encodings); + +int grpc_message_compression_algorithm_parse( + grpc_slice value, grpc_message_compression_algorithm* algorithm); + +/* Interfaces for stream compression. */ + +int grpc_stream_compression_algorithm_parse( + grpc_slice value, grpc_stream_compression_algorithm* algorithm); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_COMPRESSION_COMPRESSION_INTERNAL_H */ diff --git a/third_party/grpc/src/core/lib/compression/message_compress.cc b/third_party/grpc/src/core/lib/compression/message_compress.cc new file mode 100644 index 00000000000000..e06454f871c85f --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/message_compress.cc @@ -0,0 +1,187 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/compression/message_compress.h" + +#include + +#include +#include + +#include + +#include "src/core/lib/slice/slice_internal.h" + +#define OUTPUT_BLOCK_SIZE 1024 + +static int zlib_body(z_stream* zs, grpc_slice_buffer* input, + grpc_slice_buffer* output, + int (*flate)(z_stream* zs, int flush)) { + int r; + int flush; + size_t i; + grpc_slice outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE); + const uInt uint_max = ~static_cast(0); + + GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max); + zs->avail_out = static_cast GRPC_SLICE_LENGTH(outbuf); + zs->next_out = GRPC_SLICE_START_PTR(outbuf); + flush = Z_NO_FLUSH; + for (i = 0; i < input->count; i++) { + if (i == input->count - 1) flush = Z_FINISH; + GPR_ASSERT(GRPC_SLICE_LENGTH(input->slices[i]) <= uint_max); + zs->avail_in = static_cast GRPC_SLICE_LENGTH(input->slices[i]); + zs->next_in = GRPC_SLICE_START_PTR(input->slices[i]); + do { + if (zs->avail_out == 0) { + grpc_slice_buffer_add_indexed(output, outbuf); + outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE); + GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max); + zs->avail_out = static_cast GRPC_SLICE_LENGTH(outbuf); + zs->next_out = GRPC_SLICE_START_PTR(outbuf); + } + r = flate(zs, flush); + if (r < 0 && r != Z_BUF_ERROR /* not fatal */) { + gpr_log(GPR_INFO, "zlib error (%d)", r); + 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; + grpc_slice_buffer_add_indexed(output, outbuf); + + return 1; + +error: + grpc_slice_unref_internal(outbuf); + return 0; +} + +static void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) { + return gpr_malloc(items * size); +} + +static void zfree_gpr(void* opaque, void* address) { gpr_free(address); } + +static int zlib_compress(grpc_slice_buffer* input, grpc_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)); + zs.zalloc = zalloc_gpr; + zs.zfree = zfree_gpr; + r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0), + 8, Z_DEFAULT_STRATEGY); + GPR_ASSERT(r == Z_OK); + r = zlib_body(&zs, input, output, deflate) && output->length < input->length; + if (!r) { + for (i = count_before; i < output->count; i++) { + grpc_slice_unref_internal(output->slices[i]); + } + output->count = count_before; + output->length = length_before; + } + deflateEnd(&zs); + return r; +} + +static int zlib_decompress(grpc_slice_buffer* input, grpc_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)); + zs.zalloc = zalloc_gpr; + zs.zfree = zfree_gpr; + r = inflateInit2(&zs, 15 | (gzip ? 16 : 0)); + GPR_ASSERT(r == Z_OK); + r = zlib_body(&zs, input, output, inflate); + if (!r) { + for (i = count_before; i < output->count; i++) { + grpc_slice_unref_internal(output->slices[i]); + } + output->count = count_before; + output->length = length_before; + } + inflateEnd(&zs); + return r; +} + +static int copy(grpc_slice_buffer* input, grpc_slice_buffer* output) { + size_t i; + for (i = 0; i < input->count; i++) { + grpc_slice_buffer_add(output, grpc_slice_ref_internal(input->slices[i])); + } + return 1; +} + +static int compress_inner(grpc_message_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_slice_buffer* output) { + switch (algorithm) { + case GRPC_MESSAGE_COMPRESS_NONE: + /* the fallback path always needs to be send uncompressed: we simply + rely on that here */ + return 0; + case GRPC_MESSAGE_COMPRESS_DEFLATE: + return zlib_compress(input, output, 0); + case GRPC_MESSAGE_COMPRESS_GZIP: + return zlib_compress(input, output, 1); + case GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT: + break; + } + gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); + return 0; +} + +int grpc_msg_compress(grpc_message_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_slice_buffer* output) { + if (!compress_inner(algorithm, input, output)) { + copy(input, output); + return 0; + } + return 1; +} + +int grpc_msg_decompress(grpc_message_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_slice_buffer* output) { + switch (algorithm) { + case GRPC_MESSAGE_COMPRESS_NONE: + return copy(input, output); + case GRPC_MESSAGE_COMPRESS_DEFLATE: + return zlib_decompress(input, output, 0); + case GRPC_MESSAGE_COMPRESS_GZIP: + return zlib_decompress(input, output, 1); + case GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT: + break; + } + gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); + return 0; +} diff --git a/third_party/grpc/src/core/lib/compression/message_compress.h b/third_party/grpc/src/core/lib/compression/message_compress.h new file mode 100644 index 00000000000000..91654e47e33840 --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/message_compress.h @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H +#define GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H + +#include + +#include + +#include "src/core/lib/compression/compression_internal.h" + +/* 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_message_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_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_message_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_slice_buffer* output); + +#endif /* GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H */ diff --git a/third_party/grpc/src/core/lib/compression/stream_compression.cc b/third_party/grpc/src/core/lib/compression/stream_compression.cc new file mode 100644 index 00000000000000..46cb3daf4ccd1b --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/stream_compression.cc @@ -0,0 +1,79 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/lib/compression/stream_compression.h" +#include "src/core/lib/compression/stream_compression_gzip.h" + +extern const grpc_stream_compression_vtable + grpc_stream_compression_identity_vtable; + +bool grpc_stream_compress(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, grpc_slice_buffer* out, + size_t* output_size, size_t max_output_size, + grpc_stream_compression_flush flush) { + return ctx->vtable->compress(ctx, in, out, output_size, max_output_size, + flush); +} + +bool grpc_stream_decompress(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, grpc_slice_buffer* out, + size_t* output_size, size_t max_output_size, + bool* end_of_context) { + return ctx->vtable->decompress(ctx, in, out, output_size, max_output_size, + end_of_context); +} + +grpc_stream_compression_context* grpc_stream_compression_context_create( + grpc_stream_compression_method method) { + switch (method) { + case GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS: + case GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS: + return grpc_stream_compression_identity_vtable.context_create(method); + case GRPC_STREAM_COMPRESSION_GZIP_COMPRESS: + case GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS: + return grpc_stream_compression_gzip_vtable.context_create(method); + default: + gpr_log(GPR_ERROR, "Unknown stream compression method: %d", method); + return nullptr; + } +} + +void grpc_stream_compression_context_destroy( + grpc_stream_compression_context* ctx) { + ctx->vtable->context_destroy(ctx); +} + +int grpc_stream_compression_method_parse( + grpc_slice value, bool is_compress, + grpc_stream_compression_method* method) { + if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) { + *method = is_compress ? GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS + : GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; + return 1; + } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) { + *method = is_compress ? GRPC_STREAM_COMPRESSION_GZIP_COMPRESS + : GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS; + return 1; + } else { + return 0; + } +} diff --git a/third_party/grpc/src/core/lib/compression/stream_compression.h b/third_party/grpc/src/core/lib/compression/stream_compression.h new file mode 100644 index 00000000000000..c80f2f8692b8da --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/stream_compression.h @@ -0,0 +1,116 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H +#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_H + +#include + +#include + +#include +#include + +#include "src/core/lib/transport/static_metadata.h" + +typedef struct grpc_stream_compression_vtable grpc_stream_compression_vtable; + +/* Stream compression/decompression context */ +typedef struct grpc_stream_compression_context { + const grpc_stream_compression_vtable* vtable; +} grpc_stream_compression_context; + +typedef enum grpc_stream_compression_method { + GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS = 0, + GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS, + GRPC_STREAM_COMPRESSION_GZIP_COMPRESS, + GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS, + GRPC_STREAM_COMPRESSION_METHOD_COUNT +} grpc_stream_compression_method; + +typedef enum grpc_stream_compression_flush { + GRPC_STREAM_COMPRESSION_FLUSH_NONE = 0, + GRPC_STREAM_COMPRESSION_FLUSH_SYNC, + GRPC_STREAM_COMPRESSION_FLUSH_FINISH, + GRPC_STREAM_COMPRESSION_FLUSH_COUNT +} grpc_stream_compression_flush; + +struct grpc_stream_compression_vtable { + bool (*compress)(grpc_stream_compression_context* ctx, grpc_slice_buffer* in, + grpc_slice_buffer* out, size_t* output_size, + size_t max_output_size, grpc_stream_compression_flush flush); + bool (*decompress)(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, grpc_slice_buffer* out, + size_t* output_size, size_t max_output_size, + bool* end_of_context); + grpc_stream_compression_context* (*context_create)( + grpc_stream_compression_method method); + void (*context_destroy)(grpc_stream_compression_context* ctx); +}; + +/** + * Compress bytes provided in \a in with a given context, with an optional flush + * at the end of compression. Emits at most \a max_output_size compressed bytes + * into \a out. If all the bytes in input buffer \a in are depleted and \a flush + * is not GRPC_STREAM_COMPRESSION_FLUSH_NONE, the corresponding flush method is + * executed. The total number of bytes emitted is outputed in \a output_size. + * + * A SYNC flush indicates that the entire messages in \a in can be decompressed + * from \a out. A FINISH flush implies a SYNC flush, and that any further + * compression will not be dependent on the state of the current context and any + * previous compressed bytes. It allows corresponding decompression context to + * be dropped when reaching this boundary. + */ +bool grpc_stream_compress(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, grpc_slice_buffer* out, + size_t* output_size, size_t max_output_size, + grpc_stream_compression_flush flush); + +/** + * Decompress bytes provided in \a in with a given context. Emits at most \a + * max_output_size decompressed bytes into \a out. If decompression process + * reached the end of a gzip stream, \a end_of_context is set to true; otherwise + * it is set to false. The total number of bytes emitted is outputed in \a + * output_size. + */ +bool grpc_stream_decompress(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, grpc_slice_buffer* out, + size_t* output_size, size_t max_output_size, + bool* end_of_context); + +/** + * Creates a stream compression context. \a pending_bytes_buffer is the input + * buffer for compression/decompression operations. \a method specifies whether + * the context is for compression or decompression. + */ +grpc_stream_compression_context* grpc_stream_compression_context_create( + grpc_stream_compression_method method); + +/** + * Destroys a stream compression context. + */ +void grpc_stream_compression_context_destroy( + grpc_stream_compression_context* ctx); + +/** + * Parse stream compression method based on algorithm name + */ +int grpc_stream_compression_method_parse( + grpc_slice value, bool is_compress, grpc_stream_compression_method* method); + +#endif diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_gzip.cc b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.cc new file mode 100644 index 00000000000000..682f712843a5b6 --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.cc @@ -0,0 +1,230 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/lib/compression/stream_compression_gzip.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" + +#define OUTPUT_BLOCK_SIZE (1024) + +typedef struct grpc_stream_compression_context_gzip { + grpc_stream_compression_context base; + + z_stream zs; + int (*flate)(z_stream* zs, int flush); +} grpc_stream_compression_context_gzip; + +static bool gzip_flate(grpc_stream_compression_context_gzip* ctx, + grpc_slice_buffer* in, grpc_slice_buffer* out, + size_t* output_size, size_t max_output_size, int flush, + bool* end_of_context) { + GPR_ASSERT(flush == 0 || flush == Z_SYNC_FLUSH || flush == Z_FINISH); + /* Full flush is not allowed when inflating. */ + GPR_ASSERT(!(ctx->flate == inflate && (flush == Z_FINISH))); + + grpc_core::ExecCtx exec_ctx; + int r; + bool eoc = false; + size_t original_max_output_size = max_output_size; + while (max_output_size > 0 && (in->length > 0 || flush) && !eoc) { + size_t slice_size = max_output_size < OUTPUT_BLOCK_SIZE ? max_output_size + : OUTPUT_BLOCK_SIZE; + grpc_slice slice_out = GRPC_SLICE_MALLOC(slice_size); + ctx->zs.avail_out = static_cast(slice_size); + ctx->zs.next_out = GRPC_SLICE_START_PTR(slice_out); + while (ctx->zs.avail_out > 0 && in->length > 0 && !eoc) { + grpc_slice slice = grpc_slice_buffer_take_first(in); + ctx->zs.avail_in = static_cast GRPC_SLICE_LENGTH(slice); + ctx->zs.next_in = GRPC_SLICE_START_PTR(slice); + r = ctx->flate(&ctx->zs, Z_NO_FLUSH); + if (r < 0 && r != Z_BUF_ERROR) { + gpr_log(GPR_ERROR, "zlib error (%d)", r); + grpc_slice_unref_internal(slice_out); + + return false; + } else if (r == Z_STREAM_END && ctx->flate == inflate) { + eoc = true; + } + if (ctx->zs.avail_in > 0) { + grpc_slice_buffer_undo_take_first( + in, + grpc_slice_sub(slice, GRPC_SLICE_LENGTH(slice) - ctx->zs.avail_in, + GRPC_SLICE_LENGTH(slice))); + } + grpc_slice_unref_internal(slice); + } + if (flush != 0 && ctx->zs.avail_out > 0 && !eoc) { + GPR_ASSERT(in->length == 0); + r = ctx->flate(&ctx->zs, flush); + if (flush == Z_SYNC_FLUSH) { + switch (r) { + case Z_OK: + /* Maybe flush is not complete; just made some partial progress. */ + if (ctx->zs.avail_out > 0) { + flush = 0; + } + break; + case Z_BUF_ERROR: + case Z_STREAM_END: + flush = 0; + break; + default: + gpr_log(GPR_ERROR, "zlib error (%d)", r); + grpc_slice_unref_internal(slice_out); + + return false; + } + } else if (flush == Z_FINISH) { + switch (r) { + case Z_OK: + case Z_BUF_ERROR: + /* Wait for the next loop to assign additional output space. */ + GPR_ASSERT(ctx->zs.avail_out == 0); + break; + case Z_STREAM_END: + flush = 0; + break; + default: + gpr_log(GPR_ERROR, "zlib error (%d)", r); + grpc_slice_unref_internal(slice_out); + + return false; + } + } + } + + if (ctx->zs.avail_out == 0) { + grpc_slice_buffer_add(out, slice_out); + } else if (ctx->zs.avail_out < slice_size) { + size_t len = GRPC_SLICE_LENGTH(slice_out); + GRPC_SLICE_SET_LENGTH(slice_out, len - ctx->zs.avail_out); + grpc_slice_buffer_add(out, slice_out); + } else { + grpc_slice_unref_internal(slice_out); + } + max_output_size -= (slice_size - ctx->zs.avail_out); + } + + if (end_of_context) { + *end_of_context = eoc; + } + if (output_size) { + *output_size = original_max_output_size - max_output_size; + } + return true; +} + +static bool grpc_stream_compress_gzip(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, + grpc_slice_buffer* out, + size_t* output_size, + size_t max_output_size, + grpc_stream_compression_flush flush) { + if (ctx == nullptr) { + return false; + } + grpc_stream_compression_context_gzip* gzip_ctx = + reinterpret_cast(ctx); + GPR_ASSERT(gzip_ctx->flate == deflate); + int gzip_flush; + switch (flush) { + case GRPC_STREAM_COMPRESSION_FLUSH_NONE: + gzip_flush = 0; + break; + case GRPC_STREAM_COMPRESSION_FLUSH_SYNC: + gzip_flush = Z_SYNC_FLUSH; + break; + case GRPC_STREAM_COMPRESSION_FLUSH_FINISH: + gzip_flush = Z_FINISH; + break; + default: + gzip_flush = 0; + } + return gzip_flate(gzip_ctx, in, out, output_size, max_output_size, gzip_flush, + nullptr); +} + +static bool grpc_stream_decompress_gzip(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, + grpc_slice_buffer* out, + size_t* output_size, + size_t max_output_size, + bool* end_of_context) { + if (ctx == nullptr) { + return false; + } + grpc_stream_compression_context_gzip* gzip_ctx = + reinterpret_cast(ctx); + GPR_ASSERT(gzip_ctx->flate == inflate); + return gzip_flate(gzip_ctx, in, out, output_size, max_output_size, + Z_SYNC_FLUSH, end_of_context); +} + +static grpc_stream_compression_context* +grpc_stream_compression_context_create_gzip( + grpc_stream_compression_method method) { + GPR_ASSERT(method == GRPC_STREAM_COMPRESSION_GZIP_COMPRESS || + method == GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS); + grpc_stream_compression_context_gzip* gzip_ctx = + static_cast( + gpr_zalloc(sizeof(grpc_stream_compression_context_gzip))); + int r; + if (gzip_ctx == nullptr) { + return nullptr; + } + if (method == GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS) { + r = inflateInit2(&gzip_ctx->zs, 0x1F); + gzip_ctx->flate = inflate; + } else { + r = deflateInit2(&gzip_ctx->zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8, + Z_DEFAULT_STRATEGY); + gzip_ctx->flate = deflate; + } + if (r != Z_OK) { + gpr_free(gzip_ctx); + return nullptr; + } + + gzip_ctx->base.vtable = &grpc_stream_compression_gzip_vtable; + return reinterpret_cast(gzip_ctx); +} + +static void grpc_stream_compression_context_destroy_gzip( + grpc_stream_compression_context* ctx) { + if (ctx == nullptr) { + return; + } + grpc_stream_compression_context_gzip* gzip_ctx = + reinterpret_cast(ctx); + if (gzip_ctx->flate == inflate) { + inflateEnd(&gzip_ctx->zs); + } else { + deflateEnd(&gzip_ctx->zs); + } + gpr_free(ctx); +} + +const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable = { + grpc_stream_compress_gzip, grpc_stream_decompress_gzip, + grpc_stream_compression_context_create_gzip, + grpc_stream_compression_context_destroy_gzip}; diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_gzip.h b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.h new file mode 100644 index 00000000000000..740f09734a6d86 --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/stream_compression_gzip.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H +#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_GZIP_H + +#include + +#include "src/core/lib/compression/stream_compression.h" + +extern const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable; + +#endif diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_identity.cc b/third_party/grpc/src/core/lib/compression/stream_compression_identity.cc new file mode 100644 index 00000000000000..b7981394d5e0b8 --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/stream_compression_identity.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/lib/compression/stream_compression_identity.h" +#include "src/core/lib/slice/slice_internal.h" + +#define OUTPUT_BLOCK_SIZE (1024) + +/* Singleton context used for all identity streams. */ +static grpc_stream_compression_context identity_ctx = { + &grpc_stream_compression_identity_vtable}; + +static void grpc_stream_compression_pass_through(grpc_slice_buffer* in, + grpc_slice_buffer* out, + size_t* output_size, + size_t max_output_size) { + if (max_output_size >= in->length) { + if (output_size) { + *output_size = in->length; + } + grpc_slice_buffer_move_into(in, out); + } else { + if (output_size) { + *output_size = max_output_size; + } + grpc_slice_buffer_move_first(in, max_output_size, out); + } +} + +static bool grpc_stream_compress_identity(grpc_stream_compression_context* ctx, + grpc_slice_buffer* in, + grpc_slice_buffer* out, + size_t* output_size, + size_t max_output_size, + grpc_stream_compression_flush flush) { + if (ctx == nullptr) { + return false; + } + grpc_stream_compression_pass_through(in, out, output_size, max_output_size); + return true; +} + +static bool grpc_stream_decompress_identity( + grpc_stream_compression_context* ctx, grpc_slice_buffer* in, + grpc_slice_buffer* out, size_t* output_size, size_t max_output_size, + bool* end_of_context) { + if (ctx == nullptr) { + return false; + } + grpc_stream_compression_pass_through(in, out, output_size, max_output_size); + if (end_of_context) { + *end_of_context = false; + } + return true; +} + +static grpc_stream_compression_context* +grpc_stream_compression_context_create_identity( + grpc_stream_compression_method method) { + GPR_ASSERT(method == GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS || + method == GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS); + /* No context needed in this case. Use fake context instead. */ + return &identity_ctx; +} + +static void grpc_stream_compression_context_destroy_identity( + grpc_stream_compression_context* ctx) { + return; +} + +const grpc_stream_compression_vtable grpc_stream_compression_identity_vtable = { + grpc_stream_compress_identity, grpc_stream_decompress_identity, + grpc_stream_compression_context_create_identity, + grpc_stream_compression_context_destroy_identity}; diff --git a/third_party/grpc/src/core/lib/compression/stream_compression_identity.h b/third_party/grpc/src/core/lib/compression/stream_compression_identity.h new file mode 100644 index 00000000000000..cc77b63ecdfa02 --- /dev/null +++ b/third_party/grpc/src/core/lib/compression/stream_compression_identity.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H +#define GRPC_CORE_LIB_COMPRESSION_STREAM_COMPRESSION_IDENTITY_H + +#include + +#include "src/core/lib/compression/stream_compression.h" + +extern const grpc_stream_compression_vtable + grpc_stream_compression_identity_vtable; + +#endif diff --git a/third_party/grpc/src/core/lib/debug/stats.cc b/third_party/grpc/src/core/lib/debug/stats.cc new file mode 100644 index 00000000000000..d8ddf03ac1ca0b --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/stats.cc @@ -0,0 +1,178 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/debug/stats.h" + +#include +#include + +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" + +grpc_stats_data* grpc_stats_per_cpu_storage = nullptr; +static size_t g_num_cores; + +void grpc_stats_init(void) { + g_num_cores = GPR_MAX(1, gpr_cpu_num_cores()); + grpc_stats_per_cpu_storage = static_cast( + gpr_zalloc(sizeof(grpc_stats_data) * g_num_cores)); +} + +void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); } + +void grpc_stats_collect(grpc_stats_data* output) { + memset(output, 0, sizeof(*output)); + for (size_t core = 0; core < g_num_cores; core++) { + for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + output->counters[i] += gpr_atm_no_barrier_load( + &grpc_stats_per_cpu_storage[core].counters[i]); + } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { + output->histograms[i] += gpr_atm_no_barrier_load( + &grpc_stats_per_cpu_storage[core].histograms[i]); + } + } +} + +void grpc_stats_diff(const grpc_stats_data* b, const grpc_stats_data* a, + grpc_stats_data* c) { + for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + c->counters[i] = b->counters[i] - a->counters[i]; + } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { + c->histograms[i] = b->histograms[i] - a->histograms[i]; + } +} + +int grpc_stats_histo_find_bucket_slow(int value, const int* table, + int table_size) { + GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS(); + const int* const start = table; + while (table_size > 0) { + int step = table_size / 2; + const int* it = table + step; + if (value >= *it) { + table = it + 1; + table_size -= step + 1; + } else { + table_size = step; + } + } + return static_cast(table - start) - 1; +} + +size_t grpc_stats_histo_count(const grpc_stats_data* stats, + grpc_stats_histograms histogram) { + size_t sum = 0; + for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) { + sum += static_cast( + stats->histograms[grpc_stats_histo_start[histogram] + i]); + } + return sum; +} + +static double threshold_for_count_below(const gpr_atm* bucket_counts, + const int* bucket_boundaries, + int num_buckets, double count_below) { + double count_so_far; + double lower_bound; + double upper_bound; + int lower_idx; + int upper_idx; + + /* find the lowest bucket that gets us above count_below */ + count_so_far = 0.0; + for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) { + count_so_far += static_cast(bucket_counts[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 < num_buckets; upper_idx++) { + if (bucket_counts[upper_idx]) { + break; + } + } + return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0; + } else { + /* treat values as uniform throughout the bucket, and find where this value + should lie */ + lower_bound = bucket_boundaries[lower_idx]; + upper_bound = bucket_boundaries[lower_idx + 1]; + return upper_bound - (upper_bound - lower_bound) * + (count_so_far - count_below) / + static_cast(bucket_counts[lower_idx]); + } +} + +double grpc_stats_histo_percentile(const grpc_stats_data* stats, + grpc_stats_histograms histogram, + double percentile) { + size_t count = grpc_stats_histo_count(stats, histogram); + if (count == 0) return 0.0; + return threshold_for_count_below( + stats->histograms + grpc_stats_histo_start[histogram], + grpc_stats_histo_bucket_boundaries[histogram], + grpc_stats_histo_buckets[histogram], + static_cast(count) * percentile / 100.0); +} + +char* grpc_stats_data_as_json(const grpc_stats_data* data) { + gpr_strvec v; + char* tmp; + bool is_first = true; + gpr_strvec_init(&v); + gpr_strvec_add(&v, gpr_strdup("{")); + for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + gpr_asprintf(&tmp, "%s\"%s\": %" PRIdPTR, is_first ? "" : ", ", + grpc_stats_counter_name[i], data->counters[i]); + gpr_strvec_add(&v, tmp); + is_first = false; + } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { + gpr_asprintf(&tmp, "%s\"%s\": [", is_first ? "" : ", ", + grpc_stats_histogram_name[i]); + gpr_strvec_add(&v, tmp); + for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { + gpr_asprintf(&tmp, "%s%" PRIdPTR, j == 0 ? "" : ",", + data->histograms[grpc_stats_histo_start[i] + j]); + gpr_strvec_add(&v, tmp); + } + gpr_asprintf(&tmp, "], \"%s_bkt\": [", grpc_stats_histogram_name[i]); + gpr_strvec_add(&v, tmp); + for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { + gpr_asprintf(&tmp, "%s%d", j == 0 ? "" : ",", + grpc_stats_histo_bucket_boundaries[i][j]); + gpr_strvec_add(&v, tmp); + } + gpr_strvec_add(&v, gpr_strdup("]")); + is_first = false; + } + gpr_strvec_add(&v, gpr_strdup("}")); + tmp = gpr_strvec_flatten(&v, nullptr); + gpr_strvec_destroy(&v); + return tmp; +} diff --git a/third_party/grpc/src/core/lib/debug/stats.h b/third_party/grpc/src/core/lib/debug/stats.h new file mode 100644 index 00000000000000..9e88ad7000ff6f --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/stats.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_DEBUG_STATS_H +#define GRPC_CORE_LIB_DEBUG_STATS_H + +#include + +#include +#include "src/core/lib/debug/stats_data.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +typedef struct grpc_stats_data { + gpr_atm counters[GRPC_STATS_COUNTER_COUNT]; + gpr_atm histograms[GRPC_STATS_HISTOGRAM_BUCKETS]; +} grpc_stats_data; + +extern grpc_stats_data* grpc_stats_per_cpu_storage; + +#define GRPC_THREAD_STATS_DATA() \ + (&grpc_stats_per_cpu_storage[grpc_core::ExecCtx::Get()->starting_cpu()]) + +/* Only collect stats if GRPC_COLLECT_STATS is defined or it is a debug build. + */ +#if defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) +#define GRPC_STATS_INC_COUNTER(ctr) \ + (gpr_atm_no_barrier_fetch_add(&GRPC_THREAD_STATS_DATA()->counters[(ctr)], 1)) + +#define GRPC_STATS_INC_HISTOGRAM(histogram, index) \ + (gpr_atm_no_barrier_fetch_add( \ + &GRPC_THREAD_STATS_DATA()->histograms[histogram##_FIRST_SLOT + (index)], \ + 1)) +#else /* defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) */ +#define GRPC_STATS_INC_COUNTER(ctr) +#define GRPC_STATS_INC_HISTOGRAM(histogram, index) +#endif /* defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) */ + +void grpc_stats_init(void); +void grpc_stats_shutdown(void); +void grpc_stats_collect(grpc_stats_data* output); +// c = b-a +void grpc_stats_diff(const grpc_stats_data* b, const grpc_stats_data* a, + grpc_stats_data* c); +char* grpc_stats_data_as_json(const grpc_stats_data* data); +int grpc_stats_histo_find_bucket_slow(int value, const int* table, + int table_size); +double grpc_stats_histo_percentile(const grpc_stats_data* data, + grpc_stats_histograms histogram, + double percentile); +size_t grpc_stats_histo_count(const grpc_stats_data* data, + grpc_stats_histograms histogram); + +#endif diff --git a/third_party/grpc/src/core/lib/debug/stats_data.cc b/third_party/grpc/src/core/lib/debug/stats_data.cc new file mode 100644 index 00000000000000..f8c27db0a81daf --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/stats_data.cc @@ -0,0 +1,687 @@ +/* + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Automatically generated by tools/codegen/core/gen_stats_data.py + */ + +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/debug/stats_data.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +const char* grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT] = { + "client_calls_created", + "server_calls_created", + "cqs_created", + "client_channels_created", + "client_subchannels_created", + "server_channels_created", + "syscall_poll", + "syscall_wait", + "pollset_kick", + "pollset_kicked_without_poller", + "pollset_kicked_again", + "pollset_kick_wakeup_fd", + "pollset_kick_wakeup_cv", + "pollset_kick_own_thread", + "syscall_epoll_ctl", + "pollset_fd_cache_hits", + "histogram_slow_lookups", + "syscall_write", + "syscall_read", + "tcp_backup_pollers_created", + "tcp_backup_poller_polls", + "http2_op_batches", + "http2_op_cancel", + "http2_op_send_initial_metadata", + "http2_op_send_message", + "http2_op_send_trailing_metadata", + "http2_op_recv_initial_metadata", + "http2_op_recv_message", + "http2_op_recv_trailing_metadata", + "http2_settings_writes", + "http2_pings_sent", + "http2_writes_begun", + "http2_writes_offloaded", + "http2_writes_continued", + "http2_partial_writes", + "http2_initiate_write_due_to_initial_write", + "http2_initiate_write_due_to_start_new_stream", + "http2_initiate_write_due_to_send_message", + "http2_initiate_write_due_to_send_initial_metadata", + "http2_initiate_write_due_to_send_trailing_metadata", + "http2_initiate_write_due_to_retry_send_ping", + "http2_initiate_write_due_to_continue_pings", + "http2_initiate_write_due_to_goaway_sent", + "http2_initiate_write_due_to_rst_stream", + "http2_initiate_write_due_to_close_from_api", + "http2_initiate_write_due_to_stream_flow_control", + "http2_initiate_write_due_to_transport_flow_control", + "http2_initiate_write_due_to_send_settings", + "http2_initiate_write_due_to_bdp_estimator_ping", + "http2_initiate_write_due_to_flow_control_unstalled_by_setting", + "http2_initiate_write_due_to_flow_control_unstalled_by_update", + "http2_initiate_write_due_to_application_ping", + "http2_initiate_write_due_to_keepalive_ping", + "http2_initiate_write_due_to_transport_flow_control_unstalled", + "http2_initiate_write_due_to_ping_response", + "http2_initiate_write_due_to_force_rst_stream", + "http2_spurious_writes_begun", + "hpack_recv_indexed", + "hpack_recv_lithdr_incidx", + "hpack_recv_lithdr_incidx_v", + "hpack_recv_lithdr_notidx", + "hpack_recv_lithdr_notidx_v", + "hpack_recv_lithdr_nvridx", + "hpack_recv_lithdr_nvridx_v", + "hpack_recv_uncompressed", + "hpack_recv_huffman", + "hpack_recv_binary", + "hpack_recv_binary_base64", + "hpack_send_indexed", + "hpack_send_lithdr_incidx", + "hpack_send_lithdr_incidx_v", + "hpack_send_lithdr_notidx", + "hpack_send_lithdr_notidx_v", + "hpack_send_lithdr_nvridx", + "hpack_send_lithdr_nvridx_v", + "hpack_send_uncompressed", + "hpack_send_huffman", + "hpack_send_binary", + "hpack_send_binary_base64", + "combiner_locks_initiated", + "combiner_locks_scheduled_items", + "combiner_locks_scheduled_final_items", + "combiner_locks_offloaded", + "call_combiner_locks_initiated", + "call_combiner_locks_scheduled_items", + "call_combiner_set_notify_on_cancel", + "call_combiner_cancelled", + "executor_scheduled_short_items", + "executor_scheduled_long_items", + "executor_scheduled_to_self", + "executor_wakeup_initiated", + "executor_queue_drained", + "executor_push_retries", + "server_requested_calls", + "server_slowpath_requests_queued", + "cq_ev_queue_trylock_failures", + "cq_ev_queue_trylock_successes", + "cq_ev_queue_transient_pop_failures", +}; +const char* grpc_stats_counter_doc[GRPC_STATS_COUNTER_COUNT] = { + "Number of client side calls created by this process", + "Number of server side calls created by this process", + "Number of completion queues created", + "Number of client channels created", + "Number of client subchannels created", + "Number of server channels created", + "Number of polling syscalls (epoll_wait, poll, etc) made by this process", + "Number of sleeping syscalls made by this process", + "How many polling wakeups were performed by the process (only valid for " + "epoll1 right now)", + "How many times was a polling wakeup requested without an active poller " + "(only valid for epoll1 right now)", + "How many times was the same polling worker awoken repeatedly before " + "waking up (only valid for epoll1 right now)", + "How many times was an eventfd used as the wakeup vector for a polling " + "wakeup (only valid for epoll1 right now)", + "How many times was a condition variable used as the wakeup vector for a " + "polling wakeup (only valid for epoll1 right now)", + "How many times could a polling wakeup be satisfied by keeping the waking " + "thread awake? (only valid for epoll1 right now)", + "Number of epoll_ctl calls made (only valid for epollex right now)", + "Number of epoll_ctl calls skipped because the fd was cached as already " + "being added. (only valid for epollex right now)", + "Number of times histogram increments went through the slow (binary " + "search) path", + "Number of write syscalls (or equivalent - eg sendmsg) made by this " + "process", + "Number of read syscalls (or equivalent - eg recvmsg) made by this process", + "Number of times a backup poller has been created (this can be expensive)", + "Number of polls performed on the backup poller", + "Number of batches received by HTTP2 transport", + "Number of cancelations received by HTTP2 transport", + "Number of batches containing send initial metadata", + "Number of batches containing send message", + "Number of batches containing send trailing metadata", + "Number of batches containing receive initial metadata", + "Number of batches containing receive message", + "Number of batches containing receive trailing metadata", + "Number of settings frames sent", + "Number of HTTP2 pings sent by process", + "Number of HTTP2 writes initiated", + "Number of HTTP2 writes offloaded to the executor from application threads", + "Number of HTTP2 writes that finished seeing more data needed to be " + "written", + "Number of HTTP2 writes that were made knowing there was still more data " + "to be written (we cap maximum write size to syscall_write)", + "Number of HTTP2 writes initiated due to 'initial_write'", + "Number of HTTP2 writes initiated due to 'start_new_stream'", + "Number of HTTP2 writes initiated due to 'send_message'", + "Number of HTTP2 writes initiated due to 'send_initial_metadata'", + "Number of HTTP2 writes initiated due to 'send_trailing_metadata'", + "Number of HTTP2 writes initiated due to 'retry_send_ping'", + "Number of HTTP2 writes initiated due to 'continue_pings'", + "Number of HTTP2 writes initiated due to 'goaway_sent'", + "Number of HTTP2 writes initiated due to 'rst_stream'", + "Number of HTTP2 writes initiated due to 'close_from_api'", + "Number of HTTP2 writes initiated due to 'stream_flow_control'", + "Number of HTTP2 writes initiated due to 'transport_flow_control'", + "Number of HTTP2 writes initiated due to 'send_settings'", + "Number of HTTP2 writes initiated due to 'bdp_estimator_ping'", + "Number of HTTP2 writes initiated due to " + "'flow_control_unstalled_by_setting'", + "Number of HTTP2 writes initiated due to " + "'flow_control_unstalled_by_update'", + "Number of HTTP2 writes initiated due to 'application_ping'", + "Number of HTTP2 writes initiated due to 'keepalive_ping'", + "Number of HTTP2 writes initiated due to " + "'transport_flow_control_unstalled'", + "Number of HTTP2 writes initiated due to 'ping_response'", + "Number of HTTP2 writes initiated due to 'force_rst_stream'", + "Number of HTTP2 writes initiated with nothing to write", + "Number of HPACK indexed fields received", + "Number of HPACK literal headers received with incremental indexing", + "Number of HPACK literal headers received with incremental indexing and " + "literal keys", + "Number of HPACK literal headers received with no indexing", + "Number of HPACK literal headers received with no indexing and literal " + "keys", + "Number of HPACK literal headers received with never-indexing", + "Number of HPACK literal headers received with never-indexing and literal " + "keys", + "Number of uncompressed strings received in metadata", + "Number of huffman encoded strings received in metadata", + "Number of binary strings received in metadata", + "Number of binary strings received encoded in base64 in metadata", + "Number of HPACK indexed fields sent", + "Number of HPACK literal headers sent with incremental indexing", + "Number of HPACK literal headers sent with incremental indexing and " + "literal keys", + "Number of HPACK literal headers sent with no indexing", + "Number of HPACK literal headers sent with no indexing and literal keys", + "Number of HPACK literal headers sent with never-indexing", + "Number of HPACK literal headers sent with never-indexing and literal keys", + "Number of uncompressed strings sent in metadata", + "Number of huffman encoded strings sent in metadata", + "Number of binary strings received in metadata", + "Number of binary strings received encoded in base64 in metadata", + "Number of combiner lock entries by process (first items queued to a " + "combiner)", + "Number of items scheduled against combiner locks", + "Number of final items scheduled against combiner locks", + "Number of combiner locks offloaded to different threads", + "Number of call combiner lock entries by process (first items queued to a " + "call combiner)", + "Number of items scheduled against call combiner locks", + "Number of times a cancellation callback was set on a call combiner", + "Number of times a call combiner was cancelled", + "Number of finite runtime closures scheduled against the executor (gRPC " + "thread pool)", + "Number of potentially infinite runtime closures scheduled against the " + "executor (gRPC thread pool)", + "Number of closures scheduled by the executor to the executor", + "Number of thread wakeups initiated within the executor", + "Number of times an executor queue was drained", + "Number of times we raced and were forced to retry pushing a closure to " + "the executor", + "How many calls were requested (not necessarily received) by the server", + "How many times was the server slow path taken (indicates too few " + "outstanding requests)", + "Number of lock (trylock) acquisition failures on completion queue event " + "queue. High value here indicates high contention on completion queues", + "Number of lock (trylock) acquisition successes on completion queue event " + "queue.", + "Number of times NULL was popped out of completion queue's event queue " + "even though the event queue was not empty", +}; +const char* grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT] = { + "call_initial_size", + "poll_events_returned", + "tcp_write_size", + "tcp_write_iov_size", + "tcp_read_size", + "tcp_read_offer", + "tcp_read_offer_iov_size", + "http2_send_message_size", + "http2_send_initial_metadata_per_write", + "http2_send_message_per_write", + "http2_send_trailing_metadata_per_write", + "http2_send_flowctl_per_write", + "server_cqs_checked", +}; +const char* grpc_stats_histogram_doc[GRPC_STATS_HISTOGRAM_COUNT] = { + "Initial size of the grpc_call arena created at call start", + "How many events are called for each syscall_poll", + "Number of bytes offered to each syscall_write", + "Number of byte segments offered to each syscall_write", + "Number of bytes received by each syscall_read", + "Number of bytes offered to each syscall_read", + "Number of byte segments offered to each syscall_read", + "Size of messages received by HTTP2 transport", + "Number of streams initiated written per TCP write", + "Number of streams whose payload was written per TCP write", + "Number of streams terminated per TCP write", + "Number of flow control updates written per TCP write", + "How many completion queues were checked looking for a CQ that had " + "requested the incoming call", +}; +const int grpc_stats_table_0[65] = { + 0, 1, 2, 3, 4, 5, 7, 9, 11, 14, + 17, 21, 26, 32, 39, 47, 57, 68, 82, 98, + 117, 140, 167, 199, 238, 284, 339, 404, 482, 575, + 685, 816, 972, 1158, 1380, 1644, 1959, 2334, 2780, 3312, + 3945, 4699, 5597, 6667, 7941, 9459, 11267, 13420, 15984, 19038, + 22676, 27009, 32169, 38315, 45635, 54353, 64737, 77104, 91834, 109378, + 130273, 155159, 184799, 220100, 262144}; +const uint8_t grpc_stats_table_1[124] = { + 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, + 7, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, + 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 24, + 24, 25, 25, 26, 26, 26, 27, 27, 28, 29, 29, 30, 30, 30, 31, 31, 32, 33, + 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, + 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, + 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58}; +const int grpc_stats_table_2[129] = { + 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, 26, 27, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, + 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 94, 98, 102, 106, 110, + 114, 118, 122, 126, 131, 136, 141, 146, 151, 156, 162, 168, 174, 180, 186, + 192, 199, 206, 213, 220, 228, 236, 244, 252, 260, 269, 278, 287, 297, 307, + 317, 327, 338, 349, 360, 372, 384, 396, 409, 422, 436, 450, 464, 479, 494, + 510, 526, 543, 560, 578, 596, 615, 634, 654, 674, 695, 717, 739, 762, 785, + 809, 834, 859, 885, 912, 939, 967, 996, 1024}; +const uint8_t grpc_stats_table_3[166] = { + 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, + 17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 26, 27, 27, 28, + 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, 38, 39, + 40, 40, 41, 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 56, 57, 58, 59, 59, 60, 61, 62, 63, 63, 64, + 65, 65, 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 72, 73, 73, 74, 75, 75, 76, + 76, 77, 78, 79, 79, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 88, 89, 90, 90, + 91, 92, 92, 93, 94, 94, 95, 95, 96, 97, 97, 98, 98, 99}; +const int grpc_stats_table_4[65] = { + 0, 1, 2, 3, 4, 6, 8, 11, + 15, 20, 26, 34, 44, 57, 73, 94, + 121, 155, 199, 255, 327, 419, 537, 688, + 881, 1128, 1444, 1848, 2365, 3026, 3872, 4954, + 6338, 8108, 10373, 13270, 16976, 21717, 27782, 35541, + 45467, 58165, 74409, 95189, 121772, 155778, 199281, 254933, + 326126, 417200, 533707, 682750, 873414, 1117323, 1429345, 1828502, + 2339127, 2992348, 3827987, 4896985, 6264509, 8013925, 10251880, 13114801, + 16777216}; +const uint8_t grpc_stats_table_5[87] = { + 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, + 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23, + 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, + 36, 37, 38, 39, 39, 40, 41, 41, 42, 43, 44, 44, 45, 45, 46, 47, 48, 48, + 49, 50, 51, 51, 52, 53, 53, 54, 55, 56, 56, 57, 58, 58, 59}; +const int grpc_stats_table_6[65] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 14, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, + 51, 56, 61, 66, 72, 78, 85, 92, 100, 109, 118, 128, 139, + 151, 164, 178, 193, 209, 226, 244, 264, 285, 308, 333, 359, 387, + 418, 451, 486, 524, 565, 609, 656, 707, 762, 821, 884, 952, 1024}; +const uint8_t grpc_stats_table_7[102] = { + 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 6, 7, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 12, 13, 13, 14, 14, + 14, 15, 15, 16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 24, 25, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, + 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, + 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51, 51}; +const int grpc_stats_table_8[9] = {0, 1, 2, 4, 7, 13, 23, 39, 64}; +const uint8_t grpc_stats_table_9[9] = {0, 0, 1, 2, 2, 3, 4, 4, 5}; +void grpc_stats_inc_call_initial_size(int value) { + value = GPR_CLAMP(value, 0, 262144); + if (value < 6) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4651092515166879744ull) { + int bucket = + grpc_stats_table_1[((_val.uint - 4618441417868443648ull) >> 49)] + 6; + _bkt.dbl = grpc_stats_table_0[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_0, 64)); +} +void grpc_stats_inc_poll_events_returned(int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 29) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4642789003353915392ull) { + int bucket = + grpc_stats_table_3[((_val.uint - 4628855992006737920ull) >> 47)] + 29; + _bkt.dbl = grpc_stats_table_2[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_2, 128)); +} +void grpc_stats_inc_tcp_write_size(int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_tcp_write_iov_size(int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_tcp_read_size(int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_tcp_read_offer(int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_tcp_read_offer_iov_size(int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_http2_send_message_size(int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_http2_send_initial_metadata_per_write(int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_http2_send_message_per_write(int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_http2_send_trailing_metadata_per_write(int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_http2_send_flowctl_per_write(int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_server_cqs_checked(int value) { + value = GPR_CLAMP(value, 0, 64); + if (value < 3) { + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4625196817309499392ull) { + int bucket = + grpc_stats_table_9[((_val.uint - 4613937818241073152ull) >> 51)] + 3; + _bkt.dbl = grpc_stats_table_8[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM(GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, + grpc_stats_histo_find_bucket_slow(value, grpc_stats_table_8, 8)); +} +const int grpc_stats_histo_buckets[13] = {64, 128, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 8}; +const int grpc_stats_histo_start[13] = {0, 64, 192, 256, 320, 384, 448, + 512, 576, 640, 704, 768, 832}; +const int* const grpc_stats_histo_bucket_boundaries[13] = { + grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_4, + grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_4, + grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_6, + grpc_stats_table_6, grpc_stats_table_6, grpc_stats_table_6, + grpc_stats_table_8}; +void (*const grpc_stats_inc_histogram[13])(int x) = { + grpc_stats_inc_call_initial_size, + grpc_stats_inc_poll_events_returned, + grpc_stats_inc_tcp_write_size, + grpc_stats_inc_tcp_write_iov_size, + grpc_stats_inc_tcp_read_size, + grpc_stats_inc_tcp_read_offer, + grpc_stats_inc_tcp_read_offer_iov_size, + grpc_stats_inc_http2_send_message_size, + grpc_stats_inc_http2_send_initial_metadata_per_write, + grpc_stats_inc_http2_send_message_per_write, + grpc_stats_inc_http2_send_trailing_metadata_per_write, + grpc_stats_inc_http2_send_flowctl_per_write, + grpc_stats_inc_server_cqs_checked}; diff --git a/third_party/grpc/src/core/lib/debug/stats_data.h b/third_party/grpc/src/core/lib/debug/stats_data.h new file mode 100644 index 00000000000000..1f3861f4941d7d --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/stats_data.h @@ -0,0 +1,555 @@ +/* + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Automatically generated by tools/codegen/core/gen_stats_data.py + */ + +#ifndef GRPC_CORE_LIB_DEBUG_STATS_DATA_H +#define GRPC_CORE_LIB_DEBUG_STATS_DATA_H + +#include + +#include +#include "src/core/lib/iomgr/exec_ctx.h" + +typedef enum { + GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED, + GRPC_STATS_COUNTER_SERVER_CALLS_CREATED, + GRPC_STATS_COUNTER_CQS_CREATED, + GRPC_STATS_COUNTER_CLIENT_CHANNELS_CREATED, + GRPC_STATS_COUNTER_CLIENT_SUBCHANNELS_CREATED, + GRPC_STATS_COUNTER_SERVER_CHANNELS_CREATED, + GRPC_STATS_COUNTER_SYSCALL_POLL, + GRPC_STATS_COUNTER_SYSCALL_WAIT, + GRPC_STATS_COUNTER_POLLSET_KICK, + GRPC_STATS_COUNTER_POLLSET_KICKED_WITHOUT_POLLER, + GRPC_STATS_COUNTER_POLLSET_KICKED_AGAIN, + GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_FD, + GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_CV, + GRPC_STATS_COUNTER_POLLSET_KICK_OWN_THREAD, + GRPC_STATS_COUNTER_SYSCALL_EPOLL_CTL, + GRPC_STATS_COUNTER_POLLSET_FD_CACHE_HITS, + GRPC_STATS_COUNTER_HISTOGRAM_SLOW_LOOKUPS, + GRPC_STATS_COUNTER_SYSCALL_WRITE, + GRPC_STATS_COUNTER_SYSCALL_READ, + GRPC_STATS_COUNTER_TCP_BACKUP_POLLERS_CREATED, + GRPC_STATS_COUNTER_TCP_BACKUP_POLLER_POLLS, + GRPC_STATS_COUNTER_HTTP2_OP_BATCHES, + GRPC_STATS_COUNTER_HTTP2_OP_CANCEL, + GRPC_STATS_COUNTER_HTTP2_OP_SEND_INITIAL_METADATA, + GRPC_STATS_COUNTER_HTTP2_OP_SEND_MESSAGE, + GRPC_STATS_COUNTER_HTTP2_OP_SEND_TRAILING_METADATA, + GRPC_STATS_COUNTER_HTTP2_OP_RECV_INITIAL_METADATA, + GRPC_STATS_COUNTER_HTTP2_OP_RECV_MESSAGE, + GRPC_STATS_COUNTER_HTTP2_OP_RECV_TRAILING_METADATA, + GRPC_STATS_COUNTER_HTTP2_SETTINGS_WRITES, + GRPC_STATS_COUNTER_HTTP2_PINGS_SENT, + GRPC_STATS_COUNTER_HTTP2_WRITES_BEGUN, + GRPC_STATS_COUNTER_HTTP2_WRITES_OFFLOADED, + GRPC_STATS_COUNTER_HTTP2_WRITES_CONTINUED, + GRPC_STATS_COUNTER_HTTP2_PARTIAL_WRITES, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE, + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM, + GRPC_STATS_COUNTER_HTTP2_SPURIOUS_WRITES_BEGUN, + GRPC_STATS_COUNTER_HPACK_RECV_INDEXED, + GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX, + GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX_V, + GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX, + GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX_V, + GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX, + GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX_V, + GRPC_STATS_COUNTER_HPACK_RECV_UNCOMPRESSED, + GRPC_STATS_COUNTER_HPACK_RECV_HUFFMAN, + GRPC_STATS_COUNTER_HPACK_RECV_BINARY, + GRPC_STATS_COUNTER_HPACK_RECV_BINARY_BASE64, + GRPC_STATS_COUNTER_HPACK_SEND_INDEXED, + GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX, + GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX_V, + GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX, + GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX_V, + GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX, + GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX_V, + GRPC_STATS_COUNTER_HPACK_SEND_UNCOMPRESSED, + GRPC_STATS_COUNTER_HPACK_SEND_HUFFMAN, + GRPC_STATS_COUNTER_HPACK_SEND_BINARY, + GRPC_STATS_COUNTER_HPACK_SEND_BINARY_BASE64, + GRPC_STATS_COUNTER_COMBINER_LOCKS_INITIATED, + GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_ITEMS, + GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS, + GRPC_STATS_COUNTER_COMBINER_LOCKS_OFFLOADED, + GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_INITIATED, + GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS, + GRPC_STATS_COUNTER_CALL_COMBINER_SET_NOTIFY_ON_CANCEL, + GRPC_STATS_COUNTER_CALL_COMBINER_CANCELLED, + GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_SHORT_ITEMS, + GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_LONG_ITEMS, + GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_TO_SELF, + GRPC_STATS_COUNTER_EXECUTOR_WAKEUP_INITIATED, + GRPC_STATS_COUNTER_EXECUTOR_QUEUE_DRAINED, + GRPC_STATS_COUNTER_EXECUTOR_PUSH_RETRIES, + GRPC_STATS_COUNTER_SERVER_REQUESTED_CALLS, + GRPC_STATS_COUNTER_SERVER_SLOWPATH_REQUESTS_QUEUED, + GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_FAILURES, + GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_SUCCESSES, + GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES, + GRPC_STATS_COUNTER_COUNT +} grpc_stats_counters; +extern const char* grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT]; +extern const char* grpc_stats_counter_doc[GRPC_STATS_COUNTER_COUNT]; +typedef enum { + GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, + GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, + GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, + GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, + GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, + GRPC_STATS_HISTOGRAM_COUNT +} grpc_stats_histograms; +extern const char* grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT]; +extern const char* grpc_stats_histogram_doc[GRPC_STATS_HISTOGRAM_COUNT]; +typedef enum { + GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE_FIRST_SLOT = 0, + GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED_FIRST_SLOT = 64, + GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED_BUCKETS = 128, + GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE_FIRST_SLOT = 192, + GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE_FIRST_SLOT = 256, + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_TCP_READ_SIZE_FIRST_SLOT = 320, + GRPC_STATS_HISTOGRAM_TCP_READ_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_FIRST_SLOT = 384, + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE_FIRST_SLOT = 448, + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE_FIRST_SLOT = 512, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE_FIRST_SLOT = 576, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE_FIRST_SLOT = 640, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE_FIRST_SLOT = 704, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE_FIRST_SLOT = 768, + GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE_BUCKETS = 64, + GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED_FIRST_SLOT = 832, + GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED_BUCKETS = 8, + GRPC_STATS_HISTOGRAM_BUCKETS = 840 +} grpc_stats_histogram_constants; +#if defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) +#define GRPC_STATS_INC_CLIENT_CALLS_CREATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED) +#define GRPC_STATS_INC_SERVER_CALLS_CREATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_CALLS_CREATED) +#define GRPC_STATS_INC_CQS_CREATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQS_CREATED) +#define GRPC_STATS_INC_CLIENT_CHANNELS_CREATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CLIENT_CHANNELS_CREATED) +#define GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CLIENT_SUBCHANNELS_CREATED) +#define GRPC_STATS_INC_SERVER_CHANNELS_CREATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_CHANNELS_CREATED) +#define GRPC_STATS_INC_SYSCALL_POLL() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_POLL) +#define GRPC_STATS_INC_SYSCALL_WAIT() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_WAIT) +#define GRPC_STATS_INC_POLLSET_KICK() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK) +#define GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICKED_WITHOUT_POLLER) +#define GRPC_STATS_INC_POLLSET_KICKED_AGAIN() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICKED_AGAIN) +#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_FD) +#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK_WAKEUP_CV) +#define GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_KICK_OWN_THREAD) +#define GRPC_STATS_INC_SYSCALL_EPOLL_CTL() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_EPOLL_CTL) +#define GRPC_STATS_INC_POLLSET_FD_CACHE_HITS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_POLLSET_FD_CACHE_HITS) +#define GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HISTOGRAM_SLOW_LOOKUPS) +#define GRPC_STATS_INC_SYSCALL_WRITE() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_WRITE) +#define GRPC_STATS_INC_SYSCALL_READ() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SYSCALL_READ) +#define GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_TCP_BACKUP_POLLERS_CREATED) +#define GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_TCP_BACKUP_POLLER_POLLS) +#define GRPC_STATS_INC_HTTP2_OP_BATCHES() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_BATCHES) +#define GRPC_STATS_INC_HTTP2_OP_CANCEL() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_CANCEL) +#define GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_SEND_INITIAL_METADATA) +#define GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_SEND_MESSAGE) +#define GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_SEND_TRAILING_METADATA) +#define GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_RECV_INITIAL_METADATA) +#define GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_RECV_MESSAGE) +#define GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_OP_RECV_TRAILING_METADATA) +#define GRPC_STATS_INC_HTTP2_SETTINGS_WRITES() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_SETTINGS_WRITES) +#define GRPC_STATS_INC_HTTP2_PINGS_SENT() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_PINGS_SENT) +#define GRPC_STATS_INC_HTTP2_WRITES_BEGUN() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_WRITES_BEGUN) +#define GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_WRITES_OFFLOADED) +#define GRPC_STATS_INC_HTTP2_WRITES_CONTINUED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_WRITES_CONTINUED) +#define GRPC_STATS_INC_HTTP2_PARTIAL_WRITES() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_PARTIAL_WRITES) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE) +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM) +#define GRPC_STATS_INC_HTTP2_SPURIOUS_WRITES_BEGUN() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HTTP2_SPURIOUS_WRITES_BEGUN) +#define GRPC_STATS_INC_HPACK_RECV_INDEXED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_INDEXED) +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX) +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_INCIDX_V) +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX) +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NOTIDX_V) +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX) +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_LITHDR_NVRIDX_V) +#define GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_UNCOMPRESSED) +#define GRPC_STATS_INC_HPACK_RECV_HUFFMAN() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_HUFFMAN) +#define GRPC_STATS_INC_HPACK_RECV_BINARY() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_BINARY) +#define GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_RECV_BINARY_BASE64) +#define GRPC_STATS_INC_HPACK_SEND_INDEXED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_INDEXED) +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX) +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_INCIDX_V) +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX) +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NOTIDX_V) +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX) +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX_V() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_LITHDR_NVRIDX_V) +#define GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_UNCOMPRESSED) +#define GRPC_STATS_INC_HPACK_SEND_HUFFMAN() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_HUFFMAN) +#define GRPC_STATS_INC_HPACK_SEND_BINARY() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_BINARY) +#define GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_HPACK_SEND_BINARY_BASE64) +#define GRPC_STATS_INC_COMBINER_LOCKS_INITIATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_COMBINER_LOCKS_INITIATED) +#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_ITEMS) +#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS() \ + GRPC_STATS_INC_COUNTER( \ + GRPC_STATS_COUNTER_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS) +#define GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_COMBINER_LOCKS_OFFLOADED) +#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_INITIATED) +#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS) +#define GRPC_STATS_INC_CALL_COMBINER_SET_NOTIFY_ON_CANCEL() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_SET_NOTIFY_ON_CANCEL) +#define GRPC_STATS_INC_CALL_COMBINER_CANCELLED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CALL_COMBINER_CANCELLED) +#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_SHORT_ITEMS) +#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_LONG_ITEMS) +#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_SCHEDULED_TO_SELF) +#define GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_WAKEUP_INITIATED) +#define GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_QUEUE_DRAINED) +#define GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_EXECUTOR_PUSH_RETRIES) +#define GRPC_STATS_INC_SERVER_REQUESTED_CALLS() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_REQUESTED_CALLS) +#define GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_SERVER_SLOWPATH_REQUESTS_QUEUED) +#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_FAILURES() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_FAILURES) +#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_SUCCESSES() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRYLOCK_SUCCESSES) +#define GRPC_STATS_INC_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES() \ + GRPC_STATS_INC_COUNTER(GRPC_STATS_COUNTER_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES) +#define GRPC_STATS_INC_CALL_INITIAL_SIZE(value) \ + grpc_stats_inc_call_initial_size((int)(value)) +void grpc_stats_inc_call_initial_size(int x); +#define GRPC_STATS_INC_POLL_EVENTS_RETURNED(value) \ + grpc_stats_inc_poll_events_returned((int)(value)) +void grpc_stats_inc_poll_events_returned(int x); +#define GRPC_STATS_INC_TCP_WRITE_SIZE(value) \ + grpc_stats_inc_tcp_write_size((int)(value)) +void grpc_stats_inc_tcp_write_size(int x); +#define GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(value) \ + grpc_stats_inc_tcp_write_iov_size((int)(value)) +void grpc_stats_inc_tcp_write_iov_size(int x); +#define GRPC_STATS_INC_TCP_READ_SIZE(value) \ + grpc_stats_inc_tcp_read_size((int)(value)) +void grpc_stats_inc_tcp_read_size(int x); +#define GRPC_STATS_INC_TCP_READ_OFFER(value) \ + grpc_stats_inc_tcp_read_offer((int)(value)) +void grpc_stats_inc_tcp_read_offer(int x); +#define GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(value) \ + grpc_stats_inc_tcp_read_offer_iov_size((int)(value)) +void grpc_stats_inc_tcp_read_offer_iov_size(int x); +#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE(value) \ + grpc_stats_inc_http2_send_message_size((int)(value)) +void grpc_stats_inc_http2_send_message_size(int x); +#define GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE(value) \ + grpc_stats_inc_http2_send_initial_metadata_per_write((int)(value)) +void grpc_stats_inc_http2_send_initial_metadata_per_write(int x); +#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(value) \ + grpc_stats_inc_http2_send_message_per_write((int)(value)) +void grpc_stats_inc_http2_send_message_per_write(int x); +#define GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE(value) \ + grpc_stats_inc_http2_send_trailing_metadata_per_write((int)(value)) +void grpc_stats_inc_http2_send_trailing_metadata_per_write(int x); +#define GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(value) \ + grpc_stats_inc_http2_send_flowctl_per_write((int)(value)) +void grpc_stats_inc_http2_send_flowctl_per_write(int x); +#define GRPC_STATS_INC_SERVER_CQS_CHECKED(value) \ + grpc_stats_inc_server_cqs_checked((int)(value)) +void grpc_stats_inc_server_cqs_checked(int x); +#else +#define GRPC_STATS_INC_CLIENT_CALLS_CREATED() +#define GRPC_STATS_INC_SERVER_CALLS_CREATED() +#define GRPC_STATS_INC_CQS_CREATED() +#define GRPC_STATS_INC_CLIENT_CHANNELS_CREATED() +#define GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED() +#define GRPC_STATS_INC_SERVER_CHANNELS_CREATED() +#define GRPC_STATS_INC_SYSCALL_POLL() +#define GRPC_STATS_INC_SYSCALL_WAIT() +#define GRPC_STATS_INC_POLLSET_KICK() +#define GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER() +#define GRPC_STATS_INC_POLLSET_KICKED_AGAIN() +#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD() +#define GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV() +#define GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD() +#define GRPC_STATS_INC_SYSCALL_EPOLL_CTL() +#define GRPC_STATS_INC_POLLSET_FD_CACHE_HITS() +#define GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS() +#define GRPC_STATS_INC_SYSCALL_WRITE() +#define GRPC_STATS_INC_SYSCALL_READ() +#define GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED() +#define GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS() +#define GRPC_STATS_INC_HTTP2_OP_BATCHES() +#define GRPC_STATS_INC_HTTP2_OP_CANCEL() +#define GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA() +#define GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE() +#define GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA() +#define GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA() +#define GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE() +#define GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA() +#define GRPC_STATS_INC_HTTP2_SETTINGS_WRITES() +#define GRPC_STATS_INC_HTTP2_PINGS_SENT() +#define GRPC_STATS_INC_HTTP2_WRITES_BEGUN() +#define GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED() +#define GRPC_STATS_INC_HTTP2_WRITES_CONTINUED() +#define GRPC_STATS_INC_HTTP2_PARTIAL_WRITES() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE() +#define GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM() +#define GRPC_STATS_INC_HTTP2_SPURIOUS_WRITES_BEGUN() +#define GRPC_STATS_INC_HPACK_RECV_INDEXED() +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX() +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V() +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX() +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V() +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX() +#define GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V() +#define GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED() +#define GRPC_STATS_INC_HPACK_RECV_HUFFMAN() +#define GRPC_STATS_INC_HPACK_RECV_BINARY() +#define GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64() +#define GRPC_STATS_INC_HPACK_SEND_INDEXED() +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX() +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V() +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX() +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V() +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX() +#define GRPC_STATS_INC_HPACK_SEND_LITHDR_NVRIDX_V() +#define GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED() +#define GRPC_STATS_INC_HPACK_SEND_HUFFMAN() +#define GRPC_STATS_INC_HPACK_SEND_BINARY() +#define GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64() +#define GRPC_STATS_INC_COMBINER_LOCKS_INITIATED() +#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS() +#define GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS() +#define GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED() +#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED() +#define GRPC_STATS_INC_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS() +#define GRPC_STATS_INC_CALL_COMBINER_SET_NOTIFY_ON_CANCEL() +#define GRPC_STATS_INC_CALL_COMBINER_CANCELLED() +#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS() +#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS() +#define GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF() +#define GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED() +#define GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED() +#define GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES() +#define GRPC_STATS_INC_SERVER_REQUESTED_CALLS() +#define GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED() +#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_FAILURES() +#define GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_SUCCESSES() +#define GRPC_STATS_INC_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES() +#define GRPC_STATS_INC_CALL_INITIAL_SIZE(value) +#define GRPC_STATS_INC_POLL_EVENTS_RETURNED(value) +#define GRPC_STATS_INC_TCP_WRITE_SIZE(value) +#define GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(value) +#define GRPC_STATS_INC_TCP_READ_SIZE(value) +#define GRPC_STATS_INC_TCP_READ_OFFER(value) +#define GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(value) +#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE(value) +#define GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE(value) +#define GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(value) +#define GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE(value) +#define GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(value) +#define GRPC_STATS_INC_SERVER_CQS_CHECKED(value) +#endif /* defined(GRPC_COLLECT_STATS) || !defined(NDEBUG) */ +extern const int grpc_stats_histo_buckets[13]; +extern const int grpc_stats_histo_start[13]; +extern const int* const grpc_stats_histo_bucket_boundaries[13]; +extern void (*const grpc_stats_inc_histogram[13])(int x); + +#endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */ diff --git a/third_party/grpc/src/core/lib/debug/stats_data.yaml b/third_party/grpc/src/core/lib/debug/stats_data.yaml new file mode 100644 index 00000000000000..775b09df7402e6 --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/stats_data.yaml @@ -0,0 +1,300 @@ +# Copyright 2017 gRPC authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Stats data declaration +# use tools / codegen / core / gen_stats_data.py to turn this into stats_data.h + +# overall +- counter: client_calls_created + doc: Number of client side calls created by this process +- counter: server_calls_created + doc: Number of server side calls created by this process +- histogram: call_initial_size + max: 262144 + buckets: 64 + doc: Initial size of the grpc_call arena created at call start +- counter: cqs_created + doc: Number of completion queues created +- counter: client_channels_created + doc: Number of client channels created +- counter: client_subchannels_created + doc: Number of client subchannels created +- counter: server_channels_created + doc: Number of server channels created +# polling +- counter: syscall_poll + doc: Number of polling syscalls (epoll_wait, poll, etc) made by this process +- counter: syscall_wait + doc: Number of sleeping syscalls made by this process +- histogram: poll_events_returned + max: 1024 + buckets: 128 + doc: How many events are called for each syscall_poll +- counter: pollset_kick + doc: How many polling wakeups were performed by the process + (only valid for epoll1 right now) +- counter: pollset_kicked_without_poller + doc: How many times was a polling wakeup requested without an active poller + (only valid for epoll1 right now) +- counter: pollset_kicked_again + doc: How many times was the same polling worker awoken repeatedly before + waking up + (only valid for epoll1 right now) +- counter: pollset_kick_wakeup_fd + doc: How many times was an eventfd used as the wakeup vector for a polling + wakeup + (only valid for epoll1 right now) +- counter: pollset_kick_wakeup_cv + doc: How many times was a condition variable used as the wakeup vector for a + polling wakeup + (only valid for epoll1 right now) +- counter: pollset_kick_own_thread + doc: How many times could a polling wakeup be satisfied by keeping the waking + thread awake? + (only valid for epoll1 right now) +# polling +- counter: syscall_epoll_ctl + doc: Number of epoll_ctl calls made (only valid for epollex right now) +- counter: pollset_fd_cache_hits + doc: Number of epoll_ctl calls skipped because the fd was cached as + already being added. (only valid for epollex right now) +# stats system +- counter: histogram_slow_lookups + doc: Number of times histogram increments went through the slow + (binary search) path +# tcp +- counter: syscall_write + doc: Number of write syscalls (or equivalent - eg sendmsg) made by this process +- counter: syscall_read + doc: Number of read syscalls (or equivalent - eg recvmsg) made by this process +- histogram: tcp_write_size + max: 16777216 # 16 meg max write tracked + buckets: 64 + doc: Number of bytes offered to each syscall_write +- histogram: tcp_write_iov_size + max: 1024 + buckets: 64 + doc: Number of byte segments offered to each syscall_write +- histogram: tcp_read_size + max: 16777216 + buckets: 64 + doc: Number of bytes received by each syscall_read +- histogram: tcp_read_offer + max: 16777216 + buckets: 64 + doc: Number of bytes offered to each syscall_read +- histogram: tcp_read_offer_iov_size + max: 1024 + buckets: 64 + doc: Number of byte segments offered to each syscall_read +- counter: tcp_backup_pollers_created + doc: Number of times a backup poller has been created (this can be expensive) +- counter: tcp_backup_poller_polls + doc: Number of polls performed on the backup poller +# chttp2 +- counter: http2_op_batches + doc: Number of batches received by HTTP2 transport +- counter: http2_op_cancel + doc: Number of cancelations received by HTTP2 transport +- counter: http2_op_send_initial_metadata + doc: Number of batches containing send initial metadata +- counter: http2_op_send_message + doc: Number of batches containing send message +- counter: http2_op_send_trailing_metadata + doc: Number of batches containing send trailing metadata +- counter: http2_op_recv_initial_metadata + doc: Number of batches containing receive initial metadata +- counter: http2_op_recv_message + doc: Number of batches containing receive message +- counter: http2_op_recv_trailing_metadata + doc: Number of batches containing receive trailing metadata +- histogram: http2_send_message_size + max: 16777216 + buckets: 64 + doc: Size of messages received by HTTP2 transport +- histogram: http2_send_initial_metadata_per_write + max: 1024 + buckets: 64 + doc: Number of streams initiated written per TCP write +- histogram: http2_send_message_per_write + max: 1024 + buckets: 64 + doc: Number of streams whose payload was written per TCP write +- histogram: http2_send_trailing_metadata_per_write + max: 1024 + buckets: 64 + doc: Number of streams terminated per TCP write +- histogram: http2_send_flowctl_per_write + max: 1024 + buckets: 64 + doc: Number of flow control updates written per TCP write +- counter: http2_settings_writes + doc: Number of settings frames sent +- counter: http2_pings_sent + doc: Number of HTTP2 pings sent by process +- counter: http2_writes_begun + doc: Number of HTTP2 writes initiated +- counter: http2_writes_offloaded + doc: Number of HTTP2 writes offloaded to the executor from application threads +- counter: http2_writes_continued + doc: Number of HTTP2 writes that finished seeing more data needed to be + written +- counter: http2_partial_writes + doc: Number of HTTP2 writes that were made knowing there was still more data + to be written (we cap maximum write size to syscall_write) +- counter: http2_initiate_write_due_to_initial_write + doc: Number of HTTP2 writes initiated due to 'initial_write' +- counter: http2_initiate_write_due_to_start_new_stream + doc: Number of HTTP2 writes initiated due to 'start_new_stream' +- counter: http2_initiate_write_due_to_send_message + doc: Number of HTTP2 writes initiated due to 'send_message' +- counter: http2_initiate_write_due_to_send_initial_metadata + doc: Number of HTTP2 writes initiated due to 'send_initial_metadata' +- counter: http2_initiate_write_due_to_send_trailing_metadata + doc: Number of HTTP2 writes initiated due to 'send_trailing_metadata' +- counter: http2_initiate_write_due_to_retry_send_ping + doc: Number of HTTP2 writes initiated due to 'retry_send_ping' +- counter: http2_initiate_write_due_to_continue_pings + doc: Number of HTTP2 writes initiated due to 'continue_pings' +- counter: http2_initiate_write_due_to_goaway_sent + doc: Number of HTTP2 writes initiated due to 'goaway_sent' +- counter: http2_initiate_write_due_to_rst_stream + doc: Number of HTTP2 writes initiated due to 'rst_stream' +- counter: http2_initiate_write_due_to_close_from_api + doc: Number of HTTP2 writes initiated due to 'close_from_api' +- counter: http2_initiate_write_due_to_stream_flow_control + doc: Number of HTTP2 writes initiated due to 'stream_flow_control' +- counter: http2_initiate_write_due_to_transport_flow_control + doc: Number of HTTP2 writes initiated due to 'transport_flow_control' +- counter: http2_initiate_write_due_to_send_settings + doc: Number of HTTP2 writes initiated due to 'send_settings' +- counter: http2_initiate_write_due_to_bdp_estimator_ping + doc: Number of HTTP2 writes initiated due to 'bdp_estimator_ping' +- counter: http2_initiate_write_due_to_flow_control_unstalled_by_setting + doc: Number of HTTP2 writes initiated due to 'flow_control_unstalled_by_setting' +- counter: http2_initiate_write_due_to_flow_control_unstalled_by_update + doc: Number of HTTP2 writes initiated due to 'flow_control_unstalled_by_update' +- counter: http2_initiate_write_due_to_application_ping + doc: Number of HTTP2 writes initiated due to 'application_ping' +- counter: http2_initiate_write_due_to_keepalive_ping + doc: Number of HTTP2 writes initiated due to 'keepalive_ping' +- counter: http2_initiate_write_due_to_transport_flow_control_unstalled + doc: Number of HTTP2 writes initiated due to 'transport_flow_control_unstalled' +- counter: http2_initiate_write_due_to_ping_response + doc: Number of HTTP2 writes initiated due to 'ping_response' +- counter: http2_initiate_write_due_to_force_rst_stream + doc: Number of HTTP2 writes initiated due to 'force_rst_stream' +- counter: http2_spurious_writes_begun + doc: Number of HTTP2 writes initiated with nothing to write +- counter: hpack_recv_indexed + doc: Number of HPACK indexed fields received +- counter: hpack_recv_lithdr_incidx + doc: Number of HPACK literal headers received with incremental indexing +- counter: hpack_recv_lithdr_incidx_v + doc: Number of HPACK literal headers received with incremental indexing and literal keys +- counter: hpack_recv_lithdr_notidx + doc: Number of HPACK literal headers received with no indexing +- counter: hpack_recv_lithdr_notidx_v + doc: Number of HPACK literal headers received with no indexing and literal keys +- counter: hpack_recv_lithdr_nvridx + doc: Number of HPACK literal headers received with never-indexing +- counter: hpack_recv_lithdr_nvridx_v + doc: Number of HPACK literal headers received with never-indexing and literal keys +- counter: hpack_recv_uncompressed + doc: Number of uncompressed strings received in metadata +- counter: hpack_recv_huffman + doc: Number of huffman encoded strings received in metadata +- counter: hpack_recv_binary + doc: Number of binary strings received in metadata +- counter: hpack_recv_binary_base64 + doc: Number of binary strings received encoded in base64 in metadata +- counter: hpack_send_indexed + doc: Number of HPACK indexed fields sent +- counter: hpack_send_lithdr_incidx + doc: Number of HPACK literal headers sent with incremental indexing +- counter: hpack_send_lithdr_incidx_v + doc: Number of HPACK literal headers sent with incremental indexing and literal keys +- counter: hpack_send_lithdr_notidx + doc: Number of HPACK literal headers sent with no indexing +- counter: hpack_send_lithdr_notidx_v + doc: Number of HPACK literal headers sent with no indexing and literal keys +- counter: hpack_send_lithdr_nvridx + doc: Number of HPACK literal headers sent with never-indexing +- counter: hpack_send_lithdr_nvridx_v + doc: Number of HPACK literal headers sent with never-indexing and literal keys +- counter: hpack_send_uncompressed + doc: Number of uncompressed strings sent in metadata +- counter: hpack_send_huffman + doc: Number of huffman encoded strings sent in metadata +- counter: hpack_send_binary + doc: Number of binary strings received in metadata +- counter: hpack_send_binary_base64 + doc: Number of binary strings received encoded in base64 in metadata +# combiner locks +- counter: combiner_locks_initiated + doc: Number of combiner lock entries by process + (first items queued to a combiner) +- counter: combiner_locks_scheduled_items + doc: Number of items scheduled against combiner locks +- counter: combiner_locks_scheduled_final_items + doc: Number of final items scheduled against combiner locks +- counter: combiner_locks_offloaded + doc: Number of combiner locks offloaded to different threads +# call combiner locks +- counter: call_combiner_locks_initiated + doc: Number of call combiner lock entries by process + (first items queued to a call combiner) +- counter: call_combiner_locks_scheduled_items + doc: Number of items scheduled against call combiner locks +- counter: call_combiner_set_notify_on_cancel + doc: Number of times a cancellation callback was set on a call combiner +- counter: call_combiner_cancelled + doc: Number of times a call combiner was cancelled +# executor +- counter: executor_scheduled_short_items + doc: Number of finite runtime closures scheduled against the executor + (gRPC thread pool) +- counter: executor_scheduled_long_items + doc: Number of potentially infinite runtime closures scheduled against the + executor (gRPC thread pool) +- counter: executor_scheduled_to_self + doc: Number of closures scheduled by the executor to the executor +- counter: executor_wakeup_initiated + doc: Number of thread wakeups initiated within the executor +- counter: executor_queue_drained + doc: Number of times an executor queue was drained +- counter: executor_push_retries + doc: Number of times we raced and were forced to retry pushing a closure to + the executor +# server +- counter: server_requested_calls + doc: How many calls were requested (not necessarily received) by the server +- histogram: server_cqs_checked + buckets: 8 + max: 64 + doc: How many completion queues were checked looking for a CQ that had + requested the incoming call +- counter: server_slowpath_requests_queued + doc: How many times was the server slow path taken (indicates too few + outstanding requests) +# cq +- counter: cq_ev_queue_trylock_failures + doc: Number of lock (trylock) acquisition failures on completion queue event + queue. High value here indicates high contention on completion queues +- counter: cq_ev_queue_trylock_successes + doc: Number of lock (trylock) acquisition successes on completion queue event + queue. +- counter: cq_ev_queue_transient_pop_failures + doc: Number of times NULL was popped out of completion queue's event queue + even though the event queue was not empty diff --git a/third_party/grpc/src/core/lib/debug/stats_data_bq_schema.sql b/third_party/grpc/src/core/lib/debug/stats_data_bq_schema.sql new file mode 100644 index 00000000000000..7d1ab1dae9b7ae --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/stats_data_bq_schema.sql @@ -0,0 +1,98 @@ +client_calls_created_per_iteration:FLOAT, +server_calls_created_per_iteration:FLOAT, +cqs_created_per_iteration:FLOAT, +client_channels_created_per_iteration:FLOAT, +client_subchannels_created_per_iteration:FLOAT, +server_channels_created_per_iteration:FLOAT, +syscall_poll_per_iteration:FLOAT, +syscall_wait_per_iteration:FLOAT, +pollset_kick_per_iteration:FLOAT, +pollset_kicked_without_poller_per_iteration:FLOAT, +pollset_kicked_again_per_iteration:FLOAT, +pollset_kick_wakeup_fd_per_iteration:FLOAT, +pollset_kick_wakeup_cv_per_iteration:FLOAT, +pollset_kick_own_thread_per_iteration:FLOAT, +syscall_epoll_ctl_per_iteration:FLOAT, +pollset_fd_cache_hits_per_iteration:FLOAT, +histogram_slow_lookups_per_iteration:FLOAT, +syscall_write_per_iteration:FLOAT, +syscall_read_per_iteration:FLOAT, +tcp_backup_pollers_created_per_iteration:FLOAT, +tcp_backup_poller_polls_per_iteration:FLOAT, +http2_op_batches_per_iteration:FLOAT, +http2_op_cancel_per_iteration:FLOAT, +http2_op_send_initial_metadata_per_iteration:FLOAT, +http2_op_send_message_per_iteration:FLOAT, +http2_op_send_trailing_metadata_per_iteration:FLOAT, +http2_op_recv_initial_metadata_per_iteration:FLOAT, +http2_op_recv_message_per_iteration:FLOAT, +http2_op_recv_trailing_metadata_per_iteration:FLOAT, +http2_settings_writes_per_iteration:FLOAT, +http2_pings_sent_per_iteration:FLOAT, +http2_writes_begun_per_iteration:FLOAT, +http2_writes_offloaded_per_iteration:FLOAT, +http2_writes_continued_per_iteration:FLOAT, +http2_partial_writes_per_iteration:FLOAT, +http2_initiate_write_due_to_initial_write_per_iteration:FLOAT, +http2_initiate_write_due_to_start_new_stream_per_iteration:FLOAT, +http2_initiate_write_due_to_send_message_per_iteration:FLOAT, +http2_initiate_write_due_to_send_initial_metadata_per_iteration:FLOAT, +http2_initiate_write_due_to_send_trailing_metadata_per_iteration:FLOAT, +http2_initiate_write_due_to_retry_send_ping_per_iteration:FLOAT, +http2_initiate_write_due_to_continue_pings_per_iteration:FLOAT, +http2_initiate_write_due_to_goaway_sent_per_iteration:FLOAT, +http2_initiate_write_due_to_rst_stream_per_iteration:FLOAT, +http2_initiate_write_due_to_close_from_api_per_iteration:FLOAT, +http2_initiate_write_due_to_stream_flow_control_per_iteration:FLOAT, +http2_initiate_write_due_to_transport_flow_control_per_iteration:FLOAT, +http2_initiate_write_due_to_send_settings_per_iteration:FLOAT, +http2_initiate_write_due_to_bdp_estimator_ping_per_iteration:FLOAT, +http2_initiate_write_due_to_flow_control_unstalled_by_setting_per_iteration:FLOAT, +http2_initiate_write_due_to_flow_control_unstalled_by_update_per_iteration:FLOAT, +http2_initiate_write_due_to_application_ping_per_iteration:FLOAT, +http2_initiate_write_due_to_keepalive_ping_per_iteration:FLOAT, +http2_initiate_write_due_to_transport_flow_control_unstalled_per_iteration:FLOAT, +http2_initiate_write_due_to_ping_response_per_iteration:FLOAT, +http2_initiate_write_due_to_force_rst_stream_per_iteration:FLOAT, +http2_spurious_writes_begun_per_iteration:FLOAT, +hpack_recv_indexed_per_iteration:FLOAT, +hpack_recv_lithdr_incidx_per_iteration:FLOAT, +hpack_recv_lithdr_incidx_v_per_iteration:FLOAT, +hpack_recv_lithdr_notidx_per_iteration:FLOAT, +hpack_recv_lithdr_notidx_v_per_iteration:FLOAT, +hpack_recv_lithdr_nvridx_per_iteration:FLOAT, +hpack_recv_lithdr_nvridx_v_per_iteration:FLOAT, +hpack_recv_uncompressed_per_iteration:FLOAT, +hpack_recv_huffman_per_iteration:FLOAT, +hpack_recv_binary_per_iteration:FLOAT, +hpack_recv_binary_base64_per_iteration:FLOAT, +hpack_send_indexed_per_iteration:FLOAT, +hpack_send_lithdr_incidx_per_iteration:FLOAT, +hpack_send_lithdr_incidx_v_per_iteration:FLOAT, +hpack_send_lithdr_notidx_per_iteration:FLOAT, +hpack_send_lithdr_notidx_v_per_iteration:FLOAT, +hpack_send_lithdr_nvridx_per_iteration:FLOAT, +hpack_send_lithdr_nvridx_v_per_iteration:FLOAT, +hpack_send_uncompressed_per_iteration:FLOAT, +hpack_send_huffman_per_iteration:FLOAT, +hpack_send_binary_per_iteration:FLOAT, +hpack_send_binary_base64_per_iteration:FLOAT, +combiner_locks_initiated_per_iteration:FLOAT, +combiner_locks_scheduled_items_per_iteration:FLOAT, +combiner_locks_scheduled_final_items_per_iteration:FLOAT, +combiner_locks_offloaded_per_iteration:FLOAT, +call_combiner_locks_initiated_per_iteration:FLOAT, +call_combiner_locks_scheduled_items_per_iteration:FLOAT, +call_combiner_set_notify_on_cancel_per_iteration:FLOAT, +call_combiner_cancelled_per_iteration:FLOAT, +executor_scheduled_short_items_per_iteration:FLOAT, +executor_scheduled_long_items_per_iteration:FLOAT, +executor_scheduled_to_self_per_iteration:FLOAT, +executor_wakeup_initiated_per_iteration:FLOAT, +executor_queue_drained_per_iteration:FLOAT, +executor_push_retries_per_iteration:FLOAT, +server_requested_calls_per_iteration:FLOAT, +server_slowpath_requests_queued_per_iteration:FLOAT, +cq_ev_queue_trylock_failures_per_iteration:FLOAT, +cq_ev_queue_trylock_successes_per_iteration:FLOAT, +cq_ev_queue_transient_pop_failures_per_iteration:FLOAT diff --git a/third_party/grpc/src/core/lib/debug/trace.cc b/third_party/grpc/src/core/lib/debug/trace.cc new file mode 100644 index 00000000000000..cafdb15c699314 --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/trace.cc @@ -0,0 +1,148 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/debug/trace.h" + +#include +#include + +#include +#include +#include +#include "src/core/lib/gpr/env.h" + +int grpc_tracer_set_enabled(const char* name, int enabled); + +namespace grpc_core { + +TraceFlag* TraceFlagList::root_tracer_ = nullptr; + +bool TraceFlagList::Set(const char* name, bool enabled) { + TraceFlag* t; + if (0 == strcmp(name, "all")) { + for (t = root_tracer_; t; t = t->next_tracer_) { + t->set_enabled(enabled); + } + } else if (0 == strcmp(name, "list_tracers")) { + LogAllTracers(); + } else if (0 == strcmp(name, "refcount")) { + for (t = root_tracer_; t; t = t->next_tracer_) { + if (strstr(t->name_, "refcount") != nullptr) { + t->set_enabled(enabled); + } + } + } else { + bool found = false; + for (t = root_tracer_; t; t = t->next_tracer_) { + if (0 == strcmp(name, t->name_)) { + t->set_enabled(enabled); + found = true; + } + } + // check for unknowns, but ignore "", to allow to GRPC_TRACE= + if (!found && 0 != strcmp(name, "")) { + gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name); + return false; /* early return */ + } + } + return true; +} + +void TraceFlagList::Add(TraceFlag* flag) { + flag->next_tracer_ = root_tracer_; + root_tracer_ = flag; +} + +void TraceFlagList::LogAllTracers() { + gpr_log(GPR_DEBUG, "available tracers:"); + TraceFlag* t; + for (t = root_tracer_; t != nullptr; t = t->next_tracer_) { + gpr_log(GPR_DEBUG, "\t%s", t->name_); + } +} + +// Flags register themselves on the list during construction +TraceFlag::TraceFlag(bool default_enabled, const char* name) : name_(name) { + static_assert(std::is_trivially_destructible::value, + "TraceFlag needs to be trivially destructible."); + set_enabled(default_enabled); + TraceFlagList::Add(this); +} + +} // namespace grpc_core + +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; + size_t len; + GPR_ASSERT(end >= beg); + len = static_cast(end - beg); + s = static_cast(gpr_malloc(len + 1)); + memcpy(s, beg, len); + s[len] = 0; + *ss = static_cast(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 == nullptr) { + 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 = nullptr; + size_t nstrings = 0; + size_t i; + split(s, &strings, &nstrings); + + for (i = 0; i < nstrings; i++) { + if (strings[i][0] == '-') { + grpc_core::TraceFlagList::Set(strings[i] + 1, false); + } else { + grpc_core::TraceFlagList::Set(strings[i], true); + } + } + + 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 != nullptr) { + parse(e); + gpr_free(e); + } +} + +void grpc_tracer_shutdown(void) {} + +int grpc_tracer_set_enabled(const char* name, int enabled) { + return grpc_core::TraceFlagList::Set(name, enabled != 0); +} diff --git a/third_party/grpc/src/core/lib/debug/trace.h b/third_party/grpc/src/core/lib/debug/trace.h new file mode 100644 index 00000000000000..4623494520e1d8 --- /dev/null +++ b/third_party/grpc/src/core/lib/debug/trace.h @@ -0,0 +1,117 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_DEBUG_TRACE_H +#define GRPC_CORE_LIB_DEBUG_TRACE_H + +#include + +#include +#include + +void grpc_tracer_init(const char* env_var_name); +void grpc_tracer_shutdown(void); + +#if defined(__has_feature) +#if __has_feature(thread_sanitizer) +#define GRPC_THREADSAFE_TRACER +#endif +#endif + +namespace grpc_core { + +class TraceFlag; +class TraceFlagList { + public: + static bool Set(const char* name, bool enabled); + static void Add(TraceFlag* flag); + + private: + static void LogAllTracers(); + static TraceFlag* root_tracer_; +}; + +namespace testing { +void grpc_tracer_enable_flag(grpc_core::TraceFlag* flag); +} + +class TraceFlag { + public: + TraceFlag(bool default_enabled, const char* name); + // This needs to be trivially destructible as it is used as global variable. + ~TraceFlag() = default; + + const char* name() const { return name_; } + +// Use the symbol GRPC_USE_TRACERS to determine if tracers will be enabled in +// opt builds (tracers are always on in dbg builds). The default in OSS is for +// tracers to be on since we support binary distributions of gRPC for the +// wrapped language (wr don't want to force recompilation to get tracing). +// Internally, however, for performance reasons, we compile them out by +// default, since internal build systems make recompiling trivial. +#define GRPC_USE_TRACERS // tracers on by default in OSS +#if defined(GRPC_USE_TRACERS) || !defined(NDEBUG) + bool enabled() { +#ifdef GRPC_THREADSAFE_TRACER + return gpr_atm_no_barrier_load(&value_) != 0; +#else + return value_; +#endif // GRPC_THREADSAFE_TRACER + } +#else + bool enabled() { return false; } +#endif /* defined(GRPC_USE_TRACERS) || !defined(NDEBUG) */ + + private: + friend void grpc_core::testing::grpc_tracer_enable_flag(TraceFlag* flag); + friend class TraceFlagList; + + void set_enabled(bool enabled) { +#ifdef GRPC_THREADSAFE_TRACER + gpr_atm_no_barrier_store(&value_, enabled); +#else + value_ = enabled; +#endif + } + + TraceFlag* next_tracer_; + const char* const name_; +#ifdef GRPC_THREADSAFE_TRACER + gpr_atm value_; +#else + bool value_; +#endif +}; + +#ifndef NDEBUG +typedef TraceFlag DebugOnlyTraceFlag; +#else +class DebugOnlyTraceFlag { + public: + constexpr DebugOnlyTraceFlag(bool default_enabled, const char* name) {} + constexpr bool enabled() const { return false; } + constexpr const char* name() const { return "DebugOnlyTraceFlag"; } + + private: + void set_enabled(bool enabled) {} +}; +#endif + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_DEBUG_TRACE_H */ diff --git a/third_party/grpc/src/core/lib/gpr/README.md b/third_party/grpc/src/core/lib/gpr/README.md new file mode 100644 index 00000000000000..21fb0c796db488 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/README.md @@ -0,0 +1,8 @@ +# GPR - Google Portable Runtime for C + +The files in this directory contain basic utility code and platform +abstractions for C code. None of this code is gRPC-specific; anything +here may also be useful for other open source projects written in C. + +Note that this is one of the few places in src/core where we allow +the use of portability macros. diff --git a/third_party/grpc/src/core/lib/gpr/alloc.cc b/third_party/grpc/src/core/lib/gpr/alloc.cc new file mode 100644 index 00000000000000..611e4cceee4fd8 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/alloc.cc @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include "src/core/lib/profiling/timers.h" + +static void* zalloc_with_calloc(size_t sz) { return calloc(sz, 1); } + +static void* zalloc_with_gpr_malloc(size_t sz) { + void* p = gpr_malloc(sz); + memset(p, 0, sz); + return p; +} + +static gpr_allocation_functions g_alloc_functions = {malloc, zalloc_with_calloc, + realloc, free}; + +gpr_allocation_functions gpr_get_allocation_functions() { + return g_alloc_functions; +} + +void gpr_set_allocation_functions(gpr_allocation_functions functions) { + GPR_ASSERT(functions.malloc_fn != nullptr); + GPR_ASSERT(functions.realloc_fn != nullptr); + GPR_ASSERT(functions.free_fn != nullptr); + if (functions.zalloc_fn == nullptr) { + functions.zalloc_fn = zalloc_with_gpr_malloc; + } + g_alloc_functions = functions; +} + +void* gpr_malloc(size_t size) { + GPR_TIMER_SCOPE("gpr_malloc", 0); + void* p; + if (size == 0) return nullptr; + p = g_alloc_functions.malloc_fn(size); + if (!p) { + abort(); + } + return p; +} + +void* gpr_zalloc(size_t size) { + GPR_TIMER_SCOPE("gpr_zalloc", 0); + void* p; + if (size == 0) return nullptr; + p = g_alloc_functions.zalloc_fn(size); + if (!p) { + abort(); + } + return p; +} + +void gpr_free(void* p) { + GPR_TIMER_SCOPE("gpr_free", 0); + g_alloc_functions.free_fn(p); +} + +void* gpr_realloc(void* p, size_t size) { + GPR_TIMER_SCOPE("gpr_realloc", 0); + if ((size == 0) && (p == nullptr)) return nullptr; + p = g_alloc_functions.realloc_fn(p, size); + if (!p) { + abort(); + } + return p; +} + +void* gpr_malloc_aligned(size_t size, size_t alignment) { + GPR_ASSERT(((alignment - 1) & alignment) == 0); // Must be power of 2. + size_t extra = alignment - 1 + sizeof(void*); + void* p = gpr_malloc(size + extra); + void** ret = (void**)(((uintptr_t)p + extra) & ~(alignment - 1)); + ret[-1] = p; + return (void*)ret; +} + +void gpr_free_aligned(void* ptr) { gpr_free((static_cast(ptr))[-1]); } diff --git a/third_party/grpc/src/core/lib/gpr/alloc.h b/third_party/grpc/src/core/lib/gpr/alloc.h new file mode 100644 index 00000000000000..762b51bf663423 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/alloc.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_ALLOC_H +#define GRPC_CORE_LIB_GPR_ALLOC_H + +#include + +/// Given a size, round up to the next multiple of sizeof(void*). +#define GPR_ROUND_UP_TO_ALIGNMENT_SIZE(x) \ + (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) + +#endif /* GRPC_CORE_LIB_GPR_ALLOC_H */ diff --git a/third_party/grpc/src/core/lib/gpr/arena.cc b/third_party/grpc/src/core/lib/gpr/arena.cc new file mode 100644 index 00000000000000..836a7ca793d455 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/arena.cc @@ -0,0 +1,192 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/gpr/arena.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/alloc.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gprpp/memory.h" + +namespace { +enum init_strategy { + NO_INIT, // Do not initialize the arena blocks. + ZERO_INIT, // Initialize arena blocks with 0. + NON_ZERO_INIT, // Initialize arena blocks with a non-zero value. +}; + +gpr_once g_init_strategy_once = GPR_ONCE_INIT; +init_strategy g_init_strategy = NO_INIT; +} // namespace + +static void set_strategy_from_env() { + char* str = gpr_getenv("GRPC_ARENA_INIT_STRATEGY"); + if (str == nullptr) { + g_init_strategy = NO_INIT; + } else if (strcmp(str, "zero_init") == 0) { + g_init_strategy = ZERO_INIT; + } else if (strcmp(str, "non_zero_init") == 0) { + g_init_strategy = NON_ZERO_INIT; + } else { + g_init_strategy = NO_INIT; + } + gpr_free(str); +} + +static void* gpr_arena_alloc_maybe_init(size_t size) { + void* mem = gpr_malloc_aligned(size, GPR_MAX_ALIGNMENT); + gpr_once_init(&g_init_strategy_once, set_strategy_from_env); + if (GPR_UNLIKELY(g_init_strategy != NO_INIT)) { + if (g_init_strategy == ZERO_INIT) { + memset(mem, 0, size); + } else { // NON_ZERO_INIT. + memset(mem, 0xFE, size); + } + } + return mem; +} + +void gpr_arena_init() { + gpr_once_init(&g_init_strategy_once, set_strategy_from_env); +} + +// Uncomment this to use a simple arena that simply allocates the +// requested amount of memory for each call to gpr_arena_alloc(). This +// effectively eliminates the efficiency gain of using an arena, but it +// may be useful for debugging purposes. +//#define SIMPLE_ARENA_FOR_DEBUGGING +#ifdef SIMPLE_ARENA_FOR_DEBUGGING + +struct gpr_arena { + gpr_arena() { gpr_mu_init(&mu); } + ~gpr_arena() { + gpr_mu_destroy(&mu); + for (size_t i = 0; i < num_ptrs; ++i) { + gpr_free_aligned(ptrs[i]); + } + gpr_free(ptrs); + } + + gpr_mu mu; + void** ptrs = nullptr; + size_t num_ptrs = 0; +}; + +gpr_arena* gpr_arena_create(size_t ignored_initial_size) { + return grpc_core::New(); +} + +size_t gpr_arena_destroy(gpr_arena* arena) { + grpc_core::Delete(arena); + return 1; // Value doesn't matter, since it won't be used. +} + +void* gpr_arena_alloc(gpr_arena* arena, size_t size) { + gpr_mu_lock(&arena->mu); + arena->ptrs = + (void**)gpr_realloc(arena->ptrs, sizeof(void*) * (arena->num_ptrs + 1)); + void* retval = arena->ptrs[arena->num_ptrs++] = + gpr_arena_alloc_maybe_init(size); + gpr_mu_unlock(&arena->mu); + return retval; +} + +#else // SIMPLE_ARENA_FOR_DEBUGGING + +// TODO(roth): We currently assume that all callers need alignment of 16 +// bytes, which may be wrong in some cases. As part of converting the +// arena API to C++, we should consider replacing gpr_arena_alloc() with a +// template that takes the type of the value being allocated, which +// would allow us to use the alignment actually needed by the caller. + +typedef struct zone { + zone* next = nullptr; +} zone; + +struct gpr_arena { + gpr_arena(size_t initial_size) + : initial_zone_size(initial_size), last_zone(&initial_zone) { + gpr_mu_init(&arena_growth_mutex); + } + ~gpr_arena() { + gpr_mu_destroy(&arena_growth_mutex); + zone* z = initial_zone.next; + while (z) { + zone* next_z = z->next; + z->~zone(); + gpr_free_aligned(z); + z = next_z; + } + } + + // Keep track of the total used size. We use this in our call sizing + // historesis. + gpr_atm total_used = 0; + size_t initial_zone_size; + zone initial_zone; + zone* last_zone; + gpr_mu arena_growth_mutex; +}; + +gpr_arena* gpr_arena_create(size_t initial_size) { + initial_size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(initial_size); + return new (gpr_arena_alloc_maybe_init( + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + initial_size)) + gpr_arena(initial_size); +} + +size_t gpr_arena_destroy(gpr_arena* arena) { + const gpr_atm size = gpr_atm_no_barrier_load(&arena->total_used); + arena->~gpr_arena(); + gpr_free_aligned(arena); + return static_cast(size); +} + +void* gpr_arena_alloc(gpr_arena* arena, size_t size) { + size = GPR_ROUND_UP_TO_ALIGNMENT_SIZE(size); + size_t begin = gpr_atm_no_barrier_fetch_add(&arena->total_used, size); + if (begin + size <= arena->initial_zone_size) { + return reinterpret_cast(arena) + + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(gpr_arena)) + begin; + } else { + // If the allocation isn't able to end in the initial zone, create a new + // zone for this allocation, and any unused space in the initial zone is + // wasted. This overflowing and wasting is uncommon because of our arena + // sizing historesis (that is, most calls should have a large enough initial + // zone and will not need to grow the arena). + gpr_mu_lock(&arena->arena_growth_mutex); + zone* z = new (gpr_arena_alloc_maybe_init( + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)) + size)) zone(); + arena->last_zone->next = z; + arena->last_zone = z; + gpr_mu_unlock(&arena->arena_growth_mutex); + return reinterpret_cast(z) + + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(zone)); + } +} + +#endif // SIMPLE_ARENA_FOR_DEBUGGING diff --git a/third_party/grpc/src/core/lib/gpr/arena.h b/third_party/grpc/src/core/lib/gpr/arena.h new file mode 100644 index 00000000000000..069892b2282cf7 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/arena.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// \file Arena based allocator +// Allows very fast allocation of memory, but that memory cannot be freed until +// the arena as a whole is freed +// Tracks the total memory allocated against it, so that future arenas can +// pre-allocate the right amount of memory + +#ifndef GRPC_CORE_LIB_GPR_ARENA_H +#define GRPC_CORE_LIB_GPR_ARENA_H + +#include + +#include + +typedef struct gpr_arena gpr_arena; + +// Create an arena, with \a initial_size bytes in the first allocated buffer +gpr_arena* gpr_arena_create(size_t initial_size); +// Allocate \a size bytes from the arena +void* gpr_arena_alloc(gpr_arena* arena, size_t size); +// Destroy an arena, returning the total number of bytes allocated +size_t gpr_arena_destroy(gpr_arena* arena); +// Initializes the Arena component. +void gpr_arena_init(); + +#endif /* GRPC_CORE_LIB_GPR_ARENA_H */ diff --git a/third_party/grpc/src/core/lib/gpr/atm.cc b/third_party/grpc/src/core/lib/gpr/atm.cc new file mode 100644 index 00000000000000..649d400d38e39c --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/atm.cc @@ -0,0 +1,35 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/lib/gpr/useful.h" + +gpr_atm gpr_atm_no_barrier_clamped_add(gpr_atm* value, gpr_atm delta, + gpr_atm min, gpr_atm max) { + gpr_atm current_value; + gpr_atm new_value; + do { + current_value = gpr_atm_no_barrier_load(value); + new_value = GPR_CLAMP(current_value + delta, min, max); + if (new_value == current_value) break; + } while (!gpr_atm_no_barrier_cas(value, current_value, new_value)); + return new_value; +} diff --git a/third_party/grpc/src/core/lib/gpr/cpu_iphone.cc b/third_party/grpc/src/core/lib/gpr/cpu_iphone.cc new file mode 100644 index 00000000000000..2847e03ba58b5f --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/cpu_iphone.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#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/third_party/grpc/src/core/lib/gpr/cpu_linux.cc b/third_party/grpc/src/core/lib/gpr/cpu_linux.cc new file mode 100644 index 00000000000000..9fc2f0b141a99f --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/cpu_linux.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#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() { +#ifndef GPR_MUSL_LIBC_COMPAT + if (sched_getcpu() < 0) { + gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); + ncpus = 1; + return; + } +#endif + /* This must be signed. sysconf returns -1 when the number cannot be + determined */ + ncpus = static_cast(sysconf(_SC_NPROCESSORS_CONF)); + 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 static_cast(ncpus); +} + +unsigned gpr_cpu_current_cpu(void) { +#ifdef GPR_MUSL_LIBC_COMPAT + // sched_getcpu() is undefined on musl + return 0; +#else + if (gpr_cpu_num_cores() == 1) { + return 0; + } + int cpu = sched_getcpu(); + if (cpu < 0) { + gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); + return 0; + } + if (static_cast(cpu) >= gpr_cpu_num_cores()) { + gpr_log(GPR_ERROR, "Cannot handle hot-plugged CPUs"); + return 0; + } + return static_cast(cpu); +#endif +} + +#endif /* GPR_CPU_LINUX */ diff --git a/third_party/grpc/src/core/lib/gpr/cpu_posix.cc b/third_party/grpc/src/core/lib/gpr/cpu_posix.cc new file mode 100644 index 00000000000000..915fd4976c2280 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/cpu_posix.cc @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#if defined(GPR_CPU_POSIX) + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/useful.h" + +static long ncpus = 0; + +static pthread_key_t thread_id_key; + +static void init_ncpus() { + ncpus = sysconf(_SC_NPROCESSORS_CONF); + if (ncpus < 1 || ncpus > INT32_MAX) { + 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 (unsigned)ncpus; +} + +static void delete_thread_id(void* value) { + if (value) { + gpr_free(value); + } +} + +static void init_thread_id_key(void) { + pthread_key_create(&thread_id_key, delete_thread_id); +} + +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 */ + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_thread_id_key); + + unsigned int* thread_id = + static_cast(pthread_getspecific(thread_id_key)); + if (thread_id == nullptr) { + thread_id = static_cast(gpr_malloc(sizeof(unsigned int))); + pthread_setspecific(thread_id_key, thread_id); + } + + return (unsigned)GPR_HASH_POINTER(thread_id, gpr_cpu_num_cores()); +} + +#endif /* GPR_CPU_POSIX */ diff --git a/third_party/grpc/src/core/lib/gpr/cpu_windows.cc b/third_party/grpc/src/core/lib/gpr/cpu_windows.cc new file mode 100644 index 00000000000000..8d89453403989e --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/cpu_windows.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS +#include +#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_WINDOWS */ diff --git a/third_party/grpc/src/core/lib/gpr/env.h b/third_party/grpc/src/core/lib/gpr/env.h new file mode 100644 index 00000000000000..aec8a3166b1313 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/env.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_ENV_H +#define GRPC_CORE_LIB_GPR_ENV_H + +#include + +#include + +/* 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 environment with the specified name to the specified value. */ +void gpr_setenv(const char* name, const char* value); + +/* This is a version of gpr_getenv that does not produce any output if it has to + use an insecure version of the function. It is ONLY to be used to solve the + problem in which we need to check an env variable to configure the verbosity + level of logging. So DO NOT USE THIS. */ +const char* gpr_getenv_silent(const char* name, char** dst); + +#endif /* GRPC_CORE_LIB_GPR_ENV_H */ diff --git a/third_party/grpc/src/core/lib/gpr/env_linux.cc b/third_party/grpc/src/core/lib/gpr/env_linux.cc new file mode 100644 index 00000000000000..fadc42f22f101e --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/env_linux.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* for secure_getenv. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_ENV + +#include "src/core/lib/gpr/env.h" + +#include +#include +#include +#include + +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" + +const char* gpr_getenv_silent(const char* name, char** dst) { + const char* insecure_func_used = nullptr; + char* result = nullptr; +#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) + typedef char* (*getenv_type)(const char*); + static getenv_type getenv_func = NULL; + /* Check to see which getenv variant is supported (go from most + * to least secure) */ + const char* names[] = {"secure_getenv", "__secure_getenv", "getenv"}; + for (size_t i = 0; getenv_func == NULL && i < GPR_ARRAY_SIZE(names); i++) { + getenv_func = (getenv_type)dlsym(RTLD_DEFAULT, names[i]); + if (getenv_func != NULL && strstr(names[i], "secure") == NULL) { + insecure_func_used = names[i]; + } + } + result = getenv_func(name); +#elif __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) + result = secure_getenv(name); +#else + result = getenv(name); + insecure_func_used = "getenv"; +#endif + *dst = result == nullptr ? result : gpr_strdup(result); + return insecure_func_used; +} + +char* gpr_getenv(const char* name) { + char* result = nullptr; + const char* insecure_func_used = gpr_getenv_silent(name, &result); + if (insecure_func_used != nullptr) { + gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", + insecure_func_used); + } + return 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/third_party/grpc/src/core/lib/gpr/env_posix.cc b/third_party/grpc/src/core/lib/gpr/env_posix.cc new file mode 100644 index 00000000000000..599f85aa72b31c --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/env_posix.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_ENV + +#include "src/core/lib/gpr/env.h" + +#include + +#include + +#include +#include "src/core/lib/gpr/string.h" + +const char* gpr_getenv_silent(const char* name, char** dst) { + *dst = gpr_getenv(name); + return nullptr; +} + +char* gpr_getenv(const char* name) { + char* result = getenv(name); + return result == nullptr ? 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/third_party/grpc/src/core/lib/gpr/env_windows.cc b/third_party/grpc/src/core/lib/gpr/env_windows.cc new file mode 100644 index 00000000000000..cf8ed60d8f6364 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/env_windows.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_ENV + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/string_windows.h" + +const char* gpr_getenv_silent(const char* name, char** dst) { + *dst = gpr_getenv(name); + return NULL; +} + +char* gpr_getenv(const char* name) { + char* result = NULL; + DWORD size; + LPTSTR tresult = NULL; + LPTSTR tname = gpr_char_to_tchar(name); + DWORD ret; + + ret = GetEnvironmentVariable(tname, NULL, 0); + if (ret == 0) { + gpr_free(tname); + return NULL; + } + size = ret * (DWORD)sizeof(TCHAR); + tresult = (LPTSTR)gpr_malloc(size); + ret = GetEnvironmentVariable(tname, tresult, size); + gpr_free(tname); + if (ret == 0) { + gpr_free(tresult); + return NULL; + } + result = gpr_tchar_to_char(tresult); + gpr_free(tresult); + return result; +} + +void gpr_setenv(const char* name, const char* value) { + LPTSTR tname = gpr_char_to_tchar(name); + LPTSTR tvalue = gpr_char_to_tchar(value); + BOOL res = SetEnvironmentVariable(tname, tvalue); + gpr_free(tname); + gpr_free(tvalue); + GPR_ASSERT(res); +} + +#endif /* GPR_WINDOWS_ENV */ diff --git a/third_party/grpc/src/core/lib/gpr/host_port.cc b/third_party/grpc/src/core/lib/gpr/host_port.cc new file mode 100644 index 00000000000000..a34e01cb5165f5 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/host_port.cc @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/gpr/host_port.h" + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" + +int gpr_join_host_port(char** out, const char* host, int port) { + if (host[0] != '[' && strchr(host, ':') != nullptr) { + /* 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 = nullptr; + *port = nullptr; + + if (name[0] == '[') { + /* Parse a bracketed host, typically an IPv6 literal. */ + const char* rbracket = strchr(name, ']'); + if (rbracket == nullptr) { + /* Unmatched [ */ + return 0; + } + if (rbracket[1] == '\0') { + /* ] */ + port_start = nullptr; + } else if (rbracket[1] == ':') { + /* ]: */ + port_start = rbracket + 2; + } else { + /* ] */ + return 0; + } + host_start = name + 1; + host_len = static_cast(rbracket - host_start); + if (memchr(host_start, ':', host_len) == nullptr) { + /* 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 != nullptr && strchr(colon + 1, ':') == nullptr) { + /* Exactly 1 colon. Split into host:port. */ + host_start = name; + host_len = static_cast(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 = nullptr; + } + } + + /* Allocate return values. */ + *host = static_cast(gpr_malloc(host_len + 1)); + memcpy(*host, host_start, host_len); + (*host)[host_len] = '\0'; + + if (port_start != nullptr) { + *port = gpr_strdup(port_start); + } + + return 1; +} diff --git a/third_party/grpc/src/core/lib/gpr/host_port.h b/third_party/grpc/src/core/lib/gpr/host_port.h new file mode 100644 index 00000000000000..0bf0960f824f6e --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/host_port.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_HOST_PORT_H +#define GRPC_CORE_LIB_GPR_HOST_PORT_H + +#include + +/** 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); + +#endif /* GRPC_CORE_LIB_GPR_HOST_PORT_H */ diff --git a/third_party/grpc/src/core/lib/gpr/log.cc b/third_party/grpc/src/core/lib/gpr/log.cc new file mode 100644 index 00000000000000..01ef112fb31571 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/log.cc @@ -0,0 +1,102 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" + +#include +#include + +void gpr_default_log(gpr_log_func_args* args); +static gpr_atm g_log_func = (gpr_atm)gpr_default_log; +static gpr_atm g_min_severity_to_print = GPR_LOG_VERBOSITY_UNSET; + +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"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +int gpr_should_log(gpr_log_severity severity) { + return static_cast(severity) >= + gpr_atm_no_barrier_load(&g_min_severity_to_print) + ? 1 + : 0; +} + +void gpr_log_message(const char* file, int line, gpr_log_severity severity, + const char* message) { + if (gpr_should_log(severity) == 0) { + return; + } + + gpr_log_func_args lfargs; + memset(&lfargs, 0, sizeof(lfargs)); + lfargs.file = file; + lfargs.line = line; + lfargs.severity = severity; + lfargs.message = message; + ((gpr_log_func)gpr_atm_no_barrier_load(&g_log_func))(&lfargs); +} + +void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print) { + gpr_atm_no_barrier_store(&g_min_severity_to_print, + (gpr_atm)min_severity_to_print); +} + +void gpr_log_verbosity_init() { + char* verbosity = nullptr; + const char* insecure_getenv = gpr_getenv_silent("GRPC_VERBOSITY", &verbosity); + + gpr_atm min_severity_to_print = GPR_LOG_SEVERITY_ERROR; + if (verbosity != nullptr) { + if (gpr_stricmp(verbosity, "DEBUG") == 0) { + min_severity_to_print = static_cast(GPR_LOG_SEVERITY_DEBUG); + } else if (gpr_stricmp(verbosity, "INFO") == 0) { + min_severity_to_print = static_cast(GPR_LOG_SEVERITY_INFO); + } else if (gpr_stricmp(verbosity, "ERROR") == 0) { + min_severity_to_print = static_cast(GPR_LOG_SEVERITY_ERROR); + } + gpr_free(verbosity); + } + if ((gpr_atm_no_barrier_load(&g_min_severity_to_print)) == + GPR_LOG_VERBOSITY_UNSET) { + gpr_atm_no_barrier_store(&g_min_severity_to_print, min_severity_to_print); + } + + if (insecure_getenv != nullptr) { + gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", + insecure_getenv); + } +} + +void gpr_set_log_function(gpr_log_func f) { + gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)(f ? f : gpr_default_log)); +} diff --git a/third_party/grpc/src/core/lib/gpr/log_android.cc b/third_party/grpc/src/core/lib/gpr/log_android.cc new file mode 100644 index 00000000000000..40ef4c640da386 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/log_android.cc @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#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, ...) { + /* Avoid message construction if gpr_log_message won't log */ + if (gpr_should_log(severity) == 0) { + return; + } + 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) { + const 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/third_party/grpc/src/core/lib/gpr/log_linux.cc b/third_party/grpc/src/core/lib/gpr/log_linux.cc new file mode 100644 index 00000000000000..561276f0c20a5f --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/log_linux.cc @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_LOG + +#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, ...) { + /* Avoid message construction if gpr_log_message won't log */ + if (gpr_should_log(severity) == 0) { + return; + } + char* message = nullptr; + 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); + /* message has been allocated by vasprintf above, and needs free */ + free(message); +} + +void gpr_default_log(gpr_log_func_args* args) { + const char* final_slash; + char* prefix; + const char* display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + static __thread long tid = 0; + if (tid == 0) tid = gettid(); + + timer = static_cast(now.tv_sec); + final_slash = strrchr(args->file, '/'); + if (final_slash == nullptr) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&timer, &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.%09" PRId32 " %7ld %s:%d]", + gpr_log_severity_string(args->severity), time_buffer, + now.tv_nsec, tid, display_file, args->line); + + fprintf(stderr, "%-60s %s\n", prefix, args->message); + gpr_free(prefix); +} + +#endif /* GPR_LINUX_LOG */ diff --git a/third_party/grpc/src/core/lib/gpr/log_posix.cc b/third_party/grpc/src/core/lib/gpr/log_posix.cc new file mode 100644 index 00000000000000..0acb225572460a --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/log_posix.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_LOG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static intptr_t gettid(void) { return (intptr_t)pthread_self(); } + +void gpr_log(const char* file, int line, gpr_log_severity severity, + const char* format, ...) { + /* Avoid message construction if gpr_log_message won't log */ + if (gpr_should_log(severity) == 0) { + return; + } + char buf[64]; + char* allocated = nullptr; + char* message = nullptr; + int ret; + va_list args; + va_start(args, format); + ret = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (ret < 0) { + message = nullptr; + } else if ((size_t)ret <= sizeof(buf) - 1) { + message = buf; + } else { + message = allocated = (char*)gpr_malloc((size_t)ret + 1); + va_start(args, format); + vsnprintf(message, (size_t)(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) { + const char* final_slash; + const char* display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '/'); + if (final_slash == nullptr) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&timer, &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"); + } + + char* prefix; + 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, "%-70s %s\n", prefix, args->message); + gpr_free(prefix); +} + +#endif /* defined(GPR_POSIX_LOG) */ diff --git a/third_party/grpc/src/core/lib/gpr/log_windows.cc b/third_party/grpc/src/core/lib/gpr/log_windows.cc new file mode 100644 index 00000000000000..060be572b8091d --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/log_windows.cc @@ -0,0 +1,102 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_LOG + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/string_windows.h" + +void gpr_log(const char* file, int line, gpr_log_severity severity, + const char* format, ...) { + /* Avoid message construction if gpr_log_message won't log */ + if (gpr_should_log(severity) == 0) { + return; + } + + 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 = (char*)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) { + const char* final_slash; + const char* display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '\\'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (localtime_s(&tm, &timer)) { + 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); + fflush(stderr); +} + +#endif /* GPR_WINDOWS_LOG */ diff --git a/third_party/grpc/src/core/lib/gpr/mpscq.cc b/third_party/grpc/src/core/lib/gpr/mpscq.cc new file mode 100644 index 00000000000000..076a6bb033ca51 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/mpscq.cc @@ -0,0 +1,117 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/gpr/mpscq.h" + +#include + +void gpr_mpscq_init(gpr_mpscq* q) { + gpr_atm_no_barrier_store(&q->head, (gpr_atm)&q->stub); + q->tail = &q->stub; + gpr_atm_no_barrier_store(&q->stub.next, (gpr_atm)NULL); +} + +void gpr_mpscq_destroy(gpr_mpscq* q) { + GPR_ASSERT(gpr_atm_no_barrier_load(&q->head) == (gpr_atm)&q->stub); + GPR_ASSERT(q->tail == &q->stub); +} + +bool gpr_mpscq_push(gpr_mpscq* q, gpr_mpscq_node* n) { + gpr_atm_no_barrier_store(&n->next, (gpr_atm)NULL); + gpr_mpscq_node* prev = + (gpr_mpscq_node*)gpr_atm_full_xchg(&q->head, (gpr_atm)n); + gpr_atm_rel_store(&prev->next, (gpr_atm)n); + return prev == &q->stub; +} + +gpr_mpscq_node* gpr_mpscq_pop(gpr_mpscq* q) { + bool empty; + return gpr_mpscq_pop_and_check_end(q, &empty); +} + +gpr_mpscq_node* gpr_mpscq_pop_and_check_end(gpr_mpscq* q, bool* empty) { + gpr_mpscq_node* tail = q->tail; + gpr_mpscq_node* next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next); + if (tail == &q->stub) { + // indicates the list is actually (ephemerally) empty + if (next == nullptr) { + *empty = true; + return nullptr; + } + q->tail = next; + tail = next; + next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next); + } + if (next != nullptr) { + *empty = false; + q->tail = next; + return tail; + } + gpr_mpscq_node* head = (gpr_mpscq_node*)gpr_atm_acq_load(&q->head); + if (tail != head) { + *empty = false; + // indicates a retry is in order: we're still adding + return nullptr; + } + gpr_mpscq_push(q, &q->stub); + next = (gpr_mpscq_node*)gpr_atm_acq_load(&tail->next); + if (next != nullptr) { + *empty = false; + q->tail = next; + return tail; + } + // indicates a retry is in order: we're still adding + *empty = false; + return nullptr; +} + +void gpr_locked_mpscq_init(gpr_locked_mpscq* q) { + gpr_mpscq_init(&q->queue); + gpr_mu_init(&q->mu); +} + +void gpr_locked_mpscq_destroy(gpr_locked_mpscq* q) { + gpr_mpscq_destroy(&q->queue); + gpr_mu_destroy(&q->mu); +} + +bool gpr_locked_mpscq_push(gpr_locked_mpscq* q, gpr_mpscq_node* n) { + return gpr_mpscq_push(&q->queue, n); +} + +gpr_mpscq_node* gpr_locked_mpscq_try_pop(gpr_locked_mpscq* q) { + if (gpr_mu_trylock(&q->mu)) { + gpr_mpscq_node* n = gpr_mpscq_pop(&q->queue); + gpr_mu_unlock(&q->mu); + return n; + } + return nullptr; +} + +gpr_mpscq_node* gpr_locked_mpscq_pop(gpr_locked_mpscq* q) { + gpr_mu_lock(&q->mu); + bool empty = false; + gpr_mpscq_node* n; + do { + n = gpr_mpscq_pop_and_check_end(&q->queue, &empty); + } while (n == nullptr && !empty); + gpr_mu_unlock(&q->mu); + return n; +} diff --git a/third_party/grpc/src/core/lib/gpr/mpscq.h b/third_party/grpc/src/core/lib/gpr/mpscq.h new file mode 100644 index 00000000000000..5ded2522bd62b2 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/mpscq.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_MPSCQ_H +#define GRPC_CORE_LIB_GPR_MPSCQ_H + +#include + +#include +#include +#include +#include + +// Multiple-producer single-consumer lock free queue, based upon the +// implementation from Dmitry Vyukov here: +// http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue + +// List node (include this in a data structure at the top, and add application +// fields after it - to simulate inheritance) +typedef struct gpr_mpscq_node { + gpr_atm next; +} gpr_mpscq_node; + +// Actual queue type +typedef struct gpr_mpscq { + // make sure head & tail don't share a cacheline + union { + char padding[GPR_CACHELINE_SIZE]; + gpr_atm head; + }; + gpr_mpscq_node* tail; + gpr_mpscq_node stub; +} gpr_mpscq; + +void gpr_mpscq_init(gpr_mpscq* q); +void gpr_mpscq_destroy(gpr_mpscq* q); +// Push a node +// Thread safe - can be called from multiple threads concurrently +// Returns true if this was possibly the first node (may return true +// sporadically, will not return false sporadically) +bool gpr_mpscq_push(gpr_mpscq* q, gpr_mpscq_node* n); +// Pop a node (returns NULL if no node is ready - which doesn't indicate that +// the queue is empty!!) +// Thread compatible - can only be called from one thread at a time +gpr_mpscq_node* gpr_mpscq_pop(gpr_mpscq* q); +// Pop a node; sets *empty to true if the queue is empty, or false if it is not +gpr_mpscq_node* gpr_mpscq_pop_and_check_end(gpr_mpscq* q, bool* empty); + +// An mpscq with a lock: it's safe to pop from multiple threads, but doing +// only one thread will succeed concurrently +typedef struct gpr_locked_mpscq { + gpr_mpscq queue; + gpr_mu mu; +} gpr_locked_mpscq; + +void gpr_locked_mpscq_init(gpr_locked_mpscq* q); +void gpr_locked_mpscq_destroy(gpr_locked_mpscq* q); +// Push a node +// Thread safe - can be called from multiple threads concurrently +// Returns true if this was possibly the first node (may return true +// sporadically, will not return false sporadically) +bool gpr_locked_mpscq_push(gpr_locked_mpscq* q, gpr_mpscq_node* n); + +// Pop a node (returns NULL if no node is ready - which doesn't indicate that +// the queue is empty!!) +// Thread safe - can be called from multiple threads concurrently +gpr_mpscq_node* gpr_locked_mpscq_try_pop(gpr_locked_mpscq* q); + +// Pop a node. Returns NULL only if the queue was empty at some point after +// calling this function +gpr_mpscq_node* gpr_locked_mpscq_pop(gpr_locked_mpscq* q); + +#endif /* GRPC_CORE_LIB_GPR_MPSCQ_H */ diff --git a/third_party/grpc/src/core/lib/gpr/murmur_hash.cc b/third_party/grpc/src/core/lib/gpr/murmur_hash.cc new file mode 100644 index 00000000000000..cf25abf40d5aa4 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/murmur_hash.cc @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/gpr/murmur_hash.h" + +#include + +#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; + +uint32_t gpr_murmur_hash3(const void* key, size_t len, uint32_t seed) { + uint32_t h1 = seed; + uint32_t k1; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + const uint8_t* keyptr = static_cast(key); + const size_t bsize = sizeof(k1); + const size_t nblocks = len / bsize; + + /* body */ + for (size_t i = 0; i < nblocks; i++, keyptr += bsize) { + memcpy(&k1, keyptr, bsize); + + 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 ^= (static_cast(keyptr[2])) << 16; + /* fallthrough */ + case 2: + k1 ^= (static_cast(keyptr[1])) << 8; + /* fallthrough */ + case 1: + k1 ^= keyptr[0]; + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + h1 ^= k1; + }; + + /* finalization */ + h1 ^= static_cast(len); + FMIX32(h1); + return h1; +} diff --git a/third_party/grpc/src/core/lib/gpr/murmur_hash.h b/third_party/grpc/src/core/lib/gpr/murmur_hash.h new file mode 100644 index 00000000000000..8004889a9a59f4 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/murmur_hash.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_MURMUR_HASH_H +#define GRPC_CORE_LIB_GPR_MURMUR_HASH_H + +#include + +#include + +/* compute the hash of key (length len) */ +uint32_t gpr_murmur_hash3(const void* key, size_t len, uint32_t seed); + +#endif /* GRPC_CORE_LIB_GPR_MURMUR_HASH_H */ diff --git a/third_party/grpc/src/core/lib/gpr/spinlock.h b/third_party/grpc/src/core/lib/gpr/spinlock.h new file mode 100644 index 00000000000000..9f35530a869ef2 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/spinlock.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_SPINLOCK_H +#define GRPC_CORE_LIB_GPR_SPINLOCK_H + +#include + +#include + +/* Simple spinlock. No backoff strategy, gpr_spinlock_lock is almost always + a concurrency code smell. */ +typedef struct { + gpr_atm atm; +} gpr_spinlock; + +#ifdef __cplusplus +#define GPR_SPINLOCK_INITIALIZER (gpr_spinlock{0}) +#else +#define GPR_SPINLOCK_INITIALIZER ((gpr_spinlock){0}) +#endif +#define GPR_SPINLOCK_STATIC_INITIALIZER \ + { 0 } + +#define gpr_spinlock_trylock(lock) (gpr_atm_acq_cas(&(lock)->atm, 0, 1)) +#define gpr_spinlock_unlock(lock) (gpr_atm_rel_store(&(lock)->atm, 0)) +#define gpr_spinlock_lock(lock) \ + do { \ + } while (!gpr_spinlock_trylock((lock))) + +#endif /* GRPC_CORE_LIB_GPR_SPINLOCK_H */ diff --git a/third_party/grpc/src/core/lib/gpr/string.cc b/third_party/grpc/src/core/lib/gpr/string.cc new file mode 100644 index 00000000000000..0a76fc1f54a833 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/string.cc @@ -0,0 +1,347 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/gpr/string.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/useful.h" + +char* gpr_strdup(const char* src) { + char* dst; + size_t len; + + if (!src) { + return nullptr; + } + + len = strlen(src) + 1; + dst = static_cast(gpr_malloc(len)); + + memcpy(dst, src, len); + + return dst; +} + +typedef struct { + size_t capacity; + size_t length; + char* data; +} dump_out; + +char* gpr_format_timespec(gpr_timespec tm) { + char time_buffer[35]; + char ns_buffer[11]; // '.' + 9 digits of precision + struct tm* tm_info = localtime((const time_t*)&tm.tv_sec); + strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%H:%M:%S", tm_info); + snprintf(ns_buffer, 11, ".%09d", tm.tv_nsec); + // This loop trims off trailing zeros by inserting a null character that the + // right point. We iterate in chunks of three because we want 0, 3, 6, or 9 + // fractional digits. + for (int i = 7; i >= 1; i -= 3) { + if (ns_buffer[i] == '0' && ns_buffer[i + 1] == '0' && + ns_buffer[i + 2] == '0') { + ns_buffer[i] = '\0'; + // Edge case in which all fractional digits were 0. + if (i == 1) { + ns_buffer[0] = '\0'; + } + } else { + break; + } + } + char* full_time_str; + gpr_asprintf(&full_time_str, "%s%sZ", time_buffer, ns_buffer); + return full_time_str; +} + +static dump_out dump_out_create(void) { + dump_out r = {0, 0, nullptr}; + 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 = static_cast(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 = "0123456789abcdef"; + + const uint8_t* const beg = reinterpret_cast(buf); + const uint8_t* const end = beg + len; + const uint8_t* 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 uint8_t* const beg = reinterpret_cast(buf); + const uint8_t* const end = beg + len; + const uint8_t* 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, uint32_t 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; +} + +int gpr_parse_bytes_to_uint32(const char* buf, size_t len, uint32_t* result) { + uint32_t out = 0; + uint32_t new_val; + 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_val = 10 * out + static_cast(buf[i] - '0'); + if (new_val < out) return 0; /* overflow */ + out = new_val; + } + + *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) { + long sign; + int i = 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + sign = value < 0 ? -1 : 1; + while (value) { + string[i++] = static_cast('0' + sign * (value % 10)); + value /= 10; + } + if (sign < 0) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +int int64_ttoa(int64_t value, char* string) { + int64_t sign; + int i = 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + sign = value < 0 ? -1 : 1; + while (value) { + string[i++] = static_cast('0' + sign * (value % 10)); + value /= 10; + } + if (sign < 0) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +int gpr_parse_nonnegative_int(const char* value) { + char* end; + long result = strtol(value, &end, 0); + if (*end != '\0' || result < 0 || result > INT_MAX) return -1; + return static_cast(result); +} + +char* gpr_leftpad(const char* str, char flag, size_t length) { + const size_t str_length = strlen(str); + const size_t out_length = str_length > length ? str_length : length; + char* out = static_cast(gpr_malloc(out_length + 1)); + memset(out, flag, out_length - str_length); + memcpy(out + out_length - str_length, str, str_length); + out[out_length] = 0; + return out; +} + +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 = static_cast(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 != nullptr) { + *final_length = out_length; + } + return out; +} + +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 = static_cast( + 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); +} + +int gpr_stricmp(const char* a, const char* b) { + int ca, cb; + do { + ca = tolower(*a); + cb = tolower(*b); + ++a; + ++b; + } while (ca == cb && ca && cb); + return ca - cb; +} + +static void add_string_to_split(const char* beg, const char* end, char*** strs, + size_t* nstrs, size_t* capstrs) { + char* out = + static_cast(gpr_malloc(static_cast(end - beg) + 1)); + memcpy(out, beg, static_cast(end - beg)); + out[end - beg] = 0; + if (*nstrs == *capstrs) { + *capstrs = GPR_MAX(8, 2 * *capstrs); + *strs = static_cast(gpr_realloc(*strs, sizeof(*strs) * *capstrs)); + } + (*strs)[*nstrs] = out; + ++*nstrs; +} + +void gpr_string_split(const char* input, const char* sep, char*** strs, + size_t* nstrs) { + const char* next; + *strs = nullptr; + *nstrs = 0; + size_t capstrs = 0; + while ((next = strstr(input, sep))) { + add_string_to_split(input, next, strs, nstrs, &capstrs); + input = next + strlen(sep); + } + add_string_to_split(input, input + strlen(input), strs, nstrs, &capstrs); +} + +void* gpr_memrchr(const void* s, int c, size_t n) { + if (s == nullptr) return nullptr; + char* b = (char*)s; + size_t i; + for (i = 0; i < n; i++) { + if (b[n - i - 1] == c) { + return &b[n - i - 1]; + } + } + return nullptr; +} + +bool gpr_is_true(const char* s) { + size_t i; + if (s == nullptr) { + return false; + } + static const char* truthy[] = {"yes", "true", "1"}; + for (i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { + if (0 == gpr_stricmp(s, truthy[i])) { + return true; + } + } + return false; +} diff --git a/third_party/grpc/src/core/lib/gpr/string.h b/third_party/grpc/src/core/lib/gpr/string.h new file mode 100644 index 00000000000000..ce51fe46321b1c --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/string.h @@ -0,0 +1,119 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_STRING_H +#define GRPC_CORE_LIB_GPR_STRING_H + +#include + +#include + +#include +#include + +/* 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, uint32_t 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, + uint32_t* 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); + +/* Minimum buffer size for calling int64toa */ +#define GPR_INT64TOA_MIN_BUFSIZE (3 * sizeof(int64_t)) + +/* Convert an int64 to a string in base 10; returns the length of the +output string (or 0 on failure). +output must be at least GPR_INT64TOA_MIN_BUFSIZE bytes long. +NOTE: This function ensures sufficient bit width even on Win x64, +where long is 32bit is size.*/ +int int64_ttoa(int64_t value, char* output); + +// Parses a non-negative number from a value string. Returns -1 on error. +int gpr_parse_nonnegative_int(const char* value); + +/* Reverse a run of bytes */ +void gpr_reverse_bytes(char* str, int len); + +/* Pad a string with flag characters. The given length specifies the minimum + field width. The input string is never truncated. */ +char* gpr_leftpad(const char* str, char flag, size_t length); + +/* 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); + +void gpr_string_split(const char* input, const char* sep, char*** strs, + size_t* nstrs); + +/* Returns an allocated string that represents tm according to RFC-3339, and, + more specifically, follows: + https://developers.google.com/protocol-buffers/docs/proto3#json + + Uses RFC 3339, where generated output will always be Z-normalized and uses + 0, 3, 6 or 9 fractional digits. */ +char* gpr_format_timespec(gpr_timespec); + +/* 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); + +/** Case insensitive string comparison... return <0 if lower(a)0 if lower(a)>lower(b) */ +int gpr_stricmp(const char* a, const char* b); + +void* gpr_memrchr(const void* s, int c, size_t n); + +/** Return true if lower(s) equals "true", "yes" or "1", otherwise false. */ +bool gpr_is_true(const char* s); + +#endif /* GRPC_CORE_LIB_GPR_STRING_H */ diff --git a/third_party/grpc/src/core/lib/gpr/string_posix.cc b/third_party/grpc/src/core/lib/gpr/string_posix.cc new file mode 100644 index 00000000000000..d32775fb3b438a --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/string_posix.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_STRING + +#include +#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 = nullptr; + return -1; + } + + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = static_cast(ret) + 1; + if ((*strp = static_cast(gpr_malloc(strp_buflen))) == nullptr) { + /* 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 (static_cast(ret) == strp_buflen - 1) { + return ret; + } + + /* This should never happen. */ + gpr_free(*strp); + *strp = nullptr; + return -1; +} + +#endif /* GPR_POSIX_STRING */ diff --git a/third_party/grpc/src/core/lib/gpr/string_util_windows.cc b/third_party/grpc/src/core/lib/gpr/string_util_windows.cc new file mode 100644 index 00000000000000..8c8c99cd211091 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/string_util_windows.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix code for gpr snprintf support. */ + +#include + +#ifdef GPR_WINDOWS + +/* Some platforms (namely msys) need wchar to be included BEFORE + anything else, especially strsafe.h. */ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/string_windows.h" + +#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 = (LPTSTR)gpr_malloc((unsigned)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 = (LPSTR)gpr_malloc((unsigned)needed); + WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL); + return ret; +} +#else +LPSTR gpr_tchar_to_char(LPCTSTR input) { return (LPSTR)gpr_strdup(input); } + +LPTSTR gpr_char_to_tchar(LPCTSTR input) { return (LPTSTR)gpr_strdup(input); } +#endif + +char* gpr_format_message(int messageid) { + LPTSTR tmessage; + char* message; + DWORD status = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, (DWORD)messageid, MAKELANGID(LANG_ENGLISH, 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_WINDOWS */ diff --git a/third_party/grpc/src/core/lib/gpr/string_windows.cc b/third_party/grpc/src/core/lib/gpr/string_windows.cc new file mode 100644 index 00000000000000..25bfd412e44b27 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/string_windows.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows code for gpr snprintf support. */ + +#include + +#ifdef GPR_WINDOWS_STRING + +#include +#include +#include + +#include +#include + +#include "src/core/lib/gpr/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 = (char*)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; +} + +#endif /* GPR_WINDOWS_STRING */ diff --git a/third_party/grpc/src/core/lib/gpr/string_windows.h b/third_party/grpc/src/core/lib/gpr/string_windows.h new file mode 100644 index 00000000000000..e370f802cf9619 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/string_windows.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_STRING_WINDOWS_H +#define GRPC_CORE_LIB_GPR_STRING_WINDOWS_H + +#include + +#ifdef GPR_WINDOWS + +/* 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_WINDOWS */ + +#endif /* GRPC_CORE_LIB_GPR_STRING_WINDOWS_H */ diff --git a/third_party/grpc/src/core/lib/gpr/sync.cc b/third_party/grpc/src/core/lib/gpr/sync.cc new file mode 100644 index 00000000000000..2f18fc5ecb47d4 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/sync.cc @@ -0,0 +1,124 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Generic implementation of synchronization primitives. */ + +#include + +#include +#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[((uintptr_t)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 != nullptr); +} + +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 == nullptr) { + struct sync_array_s* s = hash(ev); + gpr_mu_lock(&s->mu); + do { + result = (void*)gpr_atm_acq_load(&ev->state); + } while (result == nullptr && !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_ref_non_zero(gpr_refcount* r) { +#ifndef NDEBUG + gpr_atm prior = gpr_atm_no_barrier_fetch_add(&r->count, 1); + assert(prior > 0); +#else + gpr_ref(r); +#endif +} + +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; +} + +int gpr_ref_is_unique(gpr_refcount* r) { + return gpr_atm_acq_load(&r->count) == 1; +} + +void gpr_stats_init(gpr_stats_counter* c, intptr_t n) { + gpr_atm_rel_store(&c->value, n); +} + +void gpr_stats_inc(gpr_stats_counter* c, intptr_t inc) { + gpr_atm_no_barrier_fetch_add(&c->value, inc); +} + +intptr_t 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/third_party/grpc/src/core/lib/gpr/sync_posix.cc b/third_party/grpc/src/core/lib/gpr/sync_posix.cc new file mode 100644 index 00000000000000..c09a7598acb902 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/sync_posix.cc @@ -0,0 +1,241 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_SYNC + +#include +#include +#include +#include +#include +#include "src/core/lib/profiling/timers.h" + +// For debug of the timer manager crash only. +// TODO (mxyan): remove after bug is fixed. +#ifdef GRPC_DEBUG_TIMER_MANAGER +#include +void (*g_grpc_debug_timer_manager_stats)( + int64_t timer_manager_init_count, int64_t timer_manager_shutdown_count, + int64_t fork_count, int64_t timer_wait_err, int64_t timer_cv_value, + int64_t timer_mu_value, int64_t abstime_sec_value, + int64_t abstime_nsec_value, int64_t abs_deadline_sec_value, + int64_t abs_deadline_nsec_value, int64_t now1_sec_value, + int64_t now1_nsec_value, int64_t now2_sec_value, int64_t now2_nsec_value, + int64_t add_result_sec_value, int64_t add_result_nsec_value, + int64_t sub_result_sec_value, int64_t sub_result_nsec_value, + int64_t next_value, int64_t start_time_sec, + int64_t start_time_nsec) = nullptr; +int64_t g_timer_manager_init_count = 0; +int64_t g_timer_manager_shutdown_count = 0; +int64_t g_fork_count = 0; +int64_t g_timer_wait_err = 0; +int64_t g_timer_cv_value = 0; +int64_t g_timer_mu_value = 0; +int64_t g_abstime_sec_value = -1; +int64_t g_abstime_nsec_value = -1; +int64_t g_abs_deadline_sec_value = -1; +int64_t g_abs_deadline_nsec_value = -1; +int64_t g_now1_sec_value = -1; +int64_t g_now1_nsec_value = -1; +int64_t g_now2_sec_value = -1; +int64_t g_now2_nsec_value = -1; +int64_t g_add_result_sec_value = -1; +int64_t g_add_result_nsec_value = -1; +int64_t g_sub_result_sec_value = -1; +int64_t g_sub_result_nsec_value = -1; +int64_t g_next_value = -1; +int64_t g_start_time_sec = -1; +int64_t g_start_time_nsec = -1; +#endif // GRPC_DEBUG_TIMER_MANAGER + +#ifdef GPR_LOW_LEVEL_COUNTERS +gpr_atm gpr_mu_locks = 0; +gpr_atm gpr_counter_atm_cas = 0; +gpr_atm gpr_counter_atm_add = 0; +#endif + +void gpr_mu_init(gpr_mu* mu) { + GPR_ASSERT(pthread_mutex_init(mu, nullptr) == 0); +} + +void gpr_mu_destroy(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); } + +void gpr_mu_lock(gpr_mu* mu) { +#ifdef GPR_LOW_LEVEL_COUNTERS + GPR_ATM_INC_COUNTER(gpr_mu_locks); +#endif + GPR_TIMER_SCOPE("gpr_mu_lock", 0); + GPR_ASSERT(pthread_mutex_lock(mu) == 0); +} + +void gpr_mu_unlock(gpr_mu* mu) { + GPR_TIMER_SCOPE("gpr_mu_unlock", 0); + GPR_ASSERT(pthread_mutex_unlock(mu) == 0); +} + +int gpr_mu_trylock(gpr_mu* mu) { + GPR_TIMER_SCOPE("gpr_mu_trylock", 0); + int err = pthread_mutex_trylock(mu); + GPR_ASSERT(err == 0 || err == EBUSY); + return err == 0; +} + +/*----------------------------------------*/ + +void gpr_cv_init(gpr_cv* cv) { + pthread_condattr_t attr; + GPR_ASSERT(pthread_condattr_init(&attr) == 0); +#if GPR_LINUX + GPR_ASSERT(pthread_condattr_setclock(&attr, CLOCK_MONOTONIC) == 0); +#endif // GPR_LINUX + GPR_ASSERT(pthread_cond_init(cv, &attr) == 0); +} + +void gpr_cv_destroy(gpr_cv* cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } + +// For debug of the timer manager crash only. +// TODO (mxyan): remove after bug is fixed. +#ifdef GRPC_DEBUG_TIMER_MANAGER +static gpr_timespec gpr_convert_clock_type_debug_timespec( + gpr_timespec t, gpr_clock_type clock_type, gpr_timespec& now1, + gpr_timespec& now2, gpr_timespec& add_result, gpr_timespec& sub_result) { + if (t.clock_type == clock_type) { + return t; + } + + if (t.tv_sec == INT64_MAX || t.tv_sec == INT64_MIN) { + 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); + } + + now1 = gpr_now(t.clock_type); + sub_result = gpr_time_sub(t, now1); + now2 = gpr_now(clock_type); + add_result = gpr_time_add(now2, sub_result); + return add_result; +} + +#define gpr_convert_clock_type_debug(t, clock_type, now1, now2, add_result, \ + sub_result) \ + gpr_convert_clock_type_debug_timespec((t), (clock_type), (now1), (now2), \ + (add_result), (sub_result)) +#else +#define gpr_convert_clock_type_debug(t, clock_type, now1, now2, add_result, \ + sub_result) \ + gpr_convert_clock_type((t), (clock_type)) +#endif + +int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) { + int err = 0; +#ifdef GRPC_DEBUG_TIMER_MANAGER + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. + gpr_timespec abs_deadline_copy; + abs_deadline_copy.tv_sec = abs_deadline.tv_sec; + abs_deadline_copy.tv_nsec = abs_deadline.tv_nsec; + gpr_timespec now1; + gpr_timespec now2; + gpr_timespec add_result; + gpr_timespec sub_result; + memset(&now1, 0, sizeof(now1)); + memset(&now2, 0, sizeof(now2)); + memset(&add_result, 0, sizeof(add_result)); + memset(&sub_result, 0, sizeof(sub_result)); +#endif + 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; +#if GPR_LINUX + abs_deadline = gpr_convert_clock_type_debug( + abs_deadline, GPR_CLOCK_MONOTONIC, now1, now2, add_result, sub_result); +#else + abs_deadline = gpr_convert_clock_type_debug( + abs_deadline, GPR_CLOCK_REALTIME, now1, now2, add_result, sub_result); +#endif // GPR_LINUX + abs_deadline_ts.tv_sec = static_cast(abs_deadline.tv_sec); + abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec; + err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts); +#ifdef GRPC_DEBUG_TIMER_MANAGER + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. + if (GPR_UNLIKELY(!(err == 0 || err == ETIMEDOUT || err == EAGAIN))) { + g_abstime_sec_value = abs_deadline_ts.tv_sec; + g_abstime_nsec_value = abs_deadline_ts.tv_nsec; + } +#endif + } + +#ifdef GRPC_DEBUG_TIMER_MANAGER + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. + if (GPR_UNLIKELY(!(err == 0 || err == ETIMEDOUT || err == EAGAIN))) { + if (g_grpc_debug_timer_manager_stats) { + g_timer_wait_err = err; + g_timer_cv_value = (int64_t)cv; + g_timer_mu_value = (int64_t)mu; + g_abs_deadline_sec_value = abs_deadline_copy.tv_sec; + g_abs_deadline_nsec_value = abs_deadline_copy.tv_nsec; + g_now1_sec_value = now1.tv_sec; + g_now1_nsec_value = now1.tv_nsec; + g_now2_sec_value = now2.tv_sec; + g_now2_nsec_value = now2.tv_nsec; + g_add_result_sec_value = add_result.tv_sec; + g_add_result_nsec_value = add_result.tv_nsec; + g_sub_result_sec_value = sub_result.tv_sec; + g_sub_result_nsec_value = sub_result.tv_nsec; + g_grpc_debug_timer_manager_stats( + g_timer_manager_init_count, g_timer_manager_shutdown_count, + g_fork_count, g_timer_wait_err, g_timer_cv_value, g_timer_mu_value, + g_abstime_sec_value, g_abstime_nsec_value, g_abs_deadline_sec_value, + g_abs_deadline_nsec_value, g_now1_sec_value, g_now1_nsec_value, + g_now2_sec_value, g_now2_nsec_value, g_add_result_sec_value, + g_add_result_nsec_value, g_sub_result_sec_value, + g_sub_result_nsec_value, g_next_value, g_start_time_sec, + g_start_time_nsec); + } + } +#endif + 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/third_party/grpc/src/core/lib/gpr/sync_windows.cc b/third_party/grpc/src/core/lib/gpr/sync_windows.cc new file mode 100644 index 00000000000000..7cd41633d53817 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/sync_windows.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Win32 code for gpr synchronization support. */ + +#include + +#ifdef GPR_WINDOWS + +#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 { + abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); + gpr_timespec now = gpr_now(abs_deadline.clock_type); + int64_t now_ms = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000; + int64_t deadline_ms = + (int64_t)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; + if (now_ms >= deadline_ms) { + timeout = 1; + } else { + if ((deadline_ms - now_ms) >= INFINITE) { + timeout_max_ms = INFINITE - 1; + } else { + timeout_max_ms = (DWORD)(deadline_ms - now_ms); + } + 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 = (struct run_once_func_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_WINDOWS */ diff --git a/third_party/grpc/src/core/support/time.c b/third_party/grpc/src/core/lib/gpr/time.cc similarity index 51% rename from third_party/grpc/src/core/support/time.c rename to third_party/grpc/src/core/lib/gpr/time.cc index 423d12ffc0fd05..64c1c98f56099a 100644 --- a/third_party/grpc/src/core/support/time.c +++ b/third_party/grpc/src/core/lib/gpr/time.cc @@ -1,48 +1,35 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ /* Generic implementation of time calls. */ +#include + +#include #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) { + if (cmp == 0 && a.tv_sec != INT64_MAX && a.tv_sec != INT64_MIN) { cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); } return cmp; @@ -80,103 +67,68 @@ gpr_timespec gpr_inf_past(gpr_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(int64_t ns, gpr_clock_type type) { - gpr_timespec result; - result.clock_type = type; - if (ns == INT64_MAX) { - result = gpr_inf_future(type); - } else if (ns == INT64_MIN) { - result = gpr_inf_past(type); - } else if (ns >= 0) { - result.tv_sec = ns / GPR_NS_PER_SEC; - result.tv_nsec = (int32_t)(ns - result.tv_sec * GPR_NS_PER_SEC); +static gpr_timespec to_seconds_from_sub_second_time(int64_t time_in_units, + int64_t units_per_sec, + gpr_clock_type type) { + gpr_timespec out; + if (time_in_units == INT64_MAX) { + out = gpr_inf_future(type); + } else if (time_in_units == INT64_MIN) { + out = gpr_inf_past(type); } 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 = (int32_t)(ns - result.tv_sec * GPR_NS_PER_SEC); + if (time_in_units >= 0) { + out.tv_sec = time_in_units / units_per_sec; + } else { + out.tv_sec = (-((units_per_sec - 1) - (time_in_units + units_per_sec)) / + units_per_sec) - + 1; + } + out.tv_nsec = + static_cast((time_in_units - out.tv_sec * units_per_sec) * + GPR_NS_PER_SEC / units_per_sec); + out.clock_type = type; } - return result; + return out; } -gpr_timespec gpr_time_from_micros(int64_t us, gpr_clock_type type) { - gpr_timespec result; - result.clock_type = type; - if (us == INT64_MAX) { - result = gpr_inf_future(type); - } else if (us == INT64_MIN) { - result = gpr_inf_past(type); - } else if (us >= 0) { - result.tv_sec = us / 1000000; - result.tv_nsec = (int32_t)((us - result.tv_sec * 1000000) * 1000); +static gpr_timespec to_seconds_from_above_second_time(int64_t time_in_units, + int64_t secs_per_unit, + gpr_clock_type type) { + gpr_timespec out; + if (time_in_units >= INT64_MAX / secs_per_unit) { + out = gpr_inf_future(type); + } else if (time_in_units <= INT64_MIN / secs_per_unit) { + out = gpr_inf_past(type); } else { - /* Calculation carefully formulated to avoid any possible under/overflow. */ - result.tv_sec = (-(999999 - (us + 1000000)) / 1000000) - 1; - result.tv_nsec = (int32_t)((us - result.tv_sec * 1000000) * 1000); + out.tv_sec = time_in_units * secs_per_unit; + out.tv_nsec = 0; + out.clock_type = type; } - return result; + return out; +} + +gpr_timespec gpr_time_from_nanos(int64_t ns, gpr_clock_type type) { + return to_seconds_from_sub_second_time(ns, GPR_NS_PER_SEC, type); +} + +gpr_timespec gpr_time_from_micros(int64_t us, gpr_clock_type type) { + return to_seconds_from_sub_second_time(us, GPR_US_PER_SEC, type); } gpr_timespec gpr_time_from_millis(int64_t ms, gpr_clock_type type) { - gpr_timespec result; - result.clock_type = type; - if (ms == INT64_MAX) { - result = gpr_inf_future(type); - } else if (ms == INT64_MIN) { - result = gpr_inf_past(type); - } else if (ms >= 0) { - result.tv_sec = ms / 1000; - result.tv_nsec = (int32_t)((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 = (int32_t)((ms - result.tv_sec * 1000) * 1000000); - } - return result; + return to_seconds_from_sub_second_time(ms, GPR_MS_PER_SEC, type); } gpr_timespec gpr_time_from_seconds(int64_t s, gpr_clock_type type) { - gpr_timespec result; - result.clock_type = type; - if (s == INT64_MAX) { - result = gpr_inf_future(type); - } else if (s == INT64_MIN) { - result = gpr_inf_past(type); - } else { - result.tv_sec = s; - result.tv_nsec = 0; - } - return result; + return to_seconds_from_sub_second_time(s, 1, type); } gpr_timespec gpr_time_from_minutes(int64_t m, gpr_clock_type type) { - gpr_timespec result; - result.clock_type = type; - if (m >= INT64_MAX / 60) { - result = gpr_inf_future(type); - } else if (m <= INT64_MIN / 60) { - result = gpr_inf_past(type); - } else { - result.tv_sec = m * 60; - result.tv_nsec = 0; - } - return result; + return to_seconds_from_above_second_time(m, 60, type); } gpr_timespec gpr_time_from_hours(int64_t h, gpr_clock_type type) { - gpr_timespec result; - result.clock_type = type; - if (h >= INT64_MAX / 3600) { - result = gpr_inf_future(type); - } else if (h <= INT64_MIN / 3600) { - result = gpr_inf_past(type); - } else { - result.tv_sec = h * 3600; - result.tv_nsec = 0; - } - return result; + return to_seconds_from_above_second_time(h, 3600, type); } gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { @@ -267,12 +219,13 @@ int32_t gpr_time_to_millis(gpr_timespec t) { care?) */ return -2147483647; } else { - return (int32_t)(t.tv_sec * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS); + return static_cast(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; + return static_cast(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) { @@ -280,15 +233,9 @@ gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) { return t; } - if (t.tv_nsec == 0) { - if (t.tv_sec == INT64_MAX) { - t.clock_type = clock_type; - return t; - } - if (t.tv_sec == INT64_MIN) { - t.clock_type = clock_type; - return t; - } + if (t.tv_sec == INT64_MAX || t.tv_sec == INT64_MIN) { + t.clock_type = clock_type; + return t; } if (clock_type == GPR_TIMESPAN) { diff --git a/third_party/grpc/src/core/support/time_posix.c b/third_party/grpc/src/core/lib/gpr/time_posix.cc similarity index 59% rename from third_party/grpc/src/core/support/time_posix.c rename to third_party/grpc/src/core/lib/gpr/time_posix.cc index 1f92d7f0908be6..28836bfa54e34f 100644 --- a/third_party/grpc/src/core/support/time_posix.c +++ b/third_party/grpc/src/core/lib/gpr/time_posix.cc @@ -1,38 +1,24 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include -#include + +#include "src/core/lib/gpr/time_precise.h" #ifdef GPR_POSIX_TIME @@ -42,9 +28,9 @@ #ifdef __linux__ #include #endif +#include #include #include -#include "src/core/support/block_annotate.h" static struct timespec timespec_from_gpr(gpr_timespec gts) { struct timespec rv; @@ -52,12 +38,12 @@ static struct timespec timespec_from_gpr(gpr_timespec gts) { /* fine to assert, as this is only used in gpr_sleep_until */ GPR_ASSERT(gts.tv_sec <= INT32_MAX && gts.tv_sec >= INT32_MIN); } - rv.tv_sec = (time_t)gts.tv_sec; + rv.tv_sec = static_cast(gts.tv_sec); rv.tv_nsec = gts.tv_nsec; return rv; } -#if _POSIX_TIMERS > 0 +#if _POSIX_TIMERS > 0 || defined(__OpenBSD__) static gpr_timespec gpr_from_timespec(struct timespec ts, gpr_clock_type clock_type) { /* @@ -67,7 +53,7 @@ static gpr_timespec gpr_from_timespec(struct timespec ts, */ gpr_timespec rv; rv.tv_sec = ts.tv_sec; - rv.tv_nsec = (int32_t)ts.tv_nsec; + rv.tv_nsec = static_cast(ts.tv_nsec); rv.clock_type = clock_type; return rv; } @@ -78,7 +64,7 @@ static const clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, void gpr_time_init(void) { gpr_precise_clock_init(); } -gpr_timespec gpr_now(gpr_clock_type clock_type) { +static gpr_timespec now_impl(gpr_clock_type clock_type) { struct timespec now; GPR_ASSERT(clock_type != GPR_TIMESPAN); if (clock_type == GPR_CLOCK_PRECISE) { @@ -86,7 +72,7 @@ gpr_timespec gpr_now(gpr_clock_type clock_type) { gpr_precise_clock_now(&ret); return ret; } else { -#if defined(__linux__) && !defined(GPR_NO_DIRECT_SYSCALLS) +#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) && defined(__linux__) /* avoid ABI problems by invoking syscalls directly */ syscall(SYS_clock_gettime, clockid_for_gpr_clock[clock_type], &now); #else @@ -96,11 +82,11 @@ gpr_timespec gpr_now(gpr_clock_type clock_type) { } } #else -/* For some reason Apple's OSes haven't implemented clock_gettime. */ + /* For some reason Apple's OSes haven't implemented clock_gettime. */ -#include #include #include +#include static double g_time_scale; static uint64_t g_time_start; @@ -114,7 +100,7 @@ void gpr_time_init(void) { g_time_start = mach_absolute_time(); } -gpr_timespec gpr_now(gpr_clock_type clock) { +static gpr_timespec now_impl(gpr_clock_type clock) { gpr_timespec now; struct timeval now_tv; double now_dbl; @@ -122,12 +108,12 @@ gpr_timespec gpr_now(gpr_clock_type clock) { now.clock_type = clock; switch (clock) { case GPR_CLOCK_REALTIME: - gettimeofday(&now_tv, NULL); + gettimeofday(&now_tv, nullptr); 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_dbl = ((double)(mach_absolute_time() - g_time_start)) * g_time_scale; now.tv_sec = (int64_t)(now_dbl * 1e-9); now.tv_nsec = (int32_t)(now_dbl - ((double)now.tv_sec) * 1e9); break; @@ -142,6 +128,19 @@ gpr_timespec gpr_now(gpr_clock_type clock) { } #endif +gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; + +#ifdef GPR_LOW_LEVEL_COUNTERS +gpr_atm gpr_now_call_count; +#endif + +gpr_timespec gpr_now(gpr_clock_type clock_type) { +#ifdef GPR_LOW_LEVEL_COUNTERS + __atomic_fetch_add(&gpr_now_call_count, 1, __ATOMIC_RELAXED); +#endif + return gpr_now_impl(clock_type); +} + void gpr_sleep_until(gpr_timespec until) { gpr_timespec now; gpr_timespec delta; @@ -158,9 +157,7 @@ void gpr_sleep_until(gpr_timespec until) { delta = gpr_time_sub(until, now); delta_ts = timespec_from_gpr(delta); - GRPC_SCHEDULING_START_BLOCKING_REGION; - ns_result = nanosleep(&delta_ts, NULL); - GRPC_SCHEDULING_END_BLOCKING_REGION; + ns_result = nanosleep(&delta_ts, nullptr); if (ns_result == 0) { break; } diff --git a/third_party/grpc/src/core/lib/gpr/time_precise.cc b/third_party/grpc/src/core/lib/gpr/time_precise.cc new file mode 100644 index 00000000000000..1b34fd7eb1b552 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/time_precise.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/time_precise.h" + +#ifdef GRPC_TIMERS_RDTSC +#if defined(__i386__) +static void gpr_get_cycle_counter(int64_t int* clk) { + int64_t int ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + *clk = ret; +} + +// ---------------------------------------------------------------- +#elif defined(__x86_64__) || defined(__amd64__) +static void gpr_get_cycle_counter(int64_t* clk) { + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + *clk = (int64_t)(high << 32) | (int64_t)low; +} +#endif + +static double cycles_per_second = 0; +static int64_t start_cycle; +void gpr_precise_clock_init(void) { + time_t start; + int64_t end_cycle; + gpr_log(GPR_DEBUG, "Calibrating timers"); + start = time(NULL); + while (time(NULL) == start) + ; + gpr_get_cycle_counter(&start_cycle); + while (time(NULL) <= start + 10) + ; + gpr_get_cycle_counter(&end_cycle); + cycles_per_second = (double)(end_cycle - start_cycle) / 10.0; + gpr_log(GPR_DEBUG, "... cycles_per_second = %f\n", cycles_per_second); +} + +void gpr_precise_clock_now(gpr_timespec* clk) { + int64_t counter; + double secs; + gpr_get_cycle_counter(&counter); + secs = (double)(counter - start_cycle) / cycles_per_second; + clk->clock_type = GPR_CLOCK_PRECISE; + clk->tv_sec = (int64_t)secs; + clk->tv_nsec = (int32_t)(1e9 * (secs - (double)clk->tv_sec)); +} + +#else /* GRPC_TIMERS_RDTSC */ +void gpr_precise_clock_init(void) {} + +void gpr_precise_clock_now(gpr_timespec* clk) { + *clk = gpr_now(GPR_CLOCK_REALTIME); + clk->clock_type = GPR_CLOCK_PRECISE; +} +#endif /* GRPC_TIMERS_RDTSC */ diff --git a/third_party/grpc/src/core/lib/gpr/time_precise.h b/third_party/grpc/src/core/lib/gpr/time_precise.h new file mode 100644 index 00000000000000..a63ea9dc689020 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/time_precise.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TIME_PRECISE_H +#define GRPC_CORE_LIB_GPR_TIME_PRECISE_H + +#include + +#include + +void gpr_precise_clock_init(void); +void gpr_precise_clock_now(gpr_timespec* clk); + +#endif /* GRPC_CORE_LIB_GPR_TIME_PRECISE_H */ diff --git a/third_party/grpc/src/core/lib/gpr/time_windows.cc b/third_party/grpc/src/core/lib/gpr/time_windows.cc new file mode 100644 index 00000000000000..247cc164682e13 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/time_windows.cc @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Win32 code for gpr time support. */ + +#include + +#ifdef GPR_WINDOWS_TIME + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/time_precise.h" + +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 / (double)frequency.QuadPart; +} + +static gpr_timespec now_impl(gpr_clock_type clock) { + gpr_timespec now_tv; + LONGLONG diff; + 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 = (int64_t)now_tb.time; + now_tv.tv_nsec = now_tb.millitm * 1000000; + break; + case GPR_CLOCK_MONOTONIC: + case GPR_CLOCK_PRECISE: + QueryPerformanceCounter(×tamp); + diff = timestamp.QuadPart - g_start_time.QuadPart; + now_dbl = (double)diff * g_time_scale; + now_tv.tv_sec = (int64_t)now_dbl; + now_tv.tv_nsec = (int32_t)((now_dbl - (double)now_tv.tv_sec) * 1e9); + break; + case GPR_TIMESPAN: + abort(); + break; + } + return now_tv; +} + +gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; + +gpr_timespec gpr_now(gpr_clock_type clock_type) { + return gpr_now_impl(clock_type); +} + +void gpr_sleep_until(gpr_timespec until) { + gpr_timespec now; + gpr_timespec delta; + int64_t 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 = + delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; + GPR_ASSERT((sleep_millis >= 0) && (sleep_millis <= INT_MAX)); + Sleep((DWORD)sleep_millis); + } +} + +#endif /* GPR_WINDOWS_TIME */ diff --git a/third_party/grpc/src/core/lib/gpr/tls.h b/third_party/grpc/src/core/lib/gpr/tls.h new file mode 100644 index 00000000000000..aee8f4d941d4c4 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tls.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TLS_H +#define GRPC_CORE_LIB_GPR_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 intptr_t. + + Declaring a thread local variable 'foo': + GPR_TLS_DECL(foo); + Thread locals always have static scope. + + Declaring a thread local class variable 'foo': + GPR_TLS_CLASS_DECL(foo); + + Defining the thread local class variable: + GPR_TLS_CLASS_DEF(foo); + + 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); + + ALL functions here may be implemented as macros. */ + +#ifdef GPR_GCC_TLS +#include "src/core/lib/gpr/tls_gcc.h" +#endif + +#ifdef GPR_MSVC_TLS +#include "src/core/lib/gpr/tls_msvc.h" +#endif + +#ifdef GPR_PTHREAD_TLS +#include "src/core/lib/gpr/tls_pthread.h" +#endif + +#endif /* GRPC_CORE_LIB_GPR_TLS_H */ diff --git a/third_party/grpc/src/core/lib/gpr/tls_gcc.h b/third_party/grpc/src/core/lib/gpr/tls_gcc.h new file mode 100644 index 00000000000000..72b360b0211df6 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tls_gcc.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TLS_GCC_H +#define GRPC_CORE_LIB_GPR_TLS_GCC_H + +#include + +#include + +#include + +/** 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 { + intptr_t value; +}; + +#define GPR_TLS_DECL(name) \ + static __thread struct gpr_gcc_thread_local name = {0} + +#define GPR_TLS_CLASS_DECL(name) \ + static __thread struct gpr_gcc_thread_local name + +#define GPR_TLS_CLASS_DEF(name) __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 /* GRPC_CORE_LIB_GPR_TLS_GCC_H */ diff --git a/third_party/grpc/src/core/lib/gpr/tls_msvc.h b/third_party/grpc/src/core/lib/gpr/tls_msvc.h new file mode 100644 index 00000000000000..f4b3f0f50f606d --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tls_msvc.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TLS_MSVC_H +#define GRPC_CORE_LIB_GPR_TLS_MSVC_H + +/** Thread local storage based on ms visual c compiler primitives. +#include + + #include tls.h to use this - and see that file for documentation */ + +struct gpr_msvc_thread_local { + intptr_t value; +}; + +/** Use GPR_TLS_DECL to declare tls static variables outside a class */ +#define GPR_TLS_DECL(name) \ + static __declspec(thread) struct gpr_msvc_thread_local name = {0} + +/** Use GPR_TLS_CLASS_DECL to declare tls static variable members of a class. + * GPR_TLS_CLASS_DEF needs to be called to define this member. */ +#define GPR_TLS_CLASS_DECL(name) \ + static __declspec(thread) struct gpr_msvc_thread_local name + +#define GPR_TLS_CLASS_DEF(name) \ + __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 /* GRPC_CORE_LIB_GPR_TLS_MSVC_H */ diff --git a/third_party/grpc/src/core/lib/gpr/tls_pthread.cc b/third_party/grpc/src/core/lib/gpr/tls_pthread.cc new file mode 100644 index 00000000000000..2e5b306909bbc0 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tls_pthread.cc @@ -0,0 +1,30 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_PTHREAD_TLS + +#include "src/core/lib/gpr/tls.h" + +intptr_t gpr_tls_set(struct gpr_pthread_thread_local* tls, intptr_t value) { + GPR_ASSERT(0 == pthread_setspecific(tls->key, (void*)value)); + return value; +} + +#endif /* GPR_PTHREAD_TLS */ diff --git a/third_party/grpc/src/core/lib/gpr/tls_pthread.h b/third_party/grpc/src/core/lib/gpr/tls_pthread.h new file mode 100644 index 00000000000000..a15f2f3389798e --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tls_pthread.h @@ -0,0 +1,56 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TLS_PTHREAD_H +#define GRPC_CORE_LIB_GPR_TLS_PTHREAD_H + +#include + +#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; +}; + +/** Use GPR_TLS_DECL to declare tls static variables outside a class */ +#define GPR_TLS_DECL(name) static struct gpr_pthread_thread_local name = {0} + +/** Use GPR_TLS_CLASS_DECL to declare tls static variable members of a class. + * GPR_TLS_CLASS_DEF needs to be called to define this member. */ +#define GPR_TLS_CLASS_DECL(name) static struct gpr_pthread_thread_local name + +/** Use GPR_TLS_CLASS_DEF to declare tls static variable members of a class. + * GPR_TLS_CLASS_DEF needs to be called to define this member. */ +#define GPR_TLS_CLASS_DEF(name) 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) ((intptr_t)pthread_getspecific((tls)->key)) +#ifdef __cplusplus +extern "C" { +#endif +intptr_t gpr_tls_set(struct gpr_pthread_thread_local* tls, intptr_t value); +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_GPR_TLS_PTHREAD_H */ diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile.h b/third_party/grpc/src/core/lib/gpr/tmpfile.h new file mode 100644 index 00000000000000..3ce3ff5e5d25cc --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tmpfile.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_TMPFILE_H +#define GRPC_CORE_LIB_GPR_TMPFILE_H + +#include + +#include + +/* 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); + +#endif /* GRPC_CORE_LIB_GPR_TMPFILE_H */ diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile_msys.cc b/third_party/grpc/src/core/lib/gpr/tmpfile_msys.cc new file mode 100644 index 00000000000000..76cd886f3a0086 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tmpfile_msys.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_MSYS_TMPFILE + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string_windows.h" +#include "src/core/lib/gpr/tmpfile.h" + +FILE* gpr_tmpfile(const char* prefix, char** tmp_filename_out) { + FILE* result = NULL; + char tmp_filename[MAX_PATH]; + UINT success; + + if (tmp_filename_out != NULL) *tmp_filename_out = NULL; + + /* Generate a unique filename with our template + temporary path. */ + success = GetTempFileNameA(".", prefix, 0, tmp_filename); + fprintf(stderr, "success = %d\n", success); + + if (success) { + /* Open a file there. */ + result = fopen(tmp_filename, "wb+"); + fprintf(stderr, "result = %p\n", result); + } + if (result != NULL && tmp_filename_out) { + *tmp_filename_out = gpr_strdup(tmp_filename); + } + + return result; +} + +#endif /* GPR_MSYS_TMPFILE */ diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile_posix.cc b/third_party/grpc/src/core/lib/gpr/tmpfile_posix.cc new file mode 100644 index 00000000000000..ffdad335d67d26 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tmpfile_posix.cc @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_TMPFILE + +#include "src/core/lib/gpr/tmpfile.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" + +FILE* gpr_tmpfile(const char* prefix, char** tmp_filename) { + FILE* result = nullptr; + char* filename_template; + int fd; + + if (tmp_filename != nullptr) *tmp_filename = nullptr; + + gpr_asprintf(&filename_template, "/tmp/%s_XXXXXX", prefix); + GPR_ASSERT(filename_template != nullptr); + + fd = mkstemp(filename_template); + if (fd == -1) { + gpr_log(GPR_ERROR, "mkstemp failed for filename_template %s with error %s.", + filename_template, strerror(errno)); + goto end; + } + result = fdopen(fd, "w+"); + if (result == nullptr) { + gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).", + filename_template, fd, strerror(errno)); + unlink(filename_template); + close(fd); + goto end; + } + +end: + if (result != nullptr && tmp_filename != nullptr) { + *tmp_filename = filename_template; + } else { + gpr_free(filename_template); + } + return result; +} + +#endif /* GPR_POSIX_TMPFILE */ diff --git a/third_party/grpc/src/core/lib/gpr/tmpfile_windows.cc b/third_party/grpc/src/core/lib/gpr/tmpfile_windows.cc new file mode 100644 index 00000000000000..d4868084189c1f --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/tmpfile_windows.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_TMPFILE + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string_windows.h" +#include "src/core/lib/gpr/tmpfile.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_WINDOWS_TMPFILE */ diff --git a/third_party/grpc/src/core/lib/gpr/useful.h b/third_party/grpc/src/core/lib/gpr/useful.h new file mode 100644 index 00000000000000..a4e73b9a6133d3 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/useful.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPR_USEFUL_H +#define GRPC_CORE_LIB_GPR_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) + +#define GPR_ICMP(a, b) ((a) < (b) ? -1 : ((a) > (b) ? 1 : 0)) + +#define GPR_HASH_POINTER(x, range) \ + ((((size_t)x) >> 4) ^ (((size_t)x) >> 9) ^ (((size_t)x) >> 14)) % (range) + +#endif /* GRPC_CORE_LIB_GPR_USEFUL_H */ diff --git a/third_party/grpc/src/core/lib/gpr/wrap_memcpy.cc b/third_party/grpc/src/core/lib/gpr/wrap_memcpy.cc new file mode 100644 index 00000000000000..9b8608e056cc38 --- /dev/null +++ b/third_party/grpc/src/core/lib/gpr/wrap_memcpy.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +/* Provide a wrapped memcpy for targets that need to be backwards + * compatible with older libc's. + * + * Enable by setting LDFLAGS=-Wl,-wrap,memcpy when linking. + */ + +extern "C" { +#ifdef __linux__ +#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT) +__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); +void* __wrap_memcpy(void* destination, const void* source, size_t num) { + return memcpy(destination, source, num); +} +#else /* !__x86_64__ */ +void* __wrap_memcpy(void* destination, const void* source, size_t num) { + return memmove(destination, source, num); +} +#endif +#endif +} diff --git a/third_party/grpc/src/core/lib/gprpp/README.md b/third_party/grpc/src/core/lib/gprpp/README.md new file mode 100644 index 00000000000000..eab018bb312009 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/README.md @@ -0,0 +1,16 @@ +# GPR++ - Google Portable Runtime for C++ + +The files in this directory contain various utility code for C++ code. +None of this code is gRPC-specific; anything here may also be useful +for other open source projects written in C++. + +Note that this is one of the few places in src/core where we allow +the use of portability macros. + +Note that this is the only place in src/core where we allow +use of the C++ standard library (i.e., anything in the `std::` +namespace). And for now, we require that any use of the +standard library is build-time-only -- i.e., we do not allow +run-time dependencies on libstdc++. For more details, see +[gRFC L6](/grpc/proposal/blob/master/L6-allow-c%2B%2B-in-grpc-core.md) and +[Moving gRPC core to C++](/grpc/grpc/blob/master/doc/core/moving-to-c%2B%2B.md). diff --git a/third_party/grpc/src/core/lib/gprpp/abstract.h b/third_party/grpc/src/core/lib/gprpp/abstract.h new file mode 100644 index 00000000000000..5b7018e07e0350 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/abstract.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_ABSTRACT_H +#define GRPC_CORE_LIB_GPRPP_ABSTRACT_H + +// This is needed to support abstract base classes in the c core. Since gRPC +// doesn't have a c++ runtime, it will hit a linker error on delete unless +// we define a virtual operator delete. See this blog for more info: +// https://eli.thegreenplace.net/2015/c-deleting-destructors-and-virtual-operator-delete/ +#define GRPC_ABSTRACT_BASE_CLASS \ + static void operator delete(void* p) { abort(); } + +// gRPC currently can't depend on libstdc++, so we can't use "= 0" for +// pure virtual methods. Instead, we use this macro. +#define GRPC_ABSTRACT \ + { \ + gpr_log(GPR_ERROR, "Function marked GRPC_ABSTRACT was not implemented"); \ + GPR_ASSERT(false); \ + } + +#endif /* GRPC_CORE_LIB_GPRPP_ABSTRACT_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/atomic.h b/third_party/grpc/src/core/lib/gprpp/atomic.h new file mode 100644 index 00000000000000..8b08fc4e9c4a03 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/atomic.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_H +#define GRPC_CORE_LIB_GPRPP_ATOMIC_H + +#include + +#ifdef GPR_HAS_CXX11_ATOMIC +#include "src/core/lib/gprpp/atomic_with_std.h" +#else +#include "src/core/lib/gprpp/atomic_with_atm.h" +#endif + +#endif /* GRPC_CORE_LIB_GPRPP_ATOMIC_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/atomic_with_atm.h b/third_party/grpc/src/core/lib/gprpp/atomic_with_atm.h new file mode 100644 index 00000000000000..3d0021bb1ce918 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/atomic_with_atm.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H +#define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H + +#include + +#include + +namespace grpc_core { + +enum MemoryOrderRelaxed { memory_order_relaxed }; + +template +class atomic; + +template <> +class atomic { + public: + atomic() { gpr_atm_no_barrier_store(&x_, static_cast(false)); } + explicit atomic(bool x) { + gpr_atm_no_barrier_store(&x_, static_cast(x)); + } + + bool compare_exchange_strong(bool& expected, bool update, MemoryOrderRelaxed, + MemoryOrderRelaxed) { + if (!gpr_atm_no_barrier_cas(&x_, static_cast(expected), + static_cast(update))) { + expected = gpr_atm_no_barrier_load(&x_) != 0; + return false; + } + return true; + } + + private: + gpr_atm x_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_ATM_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/atomic_with_std.h b/third_party/grpc/src/core/lib/gprpp/atomic_with_std.h new file mode 100644 index 00000000000000..a4ad16e5cf731a --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/atomic_with_std.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H +#define GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H + +#include + +#include + +namespace grpc_core { + +template +using atomic = std::atomic; + +typedef std::memory_order memory_order; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_ATOMIC_WITH_STD_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/debug_location.h b/third_party/grpc/src/core/lib/gprpp/debug_location.h new file mode 100644 index 00000000000000..287761beafbda7 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/debug_location.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_DEBUG_LOCATION_H +#define GRPC_CORE_LIB_GPRPP_DEBUG_LOCATION_H + +namespace grpc_core { + +// Used for tracking file and line where a call is made for debug builds. +// No-op for non-debug builds. +// Callers can use the DEBUG_LOCATION macro in either case. +#ifndef NDEBUG +class DebugLocation { + public: + DebugLocation(const char* file, int line) : file_(file), line_(line) {} + bool Log() const { return true; } + const char* file() const { return file_; } + int line() const { return line_; } + + private: + const char* file_; + const int line_; +}; +#define DEBUG_LOCATION ::grpc_core::DebugLocation(__FILE__, __LINE__) +#else +class DebugLocation { + public: + bool Log() const { return false; } + const char* file() const { return nullptr; } + int line() const { return -1; } +}; +#define DEBUG_LOCATION ::grpc_core::DebugLocation() +#endif + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_DEBUG_LOCATION_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/fork.cc b/third_party/grpc/src/core/lib/gprpp/fork.cc new file mode 100644 index 00000000000000..3b9c16510a71fc --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/fork.cc @@ -0,0 +1,268 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/gprpp/fork.h" + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/memory.h" + +/* + * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK + * AROUND VERY SPECIFIC USE CASES. + */ + +namespace grpc_core { +namespace internal { +// The exec_ctx_count has 2 modes, blocked and unblocked. +// When unblocked, the count is 2-indexed; exec_ctx_count=2 indicates +// 0 active ExecCtxs, exex_ctx_count=3 indicates 1 active ExecCtxs... + +// When blocked, the exec_ctx_count is 0-indexed. Note that ExecCtx +// creation can only be blocked if there is exactly 1 outstanding ExecCtx, +// meaning that BLOCKED and UNBLOCKED counts partition the integers +#define UNBLOCKED(n) (n + 2) +#define BLOCKED(n) (n) + +class ExecCtxState { + public: + ExecCtxState() : fork_complete_(true) { + gpr_mu_init(&mu_); + gpr_cv_init(&cv_); + gpr_atm_no_barrier_store(&count_, UNBLOCKED(0)); + } + + void IncExecCtxCount() { + gpr_atm count = gpr_atm_no_barrier_load(&count_); + while (true) { + if (count <= BLOCKED(1)) { + // This only occurs if we are trying to fork. Wait until the fork() + // operation completes before allowing new ExecCtxs. + gpr_mu_lock(&mu_); + if (gpr_atm_no_barrier_load(&count_) <= BLOCKED(1)) { + while (!fork_complete_) { + gpr_cv_wait(&cv_, &mu_, gpr_inf_future(GPR_CLOCK_REALTIME)); + } + } + gpr_mu_unlock(&mu_); + } else if (gpr_atm_no_barrier_cas(&count_, count, count + 1)) { + break; + } + count = gpr_atm_no_barrier_load(&count_); + } + } + + void DecExecCtxCount() { gpr_atm_no_barrier_fetch_add(&count_, -1); } + + bool BlockExecCtx() { + // Assumes there is an active ExecCtx when this function is called + if (gpr_atm_no_barrier_cas(&count_, UNBLOCKED(1), BLOCKED(1))) { + gpr_mu_lock(&mu_); + fork_complete_ = false; + gpr_mu_unlock(&mu_); + return true; + } + return false; + } + + void AllowExecCtx() { + gpr_mu_lock(&mu_); + gpr_atm_no_barrier_store(&count_, UNBLOCKED(0)); + fork_complete_ = true; + gpr_cv_broadcast(&cv_); + gpr_mu_unlock(&mu_); + } + + ~ExecCtxState() { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&cv_); + } + + private: + bool fork_complete_; + gpr_mu mu_; + gpr_cv cv_; + gpr_atm count_; +}; + +class ThreadState { + public: + ThreadState() : awaiting_threads_(false), threads_done_(false), count_(0) { + gpr_mu_init(&mu_); + gpr_cv_init(&cv_); + } + + void IncThreadCount() { + gpr_mu_lock(&mu_); + count_++; + gpr_mu_unlock(&mu_); + } + + void DecThreadCount() { + gpr_mu_lock(&mu_); + count_--; + if (awaiting_threads_ && count_ == 0) { + threads_done_ = true; + gpr_cv_signal(&cv_); + } + gpr_mu_unlock(&mu_); + } + void AwaitThreads() { + gpr_mu_lock(&mu_); + awaiting_threads_ = true; + threads_done_ = (count_ == 0); + while (!threads_done_) { + gpr_cv_wait(&cv_, &mu_, gpr_inf_future(GPR_CLOCK_REALTIME)); + } + awaiting_threads_ = true; + gpr_mu_unlock(&mu_); + } + + ~ThreadState() { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&cv_); + } + + private: + bool awaiting_threads_; + bool threads_done_; + gpr_mu mu_; + gpr_cv cv_; + int count_; +}; + +} // namespace + +void Fork::GlobalInit() { + if (!override_enabled_) { +#ifdef GRPC_ENABLE_FORK_SUPPORT + support_enabled_ = true; +#else + support_enabled_ = false; +#endif + bool env_var_set = false; + char* env = gpr_getenv("GRPC_ENABLE_FORK_SUPPORT"); + if (env != nullptr) { + static const char* truthy[] = {"yes", "Yes", "YES", "true", + "True", "TRUE", "1"}; + static const char* falsey[] = {"no", "No", "NO", "false", + "False", "FALSE", "0"}; + for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { + if (0 == strcmp(env, truthy[i])) { + support_enabled_ = true; + env_var_set = true; + break; + } + } + if (!env_var_set) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(falsey); i++) { + if (0 == strcmp(env, falsey[i])) { + support_enabled_ = false; + env_var_set = true; + break; + } + } + } + gpr_free(env); + } + } + if (support_enabled_) { + exec_ctx_state_ = grpc_core::New(); + thread_state_ = grpc_core::New(); + } +} + +void Fork::GlobalShutdown() { + if (support_enabled_) { + grpc_core::Delete(exec_ctx_state_); + grpc_core::Delete(thread_state_); + } +} + +bool Fork::Enabled() { return support_enabled_; } + +// Testing Only +void Fork::Enable(bool enable) { + override_enabled_ = true; + support_enabled_ = enable; +} + +void Fork::IncExecCtxCount() { + if (support_enabled_) { + exec_ctx_state_->IncExecCtxCount(); + } +} + +void Fork::DecExecCtxCount() { + if (support_enabled_) { + exec_ctx_state_->DecExecCtxCount(); + } +} + +void Fork::SetResetChildPollingEngineFunc( + Fork::child_postfork_func reset_child_polling_engine) { + reset_child_polling_engine_ = reset_child_polling_engine; +} +Fork::child_postfork_func Fork::GetResetChildPollingEngineFunc() { + return reset_child_polling_engine_; +} + +bool Fork::BlockExecCtx() { + if (support_enabled_) { + return exec_ctx_state_->BlockExecCtx(); + } + return false; +} + +void Fork::AllowExecCtx() { + if (support_enabled_) { + exec_ctx_state_->AllowExecCtx(); + } +} + +void Fork::IncThreadCount() { + if (support_enabled_) { + thread_state_->IncThreadCount(); + } +} + +void Fork::DecThreadCount() { + if (support_enabled_) { + thread_state_->DecThreadCount(); + } +} +void Fork::AwaitThreads() { + if (support_enabled_) { + thread_state_->AwaitThreads(); + } +} + +internal::ExecCtxState* Fork::exec_ctx_state_ = nullptr; +internal::ThreadState* Fork::thread_state_ = nullptr; +bool Fork::support_enabled_ = false; +bool Fork::override_enabled_ = false; +Fork::child_postfork_func Fork::reset_child_polling_engine_ = nullptr; +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/gprpp/fork.h b/third_party/grpc/src/core/lib/gprpp/fork.h new file mode 100644 index 00000000000000..5a7404f0d91721 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/fork.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_FORK_H +#define GRPC_CORE_LIB_GPRPP_FORK_H + +/* + * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK + * AROUND VERY SPECIFIC USE CASES. + */ + +namespace grpc_core { + +namespace internal { +class ExecCtxState; +class ThreadState; +} // namespace internal + +class Fork { + public: + typedef void (*child_postfork_func)(void); + + static void GlobalInit(); + static void GlobalShutdown(); + + // Returns true if fork suppport is enabled, false otherwise + static bool Enabled(); + + // Increment the count of active ExecCtxs. + // Will block until a pending fork is complete if one is in progress. + static void IncExecCtxCount(); + + // Decrement the count of active ExecCtxs + static void DecExecCtxCount(); + + // Provide a function that will be invoked in the child's postfork handler to + // reset the polling engine's internal state. + static void SetResetChildPollingEngineFunc( + child_postfork_func reset_child_polling_engine); + static child_postfork_func GetResetChildPollingEngineFunc(); + + // Check if there is a single active ExecCtx + // (the one used to invoke this function). If there are more, + // return false. Otherwise, return true and block creation of + // more ExecCtx s until AlloWExecCtx() is called + // + static bool BlockExecCtx(); + static void AllowExecCtx(); + + // Increment the count of active threads. + static void IncThreadCount(); + + // Decrement the count of active threads. + static void DecThreadCount(); + + // Await all core threads to be joined. + static void AwaitThreads(); + + // Test only: overrides environment variables/compile flags + // Must be called before grpc_init() + static void Enable(bool enable); + + private: + static internal::ExecCtxState* exec_ctx_state_; + static internal::ThreadState* thread_state_; + static bool support_enabled_; + static bool override_enabled_; + static child_postfork_func reset_child_polling_engine_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_FORK_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/inlined_vector.h b/third_party/grpc/src/core/lib/gprpp/inlined_vector.h new file mode 100644 index 00000000000000..66dc751a5673d2 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/inlined_vector.h @@ -0,0 +1,200 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H +#define GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H + +#include + +#include +#include + +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { + +// NOTE: We eventually want to use absl::InlinedVector here. However, +// there are currently build problems that prevent us from using absl. +// In the interim, we define a custom implementation as a place-holder, +// with the intent to eventually replace this with the absl +// implementation. +// +// This place-holder implementation does not implement the full set of +// functionality from the absl version; it has just the methods that we +// currently happen to need in gRPC. If additional functionality is +// needed before this gets replaced with the absl version, it can be +// added, with the following proviso: +// +// ANY METHOD ADDED HERE MUST COMPLY WITH THE INTERFACE IN THE absl +// IMPLEMENTATION! +// +// TODO(nnoble, roth): Replace this with absl::InlinedVector once we +// integrate absl into the gRPC build system in a usable way. +template +class InlinedVector { + public: + InlinedVector() { init_data(); } + ~InlinedVector() { destroy_elements(); } + + // copy constructor + InlinedVector(const InlinedVector& v) { + init_data(); + copy_from(v); + } + + InlinedVector& operator=(const InlinedVector& v) { + if (this != &v) { + clear(); + copy_from(v); + } + return *this; + } + + // move constructor + InlinedVector(InlinedVector&& v) { + init_data(); + move_from(v); + } + + InlinedVector& operator=(InlinedVector&& v) { + if (this != &v) { + clear(); + move_from(v); + } + return *this; + } + + T* data() { + return dynamic_ != nullptr ? dynamic_ : reinterpret_cast(inline_); + } + + const T* data() const { + return dynamic_ != nullptr ? dynamic_ : reinterpret_cast(inline_); + } + + T& operator[](size_t offset) { + assert(offset < size_); + return data()[offset]; + } + + const T& operator[](size_t offset) const { + assert(offset < size_); + return data()[offset]; + } + + void reserve(size_t capacity) { + if (capacity > capacity_) { + T* new_dynamic = static_cast(gpr_malloc(sizeof(T) * capacity)); + move_elements(data(), new_dynamic, size_); + gpr_free(dynamic_); + dynamic_ = new_dynamic; + capacity_ = capacity; + } + } + + template + void emplace_back(Args&&... args) { + if (size_ == capacity_) { + reserve(capacity_ * 2); + } + new (&(data()[size_])) T(std::forward(args)...); + ++size_; + } + + void push_back(const T& value) { emplace_back(value); } + + void push_back(T&& value) { emplace_back(std::move(value)); } + + void pop_back() { + assert(!empty()); + size_t s = size(); + T& value = data()[s - 1]; + value.~T(); + size_--; + } + + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + + size_t capacity() const { return capacity_; } + + void clear() { + destroy_elements(); + init_data(); + } + + private: + void copy_from(const InlinedVector& v) { + // if v is allocated, make sure we have enough capacity. + if (v.dynamic_ != nullptr) { + reserve(v.capacity_); + } + // copy over elements + for (size_t i = 0; i < v.size_; ++i) { + new (&(data()[i])) T(v[i]); + } + // copy over metadata + size_ = v.size_; + capacity_ = v.capacity_; + } + + void move_from(InlinedVector& v) { + // if v is allocated, then we steal its dynamic array; otherwise, we + // move the elements individually. + if (v.dynamic_ != nullptr) { + dynamic_ = v.dynamic_; + } else { + move_elements(v.data(), data(), v.size_); + } + // copy over metadata + size_ = v.size_; + capacity_ = v.capacity_; + // null out the original + v.init_data(); + } + + static void move_elements(T* src, T* dst, size_t num_elements) { + for (size_t i = 0; i < num_elements; ++i) { + new (&dst[i]) T(std::move(src[i])); + src[i].~T(); + } + } + + void init_data() { + dynamic_ = nullptr; + size_ = 0; + capacity_ = N; + } + + void destroy_elements() { + for (size_t i = 0; i < size_; ++i) { + T& value = data()[i]; + value.~T(); + } + gpr_free(dynamic_); + } + + typename std::aligned_storage::type inline_[N]; + T* dynamic_; + size_t size_; + size_t capacity_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_INLINED_VECTOR_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/manual_constructor.h b/third_party/grpc/src/core/lib/gprpp/manual_constructor.h new file mode 100644 index 00000000000000..7f827ca8b76dc2 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/manual_constructor.h @@ -0,0 +1,213 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_MANUAL_CONSTRUCTOR_H +#define GRPC_CORE_LIB_GPRPP_MANUAL_CONSTRUCTOR_H + +// manually construct a region of memory with some type + +#include + +#include +#include +#include +#include +#include + +#include + +namespace grpc_core { + +// this contains templated helpers needed to implement the ManualConstructors +// in this file. +namespace manual_ctor_impl { + +// is_one_of returns true it a class, Member, is present in a variadic list of +// classes, List. +template +class is_one_of; + +template +class is_one_of { + public: + static constexpr const bool value = true; +}; + +template +class is_one_of { + public: + static constexpr const bool value = is_one_of::value; +}; + +template +class is_one_of { + public: + static constexpr const bool value = false; +}; + +// max_size_of returns sizeof(Type) for the largest type in the variadic list +// of classes, Types. +template +class max_size_of; + +template +class max_size_of { + public: + static constexpr const size_t value = sizeof(A); +}; + +template +class max_size_of { + public: + static constexpr const size_t value = sizeof(A) > max_size_of::value + ? sizeof(A) + : max_size_of::value; +}; + +// max_size_of returns alignof(Type) for the largest type in the variadic list +// of classes, Types. +template +class max_align_of; + +template +class max_align_of { + public: + static constexpr const size_t value = alignof(A); +}; + +template +class max_align_of { + public: + static constexpr const size_t value = alignof(A) > max_align_of::value + ? alignof(A) + : max_align_of::value; +}; + +} // namespace manual_ctor_impl + +template +class PolymorphicManualConstructor { + public: + // No constructor or destructor because one of the most useful uses of + // this class is as part of a union, and members of a union could not have + // constructors or destructors till C++11. And, anyway, the whole point of + // this class is to bypass constructor and destructor. + + BaseType* get() { return reinterpret_cast(&space_); } + const BaseType* get() const { + return reinterpret_cast(&space_); + } + + BaseType* operator->() { return get(); } + const BaseType* operator->() const { return get(); } + + BaseType& operator*() { return *get(); } + const BaseType& operator*() const { return *get(); } + + template + void Init() { + FinishInit(new (&space_) DerivedType); + } + + // Init() constructs the Type instance using the given arguments + // (which are forwarded to Type's constructor). + // + // Note that Init() with no arguments performs default-initialization, + // not zero-initialization (i.e it behaves the same as "new Type;", not + // "new Type();"), so it will leave non-class types uninitialized. + template + void Init(Ts&&... args) { + FinishInit(new (&space_) DerivedType(std::forward(args)...)); + } + + // Init() that is equivalent to copy and move construction. + // Enables usage like this: + // ManualConstructor> v; + // v.Init({1, 2, 3}); + template + void Init(const DerivedType& x) { + FinishInit(new (&space_) DerivedType(x)); + } + template + void Init(DerivedType&& x) { + FinishInit(new (&space_) DerivedType(std::move(x))); + } + + void Destroy() { get()->~BaseType(); } + + private: + template + void FinishInit(DerivedType* p) { + static_assert( + manual_ctor_impl::is_one_of::value, + "DerivedType must be one of the predeclared DerivedTypes"); + GPR_ASSERT(static_cast(p) == p); + } + + typename std::aligned_storage< + grpc_core::manual_ctor_impl::max_size_of::value, + grpc_core::manual_ctor_impl::max_align_of::value>::type + space_; +}; + +template +class ManualConstructor { + public: + // No constructor or destructor because one of the most useful uses of + // this class is as part of a union, and members of a union could not have + // constructors or destructors till C++11. And, anyway, the whole point of + // this class is to bypass constructor and destructor. + + Type* get() { return reinterpret_cast(&space_); } + const Type* get() const { return reinterpret_cast(&space_); } + + Type* operator->() { return get(); } + const Type* operator->() const { return get(); } + + Type& operator*() { return *get(); } + const Type& operator*() const { return *get(); } + + void Init() { new (&space_) Type; } + + // Init() constructs the Type instance using the given arguments + // (which are forwarded to Type's constructor). + // + // Note that Init() with no arguments performs default-initialization, + // not zero-initialization (i.e it behaves the same as "new Type;", not + // "new Type();"), so it will leave non-class types uninitialized. + template + void Init(Ts&&... args) { + new (&space_) Type(std::forward(args)...); + } + + // Init() that is equivalent to copy and move construction. + // Enables usage like this: + // ManualConstructor> v; + // v.Init({1, 2, 3}); + void Init(const Type& x) { new (&space_) Type(x); } + void Init(Type&& x) { new (&space_) Type(std::move(x)); } + + void Destroy() { get()->~Type(); } + + private: + typename std::aligned_storage::type space_; +}; + +} // namespace grpc_core + +#endif diff --git a/third_party/grpc/src/core/lib/gprpp/memory.h b/third_party/grpc/src/core/lib/gprpp/memory.h new file mode 100644 index 00000000000000..b4b63ae771aed3 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/memory.h @@ -0,0 +1,114 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_MEMORY_H +#define GRPC_CORE_LIB_GPRPP_MEMORY_H + +#include + +#include + +#include +#include +#include + +// Add this to a class that want to use Delete(), but has a private or +// protected destructor. +#define GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE \ + template \ + friend void grpc_core::Delete(T*); +// Add this to a class that want to use New(), but has a private or +// protected constructor. +#define GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW \ + template \ + friend T* grpc_core::New(Args&&...); + +namespace grpc_core { + +// Alternative to new, since we cannot use it (for fear of libstdc++) +template +inline T* New(Args&&... args) { + void* p = gpr_malloc(sizeof(T)); + return new (p) T(std::forward(args)...); +} + +// Alternative to delete, since we cannot use it (for fear of libstdc++) +template +inline void Delete(T* p) { + if (p == nullptr) return; + p->~T(); + gpr_free(p); +} + +template +class DefaultDelete { + public: + void operator()(T* p) { Delete(p); } +}; + +template > +using UniquePtr = std::unique_ptr; + +template +inline UniquePtr MakeUnique(Args&&... args) { + return UniquePtr(New(std::forward(args)...)); +} + +// an allocator that uses gpr_malloc/gpr_free +template +class Allocator { + public: + typedef T value_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef std::false_type propagate_on_container_move_assignment; + template + struct rebind { + typedef Allocator other; + }; + typedef std::true_type is_always_equal; + + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + pointer allocate(std::size_t n, + std::allocator::const_pointer hint = nullptr) { + return static_cast(gpr_malloc(n * sizeof(T))); + } + void deallocate(T* p, std::size_t n) { gpr_free(p); } + size_t max_size() const { + return std::numeric_limits::max() / sizeof(value_type); + } + void construct(pointer p, const_reference val) { new ((void*)p) T(val); } + template + void construct(U* p, Args&&... args) { + ::new ((void*)p) U(std::forward(args)...); + } + void destroy(pointer p) { p->~T(); } + template + void destroy(U* p) { + p->~U(); + } +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_MEMORY_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/mutex_lock.h b/third_party/grpc/src/core/lib/gprpp/mutex_lock.h new file mode 100644 index 00000000000000..54751d5fe46457 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/mutex_lock.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H +#define GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H + +#include + +#include + +namespace grpc_core { + +class MutexLock { + public: + explicit MutexLock(gpr_mu* mu) : mu_(mu) { gpr_mu_lock(mu); } + ~MutexLock() { gpr_mu_unlock(mu_); } + + MutexLock(const MutexLock&) = delete; + MutexLock& operator=(const MutexLock&) = delete; + + private: + gpr_mu* const mu_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_MUTEX_LOCK_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/orphanable.h b/third_party/grpc/src/core/lib/gprpp/orphanable.h new file mode 100644 index 00000000000000..9053c60111f03e --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/orphanable.h @@ -0,0 +1,133 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_ORPHANABLE_H +#define GRPC_CORE_LIB_GPRPP_ORPHANABLE_H + +#include + +#include +#include + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/debug_location.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" + +namespace grpc_core { + +// A base class for orphanable objects, which have one external owner +// but are not necessarily destroyed immediately when the external owner +// gives up ownership. Instead, the owner calls the object's Orphan() +// method, and the object then takes responsibility for its own cleanup +// and destruction. +class Orphanable { + public: + // Gives up ownership of the object. The implementation must arrange + // to eventually destroy the object without further interaction from the + // caller. + virtual void Orphan() GRPC_ABSTRACT; + + // Not copyable or movable. + Orphanable(const Orphanable&) = delete; + Orphanable& operator=(const Orphanable&) = delete; + + GRPC_ABSTRACT_BASE_CLASS + + protected: + Orphanable() {} + virtual ~Orphanable() {} +}; + +template +class OrphanableDelete { + public: + void operator()(T* p) { p->Orphan(); } +}; + +template > +using OrphanablePtr = std::unique_ptr; + +template +inline OrphanablePtr MakeOrphanable(Args&&... args) { + return OrphanablePtr(New(std::forward(args)...)); +} + +// A type of Orphanable with internal ref-counting. +template +class InternallyRefCounted : public Orphanable { + public: + // Not copyable nor movable. + InternallyRefCounted(const InternallyRefCounted&) = delete; + InternallyRefCounted& operator=(const InternallyRefCounted&) = delete; + + GRPC_ABSTRACT_BASE_CLASS + + protected: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE + + // Allow RefCountedPtr<> to access Unref() and IncrementRefCount(). + template + friend class RefCountedPtr; + + // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag. + // Note: RefCount tracing is only enabled on debug builds, even when a + // TraceFlag is used. + template + explicit InternallyRefCounted(TraceFlagT* trace_flag = nullptr) + : refs_(1, trace_flag) {} + virtual ~InternallyRefCounted() = default; + + RefCountedPtr Ref() GRPC_MUST_USE_RESULT { + IncrementRefCount(); + return RefCountedPtr(static_cast(this)); + } + RefCountedPtr Ref(const DebugLocation& location, + const char* reason) GRPC_MUST_USE_RESULT { + IncrementRefCount(location, reason); + return RefCountedPtr(static_cast(this)); + } + + void Unref() { + if (refs_.Unref()) { + Delete(static_cast(this)); + } + } + void Unref(const DebugLocation& location, const char* reason) { + if (refs_.Unref(location, reason)) { + Delete(static_cast(this)); + } + } + + private: + void IncrementRefCount() { refs_.Ref(); } + void IncrementRefCount(const DebugLocation& location, const char* reason) { + refs_.Ref(location, reason); + } + + grpc_core::RefCount refs_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_ORPHANABLE_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/ref_counted.h b/third_party/grpc/src/core/lib/gprpp/ref_counted.h new file mode 100644 index 00000000000000..fa97ffcfed28d2 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/ref_counted.h @@ -0,0 +1,245 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H +#define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H + +#include + +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/debug_location.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" + +namespace grpc_core { + +// PolymorphicRefCount enforces polymorphic destruction of RefCounted. +class PolymorphicRefCount { + public: + GRPC_ABSTRACT_BASE_CLASS + + protected: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE + + virtual ~PolymorphicRefCount() = default; +}; + +// NonPolymorphicRefCount does not enforce polymorphic destruction of +// RefCounted. Please refer to grpc_core::RefCounted for more details, and +// when in doubt use PolymorphicRefCount. +class NonPolymorphicRefCount { + public: + GRPC_ABSTRACT_BASE_CLASS + + protected: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE + + ~NonPolymorphicRefCount() = default; +}; + +// RefCount is a simple atomic ref-count. +// +// This is a C++ implementation of gpr_refcount, with inline functions. Due to +// inline functions, this class is significantly more efficient than +// gpr_refcount and should be preferred over gpr_refcount whenever possible. +// +// TODO(soheil): Remove gpr_refcount after submitting the GRFC and the paragraph +// above. +class RefCount { + public: + using Value = intptr_t; + + // `init` is the initial refcount stored in this object. + // + // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag. + // Note: RefCount tracing is only enabled on debug builds, even when a + // TraceFlag is used. + template + constexpr explicit RefCount(Value init = 1, TraceFlagT* trace_flag = nullptr) + : +#ifndef NDEBUG + trace_flag_(trace_flag), +#endif + value_(init) { + } + + // Increases the ref-count by `n`. + void Ref(Value n = 1) { + GPR_ATM_INC_ADD_THEN(value_.fetch_add(n, std::memory_order_relaxed)); + } + void Ref(const DebugLocation& location, const char* reason, Value n = 1) { +#ifndef NDEBUG + if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) { + const RefCount::Value old_refs = get(); + gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s", + trace_flag_->name(), this, location.file(), location.line(), + old_refs, old_refs + n, reason); + } +#endif + Ref(n); + } + + // Similar to Ref() with an assert on the ref-count being non-zero. + void RefNonZero() { +#ifndef NDEBUG + const Value prior = + GPR_ATM_INC_ADD_THEN(value_.fetch_add(1, std::memory_order_relaxed)); + assert(prior > 0); +#else + Ref(); +#endif + } + void RefNonZero(const DebugLocation& location, const char* reason) { +#ifndef NDEBUG + if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) { + const RefCount::Value old_refs = get(); + gpr_log(GPR_INFO, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s", + trace_flag_->name(), this, location.file(), location.line(), + old_refs, old_refs + 1, reason); + } +#endif + RefNonZero(); + } + + // Decrements the ref-count and returns true if the ref-count reaches 0. + bool Unref() { + const Value prior = + GPR_ATM_INC_ADD_THEN(value_.fetch_sub(1, std::memory_order_acq_rel)); + GPR_DEBUG_ASSERT(prior > 0); + return prior == 1; + } + bool Unref(const DebugLocation& location, const char* reason) { +#ifndef NDEBUG + if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) { + const RefCount::Value old_refs = get(); + gpr_log(GPR_INFO, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s", + trace_flag_->name(), this, location.file(), location.line(), + old_refs, old_refs - 1, reason); + } +#endif + return Unref(); + } + + private: + Value get() const { return value_.load(std::memory_order_relaxed); } + +#ifndef NDEBUG + TraceFlag* trace_flag_; +#endif + std::atomic value_; +}; + +// A base class for reference-counted objects. +// New objects should be created via New() and start with a refcount of 1. +// When the refcount reaches 0, the object will be deleted via Delete(). +// +// This will commonly be used by CRTP (curiously-recurring template pattern) +// e.g., class MyClass : public RefCounted +// +// Use PolymorphicRefCount and NonPolymorphicRefCount to select between +// different implementations of RefCounted. +// +// Note that NonPolymorphicRefCount does not support polymorphic destruction. +// So, use NonPolymorphicRefCount only when both of the following conditions +// are guaranteed to hold: +// (a) Child is a concrete leaf class in RefCounted, and +// (b) you are gauranteed to call Unref only on concrete leaf classes and not +// their parents. +// +// The following example is illegal, because calling Unref() will not call +// the dtor of Child. +// +// class Parent : public RefCounted {} +// class Child : public Parent {} +// +// Child* ch; +// ch->Unref(); +// +template +class RefCounted : public Impl { + public: + RefCountedPtr Ref() GRPC_MUST_USE_RESULT { + IncrementRefCount(); + return RefCountedPtr(static_cast(this)); + } + + RefCountedPtr Ref(const DebugLocation& location, + const char* reason) GRPC_MUST_USE_RESULT { + IncrementRefCount(location, reason); + return RefCountedPtr(static_cast(this)); + } + + // TODO(roth): Once all of our code is converted to C++ and can use + // RefCountedPtr<> instead of manual ref-counting, make this method + // private, since it will only be used by RefCountedPtr<>, which is a + // friend of this class. + void Unref() { + if (refs_.Unref()) { + Delete(static_cast(this)); + } + } + void Unref(const DebugLocation& location, const char* reason) { + if (refs_.Unref(location, reason)) { + Delete(static_cast(this)); + } + } + + // Not copyable nor movable. + RefCounted(const RefCounted&) = delete; + RefCounted& operator=(const RefCounted&) = delete; + + GRPC_ABSTRACT_BASE_CLASS + + protected: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_DELETE + + // TraceFlagT is defined to accept both DebugOnlyTraceFlag and TraceFlag. + // Note: RefCount tracing is only enabled on debug builds, even when a + // TraceFlag is used. + template + explicit RefCounted(TraceFlagT* trace_flag = nullptr) + : refs_(1, trace_flag) {} + + // Note: Depending on the Impl used, this dtor can be implicitly virtual. + ~RefCounted() = default; + + private: + // Allow RefCountedPtr<> to access IncrementRefCount(). + template + friend class RefCountedPtr; + + void IncrementRefCount() { refs_.Ref(); } + void IncrementRefCount(const DebugLocation& location, const char* reason) { + refs_.Ref(location, reason); + } + + RefCount refs_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/ref_counted_ptr.h b/third_party/grpc/src/core/lib/gprpp/ref_counted_ptr.h new file mode 100644 index 00000000000000..19f38d7f013086 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/ref_counted_ptr.h @@ -0,0 +1,185 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H +#define GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H + +#include + +#include +#include + +#include "src/core/lib/gprpp/debug_location.h" +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { + +// A smart pointer class for objects that provide IncrementRefCount() and +// Unref() methods, such as those provided by the RefCounted base class. +template +class RefCountedPtr { + public: + RefCountedPtr() {} + RefCountedPtr(std::nullptr_t) {} + + // If value is non-null, we take ownership of a ref to it. + template + explicit RefCountedPtr(Y* value) { + value_ = value; + } + + // Move ctors. + RefCountedPtr(RefCountedPtr&& other) { + value_ = other.value_; + other.value_ = nullptr; + } + template + RefCountedPtr(RefCountedPtr&& other) { + value_ = static_cast(other.value_); + other.value_ = nullptr; + } + + // Move assignment. + RefCountedPtr& operator=(RefCountedPtr&& other) { + reset(other.value_); + other.value_ = nullptr; + return *this; + } + template + RefCountedPtr& operator=(RefCountedPtr&& other) { + reset(other.value_); + other.value_ = nullptr; + return *this; + } + + // Copy ctors. + RefCountedPtr(const RefCountedPtr& other) { + if (other.value_ != nullptr) other.value_->IncrementRefCount(); + value_ = other.value_; + } + template + RefCountedPtr(const RefCountedPtr& other) { + static_assert(std::has_virtual_destructor::value, + "T does not have a virtual dtor"); + if (other.value_ != nullptr) other.value_->IncrementRefCount(); + value_ = static_cast(other.value_); + } + + // Copy assignment. + RefCountedPtr& operator=(const RefCountedPtr& other) { + // Note: Order of reffing and unreffing is important here in case value_ + // and other.value_ are the same object. + if (other.value_ != nullptr) other.value_->IncrementRefCount(); + reset(other.value_); + return *this; + } + template + RefCountedPtr& operator=(const RefCountedPtr& other) { + static_assert(std::has_virtual_destructor::value, + "T does not have a virtual dtor"); + // Note: Order of reffing and unreffing is important here in case value_ + // and other.value_ are the same object. + if (other.value_ != nullptr) other.value_->IncrementRefCount(); + reset(other.value_); + return *this; + } + + ~RefCountedPtr() { + if (value_ != nullptr) value_->Unref(); + } + + // If value is non-null, we take ownership of a ref to it. + void reset(T* value = nullptr) { + if (value_ != nullptr) value_->Unref(); + value_ = value; + } + void reset(const DebugLocation& location, const char* reason, + T* value = nullptr) { + if (value_ != nullptr) value_->Unref(location, reason); + value_ = value; + } + template + void reset(Y* value = nullptr) { + static_assert(std::has_virtual_destructor::value, + "T does not have a virtual dtor"); + if (value_ != nullptr) value_->Unref(); + value_ = static_cast(value); + } + template + void reset(const DebugLocation& location, const char* reason, + Y* value = nullptr) { + static_assert(std::has_virtual_destructor::value, + "T does not have a virtual dtor"); + if (value_ != nullptr) value_->Unref(location, reason); + value_ = static_cast(value); + } + + // TODO(roth): This method exists solely as a transition mechanism to allow + // us to pass a ref to idiomatic C code that does not use RefCountedPtr<>. + // Once all of our code has been converted to idiomatic C++, this + // method should go away. + T* release() { + T* value = value_; + value_ = nullptr; + return value; + } + + T* get() const { return value_; } + + T& operator*() const { return *value_; } + T* operator->() const { return value_; } + + template + bool operator==(const RefCountedPtr& other) const { + return value_ == other.value_; + } + + template + bool operator==(const Y* other) const { + return value_ == other; + } + + bool operator==(std::nullptr_t) const { return value_ == nullptr; } + + template + bool operator!=(const RefCountedPtr& other) const { + return value_ != other.value_; + } + + template + bool operator!=(const Y* other) const { + return value_ != other; + } + + bool operator!=(std::nullptr_t) const { return value_ != nullptr; } + + private: + template + friend class RefCountedPtr; + + T* value_ = nullptr; +}; + +template +inline RefCountedPtr MakeRefCounted(Args&&... args) { + return RefCountedPtr(New(std::forward(args)...)); +} + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_PTR_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/thd.h b/third_party/grpc/src/core/lib/gprpp/thd.h new file mode 100644 index 00000000000000..caf0652c1a70c1 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/thd.h @@ -0,0 +1,132 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_GPRPP_THD_H +#define GRPC_CORE_LIB_GPRPP_THD_H + +/** Internal thread interface. */ + +#include + +#include +#include +#include +#include + +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { +namespace internal { + +/// Base class for platform-specific thread-state +class ThreadInternalsInterface { + public: + virtual ~ThreadInternalsInterface() {} + virtual void Start() GRPC_ABSTRACT; + virtual void Join() GRPC_ABSTRACT; + GRPC_ABSTRACT_BASE_CLASS +}; + +} // namespace internal + +class Thread { + public: + /// Default constructor only to allow use in structs that lack constructors + /// Does not produce a validly-constructed thread; must later + /// use placement new to construct a real thread. Does not init mu_ and cv_ + Thread() : state_(FAKE), impl_(nullptr) {} + + /// Normal constructor to create a thread with name \a thd_name, + /// which will execute a thread based on function \a thd_body + /// with argument \a arg once it is started. + /// The optional \a success argument indicates whether the thread + /// is successfully created. + Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success = nullptr); + + /// Move constructor for thread. After this is called, the other thread + /// no longer represents a living thread object + Thread(Thread&& other) : state_(other.state_), impl_(other.impl_) { + other.state_ = MOVED; + other.impl_ = nullptr; + } + + /// Move assignment operator for thread. After this is called, the other + /// thread no longer represents a living thread object. Not allowed if this + /// thread actually exists + Thread& operator=(Thread&& other) { + if (this != &other) { + // TODO(vjpai): if we can be sure that all Thread's are actually + // constructed, then we should assert GPR_ASSERT(impl_ == nullptr) here. + // However, as long as threads come in structures that are + // allocated via gpr_malloc, this will not be the case, so we cannot + // assert it for the time being. + state_ = other.state_; + impl_ = other.impl_; + other.state_ = MOVED; + other.impl_ = nullptr; + } + return *this; + } + + /// The destructor is strictly optional; either the thread never came to life + /// and the constructor itself killed it or it has already been joined and + /// the Join function kills it. The destructor shouldn't have to do anything. + ~Thread() { GPR_ASSERT(impl_ == nullptr); } + + void Start() { + if (impl_ != nullptr) { + GPR_ASSERT(state_ == ALIVE); + state_ = STARTED; + impl_->Start(); + } else { + GPR_ASSERT(state_ == FAILED); + } + }; + + void Join() { + if (impl_ != nullptr) { + impl_->Join(); + grpc_core::Delete(impl_); + state_ = DONE; + impl_ = nullptr; + } else { + GPR_ASSERT(state_ == FAILED); + } + }; + + private: + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; + + /// The thread states are as follows: + /// FAKE -- just a dummy placeholder Thread created by the default constructor + /// ALIVE -- an actual thread of control exists associated with this thread + /// STARTED -- the thread of control has been started + /// DONE -- the thread of control has completed and been joined + /// FAILED -- the thread of control never came alive + /// MOVED -- contents were moved out and we're no longer tracking them + enum ThreadState { FAKE, ALIVE, STARTED, DONE, FAILED, MOVED }; + ThreadState state_; + internal::ThreadInternalsInterface* impl_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_GPRPP_THD_H */ diff --git a/third_party/grpc/src/core/lib/gprpp/thd_posix.cc b/third_party/grpc/src/core/lib/gprpp/thd_posix.cc new file mode 100644 index 00000000000000..2751b221a8f490 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/thd_posix.cc @@ -0,0 +1,160 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix implementation for gpr threads. */ + +#include + +#ifdef GPR_POSIX_SYNC + +#include "src/core/lib/gprpp/thd.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/fork.h" +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { +namespace { +class ThreadInternalsPosix; +struct thd_arg { + ThreadInternalsPosix* thread; + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + const char* name; /* name of thread. Can be nullptr. */ +}; + +class ThreadInternalsPosix + : public grpc_core::internal::ThreadInternalsInterface { + public: + ThreadInternalsPosix(const char* thd_name, void (*thd_body)(void* arg), + void* arg, bool* success) + : started_(false) { + gpr_mu_init(&mu_); + gpr_cv_init(&ready_); + pthread_attr_t attr; + /* don't use gpr_malloc as we may cause an infinite recursion with + * the profiling code */ + thd_arg* info = static_cast(malloc(sizeof(*info))); + GPR_ASSERT(info != nullptr); + info->thread = this; + info->body = thd_body; + info->arg = arg; + info->name = thd_name; + grpc_core::Fork::IncThreadCount(); + + GPR_ASSERT(pthread_attr_init(&attr) == 0); + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == + 0); + + *success = + (pthread_create(&pthread_id_, &attr, + [](void* v) -> void* { + thd_arg arg = *static_cast(v); + free(v); + if (arg.name != nullptr) { +#if GPR_APPLE_PTHREAD_NAME + /* Apple supports 64 characters, and will + * truncate if it's longer. */ + pthread_setname_np(arg.name); +#elif GPR_LINUX_PTHREAD_NAME + /* Linux supports 16 characters max, and will + * error if it's longer. */ + char buf[16]; + size_t buf_len = GPR_ARRAY_SIZE(buf) - 1; + strncpy(buf, arg.name, buf_len); + buf[buf_len] = '\0'; + pthread_setname_np(pthread_self(), buf); +#endif // GPR_APPLE_PTHREAD_NAME + } + + gpr_mu_lock(&arg.thread->mu_); + while (!arg.thread->started_) { + gpr_cv_wait(&arg.thread->ready_, &arg.thread->mu_, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&arg.thread->mu_); + + (*arg.body)(arg.arg); + grpc_core::Fork::DecThreadCount(); + return nullptr; + }, + info) == 0); + + GPR_ASSERT(pthread_attr_destroy(&attr) == 0); + + if (!(*success)) { + /* don't use gpr_free, as this was allocated using malloc (see above) */ + free(info); + grpc_core::Fork::DecThreadCount(); + } + }; + + ~ThreadInternalsPosix() override { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&ready_); + } + + void Start() override { + gpr_mu_lock(&mu_); + started_ = true; + gpr_cv_signal(&ready_); + gpr_mu_unlock(&mu_); + } + + void Join() override { pthread_join(pthread_id_, nullptr); } + + private: + gpr_mu mu_; + gpr_cv ready_; + bool started_; + pthread_t pthread_id_; +}; + +} // namespace + +Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success) { + bool outcome = false; + impl_ = + grpc_core::New(thd_name, thd_body, arg, &outcome); + if (outcome) { + state_ = ALIVE; + } else { + state_ = FAILED; + grpc_core::Delete(impl_); + impl_ = nullptr; + } + + if (success != nullptr) { + *success = outcome; + } +} +} // namespace grpc_core + +// The following is in the external namespace as it is exposed as C89 API +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } + +#endif /* GPR_POSIX_SYNC */ diff --git a/third_party/grpc/src/core/lib/gprpp/thd_windows.cc b/third_party/grpc/src/core/lib/gprpp/thd_windows.cc new file mode 100644 index 00000000000000..71584fd358e528 --- /dev/null +++ b/third_party/grpc/src/core/lib/gprpp/thd_windows.cc @@ -0,0 +1,155 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows implementation for gpr threads. */ + +#include + +#ifdef GPR_WINDOWS + +#include "src/core/lib/gprpp/thd.h" + +#include +#include +#include +#include + +#include "src/core/lib/gprpp/memory.h" + +#if defined(_MSC_VER) +#define thread_local __declspec(thread) +#define WIN_LAMBDA +#elif defined(__GNUC__) +#define thread_local __thread +#define WIN_LAMBDA WINAPI +#else +#error "Unknown compiler - please file a bug report" +#endif + +namespace { +class ThreadInternalsWindows; +struct thd_info { + ThreadInternalsWindows* thread; + void (*body)(void* arg); /* body of a thread */ + void* arg; /* argument to a thread */ + HANDLE join_event; /* the join event */ +}; + +thread_local struct thd_info* g_thd_info; + +class ThreadInternalsWindows + : public grpc_core::internal::ThreadInternalsInterface { + public: + ThreadInternalsWindows(void (*thd_body)(void* arg), void* arg, bool* success) + : started_(false) { + gpr_mu_init(&mu_); + gpr_cv_init(&ready_); + + HANDLE handle; + info_ = (struct thd_info*)gpr_malloc(sizeof(*info_)); + info_->thread = this; + info_->body = thd_body; + info_->arg = arg; + + info_->join_event = CreateEvent(nullptr, FALSE, FALSE, nullptr); + if (info_->join_event == nullptr) { + gpr_free(info_); + *success = false; + } else { + handle = CreateThread( + nullptr, 64 * 1024, + [](void* v) WIN_LAMBDA -> DWORD { + g_thd_info = static_cast(v); + gpr_mu_lock(&g_thd_info->thread->mu_); + while (!g_thd_info->thread->started_) { + gpr_cv_wait(&g_thd_info->thread->ready_, &g_thd_info->thread->mu_, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + gpr_mu_unlock(&g_thd_info->thread->mu_); + g_thd_info->body(g_thd_info->arg); + BOOL ret = SetEvent(g_thd_info->join_event); + GPR_ASSERT(ret); + return 0; + }, + info_, 0, nullptr); + if (handle == nullptr) { + destroy_thread(); + *success = false; + } else { + CloseHandle(handle); + *success = true; + } + } + } + + ~ThreadInternalsWindows() override { + gpr_mu_destroy(&mu_); + gpr_cv_destroy(&ready_); + } + + void Start() override { + gpr_mu_lock(&mu_); + started_ = true; + gpr_cv_signal(&ready_); + gpr_mu_unlock(&mu_); + } + + void Join() override { + DWORD ret = WaitForSingleObject(info_->join_event, INFINITE); + GPR_ASSERT(ret == WAIT_OBJECT_0); + destroy_thread(); + } + + private: + void destroy_thread() { + CloseHandle(info_->join_event); + gpr_free(info_); + } + + gpr_mu mu_; + gpr_cv ready_; + bool started_; + thd_info* info_; +}; + +} // namespace + +namespace grpc_core { + +Thread::Thread(const char* thd_name, void (*thd_body)(void* arg), void* arg, + bool* success) { + bool outcome = false; + impl_ = grpc_core::New(thd_body, arg, &outcome); + if (outcome) { + state_ = ALIVE; + } else { + state_ = FAILED; + grpc_core::Delete(impl_); + impl_ = nullptr; + } + + if (success != nullptr) { + *success = outcome; + } +} + +} // namespace grpc_core + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } + +#endif /* GPR_WINDOWS */ diff --git a/third_party/grpc/src/core/lib/http/format_request.cc b/third_party/grpc/src/core/lib/http/format_request.cc new file mode 100644 index 00000000000000..17123446483818 --- /dev/null +++ b/third_party/grpc/src/core/lib/http/format_request.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/http/format_request.h" + +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/gpr/string.h" + +static void fill_common_header(const grpc_httpcli_request* request, + gpr_strvec* buf, bool connection_close) { + size_t i; + gpr_strvec_add(buf, gpr_strdup(request->http.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")); + if (connection_close) + 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->http.hdr_count; i++) { + gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].key)); + gpr_strvec_add(buf, gpr_strdup(": ")); + gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].value)); + gpr_strvec_add(buf, gpr_strdup("\r\n")); + } +} + +grpc_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, true); + gpr_strvec_add(&out, gpr_strdup("\r\n")); + + flat = gpr_strvec_flatten(&out, &flat_len); + gpr_strvec_destroy(&out); + + return grpc_slice_new(flat, flat_len, gpr_free); +} + +grpc_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, true); + if (body_bytes) { + uint8_t has_content_type = 0; + for (i = 0; i < request->http.hdr_count; i++) { + if (strcmp(request->http.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", + static_cast(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 = static_cast(gpr_realloc(tmp, out_len + body_size)); + memcpy(tmp + out_len, body_bytes, body_size); + out_len += body_size; + } + + return grpc_slice_new(tmp, out_len, gpr_free); +} + +grpc_slice grpc_httpcli_format_connect_request( + const grpc_httpcli_request* request) { + gpr_strvec out; + gpr_strvec_init(&out); + gpr_strvec_add(&out, gpr_strdup("CONNECT ")); + fill_common_header(request, &out, false); + gpr_strvec_add(&out, gpr_strdup("\r\n")); + size_t flat_len; + char* flat = gpr_strvec_flatten(&out, &flat_len); + gpr_strvec_destroy(&out); + return grpc_slice_new(flat, flat_len, gpr_free); +} diff --git a/third_party/grpc/src/core/lib/http/format_request.h b/third_party/grpc/src/core/lib/http/format_request.h new file mode 100644 index 00000000000000..bcc332fe6e7540 --- /dev/null +++ b/third_party/grpc/src/core/lib/http/format_request.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H +#define GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H + +#include + +#include +#include "src/core/lib/http/httpcli.h" + +grpc_slice grpc_httpcli_format_get_request(const grpc_httpcli_request* request); +grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request* request, + const char* body_bytes, + size_t body_size); +grpc_slice grpc_httpcli_format_connect_request( + const grpc_httpcli_request* request); + +#endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */ diff --git a/third_party/grpc/src/core/lib/http/httpcli.cc b/third_party/grpc/src/core/lib/http/httpcli.cc new file mode 100644 index 00000000000000..3bd7a2ce5907a2 --- /dev/null +++ b/third_party/grpc/src/core/lib/http/httpcli.cc @@ -0,0 +1,303 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/http/httpcli.h" + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/http/format_request.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/slice/slice_internal.h" + +typedef struct { + grpc_slice request_text; + grpc_http_parser parser; + grpc_resolved_addresses* addresses; + size_t next_address; + grpc_endpoint* ep; + char* host; + char* ssl_host_override; + grpc_millis deadline; + int have_read_byte; + const grpc_httpcli_handshaker* handshaker; + grpc_closure* on_done; + grpc_httpcli_context* context; + grpc_polling_entity* pollent; + grpc_iomgr_object iomgr_obj; + grpc_slice_buffer incoming; + grpc_slice_buffer outgoing; + grpc_closure on_read; + grpc_closure done_write; + grpc_closure connected; + grpc_error* overall_error; + grpc_resource_quota* resource_quota; +} internal_request; + +static grpc_httpcli_get_override g_get_override = nullptr; +static grpc_httpcli_post_override g_post_override = nullptr; + +static void plaintext_handshake(void* arg, grpc_endpoint* endpoint, + const char* host, grpc_millis deadline, + 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) { + context->pollset_set = grpc_pollset_set_create(); +} + +void grpc_httpcli_context_destroy(grpc_httpcli_context* context) { + grpc_pollset_set_destroy(context->pollset_set); +} + +static void next_address(internal_request* req, grpc_error* due_to_error); + +static void finish(internal_request* req, grpc_error* error) { + grpc_polling_entity_del_from_pollset_set(req->pollent, + req->context->pollset_set); + GRPC_CLOSURE_SCHED(req->on_done, error); + grpc_http_parser_destroy(&req->parser); + if (req->addresses != nullptr) { + grpc_resolved_addresses_destroy(req->addresses); + } + if (req->ep != nullptr) { + grpc_endpoint_destroy(req->ep); + } + grpc_slice_unref_internal(req->request_text); + gpr_free(req->host); + gpr_free(req->ssl_host_override); + grpc_iomgr_unregister_object(&req->iomgr_obj); + grpc_slice_buffer_destroy_internal(&req->incoming); + grpc_slice_buffer_destroy_internal(&req->outgoing); + GRPC_ERROR_UNREF(req->overall_error); + grpc_resource_quota_unref_internal(req->resource_quota); + gpr_free(req); +} + +static void append_error(internal_request* req, grpc_error* error) { + if (req->overall_error == GRPC_ERROR_NONE) { + req->overall_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed HTTP/1 client request"); + } + grpc_resolved_address* addr = &req->addresses->addrs[req->next_address - 1]; + char* addr_text = grpc_sockaddr_to_uri(addr); + req->overall_error = grpc_error_add_child( + req->overall_error, + grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(addr_text))); + gpr_free(addr_text); +} + +static void do_read(internal_request* req) { + grpc_endpoint_read(req->ep, &req->incoming, &req->on_read); +} + +static void on_read(void* user_data, grpc_error* error) { + internal_request* req = static_cast(user_data); + size_t i; + + for (i = 0; i < req->incoming.count; i++) { + if (GRPC_SLICE_LENGTH(req->incoming.slices[i])) { + req->have_read_byte = 1; + grpc_error* err = grpc_http_parser_parse( + &req->parser, req->incoming.slices[i], nullptr); + if (err != GRPC_ERROR_NONE) { + finish(req, err); + return; + } + } + } + + if (error == GRPC_ERROR_NONE) { + do_read(req); + } else if (!req->have_read_byte) { + next_address(req, GRPC_ERROR_REF(error)); + } else { + finish(req, grpc_http_parser_eof(&req->parser)); + } +} + +static void on_written(internal_request* req) { do_read(req); } + +static void done_write(void* arg, grpc_error* error) { + internal_request* req = static_cast(arg); + if (error == GRPC_ERROR_NONE) { + on_written(req); + } else { + next_address(req, GRPC_ERROR_REF(error)); + } +} + +static void start_write(internal_request* req) { + grpc_slice_ref_internal(req->request_text); + grpc_slice_buffer_add(&req->outgoing, req->request_text); + grpc_endpoint_write(req->ep, &req->outgoing, &req->done_write, nullptr); +} + +static void on_handshake_done(void* arg, grpc_endpoint* ep) { + internal_request* req = static_cast(arg); + + if (!ep) { + next_address(req, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Unexplained handshake failure")); + return; + } + + req->ep = ep; + start_write(req); +} + +static void on_connected(void* arg, grpc_error* error) { + internal_request* req = static_cast(arg); + + if (!req->ep) { + next_address(req, GRPC_ERROR_REF(error)); + return; + } + req->handshaker->handshake( + req, req->ep, req->ssl_host_override ? req->ssl_host_override : req->host, + req->deadline, on_handshake_done); +} + +static void next_address(internal_request* req, grpc_error* error) { + grpc_resolved_address* addr; + if (error != GRPC_ERROR_NONE) { + append_error(req, error); + } + if (req->next_address == req->addresses->naddrs) { + finish(req, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed HTTP requests to all targets", &req->overall_error, 1)); + return; + } + addr = &req->addresses->addrs[req->next_address++]; + GRPC_CLOSURE_INIT(&req->connected, on_connected, req, + grpc_schedule_on_exec_ctx); + grpc_arg arg = grpc_channel_arg_pointer_create( + (char*)GRPC_ARG_RESOURCE_QUOTA, req->resource_quota, + grpc_resource_quota_arg_vtable()); + grpc_channel_args args = {1, &arg}; + grpc_tcp_client_connect(&req->connected, &req->ep, req->context->pollset_set, + &args, addr, req->deadline); +} + +static void on_resolved(void* arg, grpc_error* error) { + internal_request* req = static_cast(arg); + if (error != GRPC_ERROR_NONE) { + finish(req, GRPC_ERROR_REF(error)); + return; + } + req->next_address = 0; + next_address(req, GRPC_ERROR_NONE); +} + +static void internal_request_begin(grpc_httpcli_context* context, + grpc_polling_entity* pollent, + grpc_resource_quota* resource_quota, + const grpc_httpcli_request* request, + grpc_millis deadline, grpc_closure* on_done, + grpc_httpcli_response* response, + const char* name, grpc_slice request_text) { + internal_request* req = + static_cast(gpr_malloc(sizeof(internal_request))); + memset(req, 0, sizeof(*req)); + req->request_text = request_text; + grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response); + req->on_done = on_done; + req->deadline = deadline; + req->handshaker = + request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; + req->context = context; + req->pollent = pollent; + req->overall_error = GRPC_ERROR_NONE; + req->resource_quota = grpc_resource_quota_ref_internal(resource_quota); + GRPC_CLOSURE_INIT(&req->on_read, on_read, req, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&req->done_write, done_write, req, + grpc_schedule_on_exec_ctx); + grpc_slice_buffer_init(&req->incoming); + grpc_slice_buffer_init(&req->outgoing); + grpc_iomgr_register_object(&req->iomgr_obj, name); + req->host = gpr_strdup(request->host); + req->ssl_host_override = gpr_strdup(request->ssl_host_override); + + GPR_ASSERT(pollent); + grpc_polling_entity_add_to_pollset_set(req->pollent, + req->context->pollset_set); + grpc_resolve_address( + request->host, req->handshaker->default_port, req->context->pollset_set, + GRPC_CLOSURE_CREATE(on_resolved, req, grpc_schedule_on_exec_ctx), + &req->addresses); +} + +void grpc_httpcli_get(grpc_httpcli_context* context, + grpc_polling_entity* pollent, + grpc_resource_quota* resource_quota, + const grpc_httpcli_request* request, grpc_millis deadline, + grpc_closure* on_done, grpc_httpcli_response* response) { + char* name; + if (g_get_override && g_get_override(request, deadline, on_done, response)) { + return; + } + gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path); + internal_request_begin(context, pollent, resource_quota, request, deadline, + on_done, response, name, + grpc_httpcli_format_get_request(request)); + gpr_free(name); +} + +void grpc_httpcli_post(grpc_httpcli_context* context, + grpc_polling_entity* pollent, + grpc_resource_quota* resource_quota, + const grpc_httpcli_request* request, + const char* body_bytes, size_t body_size, + grpc_millis deadline, grpc_closure* on_done, + grpc_httpcli_response* response) { + char* name; + if (g_post_override && g_post_override(request, body_bytes, body_size, + deadline, on_done, response)) { + return; + } + gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path); + internal_request_begin( + context, pollent, resource_quota, request, deadline, on_done, response, + name, grpc_httpcli_format_post_request(request, body_bytes, body_size)); + gpr_free(name); +} + +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/third_party/grpc/src/core/lib/http/httpcli.h b/third_party/grpc/src/core/lib/http/httpcli.h new file mode 100644 index 00000000000000..b0735081f2bad6 --- /dev/null +++ b/third_party/grpc/src/core/lib/http/httpcli.h @@ -0,0 +1,127 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_HTTP_HTTPCLI_H +#define GRPC_CORE_LIB_HTTP_HTTPCLI_H + +#include + +#include + +#include + +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/iomgr/pollset_set.h" + +/* User agent this library reports */ +#define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0" + +/* 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, + grpc_millis deadline, + 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 host to verify in the SSL handshake (or NULL) */ + char* ssl_host_override; + /* The main part of the request + The following headers are supplied automatically and MUST NOT be set here: + Host, Connection, User-Agent */ + grpc_http_request http; + /* handshaker to use ssl for the request */ + const grpc_httpcli_handshaker* handshaker; +} grpc_httpcli_request; + +/* Expose the parser response type as a httpcli response too */ +typedef struct grpc_http_response grpc_httpcli_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 */ +void grpc_httpcli_get(grpc_httpcli_context* context, + grpc_polling_entity* pollent, + grpc_resource_quota* resource_quota, + const grpc_httpcli_request* request, grpc_millis deadline, + grpc_closure* on_complete, + grpc_httpcli_response* response); + +/* 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 + Does not support ?var1=val1&var2=val2 in the path. */ +void grpc_httpcli_post(grpc_httpcli_context* context, + grpc_polling_entity* pollent, + grpc_resource_quota* resource_quota, + const grpc_httpcli_request* request, + const char* body_bytes, size_t body_size, + grpc_millis deadline, grpc_closure* on_complete, + grpc_httpcli_response* response); + +/* override functions return 1 if they handled the request, 0 otherwise */ +typedef int (*grpc_httpcli_get_override)(const grpc_httpcli_request* request, + grpc_millis deadline, + grpc_closure* on_complete, + grpc_httpcli_response* response); +typedef int (*grpc_httpcli_post_override)(const grpc_httpcli_request* request, + const char* body_bytes, + size_t body_size, + grpc_millis deadline, + grpc_closure* on_complete, + grpc_httpcli_response* response); + +void grpc_httpcli_set_override(grpc_httpcli_get_override get, + grpc_httpcli_post_override post); + +#endif /* GRPC_CORE_LIB_HTTP_HTTPCLI_H */ diff --git a/third_party/grpc/src/core/lib/http/httpcli_security_connector.cc b/third_party/grpc/src/core/lib/http/httpcli_security_connector.cc new file mode 100644 index 00000000000000..fdea7511ccaebe --- /dev/null +++ b/third_party/grpc/src/core/lib/http/httpcli_security_connector.cc @@ -0,0 +1,210 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/http/httpcli.h" + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/security_connector/ssl_utils.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/ssl_transport_security.h" + +class grpc_httpcli_ssl_channel_security_connector final + : public grpc_channel_security_connector { + public: + explicit grpc_httpcli_ssl_channel_security_connector(char* secure_peer_name) + : grpc_channel_security_connector( + /*url_scheme=*/nullptr, + /*channel_creds=*/nullptr, + /*request_metadata_creds=*/nullptr), + secure_peer_name_(secure_peer_name) {} + + ~grpc_httpcli_ssl_channel_security_connector() override { + if (handshaker_factory_ != nullptr) { + tsi_ssl_client_handshaker_factory_unref(handshaker_factory_); + } + if (secure_peer_name_ != nullptr) { + gpr_free(secure_peer_name_); + } + } + + tsi_result InitHandshakerFactory(const char* pem_root_certs, + const tsi_ssl_root_certs_store* root_store) { + tsi_ssl_client_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.pem_root_certs = pem_root_certs; + options.root_store = root_store; + return tsi_create_ssl_client_handshaker_factory_with_options( + &options, &handshaker_factory_); + } + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) override { + tsi_handshaker* handshaker = nullptr; + if (handshaker_factory_ != nullptr) { + tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker( + handshaker_factory_, secure_peer_name_, &handshaker); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + } + } + grpc_handshake_manager_add( + handshake_mgr, grpc_security_handshaker_create(handshaker, this)); + } + + tsi_ssl_client_handshaker_factory* handshaker_factory() const { + return handshaker_factory_; + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* /*auth_context*/, + grpc_closure* on_peer_checked) override { + grpc_error* error = GRPC_ERROR_NONE; + + /* Check the peer name. */ + if (secure_peer_name_ != nullptr && + !tsi_ssl_peer_matches_name(&peer, secure_peer_name_)) { + char* msg; + gpr_asprintf(&msg, "Peer name %s is not in peer certificate", + secure_peer_name_); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } + GRPC_CLOSURE_SCHED(on_peer_checked, error); + tsi_peer_destruct(&peer); + } + + int cmp(const grpc_security_connector* other_sc) const override { + auto* other = + reinterpret_cast( + other_sc); + return strcmp(secure_peer_name_, other->secure_peer_name_); + } + + bool check_call_host(const char* host, grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) override { + *error = GRPC_ERROR_NONE; + return true; + } + + void cancel_check_call_host(grpc_closure* on_call_host_checked, + grpc_error* error) override { + GRPC_ERROR_UNREF(error); + } + + const char* secure_peer_name() const { return secure_peer_name_; } + + private: + tsi_ssl_client_handshaker_factory* handshaker_factory_ = nullptr; + char* secure_peer_name_; +}; + +static grpc_core::RefCountedPtr +httpcli_ssl_channel_security_connector_create( + const char* pem_root_certs, const tsi_ssl_root_certs_store* root_store, + const char* secure_peer_name) { + if (secure_peer_name != nullptr && pem_root_certs == nullptr) { + gpr_log(GPR_ERROR, + "Cannot assert a secure peer name without a trust root."); + return nullptr; + } + grpc_core::RefCountedPtr c = + grpc_core::MakeRefCounted( + secure_peer_name == nullptr ? nullptr : gpr_strdup(secure_peer_name)); + tsi_result result = c->InitHandshakerFactory(pem_root_certs, root_store); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + return nullptr; + } + return c; +} + +/* handshaker */ + +typedef struct { + void (*func)(void* arg, grpc_endpoint* endpoint); + void* arg; + grpc_handshake_manager* handshake_mgr; +} on_done_closure; + +static void on_handshake_done(void* arg, grpc_error* error) { + grpc_handshaker_args* args = static_cast(arg); + on_done_closure* c = static_cast(args->user_data); + if (error != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(error); + gpr_log(GPR_ERROR, "Secure transport setup failed: %s", msg); + + c->func(c->arg, nullptr); + } else { + grpc_channel_args_destroy(args->args); + grpc_slice_buffer_destroy_internal(args->read_buffer); + gpr_free(args->read_buffer); + c->func(c->arg, args->endpoint); + } + grpc_handshake_manager_destroy(c->handshake_mgr); + gpr_free(c); +} + +static void ssl_handshake(void* arg, grpc_endpoint* tcp, const char* host, + grpc_millis deadline, + void (*on_done)(void* arg, grpc_endpoint* endpoint)) { + on_done_closure* c = static_cast(gpr_malloc(sizeof(*c))); + const char* pem_root_certs = + grpc_core::DefaultSslRootStore::GetPemRootCerts(); + const tsi_ssl_root_certs_store* root_store = + grpc_core::DefaultSslRootStore::GetRootStore(); + if (root_store == nullptr) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + on_done(arg, nullptr); + gpr_free(c); + return; + } + c->func = on_done; + c->arg = arg; + grpc_core::RefCountedPtr sc = + httpcli_ssl_channel_security_connector_create(pem_root_certs, root_store, + host); + GPR_ASSERT(sc != nullptr); + grpc_arg channel_arg = grpc_security_connector_to_arg(sc.get()); + grpc_channel_args args = {1, &channel_arg}; + c->handshake_mgr = grpc_handshake_manager_create(); + grpc_handshakers_add(HANDSHAKER_CLIENT, &args, + nullptr /* interested_parties */, c->handshake_mgr); + grpc_handshake_manager_do_handshake( + c->handshake_mgr, tcp, nullptr /* channel_args */, deadline, + nullptr /* acceptor */, on_handshake_done, c /* user_data */); + sc.reset(DEBUG_LOCATION, "httpcli"); +} + +const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake}; diff --git a/third_party/grpc/src/core/lib/http/parser.cc b/third_party/grpc/src/core/lib/http/parser.cc new file mode 100644 index 00000000000000..a37fdda8ea799e --- /dev/null +++ b/third_party/grpc/src/core/lib/http/parser.cc @@ -0,0 +1,371 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/http/parser.h" + +#include +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" + +grpc_core::TraceFlag grpc_http1_trace(false, "http1"); + +static char* buf2str(void* buffer, size_t length) { + char* out = static_cast(gpr_malloc(length + 1)); + memcpy(out, buffer, length); + out[length] = 0; + return out; +} + +static grpc_error* handle_response_line(grpc_http_parser* parser) { + uint8_t* beg = parser->cur_line; + uint8_t* cur = beg; + uint8_t* end = beg + parser->cur_line_length; + + if (cur == end || *cur++ != 'H') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'P') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'"); + if (cur == end || *cur++ != '/') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'"); + if (cur == end || *cur++ != '1') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '1'"); + if (cur == end || *cur++ != '.') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '.'"); + if (cur == end || *cur < '0' || *cur++ > '1') { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected HTTP/1.0 or HTTP/1.1"); + } + if (cur == end || *cur++ != ' ') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '"); + if (cur == end || *cur < '1' || *cur++ > '9') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); + parser->http.response->status = + (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0'); + if (cur == end || *cur++ != ' ') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '"); + + /* we don't really care about the status code message */ + + return GRPC_ERROR_NONE; +} + +static grpc_error* handle_request_line(grpc_http_parser* parser) { + uint8_t* beg = parser->cur_line; + uint8_t* cur = beg; + uint8_t* end = beg + parser->cur_line_length; + uint8_t vers_major = 0; + uint8_t vers_minor = 0; + + while (cur != end && *cur++ != ' ') + ; + if (cur == end) + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No method on HTTP request line"); + parser->http.request->method = + buf2str(beg, static_cast(cur - beg - 1)); + + beg = cur; + while (cur != end && *cur++ != ' ') + ; + if (cur == end) + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No path on HTTP request line"); + parser->http.request->path = buf2str(beg, static_cast(cur - beg - 1)); + + if (cur == end || *cur++ != 'H') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'P') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'"); + if (cur == end || *cur++ != '/') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'"); + vers_major = static_cast(*cur++ - '1' + 1); + ++cur; + if (cur == end) + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "End of line in HTTP version string"); + vers_minor = static_cast(*cur++ - '1' + 1); + + if (vers_major == 1) { + if (vers_minor == 0) { + parser->http.request->version = GRPC_HTTP_HTTP10; + } else if (vers_minor == 1) { + parser->http.request->version = GRPC_HTTP_HTTP11; + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + } else if (vers_major == 2) { + if (vers_minor == 0) { + parser->http.request->version = GRPC_HTTP_HTTP20; + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + + return GRPC_ERROR_NONE; +} + +static grpc_error* handle_first_line(grpc_http_parser* parser) { + switch (parser->type) { + case GRPC_HTTP_REQUEST: + return handle_request_line(parser); + case GRPC_HTTP_RESPONSE: + return handle_response_line(parser); + } + GPR_UNREACHABLE_CODE( + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); +} + +static grpc_error* add_header(grpc_http_parser* parser) { + uint8_t* beg = parser->cur_line; + uint8_t* cur = beg; + uint8_t* end = beg + parser->cur_line_length; + size_t* hdr_count = nullptr; + grpc_http_header** hdrs = nullptr; + grpc_http_header hdr = {nullptr, nullptr}; + grpc_error* error = GRPC_ERROR_NONE; + + GPR_ASSERT(cur != end); + + if (*cur == ' ' || *cur == '\t') { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Continued header lines not supported yet"); + goto done; + } + + while (cur != end && *cur != ':') { + cur++; + } + if (cur == end) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Didn't find ':' in header string"); + goto done; + } + GPR_ASSERT(cur >= beg); + hdr.key = buf2str(beg, static_cast(cur - beg)); + cur++; /* skip : */ + + while (cur != end && (*cur == ' ' || *cur == '\t')) { + cur++; + } + GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length); + hdr.value = buf2str( + cur, static_cast(end - cur) - parser->cur_line_end_length); + + switch (parser->type) { + case GRPC_HTTP_RESPONSE: + hdr_count = &parser->http.response->hdr_count; + hdrs = &parser->http.response->hdrs; + break; + case GRPC_HTTP_REQUEST: + hdr_count = &parser->http.request->hdr_count; + hdrs = &parser->http.request->hdrs; + break; + } + + if (*hdr_count == parser->hdr_capacity) { + parser->hdr_capacity = + GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2); + *hdrs = static_cast( + gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs))); + } + (*hdrs)[(*hdr_count)++] = hdr; + +done: + if (error != GRPC_ERROR_NONE) { + gpr_free(hdr.key); + gpr_free(hdr.value); + } + return error; +} + +static grpc_error* finish_line(grpc_http_parser* parser, + bool* found_body_start) { + grpc_error* err; + switch (parser->state) { + case GRPC_HTTP_FIRST_LINE: + err = handle_first_line(parser); + if (err != GRPC_ERROR_NONE) return err; + parser->state = GRPC_HTTP_HEADERS; + break; + case GRPC_HTTP_HEADERS: + if (parser->cur_line_length == parser->cur_line_end_length) { + parser->state = GRPC_HTTP_BODY; + *found_body_start = true; + break; + } + err = add_header(parser); + if (err != GRPC_ERROR_NONE) { + return err; + } + break; + case GRPC_HTTP_BODY: + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Should never reach here")); + } + + parser->cur_line_length = 0; + return GRPC_ERROR_NONE; +} + +static grpc_error* addbyte_body(grpc_http_parser* parser, uint8_t byte) { + size_t* body_length = nullptr; + char** body = nullptr; + + if (parser->type == GRPC_HTTP_RESPONSE) { + body_length = &parser->http.response->body_length; + body = &parser->http.response->body; + } else if (parser->type == GRPC_HTTP_REQUEST) { + body_length = &parser->http.request->body_length; + body = &parser->http.request->body; + } else { + GPR_UNREACHABLE_CODE( + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); + } + + if (*body_length == parser->body_capacity) { + parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2); + *body = + static_cast(gpr_realloc((void*)*body, parser->body_capacity)); + } + (*body)[*body_length] = static_cast(byte); + (*body_length)++; + + return GRPC_ERROR_NONE; +} + +static bool check_line(grpc_http_parser* parser) { + 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 true; + } + + // HTTP request with \n\r line termiantors. + else if (parser->cur_line_length >= 2 && + parser->cur_line[parser->cur_line_length - 2] == '\n' && + parser->cur_line[parser->cur_line_length - 1] == '\r') { + return true; + } + + // HTTP request with only \n line terminators. + else if (parser->cur_line_length >= 1 && + parser->cur_line[parser->cur_line_length - 1] == '\n') { + parser->cur_line_end_length = 1; + return true; + } + + return false; +} + +static grpc_error* addbyte(grpc_http_parser* parser, uint8_t byte, + bool* found_body_start) { + switch (parser->state) { + case GRPC_HTTP_FIRST_LINE: + case GRPC_HTTP_HEADERS: + if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) { + if (grpc_http1_trace.enabled()) + gpr_log(GPR_ERROR, "HTTP header max line length (%d) exceeded", + GRPC_HTTP_PARSER_MAX_HEADER_LENGTH); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "HTTP header max line length exceeded"); + } + parser->cur_line[parser->cur_line_length] = byte; + parser->cur_line_length++; + if (check_line(parser)) { + return finish_line(parser, found_body_start); + } + return GRPC_ERROR_NONE; + case GRPC_HTTP_BODY: + return addbyte_body(parser, byte); + } + GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE); +} + +void grpc_http_parser_init(grpc_http_parser* parser, grpc_http_type type, + void* request_or_response) { + memset(parser, 0, sizeof(*parser)); + parser->state = GRPC_HTTP_FIRST_LINE; + parser->type = type; + parser->http.request_or_response = request_or_response; + parser->cur_line_end_length = 2; +} + +void grpc_http_parser_destroy(grpc_http_parser* parser) {} + +void grpc_http_request_destroy(grpc_http_request* request) { + size_t i; + gpr_free(request->body); + for (i = 0; i < request->hdr_count; i++) { + gpr_free(request->hdrs[i].key); + gpr_free(request->hdrs[i].value); + } + gpr_free(request->hdrs); + gpr_free(request->method); + gpr_free(request->path); +} + +void grpc_http_response_destroy(grpc_http_response* response) { + size_t i; + gpr_free(response->body); + for (i = 0; i < response->hdr_count; i++) { + gpr_free(response->hdrs[i].key); + gpr_free(response->hdrs[i].value); + } + gpr_free(response->hdrs); +} + +grpc_error* grpc_http_parser_parse(grpc_http_parser* parser, grpc_slice slice, + size_t* start_of_body) { + for (size_t i = 0; i < GRPC_SLICE_LENGTH(slice); i++) { + bool found_body_start = false; + grpc_error* err = + addbyte(parser, GRPC_SLICE_START_PTR(slice)[i], &found_body_start); + if (err != GRPC_ERROR_NONE) return err; + if (found_body_start && start_of_body != nullptr) *start_of_body = i + 1; + } + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_http_parser_eof(grpc_http_parser* parser) { + if (parser->state != GRPC_HTTP_BODY) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Did not finish headers"); + } + return GRPC_ERROR_NONE; +} diff --git a/third_party/grpc/src/core/lib/http/parser.h b/third_party/grpc/src/core/lib/http/parser.h new file mode 100644 index 00000000000000..a8f47c96c859df --- /dev/null +++ b/third_party/grpc/src/core/lib/http/parser.h @@ -0,0 +1,113 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_HTTP_PARSER_H +#define GRPC_CORE_LIB_HTTP_PARSER_H + +#include + +#include +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/error.h" + +/* Maximum length of a header string of the form 'Key: Value\r\n' */ +#define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096 + +/* A single header to be passed in a request */ +typedef struct grpc_http_header { + char* key; + char* value; +} grpc_http_header; + +typedef enum { + GRPC_HTTP_FIRST_LINE, + GRPC_HTTP_HEADERS, + GRPC_HTTP_BODY +} grpc_http_parser_state; + +typedef enum { + GRPC_HTTP_HTTP10, + GRPC_HTTP_HTTP11, + GRPC_HTTP_HTTP20, +} grpc_http_version; + +typedef enum { + GRPC_HTTP_RESPONSE, + GRPC_HTTP_REQUEST, +} grpc_http_type; + +/* A request */ +typedef struct grpc_http_request { + /* Method of the request (e.g. GET, POST) */ + char* method; + /* The path of the resource to fetch */ + char* path; + /* HTTP version to use */ + grpc_http_version version; + /* Headers attached to the request */ + size_t hdr_count; + grpc_http_header* hdrs; + /* Body: length and contents; contents are NOT null-terminated */ + size_t body_length; + char* body; +} grpc_http_request; + +/* A response */ +typedef struct grpc_http_response { + /* HTTP status code */ + int status = 0; + /* Headers: count and key/values */ + size_t hdr_count = 0; + grpc_http_header* hdrs = nullptr; + /* Body: length and contents; contents are NOT null-terminated */ + size_t body_length = 0; + char* body = nullptr; +} grpc_http_response; + +typedef struct { + grpc_http_parser_state state; + grpc_http_type type; + + union { + grpc_http_response* response; + grpc_http_request* request; + void* request_or_response; + } http; + size_t body_capacity; + size_t hdr_capacity; + + uint8_t cur_line[GRPC_HTTP_PARSER_MAX_HEADER_LENGTH]; + size_t cur_line_length; + size_t cur_line_end_length; +} grpc_http_parser; + +void grpc_http_parser_init(grpc_http_parser* parser, grpc_http_type type, + void* request_or_response); +void grpc_http_parser_destroy(grpc_http_parser* parser); + +/* Sets \a start_of_body to the offset in \a slice of the start of the body. */ +grpc_error* grpc_http_parser_parse(grpc_http_parser* parser, grpc_slice slice, + size_t* start_of_body); +grpc_error* grpc_http_parser_eof(grpc_http_parser* parser); + +void grpc_http_request_destroy(grpc_http_request* request); +void grpc_http_response_destroy(grpc_http_response* response); + +extern grpc_core::TraceFlag grpc_http1_trace; + +#endif /* GRPC_CORE_LIB_HTTP_PARSER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/README.md b/third_party/grpc/src/core/lib/iomgr/README.md new file mode 100644 index 00000000000000..9b22b76cebf966 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/README.md @@ -0,0 +1,6 @@ +# iomgr + +Platform abstractions for I/O (mostly network). + +Provides abstractions over TCP/UDP I/O, file loading, polling, and concurrency +management for various operating systems. diff --git a/third_party/grpc/src/core/lib/iomgr/block_annotate.h b/third_party/grpc/src/core/lib/iomgr/block_annotate.h new file mode 100644 index 00000000000000..a57873aabb2007 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/block_annotate.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H +#define GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H + +void gpr_thd_start_blocking_region(); +void gpr_thd_end_blocking_region(); + +/* These annotations identify the beginning and end of regions where + the code may block for reasons other than synchronization functions. + These include poll, epoll, and getaddrinfo. */ + +#ifdef GRPC_SCHEDULING_MARK_BLOCKING_REGION +#define GRPC_SCHEDULING_START_BLOCKING_REGION \ + do { \ + gpr_thd_start_blocking_region(); \ + } while (0) +#define GRPC_SCHEDULING_END_BLOCKING_REGION \ + do { \ + gpr_thd_end_blocking_region(); \ + grpc_core::ExecCtx::Get()->InvalidateNow(); \ + } while (0) +#define GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX \ + do { \ + gpr_thd_end_blocking_region(); \ + } while (0) + +#else +#define GRPC_SCHEDULING_START_BLOCKING_REGION \ + do { \ + } while (0) +#define GRPC_SCHEDULING_END_BLOCKING_REGION \ + do { \ + grpc_core::ExecCtx::Get()->InvalidateNow(); \ + } while (0) +#define GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX \ + do { \ + } while (0) +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_BLOCK_ANNOTATE_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/buffer_list.cc b/third_party/grpc/src/core/lib/iomgr/buffer_list.cc new file mode 100644 index 00000000000000..ace17a108d1032 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/buffer_list.cc @@ -0,0 +1,145 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/buffer_list.h" +#include "src/core/lib/iomgr/port.h" + +#include + +#ifdef GRPC_LINUX_ERRQUEUE +#include + +#include "src/core/lib/gprpp/memory.h" + +namespace grpc_core { +void TracedBuffer::AddNewEntry(TracedBuffer** head, uint32_t seq_no, + void* arg) { + GPR_DEBUG_ASSERT(head != nullptr); + TracedBuffer* new_elem = New(seq_no, arg); + /* Store the current time as the sendmsg time. */ + new_elem->ts_.sendmsg_time = gpr_now(GPR_CLOCK_REALTIME); + new_elem->ts_.scheduled_time = gpr_inf_past(GPR_CLOCK_REALTIME); + new_elem->ts_.sent_time = gpr_inf_past(GPR_CLOCK_REALTIME); + new_elem->ts_.acked_time = gpr_inf_past(GPR_CLOCK_REALTIME); + if (*head == nullptr) { + *head = new_elem; + return; + } + /* Append at the end. */ + TracedBuffer* ptr = *head; + while (ptr->next_ != nullptr) { + ptr = ptr->next_; + } + ptr->next_ = new_elem; +} + +namespace { +/** Fills gpr_timespec gts based on values from timespec ts */ +void fill_gpr_from_timestamp(gpr_timespec* gts, const struct timespec* ts) { + gts->tv_sec = ts->tv_sec; + gts->tv_nsec = static_cast(ts->tv_nsec); + gts->clock_type = GPR_CLOCK_REALTIME; +} + +void default_timestamps_callback(void* arg, grpc_core::Timestamps* ts, + grpc_error* shudown_err) { + gpr_log(GPR_DEBUG, "Timestamps callback has not been registered"); +} + +/** The saved callback function that will be invoked when we get all the + * timestamps that we are going to get for a TracedBuffer. */ +void (*timestamps_callback)(void*, grpc_core::Timestamps*, + grpc_error* shutdown_err) = + default_timestamps_callback; +} /* namespace */ + +void TracedBuffer::ProcessTimestamp(TracedBuffer** head, + struct sock_extended_err* serr, + struct scm_timestamping* tss) { + GPR_DEBUG_ASSERT(head != nullptr); + TracedBuffer* elem = *head; + TracedBuffer* next = nullptr; + while (elem != nullptr) { + /* The byte number refers to the sequence number of the last byte which this + * timestamp relates to. */ + if (serr->ee_data >= elem->seq_no_) { + switch (serr->ee_info) { + case SCM_TSTAMP_SCHED: + fill_gpr_from_timestamp(&(elem->ts_.scheduled_time), &(tss->ts[0])); + elem = elem->next_; + break; + case SCM_TSTAMP_SND: + fill_gpr_from_timestamp(&(elem->ts_.sent_time), &(tss->ts[0])); + elem = elem->next_; + break; + case SCM_TSTAMP_ACK: + fill_gpr_from_timestamp(&(elem->ts_.acked_time), &(tss->ts[0])); + /* Got all timestamps. Do the callback and free this TracedBuffer. + * The thing below can be passed by value if we don't want the + * restriction on the lifetime. */ + timestamps_callback(elem->arg_, &(elem->ts_), GRPC_ERROR_NONE); + next = elem->next_; + Delete(elem); + *head = elem = next; + break; + default: + abort(); + } + } else { + break; + } + } +} + +void TracedBuffer::Shutdown(TracedBuffer** head, void* remaining, + grpc_error* shutdown_err) { + GPR_DEBUG_ASSERT(head != nullptr); + TracedBuffer* elem = *head; + while (elem != nullptr) { + timestamps_callback(elem->arg_, &(elem->ts_), shutdown_err); + auto* next = elem->next_; + Delete(elem); + elem = next; + } + *head = nullptr; + if (remaining != nullptr) { + timestamps_callback(remaining, nullptr, shutdown_err); + } + GRPC_ERROR_UNREF(shutdown_err); +} + +void grpc_tcp_set_write_timestamps_callback(void (*fn)(void*, + grpc_core::Timestamps*, + grpc_error* error)) { + timestamps_callback = fn; +} +} /* namespace grpc_core */ + +#else /* GRPC_LINUX_ERRQUEUE */ + +namespace grpc_core { +void grpc_tcp_set_write_timestamps_callback(void (*fn)(void*, + grpc_core::Timestamps*, + grpc_error* error)) { + gpr_log(GPR_DEBUG, "Timestamps callback is not enabled for this platform"); +} +} /* namespace grpc_core */ + +#endif /* GRPC_LINUX_ERRQUEUE */ diff --git a/third_party/grpc/src/core/lib/iomgr/buffer_list.h b/third_party/grpc/src/core/lib/iomgr/buffer_list.h new file mode 100644 index 00000000000000..627f1bde99a700 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/buffer_list.h @@ -0,0 +1,103 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_BUFFER_LIST_H +#define GRPC_CORE_LIB_IOMGR_BUFFER_LIST_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/internal_errqueue.h" + +namespace grpc_core { +struct Timestamps { + /* TODO(yashykt): This would also need to store OPTSTAT once support is added + */ + gpr_timespec sendmsg_time; + gpr_timespec scheduled_time; + gpr_timespec sent_time; + gpr_timespec acked_time; + + uint32_t byte_offset; /* byte offset relative to the start of the RPC */ +}; + +/** TracedBuffer is a class to keep track of timestamps for a specific buffer in + * the TCP layer. We are only tracking timestamps for Linux kernels and hence + * this class would only be used by Linux platforms. For all other platforms, + * TracedBuffer would be an empty class. + * + * The timestamps collected are according to grpc_core::Timestamps declared + * above. + * + * A TracedBuffer list is kept track of using the head element of the list. If + * the head element of the list is nullptr, then the list is empty. + */ +#ifdef GRPC_LINUX_ERRQUEUE +class TracedBuffer { + public: + /** Add a new entry in the TracedBuffer list pointed to by head. Also saves + * sendmsg_time with the current timestamp. */ + static void AddNewEntry(grpc_core::TracedBuffer** head, uint32_t seq_no, + void* arg); + + /** Processes a received timestamp based on sock_extended_err and + * scm_timestamping structures. It will invoke the timestamps callback if the + * timestamp type is SCM_TSTAMP_ACK. */ + static void ProcessTimestamp(grpc_core::TracedBuffer** head, + struct sock_extended_err* serr, + struct scm_timestamping* tss); + + /** Cleans the list by calling the callback for each traced buffer in the list + * with timestamps that it has. */ + static void Shutdown(grpc_core::TracedBuffer** head, void* remaining, + grpc_error* shutdown_err); + + private: + GPRC_ALLOW_CLASS_TO_USE_NON_PUBLIC_NEW + + TracedBuffer(uint32_t seq_no, void* arg) + : seq_no_(seq_no), arg_(arg), next_(nullptr) {} + + uint32_t seq_no_; /* The sequence number for the last byte in the buffer */ + void* arg_; /* The arg to pass to timestamps_callback */ + grpc_core::Timestamps ts_; /* The timestamps corresponding to this buffer */ + grpc_core::TracedBuffer* next_; /* The next TracedBuffer in the list */ +}; +#else /* GRPC_LINUX_ERRQUEUE */ +class TracedBuffer { + public: + /* Dummy shutdown function */ + static void Shutdown(grpc_core::TracedBuffer** head, void* remaining, + grpc_error* shutdown_err) {} +}; +#endif /* GRPC_LINUX_ERRQUEUE */ + +/** Sets the callback function to call when timestamps for a write are + * collected. The callback does not own a reference to error. */ +void grpc_tcp_set_write_timestamps_callback(void (*fn)(void*, + grpc_core::Timestamps*, + grpc_error* error)); + +}; /* namespace grpc_core */ + +#endif /* GRPC_CORE_LIB_IOMGR_BUFFER_LIST_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/call_combiner.cc b/third_party/grpc/src/core/lib/iomgr/call_combiner.cc new file mode 100644 index 00000000000000..6b5759a036f5f1 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/call_combiner.cc @@ -0,0 +1,262 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/call_combiner.h" + +#include + +#include +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/profiling/timers.h" + +grpc_core::TraceFlag grpc_call_combiner_trace(false, "call_combiner"); + +static grpc_error* decode_cancel_state_error(gpr_atm cancel_state) { + if (cancel_state & 1) { + return (grpc_error*)(cancel_state & ~static_cast(1)); + } + return GRPC_ERROR_NONE; +} + +static gpr_atm encode_cancel_state_error(grpc_error* error) { + return static_cast(1) | (gpr_atm)error; +} + +#ifdef GRPC_TSAN_ENABLED +static void tsan_closure(void* user_data, grpc_error* error) { + grpc_call_combiner* call_combiner = + static_cast(user_data); + // We ref-count the lock, and check if it's already taken. + // If it was taken, we should do nothing. Otherwise, we will mark it as + // locked. Note that if two different threads try to do this, only one of + // them will be able to mark the lock as acquired, while they both run their + // callbacks. In such cases (which should never happen for call_combiner), + // TSAN will correctly produce an error. + // + // TODO(soheil): This only covers the callbacks scheduled by + // grpc_call_combiner_(start|finish). If in the future, a + // callback gets scheduled using other mechanisms, we will need + // to add APIs to externally lock call combiners. + grpc_core::RefCountedPtr lock = + call_combiner->tsan_lock; + bool prev = false; + if (lock->taken.compare_exchange_strong(prev, true)) { + TSAN_ANNOTATE_RWLOCK_ACQUIRED(&lock->taken, true); + } else { + lock.reset(); + } + GRPC_CLOSURE_RUN(call_combiner->original_closure, GRPC_ERROR_REF(error)); + if (lock != nullptr) { + TSAN_ANNOTATE_RWLOCK_RELEASED(&lock->taken, true); + bool prev = true; + GPR_ASSERT(lock->taken.compare_exchange_strong(prev, false)); + } +} +#endif + +static void call_combiner_sched_closure(grpc_call_combiner* call_combiner, + grpc_closure* closure, + grpc_error* error) { +#ifdef GRPC_TSAN_ENABLED + call_combiner->original_closure = closure; + GRPC_CLOSURE_SCHED(&call_combiner->tsan_closure, error); +#else + GRPC_CLOSURE_SCHED(closure, error); +#endif +} + +void grpc_call_combiner_init(grpc_call_combiner* call_combiner) { + gpr_atm_no_barrier_store(&call_combiner->cancel_state, 0); + gpr_atm_no_barrier_store(&call_combiner->size, 0); + gpr_mpscq_init(&call_combiner->queue); +#ifdef GRPC_TSAN_ENABLED + GRPC_CLOSURE_INIT(&call_combiner->tsan_closure, tsan_closure, call_combiner, + grpc_schedule_on_exec_ctx); +#endif +} + +void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner) { + gpr_mpscq_destroy(&call_combiner->queue); + GRPC_ERROR_UNREF(decode_cancel_state_error(call_combiner->cancel_state)); +} + +#ifndef NDEBUG +#define DEBUG_ARGS , const char *file, int line +#define DEBUG_FMT_STR "%s:%d: " +#define DEBUG_FMT_ARGS , file, line +#else +#define DEBUG_ARGS +#define DEBUG_FMT_STR +#define DEBUG_FMT_ARGS +#endif + +void grpc_call_combiner_start(grpc_call_combiner* call_combiner, + grpc_closure* closure, + grpc_error* error DEBUG_ARGS, + const char* reason) { + GPR_TIMER_SCOPE("call_combiner_start", 0); + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, + "==> grpc_call_combiner_start() [%p] closure=%p [" DEBUG_FMT_STR + "%s] error=%s", + call_combiner, closure DEBUG_FMT_ARGS, reason, + grpc_error_string(error)); + } + size_t prev_size = static_cast( + gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)1)); + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " size: %" PRIdPTR " -> %" PRIdPTR, prev_size, + prev_size + 1); + } + GRPC_STATS_INC_CALL_COMBINER_LOCKS_SCHEDULED_ITEMS(); + if (prev_size == 0) { + GRPC_STATS_INC_CALL_COMBINER_LOCKS_INITIATED(); + + GPR_TIMER_MARK("call_combiner_initiate", 0); + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " EXECUTING IMMEDIATELY"); + } + // Queue was empty, so execute this closure immediately. + call_combiner_sched_closure(call_combiner, closure, error); + } else { + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " QUEUING"); + } + // Queue was not empty, so add closure to queue. + closure->error_data.error = error; + gpr_mpscq_push(&call_combiner->queue, + reinterpret_cast(closure)); + } +} + +void grpc_call_combiner_stop(grpc_call_combiner* call_combiner DEBUG_ARGS, + const char* reason) { + GPR_TIMER_SCOPE("call_combiner_stop", 0); + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, + "==> grpc_call_combiner_stop() [%p] [" DEBUG_FMT_STR "%s]", + call_combiner DEBUG_FMT_ARGS, reason); + } + size_t prev_size = static_cast( + gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)-1)); + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " size: %" PRIdPTR " -> %" PRIdPTR, prev_size, + prev_size - 1); + } + GPR_ASSERT(prev_size >= 1); + if (prev_size > 1) { + while (true) { + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " checking queue"); + } + bool empty; + grpc_closure* closure = reinterpret_cast( + gpr_mpscq_pop_and_check_end(&call_combiner->queue, &empty)); + if (closure == nullptr) { + // This can happen either due to a race condition within the mpscq + // code or because of a race with grpc_call_combiner_start(). + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " queue returned no result; checking again"); + } + continue; + } + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " EXECUTING FROM QUEUE: closure=%p error=%s", + closure, grpc_error_string(closure->error_data.error)); + } + call_combiner_sched_closure(call_combiner, closure, + closure->error_data.error); + break; + } + } else if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, " queue empty"); + } +} + +void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner, + grpc_closure* closure) { + GRPC_STATS_INC_CALL_COMBINER_SET_NOTIFY_ON_CANCEL(); + while (true) { + // Decode original state. + gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state); + grpc_error* original_error = decode_cancel_state_error(original_state); + // If error is set, invoke the cancellation closure immediately. + // Otherwise, store the new closure. + if (original_error != GRPC_ERROR_NONE) { + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, + "call_combiner=%p: scheduling notify_on_cancel callback=%p " + "for pre-existing cancellation", + call_combiner, closure); + } + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_REF(original_error)); + break; + } else { + if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state, + (gpr_atm)closure)) { + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, "call_combiner=%p: setting notify_on_cancel=%p", + call_combiner, closure); + } + // If we replaced an earlier closure, invoke the original + // closure with GRPC_ERROR_NONE. This allows callers to clean + // up any resources they may be holding for the callback. + if (original_state != 0) { + closure = (grpc_closure*)original_state; + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, + "call_combiner=%p: scheduling old cancel callback=%p", + call_combiner, closure); + } + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + } + break; + } + } + // cas failed, try again. + } +} + +void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner, + grpc_error* error) { + GRPC_STATS_INC_CALL_COMBINER_CANCELLED(); + while (true) { + gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state); + grpc_error* original_error = decode_cancel_state_error(original_state); + if (original_error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(error); + break; + } + if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state, + encode_cancel_state_error(error))) { + if (original_state != 0) { + grpc_closure* notify_on_cancel = (grpc_closure*)original_state; + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, + "call_combiner=%p: scheduling notify_on_cancel callback=%p", + call_combiner, notify_on_cancel); + } + GRPC_CLOSURE_SCHED(notify_on_cancel, GRPC_ERROR_REF(error)); + } + break; + } + // cas failed, try again. + } +} diff --git a/third_party/grpc/src/core/lib/iomgr/call_combiner.h b/third_party/grpc/src/core/lib/iomgr/call_combiner.h new file mode 100644 index 00000000000000..4ec0044f05608f --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/call_combiner.h @@ -0,0 +1,222 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H +#define GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H + +#include + +#include + +#include + +#include "src/core/lib/gpr/mpscq.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/dynamic_annotations.h" + +// A simple, lock-free mechanism for serializing activity related to a +// single call. This is similar to a combiner but is more lightweight. +// +// It requires the callback (or, in the common case where the callback +// actually kicks off a chain of callbacks, the last callback in that +// chain) to explicitly indicate (by calling GRPC_CALL_COMBINER_STOP()) +// when it is done with the action that was kicked off by the original +// callback. + +extern grpc_core::TraceFlag grpc_call_combiner_trace; + +struct grpc_call_combiner { + gpr_atm size = 0; // size_t, num closures in queue or currently executing + gpr_mpscq queue; + // Either 0 (if not cancelled and no cancellation closure set), + // a grpc_closure* (if the lowest bit is 0), + // or a grpc_error* (if the lowest bit is 1). + gpr_atm cancel_state = 0; +#ifdef GRPC_TSAN_ENABLED + // A fake ref-counted lock that is kept alive after the destruction of + // grpc_call_combiner, when we are running the original closure. + // + // Ideally we want to lock and unlock the call combiner as a pointer, when the + // callback is called. However, original_closure is free to trigger + // anything on the call combiner (including destruction of grpc_call). + // Thus, we need a ref-counted structure that can outlive the call combiner. + struct TsanLock + : public grpc_core::RefCounted { + TsanLock() { TSAN_ANNOTATE_RWLOCK_CREATE(&taken); } + ~TsanLock() { TSAN_ANNOTATE_RWLOCK_DESTROY(&taken); } + + // To avoid double-locking by the same thread, we should acquire/release + // the lock only when taken is false. On each acquire taken must be set to + // true. + std::atomic taken{false}; + }; + grpc_core::RefCountedPtr tsan_lock = + grpc_core::MakeRefCounted(); + grpc_closure tsan_closure; + grpc_closure* original_closure; +#endif +}; + +// Assumes memory was initialized to zero. +void grpc_call_combiner_init(grpc_call_combiner* call_combiner); + +void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner); + +#ifndef NDEBUG +#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason) \ + grpc_call_combiner_start((call_combiner), (closure), (error), __FILE__, \ + __LINE__, (reason)) +#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \ + grpc_call_combiner_stop((call_combiner), __FILE__, __LINE__, (reason)) +/// Starts processing \a closure on \a call_combiner. +void grpc_call_combiner_start(grpc_call_combiner* call_combiner, + grpc_closure* closure, grpc_error* error, + const char* file, int line, const char* reason); +/// Yields the call combiner to the next closure in the queue, if any. +void grpc_call_combiner_stop(grpc_call_combiner* call_combiner, + const char* file, int line, const char* reason); +#else +#define GRPC_CALL_COMBINER_START(call_combiner, closure, error, reason) \ + grpc_call_combiner_start((call_combiner), (closure), (error), (reason)) +#define GRPC_CALL_COMBINER_STOP(call_combiner, reason) \ + grpc_call_combiner_stop((call_combiner), (reason)) +/// Starts processing \a closure on \a call_combiner. +void grpc_call_combiner_start(grpc_call_combiner* call_combiner, + grpc_closure* closure, grpc_error* error, + const char* reason); +/// Yields the call combiner to the next closure in the queue, if any. +void grpc_call_combiner_stop(grpc_call_combiner* call_combiner, + const char* reason); +#endif + +/// Registers \a closure to be invoked by \a call_combiner when +/// grpc_call_combiner_cancel() is called. +/// +/// Once a closure is registered, it will always be scheduled exactly +/// once; this allows the closure to hold references that will be freed +/// regardless of whether or not the call was cancelled. If a cancellation +/// does occur, the closure will be scheduled with the cancellation error; +/// otherwise, it will be scheduled with GRPC_ERROR_NONE. +/// +/// The closure will be scheduled in the following cases: +/// - If grpc_call_combiner_cancel() was called prior to registering the +/// closure, it will be scheduled immediately with the cancelation error. +/// - If grpc_call_combiner_cancel() is called after registering the +/// closure, the closure will be scheduled with the cancellation error. +/// - If grpc_call_combiner_set_notify_on_cancel() is called again to +/// register a new cancellation closure, the previous cancellation +/// closure will be scheduled with GRPC_ERROR_NONE. +/// +/// If \a closure is NULL, then no closure will be invoked on +/// cancellation; this effectively unregisters the previously set closure. +/// However, most filters will not need to explicitly unregister their +/// callbacks, as this is done automatically when the call is destroyed. Filters +/// that schedule the cancellation closure on ExecCtx do not need to take a ref +/// on the call stack to guarantee closure liveness. This is done by explicitly +/// flushing ExecCtx after the unregistration during call destruction. +void grpc_call_combiner_set_notify_on_cancel(grpc_call_combiner* call_combiner, + grpc_closure* closure); + +/// Indicates that the call has been cancelled. +void grpc_call_combiner_cancel(grpc_call_combiner* call_combiner, + grpc_error* error); + +namespace grpc_core { + +// Helper for running a list of closures in a call combiner. +// +// Each callback running in the call combiner will eventually be +// returned to the surface, at which point the surface will yield the +// call combiner. So when we are running in the call combiner and have +// more than one callback to return to the surface, we need to re-enter +// the call combiner for all but one of those callbacks. +class CallCombinerClosureList { + public: + CallCombinerClosureList() {} + + // Adds a closure to the list. The closure must eventually result in + // the call combiner being yielded. + void Add(grpc_closure* closure, grpc_error* error, const char* reason) { + closures_.emplace_back(closure, error, reason); + } + + // Runs all closures in the call combiner and yields the call combiner. + // + // All but one of the closures in the list will be scheduled via + // GRPC_CALL_COMBINER_START(), and the remaining closure will be + // scheduled via GRPC_CLOSURE_SCHED(), which will eventually result in + // yielding the call combiner. If the list is empty, then the call + // combiner will be yielded immediately. + void RunClosures(grpc_call_combiner* call_combiner) { + if (closures_.empty()) { + GRPC_CALL_COMBINER_STOP(call_combiner, "no closures to schedule"); + return; + } + for (size_t i = 1; i < closures_.size(); ++i) { + auto& closure = closures_[i]; + GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error, + closure.reason); + } + if (grpc_call_combiner_trace.enabled()) { + gpr_log(GPR_INFO, + "CallCombinerClosureList executing closure while already " + "holding call_combiner %p: closure=%p error=%s reason=%s", + call_combiner, closures_[0].closure, + grpc_error_string(closures_[0].error), closures_[0].reason); + } + // This will release the call combiner. + GRPC_CLOSURE_SCHED(closures_[0].closure, closures_[0].error); + closures_.clear(); + } + + // Runs all closures in the call combiner, but does NOT yield the call + // combiner. All closures will be scheduled via GRPC_CALL_COMBINER_START(). + void RunClosuresWithoutYielding(grpc_call_combiner* call_combiner) { + for (size_t i = 0; i < closures_.size(); ++i) { + auto& closure = closures_[i]; + GRPC_CALL_COMBINER_START(call_combiner, closure.closure, closure.error, + closure.reason); + } + closures_.clear(); + } + + size_t size() const { return closures_.size(); } + + private: + struct CallCombinerClosure { + grpc_closure* closure; + grpc_error* error; + const char* reason; + + CallCombinerClosure(grpc_closure* closure, grpc_error* error, + const char* reason) + : closure(closure), error(error), reason(reason) {} + }; + + // There are generally a maximum of 6 closures to run in the call + // combiner, one for each pending op. + InlinedVector closures_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/cfstream_handle.cc b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.cc new file mode 100644 index 00000000000000..827fd24831e5c0 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.cc @@ -0,0 +1,185 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_CFSTREAM +#import +#import "src/core/lib/iomgr/cfstream_handle.h" + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; + +void* CFStreamHandle::Retain(void* info) { + CFStreamHandle* handle = static_cast(info); + CFSTREAM_HANDLE_REF(handle, "retain"); + return info; +} + +void CFStreamHandle::Release(void* info) { + CFStreamHandle* handle = static_cast(info); + CFSTREAM_HANDLE_UNREF(handle, "release"); +} + +CFStreamHandle* CFStreamHandle::CreateStreamHandle( + CFReadStreamRef read_stream, CFWriteStreamRef write_stream) { + return new CFStreamHandle(read_stream, write_stream); +} + +void CFStreamHandle::ReadCallback(CFReadStreamRef stream, + CFStreamEventType type, + void* client_callback_info) { + CFStreamHandle* handle = static_cast(client_callback_info); + CFSTREAM_HANDLE_REF(handle, "read callback"); + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + grpc_core::ExecCtx exec_ctx; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream ReadCallback (%p, %p, %lu, %p)", handle, + stream, type, client_callback_info); + } + switch (type) { + case kCFStreamEventOpenCompleted: + handle->open_event_.SetReady(); + break; + case kCFStreamEventHasBytesAvailable: + case kCFStreamEventEndEncountered: + handle->read_event_.SetReady(); + break; + case kCFStreamEventErrorOccurred: + handle->open_event_.SetReady(); + handle->read_event_.SetReady(); + break; + default: + GPR_UNREACHABLE_CODE(return ); + } + CFSTREAM_HANDLE_UNREF(handle, "read callback"); + }); +} +void CFStreamHandle::WriteCallback(CFWriteStreamRef stream, + CFStreamEventType type, + void* clientCallBackInfo) { + CFStreamHandle* handle = static_cast(clientCallBackInfo); + CFSTREAM_HANDLE_REF(handle, "write callback"); + dispatch_async( + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + grpc_core::ExecCtx exec_ctx; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream WriteCallback (%p, %p, %lu, %p)", handle, + stream, type, clientCallBackInfo); + } + switch (type) { + case kCFStreamEventOpenCompleted: + handle->open_event_.SetReady(); + break; + case kCFStreamEventCanAcceptBytes: + case kCFStreamEventEndEncountered: + handle->write_event_.SetReady(); + break; + case kCFStreamEventErrorOccurred: + handle->open_event_.SetReady(); + handle->write_event_.SetReady(); + break; + default: + GPR_UNREACHABLE_CODE(return ); + } + CFSTREAM_HANDLE_UNREF(handle, "write callback"); + }); +} + +CFStreamHandle::CFStreamHandle(CFReadStreamRef read_stream, + CFWriteStreamRef write_stream) { + gpr_ref_init(&refcount_, 1); + open_event_.InitEvent(); + read_event_.InitEvent(); + write_event_.InitEvent(); + CFStreamClientContext ctx = {0, static_cast(this), + CFStreamHandle::Retain, CFStreamHandle::Release, + nil}; + CFReadStreamSetClient( + read_stream, + kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | + kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, + CFStreamHandle::ReadCallback, &ctx); + CFWriteStreamSetClient( + write_stream, + kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | + kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, + CFStreamHandle::WriteCallback, &ctx); + CFReadStreamScheduleWithRunLoop(read_stream, CFRunLoopGetMain(), + kCFRunLoopCommonModes); + CFWriteStreamScheduleWithRunLoop(write_stream, CFRunLoopGetMain(), + kCFRunLoopCommonModes); +} + +CFStreamHandle::~CFStreamHandle() { + open_event_.DestroyEvent(); + read_event_.DestroyEvent(); + write_event_.DestroyEvent(); +} + +void CFStreamHandle::NotifyOnOpen(grpc_closure* closure) { + open_event_.NotifyOn(closure); +} + +void CFStreamHandle::NotifyOnRead(grpc_closure* closure) { + read_event_.NotifyOn(closure); +} + +void CFStreamHandle::NotifyOnWrite(grpc_closure* closure) { + write_event_.NotifyOn(closure); +} + +void CFStreamHandle::Shutdown(grpc_error* error) { + open_event_.SetShutdown(GRPC_ERROR_REF(error)); + read_event_.SetShutdown(GRPC_ERROR_REF(error)); + write_event_.SetShutdown(GRPC_ERROR_REF(error)); + GRPC_ERROR_UNREF(error); +} + +void CFStreamHandle::Ref(const char* file, int line, const char* reason) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&refcount_.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "CFStream Handle ref %p : %s %" PRIdPTR " -> %" PRIdPTR, this, + reason, val, val + 1); + } + gpr_ref(&refcount_); +} + +void CFStreamHandle::Unref(const char* file, int line, const char* reason) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&refcount_.count); + gpr_log(GPR_ERROR, + "CFStream Handle unref %p : %s %" PRIdPTR " -> %" PRIdPTR, this, + reason, val, val - 1); + } + if (gpr_unref(&refcount_)) { + delete this; + } +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/cfstream_handle.h b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.h new file mode 100644 index 00000000000000..4258e72431c063 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/cfstream_handle.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* The CFStream handle acts as an event synchronization entity for + * read/write/open/error/eos events happening on CFStream streams. */ + +#ifndef GRPC_CORE_LIB_IOMGR_CFSTREAM_HANDLE_H +#define GRPC_CORE_LIB_IOMGR_CFSTREAM_HANDLE_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_CFSTREAM +#import + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/lockfree_event.h" + +class CFStreamHandle final { + public: + static CFStreamHandle* CreateStreamHandle(CFReadStreamRef read_stream, + CFWriteStreamRef write_stream); + ~CFStreamHandle(); + CFStreamHandle(const CFReadStreamRef& ref) = delete; + CFStreamHandle(CFReadStreamRef&& ref) = delete; + CFStreamHandle& operator=(const CFStreamHandle& rhs) = delete; + + void NotifyOnOpen(grpc_closure* closure); + void NotifyOnRead(grpc_closure* closure); + void NotifyOnWrite(grpc_closure* closure); + void Shutdown(grpc_error* error); + + void Ref(const char* file = "", int line = 0, const char* reason = nullptr); + void Unref(const char* file = "", int line = 0, const char* reason = nullptr); + + private: + CFStreamHandle(CFReadStreamRef read_stream, CFWriteStreamRef write_stream); + static void ReadCallback(CFReadStreamRef stream, CFStreamEventType type, + void* client_callback_info); + static void WriteCallback(CFWriteStreamRef stream, CFStreamEventType type, + void* client_callback_info); + static void* Retain(void* info); + static void Release(void* info); + + grpc_core::LockfreeEvent open_event_; + grpc_core::LockfreeEvent read_event_; + grpc_core::LockfreeEvent write_event_; + + gpr_refcount refcount_; +}; + +#ifdef DEBUG +#define CFSTREAM_HANDLE_REF(handle, reason) \ + (handle)->Ref(__FILE__, __LINE__, (reason)) +#define CFSTREAM_HANDLE_UNREF(handle, reason) \ + (handle)->Unref(__FILE__, __LINE__, (reason)) +#else +#define CFSTREAM_HANDLE_REF(handle, reason) (handle)->Ref() +#define CFSTREAM_HANDLE_UNREF(handle, reason) (handle)->Unref() +#endif + +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_CFSTREAM_HANDLE_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/closure.h b/third_party/grpc/src/core/lib/iomgr/closure.h new file mode 100644 index 00000000000000..bde3437c02ed42 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/closure.h @@ -0,0 +1,353 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_CLOSURE_H +#define GRPC_CORE_LIB_IOMGR_CLOSURE_H + +#include + +#include +#include +#include +#include +#include "src/core/lib/gpr/mpscq.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/profiling/timers.h" + +struct grpc_closure; +typedef struct grpc_closure grpc_closure; + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_closure; + +typedef struct grpc_closure_list { + grpc_closure* head; + grpc_closure* tail; +} grpc_closure_list; + +/** gRPC Callback definition. + * + * \param arg Arbitrary input. + * \param error GRPC_ERROR_NONE if no error occurred, otherwise some grpc_error + * describing what went wrong. + * Error contract: it is not the cb's job to unref this error; + * the closure scheduler will do that after the cb returns */ +typedef void (*grpc_iomgr_cb_func)(void* arg, grpc_error* error); + +typedef struct grpc_closure_scheduler grpc_closure_scheduler; + +typedef struct grpc_closure_scheduler_vtable { + /* NOTE: for all these functions, closure->scheduler == the scheduler that was + used to find this vtable */ + void (*run)(grpc_closure* closure, grpc_error* error); + void (*sched)(grpc_closure* closure, grpc_error* error); + const char* name; +} grpc_closure_scheduler_vtable; + +/** Abstract type that can schedule closures for execution */ +struct grpc_closure_scheduler { + const grpc_closure_scheduler_vtable* vtable; +}; + +/** A closure over a grpc_iomgr_cb_func. */ +struct grpc_closure { + /** Once queued, next indicates the next queued closure; before then, scratch + * space */ + union { + grpc_closure* next; + gpr_mpscq_node atm_next; + uintptr_t scratch; + } next_data; + + /** Bound callback. */ + grpc_iomgr_cb_func cb; + + /** Arguments to be passed to "cb". */ + void* cb_arg; + + /** Scheduler to schedule against: nullptr to schedule against current + execution context */ + grpc_closure_scheduler* scheduler; + + /** Once queued, the result of the closure. Before then: scratch space */ + union { + grpc_error* error; + uintptr_t scratch; + } error_data; + +// extra tracing and debugging for grpc_closure. This incurs a decent amount of +// overhead per closure, so it must be enabled at compile time. +#ifndef NDEBUG + bool scheduled; + bool run; // true = run, false = scheduled + const char* file_created; + int line_created; + const char* file_initiated; + int line_initiated; +#endif +}; + +#ifndef NDEBUG +inline grpc_closure* grpc_closure_init(const char* file, int line, + grpc_closure* closure, + grpc_iomgr_cb_func cb, void* cb_arg, + grpc_closure_scheduler* scheduler) { +#else +inline grpc_closure* grpc_closure_init(grpc_closure* closure, + grpc_iomgr_cb_func cb, void* cb_arg, + grpc_closure_scheduler* scheduler) { +#endif + closure->cb = cb; + closure->cb_arg = cb_arg; + closure->scheduler = scheduler; + closure->error_data.error = GRPC_ERROR_NONE; +#ifndef NDEBUG + closure->scheduled = false; + closure->file_initiated = nullptr; + closure->line_initiated = 0; + closure->run = false; + closure->file_created = file; + closure->line_created = line; +#endif + return closure; +} + +/** Initializes \a closure with \a cb and \a cb_arg. Returns \a closure. */ +#ifndef NDEBUG +#define GRPC_CLOSURE_INIT(closure, cb, cb_arg, scheduler) \ + grpc_closure_init(__FILE__, __LINE__, closure, cb, cb_arg, scheduler) +#else +#define GRPC_CLOSURE_INIT(closure, cb, cb_arg, scheduler) \ + grpc_closure_init(closure, cb, cb_arg, scheduler) +#endif + +namespace closure_impl { + +typedef struct { + grpc_iomgr_cb_func cb; + void* cb_arg; + grpc_closure wrapper; +} wrapped_closure; + +inline void closure_wrapper(void* arg, grpc_error* error) { + wrapped_closure* wc = static_cast(arg); + grpc_iomgr_cb_func cb = wc->cb; + void* cb_arg = wc->cb_arg; + gpr_free(wc); + cb(cb_arg, error); +} + +} // namespace closure_impl + +#ifndef NDEBUG +inline grpc_closure* grpc_closure_create(const char* file, int line, + grpc_iomgr_cb_func cb, void* cb_arg, + grpc_closure_scheduler* scheduler) { +#else +inline grpc_closure* grpc_closure_create(grpc_iomgr_cb_func cb, void* cb_arg, + grpc_closure_scheduler* scheduler) { +#endif + closure_impl::wrapped_closure* wc = + static_cast(gpr_malloc(sizeof(*wc))); + wc->cb = cb; + wc->cb_arg = cb_arg; +#ifndef NDEBUG + grpc_closure_init(file, line, &wc->wrapper, closure_impl::closure_wrapper, wc, + scheduler); +#else + grpc_closure_init(&wc->wrapper, closure_impl::closure_wrapper, wc, scheduler); +#endif + return &wc->wrapper; +} + +/* Create a heap allocated closure: try to avoid except for very rare events */ +#ifndef NDEBUG +#define GRPC_CLOSURE_CREATE(cb, cb_arg, scheduler) \ + grpc_closure_create(__FILE__, __LINE__, cb, cb_arg, scheduler) +#else +#define GRPC_CLOSURE_CREATE(cb, cb_arg, scheduler) \ + grpc_closure_create(cb, cb_arg, scheduler) +#endif + +#define GRPC_CLOSURE_LIST_INIT \ + { nullptr, nullptr } + +inline void grpc_closure_list_init(grpc_closure_list* closure_list) { + closure_list->head = closure_list->tail = nullptr; +} + +/** add \a closure to the end of \a list + and set \a closure's result to \a error + Returns true if \a list becomes non-empty */ +inline bool grpc_closure_list_append(grpc_closure_list* closure_list, + grpc_closure* closure, grpc_error* error) { + if (closure == nullptr) { + GRPC_ERROR_UNREF(error); + return false; + } + closure->error_data.error = error; + closure->next_data.next = nullptr; + bool was_empty = (closure_list->head == nullptr); + if (was_empty) { + closure_list->head = closure; + } else { + closure_list->tail->next_data.next = closure; + } + closure_list->tail = closure; + return was_empty; +} + +/** force all success bits in \a list to false */ +inline void grpc_closure_list_fail_all(grpc_closure_list* list, + grpc_error* forced_failure) { + for (grpc_closure* c = list->head; c != nullptr; c = c->next_data.next) { + if (c->error_data.error == GRPC_ERROR_NONE) { + c->error_data.error = GRPC_ERROR_REF(forced_failure); + } + } + GRPC_ERROR_UNREF(forced_failure); +} + +/** append all closures from \a src to \a dst and empty \a src. */ +inline void grpc_closure_list_move(grpc_closure_list* src, + grpc_closure_list* dst) { + if (src->head == nullptr) { + return; + } + if (dst->head == nullptr) { + *dst = *src; + } else { + dst->tail->next_data.next = src->head; + dst->tail = src->tail; + } + src->head = src->tail = nullptr; +} + +/** return whether \a list is empty. */ +inline bool grpc_closure_list_empty(grpc_closure_list closure_list) { + return closure_list.head == nullptr; +} + +#ifndef NDEBUG +inline void grpc_closure_run(const char* file, int line, grpc_closure* c, + grpc_error* error) { +#else +inline void grpc_closure_run(grpc_closure* c, grpc_error* error) { +#endif + GPR_TIMER_SCOPE("grpc_closure_run", 0); + if (c != nullptr) { +#ifndef NDEBUG + c->file_initiated = file; + c->line_initiated = line; + c->run = true; + GPR_ASSERT(c->cb != nullptr); +#endif + c->scheduler->vtable->run(c, error); + } else { + GRPC_ERROR_UNREF(error); + } +} + +/** Run a closure directly. Caller ensures that no locks are being held above. + * Note that calling this at the end of a closure callback function itself is + * by definition safe. */ +#ifndef NDEBUG +#define GRPC_CLOSURE_RUN(closure, error) \ + grpc_closure_run(__FILE__, __LINE__, closure, error) +#else +#define GRPC_CLOSURE_RUN(closure, error) grpc_closure_run(closure, error) +#endif + +#ifndef NDEBUG +inline void grpc_closure_sched(const char* file, int line, grpc_closure* c, + grpc_error* error) { +#else +inline void grpc_closure_sched(grpc_closure* c, grpc_error* error) { +#endif + GPR_TIMER_SCOPE("grpc_closure_sched", 0); + if (c != nullptr) { +#ifndef NDEBUG + if (c->scheduled) { + gpr_log(GPR_ERROR, + "Closure already scheduled. (closure: %p, created: [%s:%d], " + "previously scheduled at: [%s: %d], newly scheduled at [%s: %d], " + "run?: %s", + c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated, file, line, c->run ? "true" : "false"); + abort(); + } + c->scheduled = true; + c->file_initiated = file; + c->line_initiated = line; + c->run = false; + GPR_ASSERT(c->cb != nullptr); +#endif + c->scheduler->vtable->sched(c, error); + } else { + GRPC_ERROR_UNREF(error); + } +} + +/** Schedule a closure to be run. Does not need to be run from a safe point. */ +#ifndef NDEBUG +#define GRPC_CLOSURE_SCHED(closure, error) \ + grpc_closure_sched(__FILE__, __LINE__, closure, error) +#else +#define GRPC_CLOSURE_SCHED(closure, error) grpc_closure_sched(closure, error) +#endif + +#ifndef NDEBUG +inline void grpc_closure_list_sched(const char* file, int line, + grpc_closure_list* list) { +#else +inline void grpc_closure_list_sched(grpc_closure_list* list) { +#endif + grpc_closure* c = list->head; + while (c != nullptr) { + grpc_closure* next = c->next_data.next; +#ifndef NDEBUG + if (c->scheduled) { + gpr_log(GPR_ERROR, + "Closure already scheduled. (closure: %p, created: [%s:%d], " + "previously scheduled at: [%s: %d] run?: %s", + c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated, c->run ? "true" : "false"); + abort(); + } + c->scheduled = true; + c->file_initiated = file; + c->line_initiated = line; + c->run = false; + GPR_ASSERT(c->cb != nullptr); +#endif + c->scheduler->vtable->sched(c, c->error_data.error); + c = next; + } + list->head = list->tail = nullptr; +} + +/** Schedule all closures in a list to be run. Does not need to be run from a + * safe point. */ +#ifndef NDEBUG +#define GRPC_CLOSURE_LIST_SCHED(closure_list) \ + grpc_closure_list_sched(__FILE__, __LINE__, closure_list) +#else +#define GRPC_CLOSURE_LIST_SCHED(closure_list) \ + grpc_closure_list_sched(closure_list) +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_CLOSURE_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/combiner.cc b/third_party/grpc/src/core/lib/iomgr/combiner.cc new file mode 100644 index 00000000000000..7c0062eb4ecc38 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/combiner.cc @@ -0,0 +1,375 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/combiner.h" + +#include +#include +#include + +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/profiling/timers.h" + +grpc_core::DebugOnlyTraceFlag grpc_combiner_trace(false, "combiner"); + +#define GRPC_COMBINER_TRACE(fn) \ + do { \ + if (grpc_combiner_trace.enabled()) { \ + fn; \ + } \ + } while (0) + +#define STATE_UNORPHANED 1 +#define STATE_ELEM_COUNT_LOW_BIT 2 + +struct grpc_combiner { + grpc_combiner* next_combiner_on_this_exec_ctx; + grpc_closure_scheduler scheduler; + grpc_closure_scheduler finally_scheduler; + gpr_mpscq queue; + // either: + // a pointer to the initiating exec ctx if that is the only exec_ctx that has + // ever queued to this combiner, or NULL. If this is non-null, it's not + // dereferencable (since the initiating exec_ctx may have gone out of scope) + gpr_atm initiating_exec_ctx_or_null; + // state is: + // lower bit - zero if orphaned (STATE_UNORPHANED) + // other bits - number of items queued on the lock (STATE_ELEM_COUNT_LOW_BIT) + gpr_atm state; + bool time_to_execute_final_list; + grpc_closure_list final_list; + grpc_closure offload; + gpr_refcount refs; +}; + +static void combiner_run(grpc_closure* closure, grpc_error* error); +static void combiner_exec(grpc_closure* closure, grpc_error* error); +static void combiner_finally_exec(grpc_closure* closure, grpc_error* error); + +static const grpc_closure_scheduler_vtable scheduler = { + combiner_run, combiner_exec, "combiner:immediately"}; +static const grpc_closure_scheduler_vtable finally_scheduler = { + combiner_finally_exec, combiner_finally_exec, "combiner:finally"}; + +static void offload(void* arg, grpc_error* error); + +grpc_combiner* grpc_combiner_create(void) { + grpc_combiner* lock = static_cast(gpr_zalloc(sizeof(*lock))); + gpr_ref_init(&lock->refs, 1); + lock->scheduler.vtable = &scheduler; + lock->finally_scheduler.vtable = &finally_scheduler; + gpr_atm_no_barrier_store(&lock->state, STATE_UNORPHANED); + gpr_mpscq_init(&lock->queue); + grpc_closure_list_init(&lock->final_list); + GRPC_CLOSURE_INIT(&lock->offload, offload, lock, + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); + GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, "C:%p create", lock)); + return lock; +} + +static void really_destroy(grpc_combiner* lock) { + GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, "C:%p really_destroy", lock)); + GPR_ASSERT(gpr_atm_no_barrier_load(&lock->state) == 0); + gpr_mpscq_destroy(&lock->queue); + gpr_free(lock); +} + +static void start_destroy(grpc_combiner* lock) { + gpr_atm old_state = gpr_atm_full_fetch_add(&lock->state, -STATE_UNORPHANED); + GRPC_COMBINER_TRACE(gpr_log( + GPR_INFO, "C:%p really_destroy old_state=%" PRIdPTR, lock, old_state)); + if (old_state == 1) { + really_destroy(lock); + } +} + +#ifndef NDEBUG +#define GRPC_COMBINER_DEBUG_SPAM(op, delta) \ + if (grpc_combiner_trace.enabled()) { \ + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, \ + "C:%p %s %" PRIdPTR " --> %" PRIdPTR " %s", lock, (op), \ + gpr_atm_no_barrier_load(&lock->refs.count), \ + gpr_atm_no_barrier_load(&lock->refs.count) + (delta), reason); \ + } +#else +#define GRPC_COMBINER_DEBUG_SPAM(op, delta) +#endif + +void grpc_combiner_unref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS) { + GRPC_COMBINER_DEBUG_SPAM("UNREF", -1); + if (gpr_unref(&lock->refs)) { + start_destroy(lock); + } +} + +grpc_combiner* grpc_combiner_ref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS) { + GRPC_COMBINER_DEBUG_SPAM(" REF", 1); + gpr_ref(&lock->refs); + return lock; +} + +static void push_last_on_exec_ctx(grpc_combiner* lock) { + lock->next_combiner_on_this_exec_ctx = nullptr; + if (grpc_core::ExecCtx::Get()->combiner_data()->active_combiner == nullptr) { + grpc_core::ExecCtx::Get()->combiner_data()->active_combiner = + grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = lock; + } else { + grpc_core::ExecCtx::Get() + ->combiner_data() + ->last_combiner->next_combiner_on_this_exec_ctx = lock; + grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = lock; + } +} + +static void push_first_on_exec_ctx(grpc_combiner* lock) { + lock->next_combiner_on_this_exec_ctx = + grpc_core::ExecCtx::Get()->combiner_data()->active_combiner; + grpc_core::ExecCtx::Get()->combiner_data()->active_combiner = lock; + if (lock->next_combiner_on_this_exec_ctx == nullptr) { + grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = lock; + } +} + +#define COMBINER_FROM_CLOSURE_SCHEDULER(closure, scheduler_name) \ + ((grpc_combiner*)(((char*)((closure)->scheduler)) - \ + offsetof(grpc_combiner, scheduler_name))) + +static void combiner_exec(grpc_closure* cl, grpc_error* error) { + GPR_TIMER_SCOPE("combiner.execute", 0); + GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS(); + grpc_combiner* lock = COMBINER_FROM_CLOSURE_SCHEDULER(cl, scheduler); + gpr_atm last = gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT); + GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, + "C:%p grpc_combiner_execute c=%p last=%" PRIdPTR, + lock, cl, last)); + if (last == 1) { + GRPC_STATS_INC_COMBINER_LOCKS_INITIATED(); + GPR_TIMER_MARK("combiner.initiated", 0); + gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null, + (gpr_atm)grpc_core::ExecCtx::Get()); + // first element on this list: add it to the list of combiner locks + // executing within this exec_ctx + push_last_on_exec_ctx(lock); + } else { + // there may be a race with setting here: if that happens, we may delay + // offload for one or two actions, and that's fine + gpr_atm initiator = + gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null); + if (initiator != 0 && initiator != (gpr_atm)grpc_core::ExecCtx::Get()) { + gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null, 0); + } + } + GPR_ASSERT(last & STATE_UNORPHANED); // ensure lock has not been destroyed + assert(cl->cb); + cl->error_data.error = error; + gpr_mpscq_push(&lock->queue, &cl->next_data.atm_next); +} + +static void move_next() { + grpc_core::ExecCtx::Get()->combiner_data()->active_combiner = + grpc_core::ExecCtx::Get() + ->combiner_data() + ->active_combiner->next_combiner_on_this_exec_ctx; + if (grpc_core::ExecCtx::Get()->combiner_data()->active_combiner == nullptr) { + grpc_core::ExecCtx::Get()->combiner_data()->last_combiner = nullptr; + } +} + +static void offload(void* arg, grpc_error* error) { + grpc_combiner* lock = static_cast(arg); + push_last_on_exec_ctx(lock); +} + +static void queue_offload(grpc_combiner* lock) { + GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED(); + move_next(); + GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, "C:%p queue_offload", lock)); + GRPC_CLOSURE_SCHED(&lock->offload, GRPC_ERROR_NONE); +} + +bool grpc_combiner_continue_exec_ctx() { + GPR_TIMER_SCOPE("combiner.continue_exec_ctx", 0); + grpc_combiner* lock = + grpc_core::ExecCtx::Get()->combiner_data()->active_combiner; + if (lock == nullptr) { + return false; + } + + bool contended = + gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null) == 0; + + GRPC_COMBINER_TRACE(gpr_log(GPR_INFO, + "C:%p grpc_combiner_continue_exec_ctx " + "contended=%d " + "exec_ctx_ready_to_finish=%d " + "time_to_execute_final_list=%d", + lock, contended, + grpc_core::ExecCtx::Get()->IsReadyToFinish(), + lock->time_to_execute_final_list)); + + if (contended && grpc_core::ExecCtx::Get()->IsReadyToFinish() && + grpc_executor_is_threaded()) { + GPR_TIMER_MARK("offload_from_finished_exec_ctx", 0); + // this execution context wants to move on: schedule remaining work to be + // picked up on the executor + queue_offload(lock); + return true; + } + + if (!lock->time_to_execute_final_list || + // peek to see if something new has shown up, and execute that with + // priority + (gpr_atm_acq_load(&lock->state) >> 1) > 1) { + gpr_mpscq_node* n = gpr_mpscq_pop(&lock->queue); + GRPC_COMBINER_TRACE( + gpr_log(GPR_INFO, "C:%p maybe_finish_one n=%p", lock, n)); + if (n == nullptr) { + // queue is in an inconsistent state: use this as a cue that we should + // go off and do something else for a while (and come back later) + GPR_TIMER_MARK("delay_busy", 0); + queue_offload(lock); + return true; + } + GPR_TIMER_SCOPE("combiner.exec1", 0); + grpc_closure* cl = reinterpret_cast(n); + grpc_error* cl_err = cl->error_data.error; +#ifndef NDEBUG + cl->scheduled = false; +#endif + cl->cb(cl->cb_arg, cl_err); + GRPC_ERROR_UNREF(cl_err); + } else { + grpc_closure* c = lock->final_list.head; + GPR_ASSERT(c != nullptr); + grpc_closure_list_init(&lock->final_list); + int loops = 0; + while (c != nullptr) { + GPR_TIMER_SCOPE("combiner.exec_1final", 0); + GRPC_COMBINER_TRACE( + gpr_log(GPR_INFO, "C:%p execute_final[%d] c=%p", lock, loops, c)); + grpc_closure* next = c->next_data.next; + grpc_error* error = c->error_data.error; +#ifndef NDEBUG + c->scheduled = false; +#endif + c->cb(c->cb_arg, error); + GRPC_ERROR_UNREF(error); + c = next; + } + } + + GPR_TIMER_MARK("unref", 0); + move_next(); + lock->time_to_execute_final_list = false; + gpr_atm old_state = + gpr_atm_full_fetch_add(&lock->state, -STATE_ELEM_COUNT_LOW_BIT); + GRPC_COMBINER_TRACE( + gpr_log(GPR_INFO, "C:%p finish old_state=%" PRIdPTR, lock, old_state)); +// Define a macro to ease readability of the following switch statement. +#define OLD_STATE_WAS(orphaned, elem_count) \ + (((orphaned) ? 0 : STATE_UNORPHANED) | \ + ((elem_count)*STATE_ELEM_COUNT_LOW_BIT)) + // Depending on what the previous state was, we need to perform different + // actions. + switch (old_state) { + default: + // we have multiple queued work items: just continue executing them + break; + case OLD_STATE_WAS(false, 2): + case OLD_STATE_WAS(true, 2): + // we're down to one queued item: if it's the final list we should do that + if (!grpc_closure_list_empty(lock->final_list)) { + lock->time_to_execute_final_list = true; + } + break; + case OLD_STATE_WAS(false, 1): + // had one count, one unorphaned --> unlocked unorphaned + return true; + case OLD_STATE_WAS(true, 1): + // and one count, one orphaned --> unlocked and orphaned + really_destroy(lock); + return true; + case OLD_STATE_WAS(false, 0): + case OLD_STATE_WAS(true, 0): + // these values are illegal - representing an already unlocked or + // deleted lock + GPR_UNREACHABLE_CODE(return true); + } + push_first_on_exec_ctx(lock); + return true; +} + +static void enqueue_finally(void* closure, grpc_error* error); + +static void combiner_finally_exec(grpc_closure* closure, grpc_error* error) { + GPR_TIMER_SCOPE("combiner.execute_finally", 0); + GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS(); + grpc_combiner* lock = + COMBINER_FROM_CLOSURE_SCHEDULER(closure, finally_scheduler); + GRPC_COMBINER_TRACE(gpr_log( + GPR_INFO, "C:%p grpc_combiner_execute_finally c=%p; ac=%p", lock, closure, + grpc_core::ExecCtx::Get()->combiner_data()->active_combiner)); + if (grpc_core::ExecCtx::Get()->combiner_data()->active_combiner != lock) { + GPR_TIMER_MARK("slowpath", 0); + GRPC_CLOSURE_SCHED(GRPC_CLOSURE_CREATE(enqueue_finally, closure, + grpc_combiner_scheduler(lock)), + error); + return; + } + + if (grpc_closure_list_empty(lock->final_list)) { + gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT); + } + grpc_closure_list_append(&lock->final_list, closure, error); +} + +static void combiner_run(grpc_closure* closure, grpc_error* error) { + grpc_combiner* lock = COMBINER_FROM_CLOSURE_SCHEDULER(closure, scheduler); +#ifndef NDEBUG + closure->scheduled = false; + GRPC_COMBINER_TRACE(gpr_log( + GPR_DEBUG, + "Combiner:%p grpc_combiner_run closure:%p created [%s:%d] run [%s:%d]", + lock, closure, closure->file_created, closure->line_created, + closure->file_initiated, closure->line_initiated)); +#endif + GPR_ASSERT(grpc_core::ExecCtx::Get()->combiner_data()->active_combiner == + lock); + closure->cb(closure->cb_arg, error); + GRPC_ERROR_UNREF(error); +} + +static void enqueue_finally(void* closure, grpc_error* error) { + combiner_finally_exec(static_cast(closure), + GRPC_ERROR_REF(error)); +} + +grpc_closure_scheduler* grpc_combiner_scheduler(grpc_combiner* combiner) { + return &combiner->scheduler; +} + +grpc_closure_scheduler* grpc_combiner_finally_scheduler( + grpc_combiner* combiner) { + return &combiner->finally_scheduler; +} diff --git a/third_party/grpc/src/core/lib/iomgr/combiner.h b/third_party/grpc/src/core/lib/iomgr/combiner.h new file mode 100644 index 00000000000000..3c947bf9d64859 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/combiner.h @@ -0,0 +1,66 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_COMBINER_H +#define GRPC_CORE_LIB_IOMGR_COMBINER_H + +#include + +#include + +#include +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/mpscq.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +// Provides serialized access to some resource. +// Each action queued on a combiner is executed serially in a borrowed thread. +// The actual thread executing actions may change over time (but there will only +// every be one at a time). + +// Initialize the lock, with an optional workqueue to shift load to when +// necessary +grpc_combiner* grpc_combiner_create(void); + +#ifndef NDEBUG +#define GRPC_COMBINER_DEBUG_ARGS \ + , const char *file, int line, const char *reason +#define GRPC_COMBINER_REF(combiner, reason) \ + grpc_combiner_ref((combiner), __FILE__, __LINE__, (reason)) +#define GRPC_COMBINER_UNREF(combiner, reason) \ + grpc_combiner_unref((combiner), __FILE__, __LINE__, (reason)) +#else +#define GRPC_COMBINER_DEBUG_ARGS +#define GRPC_COMBINER_REF(combiner, reason) grpc_combiner_ref((combiner)) +#define GRPC_COMBINER_UNREF(combiner, reason) grpc_combiner_unref((combiner)) +#endif + +// Ref/unref the lock, for when we're sharing the lock ownership +// Prefer to use the macros above +grpc_combiner* grpc_combiner_ref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS); +void grpc_combiner_unref(grpc_combiner* lock GRPC_COMBINER_DEBUG_ARGS); +// Fetch a scheduler to schedule closures against +grpc_closure_scheduler* grpc_combiner_scheduler(grpc_combiner* lock); +// Scheduler to execute \a action within the lock just prior to unlocking. +grpc_closure_scheduler* grpc_combiner_finally_scheduler(grpc_combiner* lock); + +bool grpc_combiner_continue_exec_ctx(); + +extern grpc_core::DebugOnlyTraceFlag grpc_combiner_trace; + +#endif /* GRPC_CORE_LIB_IOMGR_COMBINER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/dynamic_annotations.h b/third_party/grpc/src/core/lib/iomgr/dynamic_annotations.h new file mode 100644 index 00000000000000..713928023ae843 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/dynamic_annotations.h @@ -0,0 +1,67 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_DYNAMIC_ANNOTATIONS_H +#define GRPC_CORE_LIB_IOMGR_DYNAMIC_ANNOTATIONS_H + +#include + +#ifdef GRPC_TSAN_ENABLED + +#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr) \ + AnnotateHappensBefore(__FILE__, __LINE__, (void*)(addr)) +#define TSAN_ANNOTATE_HAPPENS_AFTER(addr) \ + AnnotateHappensAfter(__FILE__, __LINE__, (void*)(addr)) +#define TSAN_ANNOTATE_RWLOCK_CREATE(addr) \ + AnnotateRWLockCreate(__FILE__, __LINE__, (void*)(addr)) +#define TSAN_ANNOTATE_RWLOCK_DESTROY(addr) \ + AnnotateRWLockDestroy(__FILE__, __LINE__, (void*)(addr)) +#define TSAN_ANNOTATE_RWLOCK_ACQUIRED(addr, is_w) \ + AnnotateRWLockAcquired(__FILE__, __LINE__, (void*)(addr), (is_w)) +#define TSAN_ANNOTATE_RWLOCK_RELEASED(addr, is_w) \ + AnnotateRWLockReleased(__FILE__, __LINE__, (void*)(addr), (is_w)) + +#ifdef __cplusplus +extern "C" { +#endif +void AnnotateHappensBefore(const char* file, int line, const volatile void* cv); +void AnnotateHappensAfter(const char* file, int line, const volatile void* cv); +void AnnotateRWLockCreate(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockDestroy(const char* file, int line, + const volatile void* lock); +void AnnotateRWLockAcquired(const char* file, int line, + const volatile void* lock, long is_w); +void AnnotateRWLockReleased(const char* file, int line, + const volatile void* lock, long is_w); +#ifdef __cplusplus +} +#endif + +#else /* GRPC_TSAN_ENABLED */ + +#define TSAN_ANNOTATE_HAPPENS_BEFORE(addr) +#define TSAN_ANNOTATE_HAPPENS_AFTER(addr) +#define TSAN_ANNOTATE_RWLOCK_CREATE(addr) +#define TSAN_ANNOTATE_RWLOCK_DESTROY(addr) +#define TSAN_ANNOTATE_RWLOCK_ACQUIRED(addr, is_w) +#define TSAN_ANNOTATE_RWLOCK_RELEASED(addr, is_w) + +#endif /* GRPC_TSAN_ENABLED */ + +#endif /* GRPC_CORE_LIB_IOMGR_DYNAMIC_ANNOTATIONS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint.cc b/third_party/grpc/src/core/lib/iomgr/endpoint.cc new file mode 100644 index 00000000000000..06316c603159ef --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint.cc @@ -0,0 +1,67 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/endpoint.h" + +grpc_core::TraceFlag grpc_tcp_trace(false, "tcp"); + +void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices, + grpc_closure* cb) { + ep->vtable->read(ep, slices, cb); +} + +void grpc_endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* slices, + grpc_closure* cb, void* arg) { + ep->vtable->write(ep, slices, cb, arg); +} + +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_delete_from_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset_set) { + ep->vtable->delete_from_pollset_set(ep, pollset_set); +} + +void grpc_endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) { + ep->vtable->shutdown(ep, why); +} + +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); +} + +int grpc_endpoint_get_fd(grpc_endpoint* ep) { return ep->vtable->get_fd(ep); } + +grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* ep) { + return ep->vtable->get_resource_user(ep); +} + +bool grpc_endpoint_can_track_err(grpc_endpoint* ep) { + return ep->vtable->can_track_err(ep); +} diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint.h b/third_party/grpc/src/core/lib/iomgr/endpoint.h new file mode 100644 index 00000000000000..79c8ece263a94c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint.h @@ -0,0 +1,105 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_H +#define GRPC_CORE_LIB_IOMGR_ENDPOINT_H + +#include + +#include +#include +#include +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/iomgr/resource_quota.h" + +/* 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; +class Timestamps; + +struct grpc_endpoint_vtable { + void (*read)(grpc_endpoint* ep, grpc_slice_buffer* slices, grpc_closure* cb); + void (*write)(grpc_endpoint* ep, grpc_slice_buffer* slices, grpc_closure* cb, + void* arg); + void (*add_to_pollset)(grpc_endpoint* ep, grpc_pollset* pollset); + void (*add_to_pollset_set)(grpc_endpoint* ep, grpc_pollset_set* pollset); + void (*delete_from_pollset_set)(grpc_endpoint* ep, grpc_pollset_set* pollset); + void (*shutdown)(grpc_endpoint* ep, grpc_error* why); + void (*destroy)(grpc_endpoint* ep); + grpc_resource_user* (*get_resource_user)(grpc_endpoint* ep); + char* (*get_peer)(grpc_endpoint* ep); + int (*get_fd)(grpc_endpoint* ep); + bool (*can_track_err)(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 when the callback is + invoked with error != GRPC_ERROR_NONE. */ +void grpc_endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* slices, + grpc_closure* cb); + +char* grpc_endpoint_get_peer(grpc_endpoint* ep); + +/* Get the file descriptor used by \a ep. Return -1 if \a ep is not using an fd. + */ +int grpc_endpoint_get_fd(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. + \a arg is platform specific. It is currently only used by TCP on linux + platforms as an argument that would be forwarded to the timestamps callback. + */ +void grpc_endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* slices, + grpc_closure* cb, void* arg); + +/* Causes any pending and future read/write callbacks to run immediately with + success==0 */ +void grpc_endpoint_shutdown(grpc_endpoint* ep, grpc_error* why); +void grpc_endpoint_destroy(grpc_endpoint* ep); + +/* Add an endpoint to a pollset or pollset_set, 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); + +/* Delete an endpoint from a pollset_set */ +void grpc_endpoint_delete_from_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset_set); + +grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* endpoint); + +bool grpc_endpoint_can_track_err(grpc_endpoint* ep); + +struct grpc_endpoint { + const grpc_endpoint_vtable* vtable; +}; + +#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.cc new file mode 100644 index 00000000000000..7c4bc1ace2a368 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.cc @@ -0,0 +1,375 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_CFSTREAM_ENDPOINT + +#import +#import "src/core/lib/iomgr/endpoint_cfstream.h" + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/cfstream_handle.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/error_cfstream.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; + +typedef struct { + grpc_endpoint base; + gpr_refcount refcount; + + CFReadStreamRef read_stream; + CFWriteStreamRef write_stream; + CFStreamHandle* stream_sync; + + grpc_closure* read_cb; + grpc_closure* write_cb; + grpc_slice_buffer* read_slices; + grpc_slice_buffer* write_slices; + + grpc_closure read_action; + grpc_closure write_action; + + char* peer_string; + grpc_resource_user* resource_user; + grpc_resource_user_slice_allocator slice_allocator; +} CFStreamEndpoint; + +static void CFStreamFree(CFStreamEndpoint* ep) { + grpc_resource_user_unref(ep->resource_user); + CFRelease(ep->read_stream); + CFRelease(ep->write_stream); + CFSTREAM_HANDLE_UNREF(ep->stream_sync, "free"); + gpr_free(ep->peer_string); + gpr_free(ep); +} + +#ifndef NDEBUG +#define EP_REF(ep, reason) CFStreamRef((ep), (reason), __FILE__, __LINE__) +#define EP_UNREF(ep, reason) CFStreamUnref((ep), (reason), __FILE__, __LINE__) +static void CFStreamUnref(CFStreamEndpoint* ep, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "CFStream endpoint unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, + reason, val, val - 1); + } + if (gpr_unref(&ep->refcount)) { + CFStreamFree(ep); + } +} +static void CFStreamRef(CFStreamEndpoint* ep, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&ep->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "CFStream endpoint ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, + reason, val, val + 1); + } + gpr_ref(&ep->refcount); +} +#else +#define EP_REF(ep, reason) CFStreamRef((ep)) +#define EP_UNREF(ep, reason) CFStreamUnref((ep)) +static void CFStreamUnref(CFStreamEndpoint* ep) { + if (gpr_unref(&ep->refcount)) { + CFStreamFree(ep); + } +} +static void CFStreamRef(CFStreamEndpoint* ep) { gpr_ref(&ep->refcount); } +#endif + +static grpc_error* CFStreamAnnotateError(grpc_error* src_error, + CFStreamEndpoint* ep) { + return grpc_error_set_str( + grpc_error_set_int(src_error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE), + GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(ep->peer_string)); +} + +static void CallReadCb(CFStreamEndpoint* ep, grpc_error* error) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_read_cb %p %p:%p", ep, + ep->read_cb, ep->read_cb->cb, ep->read_cb->cb_arg); + size_t i; + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "read: error=%s", str); + + for (i = 0; i < ep->read_slices->count; i++) { + char* dump = grpc_dump_slice(ep->read_slices->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", ep, ep->peer_string, dump); + gpr_free(dump); + } + } + grpc_closure* cb = ep->read_cb; + ep->read_cb = nullptr; + ep->read_slices = nullptr; + GRPC_CLOSURE_SCHED(cb, error); +} + +static void CallWriteCb(CFStreamEndpoint* ep, grpc_error* error) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream endpoint:%p call_write_cb %p %p:%p", ep, + ep->write_cb, ep->write_cb->cb, ep->write_cb->cb_arg); + const char* str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "write: error=%s", str); + } + grpc_closure* cb = ep->write_cb; + ep->write_cb = nullptr; + ep->write_slices = nullptr; + GRPC_CLOSURE_SCHED(cb, error); +} + +static void ReadAction(void* arg, grpc_error* error) { + CFStreamEndpoint* ep = static_cast(arg); + GPR_ASSERT(ep->read_cb != nullptr); + if (error) { + grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); + CallReadCb(ep, GRPC_ERROR_REF(error)); + EP_UNREF(ep, "read"); + return; + } + + GPR_ASSERT(ep->read_slices->count == 1); + grpc_slice slice = ep->read_slices->slices[0]; + size_t len = GRPC_SLICE_LENGTH(slice); + CFIndex read_size = + CFReadStreamRead(ep->read_stream, GRPC_SLICE_START_PTR(slice), len); + if (read_size == -1) { + grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); + CFErrorRef stream_error = CFReadStreamCopyError(ep->read_stream); + if (stream_error != nullptr) { + error = CFStreamAnnotateError( + GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "Read error"), ep); + CFRelease(stream_error); + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Read error"); + } + CallReadCb(ep, error); + EP_UNREF(ep, "read"); + } else if (read_size == 0) { + grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); + CallReadCb(ep, + CFStreamAnnotateError( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), ep)); + EP_UNREF(ep, "read"); + } else { + if (read_size < len) { + grpc_slice_buffer_trim_end(ep->read_slices, len - read_size, nullptr); + } + CallReadCb(ep, GRPC_ERROR_NONE); + EP_UNREF(ep, "read"); + } +} + +static void WriteAction(void* arg, grpc_error* error) { + CFStreamEndpoint* ep = static_cast(arg); + GPR_ASSERT(ep->write_cb != nullptr); + if (error) { + grpc_slice_buffer_reset_and_unref_internal(ep->write_slices); + CallWriteCb(ep, GRPC_ERROR_REF(error)); + EP_UNREF(ep, "write"); + return; + } + + grpc_slice slice = grpc_slice_buffer_take_first(ep->write_slices); + size_t slice_len = GRPC_SLICE_LENGTH(slice); + CFIndex write_size = CFWriteStreamWrite( + ep->write_stream, GRPC_SLICE_START_PTR(slice), slice_len); + if (write_size == -1) { + grpc_slice_buffer_reset_and_unref_internal(ep->write_slices); + CFErrorRef stream_error = CFWriteStreamCopyError(ep->write_stream); + if (stream_error != nullptr) { + error = CFStreamAnnotateError( + GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "write failed."), ep); + CFRelease(stream_error); + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("write failed."); + } + CallWriteCb(ep, error); + EP_UNREF(ep, "write"); + } else { + if (write_size < GRPC_SLICE_LENGTH(slice)) { + grpc_slice_buffer_undo_take_first( + ep->write_slices, grpc_slice_sub(slice, write_size, slice_len)); + } + if (ep->write_slices->length > 0) { + ep->stream_sync->NotifyOnWrite(&ep->write_action); + } else { + CallWriteCb(ep, GRPC_ERROR_NONE); + EP_UNREF(ep, "write"); + } + + if (grpc_tcp_trace.enabled()) { + grpc_slice trace_slice = grpc_slice_sub(slice, 0, write_size); + char* dump = grpc_dump_slice(trace_slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", ep, ep->peer_string, dump); + gpr_free(dump); + grpc_slice_unref_internal(trace_slice); + } + } + grpc_slice_unref_internal(slice); +} + +static void CFStreamReadAllocationDone(void* arg, grpc_error* error) { + CFStreamEndpoint* ep = static_cast(arg); + if (error == GRPC_ERROR_NONE) { + ep->stream_sync->NotifyOnRead(&ep->read_action); + } else { + grpc_slice_buffer_reset_and_unref_internal(ep->read_slices); + CallReadCb(ep, error); + EP_UNREF(ep, "read"); + } +} + +static void CFStreamRead(grpc_endpoint* ep, grpc_slice_buffer* slices, + grpc_closure* cb) { + CFStreamEndpoint* ep_impl = reinterpret_cast(ep); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream endpoint:%p read (%p, %p) length:%zu", ep_impl, + slices, cb, slices->length); + } + GPR_ASSERT(ep_impl->read_cb == nullptr); + ep_impl->read_cb = cb; + ep_impl->read_slices = slices; + grpc_slice_buffer_reset_and_unref_internal(slices); + grpc_resource_user_alloc_slices(&ep_impl->slice_allocator, + GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1, + ep_impl->read_slices); + EP_REF(ep_impl, "read"); +} + +static void CFStreamWrite(grpc_endpoint* ep, grpc_slice_buffer* slices, + grpc_closure* cb, void* arg) { + CFStreamEndpoint* ep_impl = reinterpret_cast(ep); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream endpoint:%p write (%p, %p) length:%zu", + ep_impl, slices, cb, slices->length); + } + GPR_ASSERT(ep_impl->write_cb == nullptr); + ep_impl->write_cb = cb; + ep_impl->write_slices = slices; + EP_REF(ep_impl, "write"); + ep_impl->stream_sync->NotifyOnWrite(&ep_impl->write_action); +} + +void CFStreamShutdown(grpc_endpoint* ep, grpc_error* why) { + CFStreamEndpoint* ep_impl = reinterpret_cast(ep); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown (%p)", ep_impl, why); + } + CFReadStreamClose(ep_impl->read_stream); + CFWriteStreamClose(ep_impl->write_stream); + ep_impl->stream_sync->Shutdown(why); + grpc_resource_user_shutdown(ep_impl->resource_user); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream endpoint:%p shutdown DONE (%p)", ep_impl, why); + } +} + +void CFStreamDestroy(grpc_endpoint* ep) { + CFStreamEndpoint* ep_impl = reinterpret_cast(ep); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CFStream endpoint:%p destroy", ep_impl); + } + EP_UNREF(ep_impl, "destroy"); +} + +grpc_resource_user* CFStreamGetResourceUser(grpc_endpoint* ep) { + CFStreamEndpoint* ep_impl = reinterpret_cast(ep); + return ep_impl->resource_user; +} + +char* CFStreamGetPeer(grpc_endpoint* ep) { + CFStreamEndpoint* ep_impl = reinterpret_cast(ep); + return gpr_strdup(ep_impl->peer_string); +} + +int CFStreamGetFD(grpc_endpoint* ep) { return 0; } + +bool CFStreamCanTrackErr(grpc_endpoint* ep) { return false; } + +void CFStreamAddToPollset(grpc_endpoint* ep, grpc_pollset* pollset) {} +void CFStreamAddToPollsetSet(grpc_endpoint* ep, grpc_pollset_set* pollset) {} +void CFStreamDeleteFromPollsetSet(grpc_endpoint* ep, + grpc_pollset_set* pollset) {} + +static const grpc_endpoint_vtable vtable = {CFStreamRead, + CFStreamWrite, + CFStreamAddToPollset, + CFStreamAddToPollsetSet, + CFStreamDeleteFromPollsetSet, + CFStreamShutdown, + CFStreamDestroy, + CFStreamGetResourceUser, + CFStreamGetPeer, + CFStreamGetFD, + CFStreamCanTrackErr}; + +grpc_endpoint* grpc_cfstream_endpoint_create( + CFReadStreamRef read_stream, CFWriteStreamRef write_stream, + const char* peer_string, grpc_resource_quota* resource_quota, + CFStreamHandle* stream_sync) { + CFStreamEndpoint* ep_impl = + static_cast(gpr_malloc(sizeof(CFStreamEndpoint))); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, + "CFStream endpoint:%p create readStream:%p writeStream: %p", + ep_impl, read_stream, write_stream); + } + ep_impl->base.vtable = &vtable; + gpr_ref_init(&ep_impl->refcount, 1); + ep_impl->read_stream = read_stream; + ep_impl->write_stream = write_stream; + CFRetain(read_stream); + CFRetain(write_stream); + ep_impl->stream_sync = stream_sync; + CFSTREAM_HANDLE_REF(ep_impl->stream_sync, "endpoint create"); + + ep_impl->peer_string = gpr_strdup(peer_string); + ep_impl->read_cb = nil; + ep_impl->write_cb = nil; + ep_impl->read_slices = nil; + ep_impl->write_slices = nil; + GRPC_CLOSURE_INIT(&ep_impl->read_action, ReadAction, + static_cast(ep_impl), grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&ep_impl->write_action, WriteAction, + static_cast(ep_impl), grpc_schedule_on_exec_ctx); + ep_impl->resource_user = + grpc_resource_user_create(resource_quota, peer_string); + grpc_resource_user_slice_allocator_init(&ep_impl->slice_allocator, + ep_impl->resource_user, + CFStreamReadAllocationDone, ep_impl); + + return &ep_impl->base; +} + +#endif /* GRPC_CFSTREAM_ENDPOINT */ diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.h b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.h new file mode 100644 index 00000000000000..ef957c1f111ec5 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint_cfstream.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_CFSTREAM_H +#define GRPC_CORE_LIB_IOMGR_ENDPOINT_CFSTREAM_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 + +#ifdef GRPC_CFSTREAM + +#import + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/cfstream_handle.h" +#include "src/core/lib/iomgr/endpoint.h" + +grpc_endpoint* grpc_cfstream_endpoint_create( + CFReadStreamRef read_stream, CFWriteStreamRef write_stream, + const char* peer_string, grpc_resource_quota* resource_quota, + CFStreamHandle* stream_sync); + +#endif /* GRPC_CFSTREAM */ + +#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_CFSTREAM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair.h b/third_party/grpc/src/core/lib/iomgr/endpoint_pair.h new file mode 100644 index 00000000000000..08f9e3cabc1ae1 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H +#define GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H + +#include + +#include "src/core/lib/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, + grpc_channel_args* args); + +#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair_posix.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_posix.cc new file mode 100644 index 00000000000000..5c5c246f998427 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_posix.cc @@ -0,0 +1,73 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/endpoint_pair.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/tcp_posix.h" + +static void create_sockets(int sv[2]) { + int flags; + grpc_create_socketpair_if_unix(sv); + 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); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == GRPC_ERROR_NONE); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == GRPC_ERROR_NONE); +} + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char* name, + grpc_channel_args* args) { + int sv[2]; + grpc_endpoint_pair p; + char* final_name; + create_sockets(sv); + + grpc_core::ExecCtx exec_ctx; + + gpr_asprintf(&final_name, "%s:client", name); + p.client = grpc_tcp_create(grpc_fd_create(sv[1], final_name, false), args, + "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, false), args, + "socketpair-client"); + gpr_free(final_name); + + return p; +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair_uv.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_uv.cc new file mode 100644 index 00000000000000..b99d178cb67c3f --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_uv.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include + +#include + +#include "src/core/lib/iomgr/endpoint_pair.h" + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char* name, + grpc_channel_args* args) { + grpc_endpoint_pair endpoint_pair; + // TODO(mlumish): implement this properly under libuv + GPR_ASSERT(false && + "grpc_iomgr_create_endpoint_pair is not suppoted with libuv"); + return endpoint_pair; +} + +#endif /* GRPC_UV */ diff --git a/third_party/grpc/src/core/lib/iomgr/endpoint_pair_windows.cc b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_windows.cc new file mode 100644 index 00000000000000..177331d681234d --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/endpoint_pair_windows.cc @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET +#include "src/core/lib/iomgr/endpoint_pair.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include +#include +#include + +#include +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_windows.h" + +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, (grpc_sockaddr*)&addr, sizeof(addr)) != + SOCKET_ERROR); + GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR); + GPR_ASSERT(getsockname(lst_sock, (grpc_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, (grpc_sockaddr*)&addr, addr_len, NULL, NULL, + NULL, NULL) == 0); + svr_sock = accept(lst_sock, (grpc_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, grpc_channel_args* channel_args) { + SOCKET sv[2]; + grpc_endpoint_pair p; + create_sockets(sv); + grpc_core::ExecCtx exec_ctx; + p.client = grpc_tcp_create(grpc_winsocket_create(sv[1], "endpoint:client"), + channel_args, "endpoint:server"); + p.server = grpc_tcp_create(grpc_winsocket_create(sv[0], "endpoint:server"), + channel_args, "endpoint:client"); + + return p; +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/error.cc b/third_party/grpc/src/core/lib/iomgr/error.cc new file mode 100644 index 00000000000000..6ae077fd5489b2 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/error.cc @@ -0,0 +1,801 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "src/core/lib/iomgr/error.h" + +#include +#include + +#include +#include +#include +#include + +#ifdef GPR_WINDOWS +#include +#endif + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/error_internal.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" + +grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount(false, + "error_refcount"); +grpc_core::DebugOnlyTraceFlag grpc_trace_closure(false, "closure"); + +static const char* error_int_name(grpc_error_ints key) { + switch (key) { + case GRPC_ERROR_INT_ERRNO: + return "errno"; + case GRPC_ERROR_INT_FILE_LINE: + return "file_line"; + case GRPC_ERROR_INT_STREAM_ID: + return "stream_id"; + case GRPC_ERROR_INT_GRPC_STATUS: + return "grpc_status"; + case GRPC_ERROR_INT_OFFSET: + return "offset"; + case GRPC_ERROR_INT_INDEX: + return "index"; + case GRPC_ERROR_INT_SIZE: + return "size"; + case GRPC_ERROR_INT_HTTP2_ERROR: + return "http2_error"; + case GRPC_ERROR_INT_TSI_CODE: + return "tsi_code"; + case GRPC_ERROR_INT_SECURITY_STATUS: + return "security_status"; + case GRPC_ERROR_INT_FD: + return "fd"; + case GRPC_ERROR_INT_WSA_ERROR: + return "wsa_error"; + case GRPC_ERROR_INT_HTTP_STATUS: + return "http_status"; + case GRPC_ERROR_INT_LIMIT: + return "limit"; + case GRPC_ERROR_INT_OCCURRED_DURING_WRITE: + return "occurred_during_write"; + case GRPC_ERROR_INT_MAX: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +static const char* error_str_name(grpc_error_strs key) { + switch (key) { + case GRPC_ERROR_STR_KEY: + return "key"; + case GRPC_ERROR_STR_VALUE: + return "value"; + case GRPC_ERROR_STR_DESCRIPTION: + return "description"; + case GRPC_ERROR_STR_OS_ERROR: + return "os_error"; + case GRPC_ERROR_STR_TARGET_ADDRESS: + return "target_address"; + case GRPC_ERROR_STR_SYSCALL: + return "syscall"; + case GRPC_ERROR_STR_FILE: + return "file"; + case GRPC_ERROR_STR_GRPC_MESSAGE: + return "grpc_message"; + case GRPC_ERROR_STR_RAW_BYTES: + return "raw_bytes"; + case GRPC_ERROR_STR_TSI_ERROR: + return "tsi_error"; + case GRPC_ERROR_STR_FILENAME: + return "filename"; + case GRPC_ERROR_STR_QUEUED_BUFFERS: + return "queued_buffers"; + case GRPC_ERROR_STR_MAX: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +static const char* error_time_name(grpc_error_times key) { + switch (key) { + case GRPC_ERROR_TIME_CREATED: + return "created"; + case GRPC_ERROR_TIME_MAX: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +#ifndef NDEBUG +grpc_error* grpc_error_do_ref(grpc_error* err, const char* file, int line) { + if (grpc_trace_error_refcount.enabled()) { + gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err, + gpr_atm_no_barrier_load(&err->atomics.refs.count), + gpr_atm_no_barrier_load(&err->atomics.refs.count) + 1, file, line); + } + gpr_ref(&err->atomics.refs); + return err; +} +#else +grpc_error* grpc_error_do_ref(grpc_error* err) { + gpr_ref(&err->atomics.refs); + return err; +} +#endif + +static void unref_errs(grpc_error* err) { + uint8_t slot = err->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error* lerr = + reinterpret_cast(err->arena + slot); + GRPC_ERROR_UNREF(lerr->err); + GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX + : lerr->next != UINT8_MAX); + slot = lerr->next; + } +} + +static void unref_slice(grpc_slice slice) { grpc_slice_unref_internal(slice); } + +static void unref_strs(grpc_error* err) { + for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) { + uint8_t slot = err->strs[which]; + if (slot != UINT8_MAX) { + unref_slice(*reinterpret_cast(err->arena + slot)); + } + } +} + +static void error_destroy(grpc_error* err) { + GPR_ASSERT(!grpc_error_is_special(err)); + unref_errs(err); + unref_strs(err); + gpr_free((void*)gpr_atm_acq_load(&err->atomics.error_string)); + gpr_free(err); +} + +#ifndef NDEBUG +void grpc_error_do_unref(grpc_error* err, const char* file, int line) { + if (grpc_trace_error_refcount.enabled()) { + gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err, + gpr_atm_no_barrier_load(&err->atomics.refs.count), + gpr_atm_no_barrier_load(&err->atomics.refs.count) - 1, file, line); + } + if (gpr_unref(&err->atomics.refs)) { + error_destroy(err); + } +} +#else +void grpc_error_do_unref(grpc_error* err) { + if (gpr_unref(&err->atomics.refs)) { + error_destroy(err); + } +} +#endif + +static uint8_t get_placement(grpc_error** err, size_t size) { + GPR_ASSERT(*err); + uint8_t slots = static_cast(size / sizeof(intptr_t)); + if ((*err)->arena_size + slots > (*err)->arena_capacity) { + (*err)->arena_capacity = static_cast GPR_MIN( + UINT8_MAX - 1, (3 * (*err)->arena_capacity / 2)); + if ((*err)->arena_size + slots > (*err)->arena_capacity) { + return UINT8_MAX; + } +#ifndef NDEBUG + grpc_error* orig = *err; +#endif + *err = static_cast(gpr_realloc( + *err, sizeof(grpc_error) + (*err)->arena_capacity * sizeof(intptr_t))); +#ifndef NDEBUG + if (grpc_trace_error_refcount.enabled()) { + if (*err != orig) { + gpr_log(GPR_DEBUG, "realloc %p -> %p", orig, *err); + } + } +#endif + } + uint8_t placement = (*err)->arena_size; + (*err)->arena_size = static_cast((*err)->arena_size + slots); + return placement; +} + +static void internal_set_int(grpc_error** err, grpc_error_ints which, + intptr_t value) { + uint8_t slot = (*err)->ints[which]; + if (slot == UINT8_MAX) { + slot = get_placement(err, sizeof(value)); + if (slot == UINT8_MAX) { + gpr_log(GPR_ERROR, "Error %p is full, dropping int {\"%s\":%" PRIiPTR "}", + *err, error_int_name(which), value); + return; + } + } + (*err)->ints[which] = slot; + (*err)->arena[slot] = value; +} + +static void internal_set_str(grpc_error** err, grpc_error_strs which, + grpc_slice value) { + uint8_t slot = (*err)->strs[which]; + if (slot == UINT8_MAX) { + slot = get_placement(err, sizeof(value)); + if (slot == UINT8_MAX) { + const char* str = grpc_slice_to_c_string(value); + gpr_log(GPR_ERROR, "Error %p is full, dropping string {\"%s\":\"%s\"}", + *err, error_str_name(which), str); + gpr_free((void*)str); + return; + } + } else { + unref_slice(*reinterpret_cast((*err)->arena + slot)); + } + (*err)->strs[which] = slot; + memcpy((*err)->arena + slot, &value, sizeof(value)); +} + +static char* fmt_time(gpr_timespec tm); +static void internal_set_time(grpc_error** err, grpc_error_times which, + gpr_timespec value) { + uint8_t slot = (*err)->times[which]; + if (slot == UINT8_MAX) { + slot = get_placement(err, sizeof(value)); + if (slot == UINT8_MAX) { + const char* time_str = fmt_time(value); + gpr_log(GPR_ERROR, "Error %p is full, dropping \"%s\":\"%s\"}", *err, + error_time_name(which), time_str); + gpr_free((void*)time_str); + return; + } + } + (*err)->times[which] = slot; + memcpy((*err)->arena + slot, &value, sizeof(value)); +} + +static void internal_add_error(grpc_error** err, grpc_error* new_err) { + grpc_linked_error new_last = {new_err, UINT8_MAX}; + uint8_t slot = get_placement(err, sizeof(grpc_linked_error)); + if (slot == UINT8_MAX) { + gpr_log(GPR_ERROR, "Error %p is full, dropping error %p = %s", *err, + new_err, grpc_error_string(new_err)); + GRPC_ERROR_UNREF(new_err); + return; + } + if ((*err)->first_err == UINT8_MAX) { + GPR_ASSERT((*err)->last_err == UINT8_MAX); + (*err)->last_err = slot; + (*err)->first_err = slot; + } else { + GPR_ASSERT((*err)->last_err != UINT8_MAX); + grpc_linked_error* old_last = + reinterpret_cast((*err)->arena + (*err)->last_err); + old_last->next = slot; + (*err)->last_err = slot; + } + memcpy((*err)->arena + slot, &new_last, sizeof(grpc_linked_error)); +} + +#define SLOTS_PER_INT (sizeof(intptr_t) / sizeof(intptr_t)) +#define SLOTS_PER_STR (sizeof(grpc_slice) / sizeof(intptr_t)) +#define SLOTS_PER_TIME (sizeof(gpr_timespec) / sizeof(intptr_t)) +#define SLOTS_PER_LINKED_ERROR (sizeof(grpc_linked_error) / sizeof(intptr_t)) + +// size of storing one int and two slices and a timespec. For line, desc, file, +// and time created +#define DEFAULT_ERROR_CAPACITY \ + (SLOTS_PER_INT + (SLOTS_PER_STR * 2) + SLOTS_PER_TIME) + +// It is very common to include and extra int and string in an error +#define SURPLUS_CAPACITY (2 * SLOTS_PER_INT + SLOTS_PER_TIME) + +static bool g_error_creation_allowed = true; + +void grpc_disable_error_creation() { g_error_creation_allowed = false; } + +void grpc_enable_error_creation() { g_error_creation_allowed = true; } + +grpc_error* grpc_error_create(const char* file, int line, grpc_slice desc, + grpc_error** referencing, + size_t num_referencing) { + GPR_TIMER_SCOPE("grpc_error_create", 0); + uint8_t initial_arena_capacity = static_cast( + DEFAULT_ERROR_CAPACITY + + static_cast(num_referencing * SLOTS_PER_LINKED_ERROR) + + SURPLUS_CAPACITY); + grpc_error* err = static_cast( + gpr_malloc(sizeof(*err) + initial_arena_capacity * sizeof(intptr_t))); + if (err == nullptr) { // TODO(ctiller): make gpr_malloc return NULL + return GRPC_ERROR_OOM; + } +#ifndef NDEBUG + if (!g_error_creation_allowed) { + gpr_log(GPR_ERROR, + "Error creation occurred when error creation was disabled [%s:%d]", + file, line); + abort(); + } + if (grpc_trace_error_refcount.enabled()) { + gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line); + } +#endif + + err->arena_size = 0; + err->arena_capacity = initial_arena_capacity; + err->first_err = UINT8_MAX; + err->last_err = UINT8_MAX; + + memset(err->ints, UINT8_MAX, GRPC_ERROR_INT_MAX); + memset(err->strs, UINT8_MAX, GRPC_ERROR_STR_MAX); + memset(err->times, UINT8_MAX, GRPC_ERROR_TIME_MAX); + + internal_set_int(&err, GRPC_ERROR_INT_FILE_LINE, line); + internal_set_str(&err, GRPC_ERROR_STR_FILE, + grpc_slice_from_static_string(file)); + internal_set_str(&err, GRPC_ERROR_STR_DESCRIPTION, desc); + + for (size_t i = 0; i < num_referencing; ++i) { + if (referencing[i] == GRPC_ERROR_NONE) continue; + internal_add_error( + &err, + GRPC_ERROR_REF( + referencing[i])); // TODO(ncteisen), change ownership semantics + } + + internal_set_time(&err, GRPC_ERROR_TIME_CREATED, gpr_now(GPR_CLOCK_REALTIME)); + + gpr_atm_no_barrier_store(&err->atomics.error_string, 0); + gpr_ref_init(&err->atomics.refs, 1); + return err; +} + +static void ref_strs(grpc_error* err) { + for (size_t i = 0; i < GRPC_ERROR_STR_MAX; ++i) { + uint8_t slot = err->strs[i]; + if (slot != UINT8_MAX) { + grpc_slice_ref_internal( + *reinterpret_cast(err->arena + slot)); + } + } +} + +static void ref_errs(grpc_error* err) { + uint8_t slot = err->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error* lerr = + reinterpret_cast(err->arena + slot); + GRPC_ERROR_REF(lerr->err); + slot = lerr->next; + } +} + +static grpc_error* copy_error_and_unref(grpc_error* in) { + GPR_TIMER_SCOPE("copy_error_and_unref", 0); + grpc_error* out; + if (grpc_error_is_special(in)) { + out = GRPC_ERROR_CREATE_FROM_STATIC_STRING("unknown"); + if (in == GRPC_ERROR_NONE) { + internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_static_string("no error")); + internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK); + } else if (in == GRPC_ERROR_OOM) { + internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_static_string("oom")); + } else if (in == GRPC_ERROR_CANCELLED) { + internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_static_string("cancelled")); + internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED); + } + } else if (gpr_ref_is_unique(&in->atomics.refs)) { + out = in; + } else { + uint8_t new_arena_capacity = in->arena_capacity; + // the returned err will be added to, so we ensure this is room to avoid + // unneeded allocations. + if (in->arena_capacity - in->arena_size < + static_cast SLOTS_PER_STR) { + new_arena_capacity = static_cast(3 * new_arena_capacity / 2); + } + out = static_cast( + gpr_malloc(sizeof(*in) + new_arena_capacity * sizeof(intptr_t))); +#ifndef NDEBUG + if (grpc_trace_error_refcount.enabled()) { + gpr_log(GPR_DEBUG, "%p create copying %p", out, in); + } +#endif + // bulk memcpy of the rest of the struct. + size_t skip = sizeof(&out->atomics); + memcpy((void*)((uintptr_t)out + skip), (void*)((uintptr_t)in + skip), + sizeof(*in) + (in->arena_size * sizeof(intptr_t)) - skip); + // manually set the atomics and the new capacity + gpr_atm_no_barrier_store(&out->atomics.error_string, 0); + gpr_ref_init(&out->atomics.refs, 1); + out->arena_capacity = new_arena_capacity; + ref_strs(out); + ref_errs(out); + GRPC_ERROR_UNREF(in); + } + return out; +} + +grpc_error* grpc_error_set_int(grpc_error* src, grpc_error_ints which, + intptr_t value) { + GPR_TIMER_SCOPE("grpc_error_set_int", 0); + grpc_error* new_err = copy_error_and_unref(src); + internal_set_int(&new_err, which, value); + return new_err; +} + +typedef struct { + grpc_status_code code; + const char* msg; +} special_error_status_map; +static const special_error_status_map error_status_map[] = { + {GRPC_STATUS_OK, ""}, // GRPC_ERROR_NONE + {GRPC_STATUS_INVALID_ARGUMENT, ""}, // GRPC_ERROR_RESERVED_1 + {GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"}, // GRPC_ERROR_OOM + {GRPC_STATUS_INVALID_ARGUMENT, ""}, // GRPC_ERROR_RESERVED_2 + {GRPC_STATUS_CANCELLED, "Cancelled"}, // GRPC_ERROR_CANCELLED +}; + +bool grpc_error_get_int(grpc_error* err, grpc_error_ints which, intptr_t* p) { + GPR_TIMER_SCOPE("grpc_error_get_int", 0); + if (grpc_error_is_special(err)) { + if (which != GRPC_ERROR_INT_GRPC_STATUS) return false; + *p = error_status_map[reinterpret_cast(err)].code; + return true; + } + uint8_t slot = err->ints[which]; + if (slot != UINT8_MAX) { + if (p != nullptr) *p = err->arena[slot]; + return true; + } + return false; +} + +grpc_error* grpc_error_set_str(grpc_error* src, grpc_error_strs which, + grpc_slice str) { + GPR_TIMER_SCOPE("grpc_error_set_str", 0); + grpc_error* new_err = copy_error_and_unref(src); + internal_set_str(&new_err, which, str); + return new_err; +} + +bool grpc_error_get_str(grpc_error* err, grpc_error_strs which, + grpc_slice* str) { + if (grpc_error_is_special(err)) { + if (which != GRPC_ERROR_STR_GRPC_MESSAGE) return false; + *str = grpc_slice_from_static_string( + error_status_map[reinterpret_cast(err)].msg); + return true; + } + uint8_t slot = err->strs[which]; + if (slot != UINT8_MAX) { + *str = *reinterpret_cast(err->arena + slot); + return true; + } else { + return false; + } +} + +grpc_error* grpc_error_add_child(grpc_error* src, grpc_error* child) { + GPR_TIMER_SCOPE("grpc_error_add_child", 0); + if (src != GRPC_ERROR_NONE) { + if (child == GRPC_ERROR_NONE) { + /* \a child is empty. Simply return the ref to \a src */ + return src; + } else if (child != src) { + grpc_error* new_err = copy_error_and_unref(src); + internal_add_error(&new_err, child); + return new_err; + } else { + /* \a src and \a child are the same. Drop one of the references and return + * the other */ + GRPC_ERROR_UNREF(child); + return src; + } + } else { + /* \a src is empty. Simply return the ref to \a child */ + return child; + } +} + +static const char* no_error_string = "\"No Error\""; +static const char* oom_error_string = "\"Out of memory\""; +static const char* cancelled_error_string = "\"Cancelled\""; + +typedef struct { + char* key; + char* value; +} kv_pair; + +typedef struct { + kv_pair* kvs; + size_t num_kvs; + size_t cap_kvs; +} kv_pairs; + +static void append_chr(char c, char** s, size_t* sz, size_t* cap) { + if (*sz == *cap) { + *cap = GPR_MAX(8, 3 * *cap / 2); + *s = static_cast(gpr_realloc(*s, *cap)); + } + (*s)[(*sz)++] = c; +} + +static void append_str(const char* str, char** s, size_t* sz, size_t* cap) { + for (const char* c = str; *c; c++) { + append_chr(*c, s, sz, cap); + } +} + +static void append_esc_str(const uint8_t* str, size_t len, char** s, size_t* sz, + size_t* cap) { + static const char* hex = "0123456789abcdef"; + append_chr('"', s, sz, cap); + for (size_t i = 0; i < len; i++, str++) { + if (*str < 32 || *str >= 127) { + append_chr('\\', s, sz, cap); + switch (*str) { + case '\b': + append_chr('b', s, sz, cap); + break; + case '\f': + append_chr('f', s, sz, cap); + break; + case '\n': + append_chr('n', s, sz, cap); + break; + case '\r': + append_chr('r', s, sz, cap); + break; + case '\t': + append_chr('t', s, sz, cap); + break; + default: + append_chr('u', s, sz, cap); + append_chr('0', s, sz, cap); + append_chr('0', s, sz, cap); + append_chr(hex[*str >> 4], s, sz, cap); + append_chr(hex[*str & 0x0f], s, sz, cap); + break; + } + } else { + append_chr(static_cast(*str), s, sz, cap); + } + } + append_chr('"', s, sz, cap); +} + +static void append_kv(kv_pairs* kvs, char* key, char* value) { + if (kvs->num_kvs == kvs->cap_kvs) { + kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4); + kvs->kvs = static_cast( + gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs)); + } + kvs->kvs[kvs->num_kvs].key = key; + kvs->kvs[kvs->num_kvs].value = value; + kvs->num_kvs++; +} + +static char* key_int(grpc_error_ints which) { + return gpr_strdup(error_int_name(which)); +} + +static char* fmt_int(intptr_t p) { + char* s; + gpr_asprintf(&s, "%" PRIdPTR, p); + return s; +} + +static void collect_ints_kvs(grpc_error* err, kv_pairs* kvs) { + for (size_t which = 0; which < GRPC_ERROR_INT_MAX; ++which) { + uint8_t slot = err->ints[which]; + if (slot != UINT8_MAX) { + append_kv(kvs, key_int(static_cast(which)), + fmt_int(err->arena[slot])); + } + } +} + +static char* key_str(grpc_error_strs which) { + return gpr_strdup(error_str_name(which)); +} + +static char* fmt_str(grpc_slice slice) { + char* s = nullptr; + size_t sz = 0; + size_t cap = 0; + append_esc_str((const uint8_t*)GRPC_SLICE_START_PTR(slice), + GRPC_SLICE_LENGTH(slice), &s, &sz, &cap); + append_chr(0, &s, &sz, &cap); + return s; +} + +static void collect_strs_kvs(grpc_error* err, kv_pairs* kvs) { + for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) { + uint8_t slot = err->strs[which]; + if (slot != UINT8_MAX) { + append_kv(kvs, key_str(static_cast(which)), + fmt_str(*reinterpret_cast(err->arena + slot))); + } + } +} + +static char* key_time(grpc_error_times which) { + return gpr_strdup(error_time_name(which)); +} + +static char* fmt_time(gpr_timespec tm) { + char* out; + const char* pfx = "!!"; + switch (tm.clock_type) { + case GPR_CLOCK_MONOTONIC: + pfx = "@monotonic:"; + break; + case GPR_CLOCK_REALTIME: + pfx = "@"; + break; + case GPR_CLOCK_PRECISE: + pfx = "@precise:"; + break; + case GPR_TIMESPAN: + pfx = ""; + break; + } + gpr_asprintf(&out, "\"%s%" PRId64 ".%09d\"", pfx, tm.tv_sec, tm.tv_nsec); + return out; +} + +static void collect_times_kvs(grpc_error* err, kv_pairs* kvs) { + for (size_t which = 0; which < GRPC_ERROR_TIME_MAX; ++which) { + uint8_t slot = err->times[which]; + if (slot != UINT8_MAX) { + append_kv(kvs, key_time(static_cast(which)), + fmt_time(*reinterpret_cast(err->arena + slot))); + } + } +} + +static void add_errs(grpc_error* err, char** s, size_t* sz, size_t* cap) { + uint8_t slot = err->first_err; + bool first = true; + while (slot != UINT8_MAX) { + grpc_linked_error* lerr = + reinterpret_cast(err->arena + slot); + if (!first) append_chr(',', s, sz, cap); + first = false; + const char* e = grpc_error_string(lerr->err); + append_str(e, s, sz, cap); + GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX + : lerr->next != UINT8_MAX); + slot = lerr->next; + } +} + +static char* errs_string(grpc_error* err) { + char* s = nullptr; + size_t sz = 0; + size_t cap = 0; + append_chr('[', &s, &sz, &cap); + add_errs(err, &s, &sz, &cap); + append_chr(']', &s, &sz, &cap); + append_chr(0, &s, &sz, &cap); + return s; +} + +static int cmp_kvs(const void* a, const void* b) { + const kv_pair* ka = static_cast(a); + const kv_pair* kb = static_cast(b); + return strcmp(ka->key, kb->key); +} + +static char* finish_kvs(kv_pairs* kvs) { + char* s = nullptr; + size_t sz = 0; + size_t cap = 0; + + append_chr('{', &s, &sz, &cap); + for (size_t i = 0; i < kvs->num_kvs; i++) { + if (i != 0) append_chr(',', &s, &sz, &cap); + append_esc_str(reinterpret_cast(kvs->kvs[i].key), + strlen(kvs->kvs[i].key), &s, &sz, &cap); + gpr_free(kvs->kvs[i].key); + append_chr(':', &s, &sz, &cap); + append_str(kvs->kvs[i].value, &s, &sz, &cap); + gpr_free(kvs->kvs[i].value); + } + append_chr('}', &s, &sz, &cap); + append_chr(0, &s, &sz, &cap); + + gpr_free(kvs->kvs); + return s; +} + +const char* grpc_error_string(grpc_error* err) { + GPR_TIMER_SCOPE("grpc_error_string", 0); + if (err == GRPC_ERROR_NONE) return no_error_string; + if (err == GRPC_ERROR_OOM) return oom_error_string; + if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string; + + void* p = (void*)gpr_atm_acq_load(&err->atomics.error_string); + if (p != nullptr) { + return static_cast(p); + } + + kv_pairs kvs; + memset(&kvs, 0, sizeof(kvs)); + + collect_ints_kvs(err, &kvs); + collect_strs_kvs(err, &kvs); + collect_times_kvs(err, &kvs); + if (err->first_err != UINT8_MAX) { + append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err)); + } + + qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs); + + char* out = finish_kvs(&kvs); + + if (!gpr_atm_rel_cas(&err->atomics.error_string, 0, (gpr_atm)out)) { + gpr_free(out); + out = (char*)gpr_atm_acq_load(&err->atomics.error_string); + } + + return out; +} + +grpc_error* grpc_os_error(const char* file, int line, int err, + const char* call_name) { + return grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_int( + grpc_error_create(file, line, + grpc_slice_from_static_string("OS Error"), + nullptr, 0), + GRPC_ERROR_INT_ERRNO, err), + GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(strerror(err))), + GRPC_ERROR_STR_SYSCALL, grpc_slice_from_copied_string(call_name)); +} + +#ifdef GPR_WINDOWS +grpc_error* grpc_wsa_error(const char* file, int line, int err, + const char* call_name) { + char* utf8_message = gpr_format_message(err); + grpc_error* error = grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_int( + grpc_error_create(file, line, + grpc_slice_from_static_string("OS Error"), NULL, + 0), + GRPC_ERROR_INT_WSA_ERROR, err), + GRPC_ERROR_STR_OS_ERROR, grpc_slice_from_copied_string(utf8_message)), + GRPC_ERROR_STR_SYSCALL, grpc_slice_from_static_string(call_name)); + gpr_free(utf8_message); + return error; +} +#endif + +bool grpc_log_if_error(const char* what, grpc_error* error, const char* file, + int line) { + if (error == GRPC_ERROR_NONE) return true; + const char* msg = grpc_error_string(error); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "%s: %s", what, msg); + GRPC_ERROR_UNREF(error); + return false; +} diff --git a/third_party/grpc/src/core/lib/iomgr/error.h b/third_party/grpc/src/core/lib/iomgr/error.h new file mode 100644 index 00000000000000..cb740d5b01cf08 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/error.h @@ -0,0 +1,245 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_ERROR_H +#define GRPC_CORE_LIB_IOMGR_ERROR_H + +#include + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/debug/trace.h" + +/// Opaque representation of an error. +/// See https://github.com/grpc/grpc/blob/master/doc/core/grpc-error.md for a +/// full write up of this object. + +typedef struct grpc_error grpc_error; + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_error_refcount; + +typedef enum { + /// 'errno' from the operating system + GRPC_ERROR_INT_ERRNO, + /// __LINE__ from the call site creating the error + GRPC_ERROR_INT_FILE_LINE, + /// stream identifier: for errors that are associated with an individual + /// wire stream + GRPC_ERROR_INT_STREAM_ID, + /// grpc status code representing this error + GRPC_ERROR_INT_GRPC_STATUS, + /// offset into some binary blob (usually represented by + /// GRPC_ERROR_STR_RAW_BYTES) where the error occurred + GRPC_ERROR_INT_OFFSET, + /// context sensitive index associated with the error + GRPC_ERROR_INT_INDEX, + /// context sensitive size associated with the error + GRPC_ERROR_INT_SIZE, + /// http2 error code associated with the error (see the HTTP2 RFC) + GRPC_ERROR_INT_HTTP2_ERROR, + /// TSI status code associated with the error + GRPC_ERROR_INT_TSI_CODE, + /// grpc_security_status associated with the error + GRPC_ERROR_INT_SECURITY_STATUS, + /// WSAGetLastError() reported when this error occurred + GRPC_ERROR_INT_WSA_ERROR, + /// File descriptor associated with this error + GRPC_ERROR_INT_FD, + /// HTTP status (i.e. 404) + GRPC_ERROR_INT_HTTP_STATUS, + /// context sensitive limit associated with the error + GRPC_ERROR_INT_LIMIT, + /// chttp2: did the error occur while a write was in progress + GRPC_ERROR_INT_OCCURRED_DURING_WRITE, + + /// Must always be last + GRPC_ERROR_INT_MAX, +} grpc_error_ints; + +typedef enum { + /// top-level textual description of this error + GRPC_ERROR_STR_DESCRIPTION, + /// source file in which this error occurred + GRPC_ERROR_STR_FILE, + /// operating system description of this error + GRPC_ERROR_STR_OS_ERROR, + /// syscall that generated this error + GRPC_ERROR_STR_SYSCALL, + /// peer that we were trying to communicate when this error occurred + GRPC_ERROR_STR_TARGET_ADDRESS, + /// grpc status message associated with this error + GRPC_ERROR_STR_GRPC_MESSAGE, + /// hex dump (or similar) with the data that generated this error + GRPC_ERROR_STR_RAW_BYTES, + /// tsi error string associated with this error + GRPC_ERROR_STR_TSI_ERROR, + /// filename that we were trying to read/write when this error occurred + GRPC_ERROR_STR_FILENAME, + /// which data was queued for writing when the error occurred + GRPC_ERROR_STR_QUEUED_BUFFERS, + /// key associated with the error + GRPC_ERROR_STR_KEY, + /// value associated with the error + GRPC_ERROR_STR_VALUE, + + /// Must always be last + GRPC_ERROR_STR_MAX, +} grpc_error_strs; + +typedef enum { + /// timestamp of error creation + GRPC_ERROR_TIME_CREATED, + + /// Must always be last + GRPC_ERROR_TIME_MAX, +} grpc_error_times; + +/// The following "special" errors can be propagated without allocating memory. +/// They are always even so that other code (particularly combiner locks, +/// polling engines) can safely use the lower bit for themselves. + +#define GRPC_ERROR_NONE ((grpc_error*)NULL) +#define GRPC_ERROR_RESERVED_1 ((grpc_error*)1) +#define GRPC_ERROR_OOM ((grpc_error*)2) +#define GRPC_ERROR_RESERVED_2 ((grpc_error*)3) +#define GRPC_ERROR_CANCELLED ((grpc_error*)4) +#define GRPC_ERROR_SPECIAL_MAX GRPC_ERROR_CANCELLED + +inline bool grpc_error_is_special(struct grpc_error* err) { + return err <= GRPC_ERROR_SPECIAL_MAX; +} + +// debug only toggles that allow for a sanity to check that ensures we will +// never create any errors in the per-RPC hotpath. +void grpc_disable_error_creation(); +void grpc_enable_error_creation(); + +const char* grpc_error_string(grpc_error* error); + +/// Create an error - but use GRPC_ERROR_CREATE instead +grpc_error* grpc_error_create(const char* file, int line, grpc_slice desc, + grpc_error** referencing, size_t num_referencing); +/// Create an error (this is the preferred way of generating an error that is +/// not due to a system call - for system calls, use GRPC_OS_ERROR or +/// GRPC_WSA_ERROR as appropriate) +/// \a referencing is an array of num_referencing elements indicating one or +/// more errors that are believed to have contributed to this one +/// err = grpc_error_create(x, y, z, r, nr) is equivalent to: +/// err = grpc_error_create(x, y, z, NULL, 0); +/// for (i=0; i + +#ifdef GRPC_CFSTREAM +#include + +#include +#include + +#include "src/core/lib/iomgr/error.h" + +#define MAX_ERROR_DESCRIPTION 256 + +grpc_error* grpc_error_create_from_cferror(const char* file, int line, + void* arg, const char* custom_desc) { + CFErrorRef error = static_cast(arg); + char buf_domain[MAX_ERROR_DESCRIPTION]; + char buf_desc[MAX_ERROR_DESCRIPTION]; + char* error_msg; + CFErrorDomain domain = CFErrorGetDomain((error)); + CFIndex code = CFErrorGetCode((error)); + CFStringRef desc = CFErrorCopyDescription((error)); + CFStringGetCString(domain, buf_domain, MAX_ERROR_DESCRIPTION, + kCFStringEncodingUTF8); + CFStringGetCString(desc, buf_desc, MAX_ERROR_DESCRIPTION, + kCFStringEncodingUTF8); + gpr_asprintf(&error_msg, "%s (error domain:%s, code:%ld, description:%s)", + custom_desc, buf_domain, code, buf_desc); + CFRelease(desc); + grpc_error* return_error = grpc_error_create( + file, line, grpc_slice_from_copied_string(error_msg), NULL, 0); + gpr_free(error_msg); + return return_error; +} +#endif /* GRPC_CFSTREAM */ diff --git a/third_party/grpc/src/core/lib/iomgr/error_cfstream.h b/third_party/grpc/src/core/lib/iomgr/error_cfstream.h new file mode 100644 index 00000000000000..06ab7513296ff5 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/error_cfstream.h @@ -0,0 +1,31 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_ERROR_CFSTREAM_H +#define GRPC_CORE_LIB_IOMGR_ERROR_CFSTREAM_H + +#ifdef GRPC_CFSTREAM +// Create an error from Apple Core Foundation CFError object +#define GRPC_ERROR_CREATE_FROM_CFERROR(error, desc) \ + grpc_error_create_from_cferror(__FILE__, __LINE__, \ + static_cast((error)), (desc)) +grpc_error* grpc_error_create_from_cferror(const char* file, int line, + void* arg, const char* desc); +#endif /* GRPC_CFSTREAM */ + +#endif /* GRPC_CORE_LIB_IOMGR_ERROR_CFSTREAM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/error_internal.h b/third_party/grpc/src/core/lib/iomgr/error_internal.h new file mode 100644 index 00000000000000..802739601988f4 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/error_internal.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H +#define GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H + +#include + +#include +#include // TODO, do we need this? + +#include +#include "src/core/lib/iomgr/error.h" + +typedef struct grpc_linked_error grpc_linked_error; + +struct grpc_linked_error { + grpc_error* err; + uint8_t next; +}; + +// c core representation of an error. See error.h for high level description of +// this object. +struct grpc_error { + // All atomics in grpc_error must be stored in this nested struct. The rest of + // the object is memcpy-ed in bulk in copy_and_unref. + struct atomics { + gpr_refcount refs; + gpr_atm error_string; + } atomics; + // These arrays index into dynamic arena at the bottom of the struct. + // UINT8_MAX is used as a sentinel value. + uint8_t ints[GRPC_ERROR_INT_MAX]; + uint8_t strs[GRPC_ERROR_STR_MAX]; + uint8_t times[GRPC_ERROR_TIME_MAX]; + // The child errors are stored in the arena, but are effectively a linked list + // structure, since they are contained withing grpc_linked_error objects. + uint8_t first_err; + uint8_t last_err; + // The arena is dynamically reallocated with a grow factor of 1.5. + uint8_t arena_size; + uint8_t arena_capacity; + intptr_t arena[0]; +}; + +#endif /* GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.cc b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.cc new file mode 100644 index 00000000000000..4b8c891e9b01df --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.cc @@ -0,0 +1,1347 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +/* This polling engine is only relevant on linux kernels supporting epoll + epoll_create() or epoll_create1() */ +#ifdef GRPC_LINUX_EPOLL +#include "src/core/lib/iomgr/ev_epoll1_linux.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/tls.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/block_annotate.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/lockfree_event.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" + +static grpc_wakeup_fd global_wakeup_fd; + +/******************************************************************************* + * Singleton epoll set related fields + */ + +#define MAX_EPOLL_EVENTS 100 +#define MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION 1 + +/* NOTE ON SYNCHRONIZATION: + * - Fields in this struct are only modified by the designated poller. Hence + * there is no need for any locks to protect the struct. + * - num_events and cursor fields have to be of atomic type to provide memory + * visibility guarantees only. i.e In case of multiple pollers, the designated + * polling thread keeps changing; the thread that wrote these values may be + * different from the thread reading the values + */ +typedef struct epoll_set { + int epfd; + + /* The epoll_events after the last call to epoll_wait() */ + struct epoll_event events[MAX_EPOLL_EVENTS]; + + /* The number of epoll_events after the last call to epoll_wait() */ + gpr_atm num_events; + + /* Index of the first event in epoll_events that has to be processed. This + * field is only valid if num_events > 0 */ + gpr_atm cursor; +} epoll_set; + +/* The global singleton epoll set */ +static epoll_set g_epoll_set; + +static int epoll_create_and_cloexec() { +#ifdef GRPC_LINUX_EPOLL_CREATE1 + int fd = epoll_create1(EPOLL_CLOEXEC); + if (fd < 0) { + gpr_log(GPR_ERROR, "epoll_create1 unavailable"); + } +#else + int fd = epoll_create(MAX_EPOLL_EVENTS); + if (fd < 0) { + gpr_log(GPR_ERROR, "epoll_create unavailable"); + } else if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { + gpr_log(GPR_ERROR, "fcntl following epoll_create failed"); + return -1; + } +#endif + return fd; +} + +/* Must be called *only* once */ +static bool epoll_set_init() { + g_epoll_set.epfd = epoll_create_and_cloexec(); + if (g_epoll_set.epfd < 0) { + return false; + } + + gpr_log(GPR_INFO, "grpc epoll fd: %d", g_epoll_set.epfd); + gpr_atm_no_barrier_store(&g_epoll_set.num_events, 0); + gpr_atm_no_barrier_store(&g_epoll_set.cursor, 0); + return true; +} + +/* epoll_set_init() MUST be called before calling this. */ +static void epoll_set_shutdown() { + if (g_epoll_set.epfd >= 0) { + close(g_epoll_set.epfd); + g_epoll_set.epfd = -1; + } +} + +/******************************************************************************* + * Fd Declarations + */ + +/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */ +struct grpc_fork_fd_list { + grpc_fd* fd; + grpc_fd* next; + grpc_fd* prev; +}; + +struct grpc_fd { + int fd; + + grpc_core::ManualConstructor read_closure; + grpc_core::ManualConstructor write_closure; + grpc_core::ManualConstructor error_closure; + + struct grpc_fd* freelist_next; + + grpc_iomgr_object iomgr_object; + + /* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */ + grpc_fork_fd_list* fork_fd_list; +}; + +static void fd_global_init(void); +static void fd_global_shutdown(void); + +/******************************************************************************* + * Pollset Declarations + */ + +typedef enum { UNKICKED, KICKED, DESIGNATED_POLLER } kick_state; + +static const char* kick_state_string(kick_state st) { + switch (st) { + case UNKICKED: + return "UNKICKED"; + case KICKED: + return "KICKED"; + case DESIGNATED_POLLER: + return "DESIGNATED_POLLER"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +struct grpc_pollset_worker { + kick_state state; + int kick_state_mutator; // which line of code last changed kick state + bool initialized_cv; + grpc_pollset_worker* next; + grpc_pollset_worker* prev; + gpr_cv cv; + grpc_closure_list schedule_on_end_work; +}; + +#define SET_KICK_STATE(worker, kick_state) \ + do { \ + (worker)->state = (kick_state); \ + (worker)->kick_state_mutator = __LINE__; \ + } while (false) + +#define MAX_NEIGHBORHOODS 1024 + +typedef struct pollset_neighborhood { + union { + char pad[GPR_CACHELINE_SIZE]; + struct { + gpr_mu mu; + grpc_pollset* active_root; + }; + }; +} pollset_neighborhood; + +struct grpc_pollset { + gpr_mu mu; + pollset_neighborhood* neighborhood; + bool reassigning_neighborhood; + grpc_pollset_worker* root_worker; + bool kicked_without_poller; + + /* Set to true if the pollset is observed to have no workers available to + poll */ + bool seen_inactive; + bool shutting_down; /* Is the pollset shutting down ? */ + grpc_closure* shutdown_closure; /* Called after after shutdown is complete */ + + /* Number of workers who are *about-to* attach themselves to the pollset + * worker list */ + int begin_refs; + + grpc_pollset* next; + grpc_pollset* prev; +}; + +/******************************************************************************* + * Pollset-set Declarations + */ + +struct grpc_pollset_set { + char unused; +}; + +/******************************************************************************* + * Common helpers + */ + +static bool append_error(grpc_error** composite, grpc_error* error, + const char* desc) { + if (error == GRPC_ERROR_NONE) return true; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); + } + *composite = grpc_error_add_child(*composite, error); + return false; +} + +/******************************************************************************* + * Fd Definitions + */ + +/* 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. + */ + +/* The alarm system needs to be able to wakeup 'some poller' sometimes + * (specifically when a new alarm needs to be triggered earlier than the next + * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a + * case occurs. */ + +static grpc_fd* fd_freelist = nullptr; +static gpr_mu fd_freelist_mu; + +/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */ +static grpc_fd* fork_fd_list_head = nullptr; +static gpr_mu fork_fd_list_mu; + +static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } + +static void fd_global_shutdown(void) { + // TODO(guantaol): We don't have a reasonable explanation about this + // lock()/unlock() pattern. It can be a valid barrier if there is at most one + // pending lock() at this point. Otherwise, there is still a possibility of + // use-after-free race. Need to reason about the code and/or clean it up. + gpr_mu_lock(&fd_freelist_mu); + gpr_mu_unlock(&fd_freelist_mu); + while (fd_freelist != nullptr) { + grpc_fd* fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + gpr_free(fd); + } + gpr_mu_destroy(&fd_freelist_mu); +} + +static void fork_fd_list_add_grpc_fd(grpc_fd* fd) { + if (grpc_core::Fork::Enabled()) { + gpr_mu_lock(&fork_fd_list_mu); + fd->fork_fd_list = + static_cast(gpr_malloc(sizeof(grpc_fork_fd_list))); + fd->fork_fd_list->next = fork_fd_list_head; + fd->fork_fd_list->prev = nullptr; + if (fork_fd_list_head != nullptr) { + fork_fd_list_head->fork_fd_list->prev = fd; + } + fork_fd_list_head = fd; + gpr_mu_unlock(&fork_fd_list_mu); + } +} + +static void fork_fd_list_remove_grpc_fd(grpc_fd* fd) { + if (grpc_core::Fork::Enabled()) { + gpr_mu_lock(&fork_fd_list_mu); + if (fork_fd_list_head == fd) { + fork_fd_list_head = fd->fork_fd_list->next; + } + if (fd->fork_fd_list->prev != nullptr) { + fd->fork_fd_list->prev->fork_fd_list->next = fd->fork_fd_list->next; + } + if (fd->fork_fd_list->next != nullptr) { + fd->fork_fd_list->next->fork_fd_list->prev = fd->fork_fd_list->prev; + } + gpr_free(fd->fork_fd_list); + gpr_mu_unlock(&fork_fd_list_mu); + } +} + +static grpc_fd* fd_create(int fd, const char* name, bool track_err) { + grpc_fd* new_fd = nullptr; + + gpr_mu_lock(&fd_freelist_mu); + if (fd_freelist != nullptr) { + new_fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + } + gpr_mu_unlock(&fd_freelist_mu); + + if (new_fd == nullptr) { + new_fd = static_cast(gpr_malloc(sizeof(grpc_fd))); + new_fd->read_closure.Init(); + new_fd->write_closure.Init(); + new_fd->error_closure.Init(); + } + new_fd->fd = fd; + new_fd->read_closure->InitEvent(); + new_fd->write_closure->InitEvent(); + new_fd->error_closure->InitEvent(); + + new_fd->freelist_next = nullptr; + + char* fd_name; + gpr_asprintf(&fd_name, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); + fork_fd_list_add_grpc_fd(new_fd); +#ifndef NDEBUG + if (grpc_trace_fd_refcount.enabled()) { + gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name); + } +#endif + gpr_free(fd_name); + + struct epoll_event ev; + ev.events = static_cast(EPOLLIN | EPOLLOUT | EPOLLET); + /* Use the least significant bit of ev.data.ptr to store track_err. We expect + * the addresses to be word aligned. We need to store track_err to avoid + * synchronization issues when accessing it after receiving an event. + * Accessing fd would be a data race there because the fd might have been + * returned to the free list at that point. */ + ev.data.ptr = reinterpret_cast(reinterpret_cast(new_fd) | + (track_err ? 1 : 0)); + if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, fd, &ev) != 0) { + gpr_log(GPR_ERROR, "epoll_ctl failed: %s", strerror(errno)); + } + + return new_fd; +} + +static int fd_wrapped_fd(grpc_fd* fd) { return fd->fd; } + +/* if 'releasing_fd' is true, it means that we are going to detach the internal + * fd from grpc_fd structure (i.e which means we should not be calling + * shutdown() syscall on that fd) */ +static void fd_shutdown_internal(grpc_fd* fd, grpc_error* why, + bool releasing_fd) { + if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) { + if (!releasing_fd) { + shutdown(fd->fd, SHUT_RDWR); + } + fd->write_closure->SetShutdown(GRPC_ERROR_REF(why)); + fd->error_closure->SetShutdown(GRPC_ERROR_REF(why)); + } + GRPC_ERROR_UNREF(why); +} + +/* Might be called multiple times */ +static void fd_shutdown(grpc_fd* fd, grpc_error* why) { + fd_shutdown_internal(fd, why, false); +} + +static void fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd, + const char* reason) { + grpc_error* error = GRPC_ERROR_NONE; + bool is_release_fd = (release_fd != nullptr); + + if (!fd->read_closure->IsShutdown()) { + fd_shutdown_internal(fd, GRPC_ERROR_CREATE_FROM_COPIED_STRING(reason), + is_release_fd); + } + + /* If release_fd is not NULL, we should be relinquishing control of the file + descriptor fd->fd (but we still own the grpc_fd structure). */ + if (is_release_fd) { + *release_fd = fd->fd; + } else { + close(fd->fd); + } + + GRPC_CLOSURE_SCHED(on_done, GRPC_ERROR_REF(error)); + + grpc_iomgr_unregister_object(&fd->iomgr_object); + fork_fd_list_remove_grpc_fd(fd); + fd->read_closure->DestroyEvent(); + fd->write_closure->DestroyEvent(); + fd->error_closure->DestroyEvent(); + + gpr_mu_lock(&fd_freelist_mu); + fd->freelist_next = fd_freelist; + fd_freelist = fd; + gpr_mu_unlock(&fd_freelist_mu); +} + +static bool fd_is_shutdown(grpc_fd* fd) { + return fd->read_closure->IsShutdown(); +} + +static void fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) { + fd->read_closure->NotifyOn(closure); +} + +static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) { + fd->write_closure->NotifyOn(closure); +} + +static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) { + fd->error_closure->NotifyOn(closure); +} + +static void fd_become_readable(grpc_fd* fd) { fd->read_closure->SetReady(); } + +static void fd_become_writable(grpc_fd* fd) { fd->write_closure->SetReady(); } + +static void fd_has_errors(grpc_fd* fd) { fd->error_closure->SetReady(); } + +/******************************************************************************* + * Pollset Definitions + */ + +GPR_TLS_DECL(g_current_thread_pollset); +GPR_TLS_DECL(g_current_thread_worker); + +/* The designated poller */ +static gpr_atm g_active_poller; + +static pollset_neighborhood* g_neighborhoods; +static size_t g_num_neighborhoods; + +/* Return true if first in list */ +static bool worker_insert(grpc_pollset* pollset, grpc_pollset_worker* worker) { + if (pollset->root_worker == nullptr) { + pollset->root_worker = worker; + worker->next = worker->prev = worker; + return true; + } else { + worker->next = pollset->root_worker; + worker->prev = worker->next->prev; + worker->next->prev = worker; + worker->prev->next = worker; + return false; + } +} + +/* Return true if last in list */ +typedef enum { EMPTIED, NEW_ROOT, REMOVED } worker_remove_result; + +static worker_remove_result worker_remove(grpc_pollset* pollset, + grpc_pollset_worker* worker) { + if (worker == pollset->root_worker) { + if (worker == worker->next) { + pollset->root_worker = nullptr; + return EMPTIED; + } else { + pollset->root_worker = worker->next; + worker->prev->next = worker->next; + worker->next->prev = worker->prev; + return NEW_ROOT; + } + } else { + worker->prev->next = worker->next; + worker->next->prev = worker->prev; + return REMOVED; + } +} + +static size_t choose_neighborhood(void) { + return static_cast(gpr_cpu_current_cpu()) % g_num_neighborhoods; +} + +static grpc_error* pollset_global_init(void) { + gpr_tls_init(&g_current_thread_pollset); + gpr_tls_init(&g_current_thread_worker); + gpr_atm_no_barrier_store(&g_active_poller, 0); + global_wakeup_fd.read_fd = -1; + grpc_error* err = grpc_wakeup_fd_init(&global_wakeup_fd); + if (err != GRPC_ERROR_NONE) return err; + struct epoll_event ev; + ev.events = static_cast(EPOLLIN | EPOLLET); + ev.data.ptr = &global_wakeup_fd; + if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, global_wakeup_fd.read_fd, + &ev) != 0) { + return GRPC_OS_ERROR(errno, "epoll_ctl"); + } + g_num_neighborhoods = GPR_CLAMP(gpr_cpu_num_cores(), 1, MAX_NEIGHBORHOODS); + g_neighborhoods = static_cast( + gpr_zalloc(sizeof(*g_neighborhoods) * g_num_neighborhoods)); + for (size_t i = 0; i < g_num_neighborhoods; i++) { + gpr_mu_init(&g_neighborhoods[i].mu); + } + return GRPC_ERROR_NONE; +} + +static void pollset_global_shutdown(void) { + gpr_tls_destroy(&g_current_thread_pollset); + gpr_tls_destroy(&g_current_thread_worker); + if (global_wakeup_fd.read_fd != -1) grpc_wakeup_fd_destroy(&global_wakeup_fd); + for (size_t i = 0; i < g_num_neighborhoods; i++) { + gpr_mu_destroy(&g_neighborhoods[i].mu); + } + gpr_free(g_neighborhoods); +} + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + gpr_mu_init(&pollset->mu); + *mu = &pollset->mu; + pollset->neighborhood = &g_neighborhoods[choose_neighborhood()]; + pollset->reassigning_neighborhood = false; + pollset->root_worker = nullptr; + pollset->kicked_without_poller = false; + pollset->seen_inactive = true; + pollset->shutting_down = false; + pollset->shutdown_closure = nullptr; + pollset->begin_refs = 0; + pollset->next = pollset->prev = nullptr; +} + +static void pollset_destroy(grpc_pollset* pollset) { + gpr_mu_lock(&pollset->mu); + if (!pollset->seen_inactive) { + pollset_neighborhood* neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + retry_lock_neighborhood: + gpr_mu_lock(&neighborhood->mu); + gpr_mu_lock(&pollset->mu); + if (!pollset->seen_inactive) { + if (pollset->neighborhood != neighborhood) { + gpr_mu_unlock(&neighborhood->mu); + neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + goto retry_lock_neighborhood; + } + pollset->prev->next = pollset->next; + pollset->next->prev = pollset->prev; + if (pollset == pollset->neighborhood->active_root) { + pollset->neighborhood->active_root = + pollset->next == pollset ? nullptr : pollset->next; + } + } + gpr_mu_unlock(&pollset->neighborhood->mu); + } + gpr_mu_unlock(&pollset->mu); + gpr_mu_destroy(&pollset->mu); +} + +static grpc_error* pollset_kick_all(grpc_pollset* pollset) { + GPR_TIMER_SCOPE("pollset_kick_all", 0); + grpc_error* error = GRPC_ERROR_NONE; + if (pollset->root_worker != nullptr) { + grpc_pollset_worker* worker = pollset->root_worker; + do { + GRPC_STATS_INC_POLLSET_KICK(); + switch (worker->state) { + case KICKED: + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(); + break; + case UNKICKED: + SET_KICK_STATE(worker, KICKED); + if (worker->initialized_cv) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(); + gpr_cv_signal(&worker->cv); + } + break; + case DESIGNATED_POLLER: + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(); + SET_KICK_STATE(worker, KICKED); + append_error(&error, grpc_wakeup_fd_wakeup(&global_wakeup_fd), + "pollset_kick_all"); + break; + } + + worker = worker->next; + } while (worker != pollset->root_worker); + } + // TODO: sreek. Check if we need to set 'kicked_without_poller' to true here + // in the else case + return error; +} + +static void pollset_maybe_finish_shutdown(grpc_pollset* pollset) { + if (pollset->shutdown_closure != nullptr && pollset->root_worker == nullptr && + pollset->begin_refs == 0) { + GPR_TIMER_MARK("pollset_finish_shutdown", 0); + GRPC_CLOSURE_SCHED(pollset->shutdown_closure, GRPC_ERROR_NONE); + pollset->shutdown_closure = nullptr; + } +} + +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + GPR_TIMER_SCOPE("pollset_shutdown", 0); + GPR_ASSERT(pollset->shutdown_closure == nullptr); + GPR_ASSERT(!pollset->shutting_down); + pollset->shutdown_closure = closure; + pollset->shutting_down = true; + GRPC_LOG_IF_ERROR("pollset_shutdown", pollset_kick_all(pollset)); + pollset_maybe_finish_shutdown(pollset); +} + +static int poll_deadline_to_millis_timeout(grpc_millis millis) { + if (millis == GRPC_MILLIS_INF_FUTURE) return -1; + grpc_millis delta = millis - grpc_core::ExecCtx::Get()->Now(); + if (delta > INT_MAX) { + return INT_MAX; + } else if (delta < 0) { + return 0; + } else { + return static_cast(delta); + } +} + +/* Process the epoll events found by do_epoll_wait() function. + - g_epoll_set.cursor points to the index of the first event to be processed + - This function then processes up-to MAX_EPOLL_EVENTS_PER_ITERATION and + updates the g_epoll_set.cursor + + NOTE ON SYNCRHONIZATION: Similar to do_epoll_wait(), this function is only + called by g_active_poller thread. So there is no need for synchronization + when accessing fields in g_epoll_set */ +static grpc_error* process_epoll_events(grpc_pollset* pollset) { + GPR_TIMER_SCOPE("process_epoll_events", 0); + + static const char* err_desc = "process_events"; + grpc_error* error = GRPC_ERROR_NONE; + long num_events = gpr_atm_acq_load(&g_epoll_set.num_events); + long cursor = gpr_atm_acq_load(&g_epoll_set.cursor); + for (int idx = 0; + (idx < MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION) && cursor != num_events; + idx++) { + long c = cursor++; + struct epoll_event* ev = &g_epoll_set.events[c]; + void* data_ptr = ev->data.ptr; + + if (data_ptr == &global_wakeup_fd) { + append_error(&error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd), + err_desc); + } else { + grpc_fd* fd = reinterpret_cast( + reinterpret_cast(data_ptr) & ~static_cast(1)); + bool track_err = + reinterpret_cast(data_ptr) & static_cast(1); + bool cancel = (ev->events & EPOLLHUP) != 0; + bool error = (ev->events & EPOLLERR) != 0; + bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0; + bool write_ev = (ev->events & EPOLLOUT) != 0; + bool err_fallback = error && !track_err; + + if (error && !err_fallback) { + fd_has_errors(fd); + } + + if (read_ev || cancel || err_fallback) { + fd_become_readable(fd); + } + + if (write_ev || cancel || err_fallback) { + fd_become_writable(fd); + } + } + } + gpr_atm_rel_store(&g_epoll_set.cursor, cursor); + return error; +} + +/* Do epoll_wait and store the events in g_epoll_set.events field. This does not + "process" any of the events yet; that is done in process_epoll_events(). + *See process_epoll_events() function for more details. + + NOTE ON SYNCHRONIZATION: At any point of time, only the g_active_poller + (i.e the designated poller thread) will be calling this function. So there is + no need for any synchronization when accesing fields in g_epoll_set */ +static grpc_error* do_epoll_wait(grpc_pollset* ps, grpc_millis deadline) { + GPR_TIMER_SCOPE("do_epoll_wait", 0); + + int r; + int timeout = poll_deadline_to_millis_timeout(deadline); + if (timeout != 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; + } + do { + GRPC_STATS_INC_SYSCALL_POLL(); + r = epoll_wait(g_epoll_set.epfd, g_epoll_set.events, MAX_EPOLL_EVENTS, + timeout); + } while (r < 0 && errno == EINTR); + if (timeout != 0) { + GRPC_SCHEDULING_END_BLOCKING_REGION; + } + + if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait"); + + GRPC_STATS_INC_POLL_EVENTS_RETURNED(r); + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "ps: %p poll got %d events", ps, r); + } + + gpr_atm_rel_store(&g_epoll_set.num_events, r); + gpr_atm_rel_store(&g_epoll_set.cursor, 0); + + return GRPC_ERROR_NONE; +} + +static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GPR_TIMER_SCOPE("begin_worker", 0); + if (worker_hdl != nullptr) *worker_hdl = worker; + worker->initialized_cv = false; + SET_KICK_STATE(worker, UNKICKED); + worker->schedule_on_end_work = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; + pollset->begin_refs++; + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p BEGIN_STARTS:%p", pollset, worker); + } + + if (pollset->seen_inactive) { + // pollset has been observed to be inactive, we need to move back to the + // active list + bool is_reassigning = false; + if (!pollset->reassigning_neighborhood) { + is_reassigning = true; + pollset->reassigning_neighborhood = true; + pollset->neighborhood = &g_neighborhoods[choose_neighborhood()]; + } + pollset_neighborhood* neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + // pollset unlocked: state may change (even worker->kick_state) + retry_lock_neighborhood: + gpr_mu_lock(&neighborhood->mu); + gpr_mu_lock(&pollset->mu); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p BEGIN_REORG:%p kick_state=%s is_reassigning=%d", + pollset, worker, kick_state_string(worker->state), + is_reassigning); + } + if (pollset->seen_inactive) { + if (neighborhood != pollset->neighborhood) { + gpr_mu_unlock(&neighborhood->mu); + neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + goto retry_lock_neighborhood; + } + + /* In the brief time we released the pollset locks above, the worker MAY + have been kicked. In this case, the worker should get out of this + pollset ASAP and hence this should neither add the pollset to + neighborhood nor mark the pollset as active. + + On a side note, the only way a worker's kick state could have changed + at this point is if it were "kicked specifically". Since the worker has + not added itself to the pollset yet (by calling worker_insert()), it is + not visible in the "kick any" path yet */ + if (worker->state == UNKICKED) { + pollset->seen_inactive = false; + if (neighborhood->active_root == nullptr) { + neighborhood->active_root = pollset->next = pollset->prev = pollset; + /* Make this the designated poller if there isn't one already */ + if (worker->state == UNKICKED && + gpr_atm_no_barrier_cas(&g_active_poller, 0, (gpr_atm)worker)) { + SET_KICK_STATE(worker, DESIGNATED_POLLER); + } + } else { + pollset->next = neighborhood->active_root; + pollset->prev = pollset->next->prev; + pollset->next->prev = pollset->prev->next = pollset; + } + } + } + if (is_reassigning) { + GPR_ASSERT(pollset->reassigning_neighborhood); + pollset->reassigning_neighborhood = false; + } + gpr_mu_unlock(&neighborhood->mu); + } + + worker_insert(pollset, worker); + pollset->begin_refs--; + if (worker->state == UNKICKED && !pollset->kicked_without_poller) { + GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker); + worker->initialized_cv = true; + gpr_cv_init(&worker->cv); + while (worker->state == UNKICKED && !pollset->shutting_down) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p BEGIN_WAIT:%p kick_state=%s shutdown=%d", + pollset, worker, kick_state_string(worker->state), + pollset->shutting_down); + } + + if (gpr_cv_wait(&worker->cv, &pollset->mu, + grpc_millis_to_timespec(deadline, GPR_CLOCK_MONOTONIC)) && + worker->state == UNKICKED) { + /* If gpr_cv_wait returns true (i.e a timeout), pretend that the worker + received a kick */ + SET_KICK_STATE(worker, KICKED); + } + } + grpc_core::ExecCtx::Get()->InvalidateNow(); + } + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, + "PS:%p BEGIN_DONE:%p kick_state=%s shutdown=%d " + "kicked_without_poller: %d", + pollset, worker, kick_state_string(worker->state), + pollset->shutting_down, pollset->kicked_without_poller); + } + + /* We release pollset lock in this function at a couple of places: + * 1. Briefly when assigning pollset to a neighborhood + * 2. When doing gpr_cv_wait() + * It is possible that 'kicked_without_poller' was set to true during (1) and + * 'shutting_down' is set to true during (1) or (2). If either of them is + * true, this worker cannot do polling */ + /* TODO(sreek): Perhaps there is a better way to handle kicked_without_poller + * case; especially when the worker is the DESIGNATED_POLLER */ + + if (pollset->kicked_without_poller) { + pollset->kicked_without_poller = false; + return false; + } + + return worker->state == DESIGNATED_POLLER && !pollset->shutting_down; +} + +static bool check_neighborhood_for_available_poller( + pollset_neighborhood* neighborhood) { + GPR_TIMER_SCOPE("check_neighborhood_for_available_poller", 0); + bool found_worker = false; + do { + grpc_pollset* inspect = neighborhood->active_root; + if (inspect == nullptr) { + break; + } + gpr_mu_lock(&inspect->mu); + GPR_ASSERT(!inspect->seen_inactive); + grpc_pollset_worker* inspect_worker = inspect->root_worker; + if (inspect_worker != nullptr) { + do { + switch (inspect_worker->state) { + case UNKICKED: + if (gpr_atm_no_barrier_cas(&g_active_poller, 0, + (gpr_atm)inspect_worker)) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. choose next poller to be %p", + inspect_worker); + } + SET_KICK_STATE(inspect_worker, DESIGNATED_POLLER); + if (inspect_worker->initialized_cv) { + GPR_TIMER_MARK("signal worker", 0); + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(); + gpr_cv_signal(&inspect_worker->cv); + } + } else { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. beaten to choose next poller"); + } + } + // even if we didn't win the cas, there's a worker, we can stop + found_worker = true; + break; + case KICKED: + break; + case DESIGNATED_POLLER: + found_worker = true; // ok, so someone else found the worker, but + // we'll accept that + break; + } + inspect_worker = inspect_worker->next; + } while (!found_worker && inspect_worker != inspect->root_worker); + } + if (!found_worker) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. mark pollset %p inactive", inspect); + } + inspect->seen_inactive = true; + if (inspect == neighborhood->active_root) { + neighborhood->active_root = + inspect->next == inspect ? nullptr : inspect->next; + } + inspect->next->prev = inspect->prev; + inspect->prev->next = inspect->next; + inspect->next = inspect->prev = nullptr; + } + gpr_mu_unlock(&inspect->mu); + } while (!found_worker); + return found_worker; +} + +static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker, + grpc_pollset_worker** worker_hdl) { + GPR_TIMER_SCOPE("end_worker", 0); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p END_WORKER:%p", pollset, worker); + } + if (worker_hdl != nullptr) *worker_hdl = nullptr; + /* Make sure we appear kicked */ + SET_KICK_STATE(worker, KICKED); + grpc_closure_list_move(&worker->schedule_on_end_work, + grpc_core::ExecCtx::Get()->closure_list()); + if (gpr_atm_no_barrier_load(&g_active_poller) == (gpr_atm)worker) { + if (worker->next != worker && worker->next->state == UNKICKED) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. choose next poller to be peer %p", worker); + } + GPR_ASSERT(worker->next->initialized_cv); + gpr_atm_no_barrier_store(&g_active_poller, (gpr_atm)worker->next); + SET_KICK_STATE(worker->next, DESIGNATED_POLLER); + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(); + gpr_cv_signal(&worker->next->cv); + if (grpc_core::ExecCtx::Get()->HasWork()) { + gpr_mu_unlock(&pollset->mu); + grpc_core::ExecCtx::Get()->Flush(); + gpr_mu_lock(&pollset->mu); + } + } else { + gpr_atm_no_barrier_store(&g_active_poller, 0); + size_t poller_neighborhood_idx = + static_cast(pollset->neighborhood - g_neighborhoods); + gpr_mu_unlock(&pollset->mu); + bool found_worker = false; + bool scan_state[MAX_NEIGHBORHOODS]; + for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) { + pollset_neighborhood* neighborhood = + &g_neighborhoods[(poller_neighborhood_idx + i) % + g_num_neighborhoods]; + if (gpr_mu_trylock(&neighborhood->mu)) { + found_worker = check_neighborhood_for_available_poller(neighborhood); + gpr_mu_unlock(&neighborhood->mu); + scan_state[i] = true; + } else { + scan_state[i] = false; + } + } + for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) { + if (scan_state[i]) continue; + pollset_neighborhood* neighborhood = + &g_neighborhoods[(poller_neighborhood_idx + i) % + g_num_neighborhoods]; + gpr_mu_lock(&neighborhood->mu); + found_worker = check_neighborhood_for_available_poller(neighborhood); + gpr_mu_unlock(&neighborhood->mu); + } + grpc_core::ExecCtx::Get()->Flush(); + gpr_mu_lock(&pollset->mu); + } + } else if (grpc_core::ExecCtx::Get()->HasWork()) { + gpr_mu_unlock(&pollset->mu); + grpc_core::ExecCtx::Get()->Flush(); + gpr_mu_lock(&pollset->mu); + } + if (worker->initialized_cv) { + gpr_cv_destroy(&worker->cv); + } + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. remove worker"); + } + if (EMPTIED == worker_remove(pollset, worker)) { + pollset_maybe_finish_shutdown(pollset); + } + GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker); +} + +/* pollset->po.mu lock must be held by the caller before calling this. + The function pollset_work() may temporarily release the lock (pollset->po.mu) + during the course of its execution but it will always re-acquire the lock and + ensure that it is held by the time the function returns */ +static grpc_error* pollset_work(grpc_pollset* ps, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GPR_TIMER_SCOPE("pollset_work", 0); + grpc_pollset_worker worker; + grpc_error* error = GRPC_ERROR_NONE; + static const char* err_desc = "pollset_work"; + if (ps->kicked_without_poller) { + ps->kicked_without_poller = false; + return GRPC_ERROR_NONE; + } + + if (begin_worker(ps, &worker, worker_hdl, deadline)) { + gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps); + gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); + GPR_ASSERT(!ps->shutting_down); + GPR_ASSERT(!ps->seen_inactive); + + gpr_mu_unlock(&ps->mu); /* unlock */ + /* This is the designated polling thread at this point and should ideally do + polling. However, if there are unprocessed events left from a previous + call to do_epoll_wait(), skip calling epoll_wait() in this iteration and + process the pending epoll events. + + The reason for decoupling do_epoll_wait and process_epoll_events is to + better distrubute the work (i.e handling epoll events) across multiple + threads + + process_epoll_events() returns very quickly: It just queues the work on + exec_ctx but does not execute it (the actual exectution or more + accurately grpc_core::ExecCtx::Get()->Flush() happens in end_worker() + AFTER selecting a designated poller). So we are not waiting long periods + without a designated poller */ + if (gpr_atm_acq_load(&g_epoll_set.cursor) == + gpr_atm_acq_load(&g_epoll_set.num_events)) { + append_error(&error, do_epoll_wait(ps, deadline), err_desc); + } + append_error(&error, process_epoll_events(ps), err_desc); + + gpr_mu_lock(&ps->mu); /* lock */ + + gpr_tls_set(&g_current_thread_worker, 0); + } else { + gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps); + } + end_worker(ps, &worker, worker_hdl); + + gpr_tls_set(&g_current_thread_pollset, 0); + return error; +} + +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + GPR_TIMER_SCOPE("pollset_kick", 0); + GRPC_STATS_INC_POLLSET_KICK(); + grpc_error* ret_err = GRPC_ERROR_NONE; + if (grpc_polling_trace.enabled()) { + gpr_strvec log; + gpr_strvec_init(&log); + char* tmp; + gpr_asprintf(&tmp, "PS:%p KICK:%p curps=%p curworker=%p root=%p", pollset, + specific_worker, (void*)gpr_tls_get(&g_current_thread_pollset), + (void*)gpr_tls_get(&g_current_thread_worker), + pollset->root_worker); + gpr_strvec_add(&log, tmp); + if (pollset->root_worker != nullptr) { + gpr_asprintf(&tmp, " {kick_state=%s next=%p {kick_state=%s}}", + kick_state_string(pollset->root_worker->state), + pollset->root_worker->next, + kick_state_string(pollset->root_worker->next->state)); + gpr_strvec_add(&log, tmp); + } + if (specific_worker != nullptr) { + gpr_asprintf(&tmp, " worker_kick_state=%s", + kick_state_string(specific_worker->state)); + gpr_strvec_add(&log, tmp); + } + tmp = gpr_strvec_flatten(&log, nullptr); + gpr_strvec_destroy(&log); + gpr_log(GPR_DEBUG, "%s", tmp); + gpr_free(tmp); + } + + if (specific_worker == nullptr) { + if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) { + grpc_pollset_worker* root_worker = pollset->root_worker; + if (root_worker == nullptr) { + GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(); + pollset->kicked_without_poller = true; + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. kicked_without_poller"); + } + goto done; + } + grpc_pollset_worker* next_worker = root_worker->next; + if (root_worker->state == KICKED) { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. already kicked %p", root_worker); + } + SET_KICK_STATE(root_worker, KICKED); + goto done; + } else if (next_worker->state == KICKED) { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. already kicked %p", next_worker); + } + SET_KICK_STATE(next_worker, KICKED); + goto done; + } else if (root_worker == + next_worker && // only try and wake up a poller if + // there is no next worker + root_worker == (grpc_pollset_worker*)gpr_atm_no_barrier_load( + &g_active_poller)) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. kicked %p", root_worker); + } + SET_KICK_STATE(root_worker, KICKED); + ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); + goto done; + } else if (next_worker->state == UNKICKED) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. kicked %p", next_worker); + } + GPR_ASSERT(next_worker->initialized_cv); + SET_KICK_STATE(next_worker, KICKED); + gpr_cv_signal(&next_worker->cv); + goto done; + } else if (next_worker->state == DESIGNATED_POLLER) { + if (root_worker->state != DESIGNATED_POLLER) { + if (grpc_polling_trace.enabled()) { + gpr_log( + GPR_INFO, + " .. kicked root non-poller %p (initialized_cv=%d) (poller=%p)", + root_worker, root_worker->initialized_cv, next_worker); + } + SET_KICK_STATE(root_worker, KICKED); + if (root_worker->initialized_cv) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(); + gpr_cv_signal(&root_worker->cv); + } + goto done; + } else { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. non-root poller %p (root=%p)", next_worker, + root_worker); + } + SET_KICK_STATE(next_worker, KICKED); + ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); + goto done; + } + } else { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(); + GPR_ASSERT(next_worker->state == KICKED); + SET_KICK_STATE(next_worker, KICKED); + goto done; + } + } else { + GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. kicked while waking up"); + } + goto done; + } + + GPR_UNREACHABLE_CODE(goto done); + } + + if (specific_worker->state == KICKED) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. specific worker already kicked"); + } + goto done; + } else if (gpr_tls_get(&g_current_thread_worker) == + (intptr_t)specific_worker) { + GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. mark %p kicked", specific_worker); + } + SET_KICK_STATE(specific_worker, KICKED); + goto done; + } else if (specific_worker == + (grpc_pollset_worker*)gpr_atm_no_barrier_load(&g_active_poller)) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. kick active poller"); + } + SET_KICK_STATE(specific_worker, KICKED); + ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); + goto done; + } else if (specific_worker->initialized_cv) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. kick waiting worker"); + } + SET_KICK_STATE(specific_worker, KICKED); + gpr_cv_signal(&specific_worker->cv); + goto done; + } else { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, " .. kick non-waiting worker"); + } + SET_KICK_STATE(specific_worker, KICKED); + goto done; + } +done: + return ret_err; +} + +static void pollset_add_fd(grpc_pollset* pollset, grpc_fd* fd) {} + +/******************************************************************************* + * Pollset-set Definitions + */ + +static grpc_pollset_set* pollset_set_create(void) { + return (grpc_pollset_set*)(static_cast(0xdeafbeef)); +} + +static void pollset_set_destroy(grpc_pollset_set* pss) {} + +static void pollset_set_add_fd(grpc_pollset_set* pss, grpc_fd* fd) {} + +static void pollset_set_del_fd(grpc_pollset_set* pss, grpc_fd* fd) {} + +static void pollset_set_add_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {} + +static void pollset_set_del_pollset(grpc_pollset_set* pss, grpc_pollset* ps) {} + +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +/******************************************************************************* + * Event engine binding + */ + +static void shutdown_background_closure(void) {} + +static void shutdown_engine(void) { + fd_global_shutdown(); + pollset_global_shutdown(); + epoll_set_shutdown(); + if (grpc_core::Fork::Enabled()) { + gpr_mu_destroy(&fork_fd_list_mu); + grpc_core::Fork::SetResetChildPollingEngineFunc(nullptr); + } +} + +static const grpc_event_engine_vtable vtable = { + sizeof(grpc_pollset), + true, + false, + + fd_create, + fd_wrapped_fd, + fd_orphan, + fd_shutdown, + fd_notify_on_read, + fd_notify_on_write, + fd_notify_on_error, + fd_become_readable, + fd_become_writable, + fd_has_errors, + fd_is_shutdown, + + pollset_init, + pollset_shutdown, + pollset_destroy, + pollset_work, + pollset_kick, + pollset_add_fd, + + pollset_set_create, + pollset_set_destroy, + pollset_set_add_pollset, + pollset_set_del_pollset, + pollset_set_add_pollset_set, + pollset_set_del_pollset_set, + pollset_set_add_fd, + pollset_set_del_fd, + + shutdown_background_closure, + shutdown_engine, +}; + +/* Called by the child process's post-fork handler to close open fds, including + * the global epoll fd. This allows gRPC to shutdown in the child process + * without interfering with connections or RPCs ongoing in the parent. */ +static void reset_event_manager_on_fork() { + gpr_mu_lock(&fork_fd_list_mu); + while (fork_fd_list_head != nullptr) { + close(fork_fd_list_head->fd); + fork_fd_list_head->fd = -1; + fork_fd_list_head = fork_fd_list_head->fork_fd_list->next; + } + gpr_mu_unlock(&fork_fd_list_mu); + shutdown_engine(); + grpc_init_epoll1_linux(true); +} + +/* It is possible that GLIBC has epoll but the underlying kernel doesn't. + * Create epoll_fd (epoll_set_init() takes care of that) to make sure epoll + * support is available */ +const grpc_event_engine_vtable* grpc_init_epoll1_linux(bool explicit_request) { + if (!grpc_has_wakeup_fd()) { + gpr_log(GPR_ERROR, "Skipping epoll1 because of no wakeup fd."); + return nullptr; + } + + if (!epoll_set_init()) { + return nullptr; + } + + fd_global_init(); + + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + fd_global_shutdown(); + epoll_set_shutdown(); + return nullptr; + } + + if (grpc_core::Fork::Enabled()) { + gpr_mu_init(&fork_fd_list_mu); + grpc_core::Fork::SetResetChildPollingEngineFunc( + reset_event_manager_on_fork); + } + return &vtable; +} + +#else /* defined(GRPC_LINUX_EPOLL) */ +#if defined(GRPC_POSIX_SOCKET_EV_EPOLL1) +#include "src/core/lib/iomgr/ev_epoll1_linux.h" +/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return + * NULL */ +const grpc_event_engine_vtable* grpc_init_epoll1_linux(bool explicit_request) { + return nullptr; +} +#endif /* defined(GRPC_POSIX_SOCKET_EV_EPOLL1) */ +#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.h b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.h new file mode 100644 index 00000000000000..ca0db7250f07c0 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_epoll1_linux.h @@ -0,0 +1,31 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H +#define GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H + +#include + +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/port.h" + +// a polling engine that utilizes a singleton epoll set and turnstile polling + +const grpc_event_engine_vtable* grpc_init_epoll1_linux(bool explicit_request); + +#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.cc b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.cc new file mode 100644 index 00000000000000..7a4870db785648 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.cc @@ -0,0 +1,1685 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +/* This polling engine is only relevant on linux kernels supporting epoll() */ +#ifdef GRPC_LINUX_EPOLL_CREATE1 + +#include "src/core/lib/iomgr/ev_epollex_linux.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/spinlock.h" +#include "src/core/lib/gpr/tls.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/iomgr/block_annotate.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/is_epollexclusive_available.h" +#include "src/core/lib/iomgr/lockfree_event.h" +#include "src/core/lib/iomgr/sys_epoll_wrapper.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" + +// debug aid: create workers on the heap (allows asan to spot +// use-after-destruction) +//#define GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP 1 + +#define MAX_EPOLL_EVENTS 100 +// TODO(juanlishen): We use a greater-than-one value here as a workaround fix to +// a keepalive ping timeout issue. We may want to revert https://github +// .com/grpc/grpc/pull/14943 once we figure out the root cause. +#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 16 +#define MAX_FDS_IN_CACHE 32 + +grpc_core::DebugOnlyTraceFlag grpc_trace_pollable_refcount(false, + "pollable_refcount"); + +/******************************************************************************* + * pollable Declarations + */ + +typedef enum { PO_MULTI, PO_FD, PO_EMPTY } pollable_type; + +typedef struct pollable pollable; + +typedef struct cached_fd { + // Set to the grpc_fd's salt value. See 'salt' variable' in grpc_fd for more + // details + intptr_t salt; + + // The underlying fd + int fd; + + // A recency time counter that helps to determine the LRU fd in the cache + uint64_t last_used; +} cached_fd; + +/// A pollable is something that can be polled: it has an epoll set to poll on, +/// and a wakeup fd for kicks +/// There are three broad types: +/// - PO_EMPTY - the empty pollable, used before file descriptors are added to +/// a pollset +/// - PO_FD - a pollable containing only one FD - used to optimize single-fd +/// pollsets (which are common with synchronous api usage) +/// - PO_MULTI - a pollable containing many fds +struct pollable { + pollable_type type; // immutable + gpr_refcount refs; + + int epfd; + grpc_wakeup_fd wakeup; + + // The following are relevant only for type PO_FD + grpc_fd* owner_fd; // Set to the owner_fd if the type is PO_FD + gpr_mu owner_orphan_mu; // Synchronizes access to owner_orphaned field + bool owner_orphaned; // Is the owner fd orphaned + + grpc_pollset_set* pollset_set; + pollable* next; + pollable* prev; + + gpr_mu mu; + grpc_pollset_worker* root_worker; + + int event_cursor; + int event_count; + struct epoll_event events[MAX_EPOLL_EVENTS]; + + // We may be calling pollable_add_fd() on the same (pollable, fd) multiple + // times. To prevent pollable_add_fd() from making multiple sys calls to + // epoll_ctl() to add the fd, we maintain a cache of what fds are already + // present in the underlying epoll-set. + // + // Since this is not a correctness issue, we do not need to maintain all the + // fds in the cache. Hence we just use an LRU cache of size 'MAX_FDS_IN_CACHE' + // + // NOTE: An ideal implementation of this should do the following: + // 1) Add fds to the cache in pollable_add_fd() function (i.e whenever the fd + // is added to the pollable's epoll set) + // 2) Remove the fd from the cache whenever the fd is removed from the + // underlying epoll set (i.e whenever fd_orphan() is called). + // + // Implementing (2) above (i.e removing fds from cache on fd_orphan) adds a + // lot of complexity since an fd can be present in multiple pollables. So our + // implementation ONLY DOES (1) and NOT (2). + // + // The cache_fd.salt variable helps here to maintain correctness (it serves as + // an epoch that differentiates one grpc_fd from the other even though both of + // them may have the same fd number) + // + // The following implements LRU-eviction cache of fds in this pollable + cached_fd fd_cache[MAX_FDS_IN_CACHE]; + int fd_cache_size; + uint64_t fd_cache_counter; // Recency timer tick counter +}; + +static const char* pollable_type_string(pollable_type t) { + switch (t) { + case PO_MULTI: + return "pollset"; + case PO_FD: + return "fd"; + case PO_EMPTY: + return "empty"; + } + return ""; +} + +static char* pollable_desc(pollable* p) { + char* out; + gpr_asprintf(&out, "type=%s epfd=%d wakeup=%d", pollable_type_string(p->type), + p->epfd, p->wakeup.read_fd); + return out; +} + +/// Shared empty pollable - used by pollset to poll on until the first fd is +/// added +static pollable* g_empty_pollable; + +static grpc_error* pollable_create(pollable_type type, pollable** p); +#ifdef NDEBUG +static pollable* pollable_ref(pollable* p); +static void pollable_unref(pollable* p); +#define POLLABLE_REF(p, r) pollable_ref(p) +#define POLLABLE_UNREF(p, r) pollable_unref(p) +#else +static pollable* pollable_ref(pollable* p, int line, const char* reason); +static void pollable_unref(pollable* p, int line, const char* reason); +#define POLLABLE_REF(p, r) pollable_ref((p), __LINE__, (r)) +#define POLLABLE_UNREF(p, r) pollable_unref((p), __LINE__, (r)) +#endif + +/******************************************************************************* + * Fd Declarations + */ + +// Monotonically increasing Epoch counter that is assinged to each grpc_fd. See +// the description of 'salt' variable in 'grpc_fd' for more details +// TODO: (sreek/kpayson) gpr_atm is intptr_t which may not be wide-enough on +// 32-bit systems. Change this to int_64 - atleast on 32-bit systems +static gpr_atm g_fd_salt; + +struct grpc_fd { + int fd; + + // Since fd numbers can be reused (after old fds are closed), this serves as + // an epoch that uniquely identifies this fd (i.e the pair (salt, fd) is + // unique (until the salt counter (i.e g_fd_salt) overflows) + intptr_t salt; + + // refst format: + // bit 0 : 1=Active / 0=Orphaned + // bits 1-n : refcount + // Ref/Unref by two to avoid altering the orphaned bit + gpr_atm refst; + + gpr_mu orphan_mu; + + gpr_mu pollable_mu; + pollable* pollable_obj; + + grpc_core::ManualConstructor read_closure; + grpc_core::ManualConstructor write_closure; + grpc_core::ManualConstructor error_closure; + + struct grpc_fd* freelist_next; + grpc_closure* on_done_closure; + + grpc_iomgr_object iomgr_object; + + // Do we need to track EPOLLERR events separately? + bool track_err; +}; + +static void fd_global_init(void); +static void fd_global_shutdown(void); + +/******************************************************************************* + * Pollset Declarations + */ + +typedef struct { + grpc_pollset_worker* next; + grpc_pollset_worker* prev; +} pwlink; + +typedef enum { PWLINK_POLLABLE = 0, PWLINK_POLLSET, PWLINK_COUNT } pwlinks; + +struct grpc_pollset_worker { + bool kicked; + bool initialized_cv; +#ifndef NDEBUG + // debug aid: which thread started this worker + pid_t originator; +#endif + gpr_cv cv; + grpc_pollset* pollset; + pollable* pollable_obj; + + pwlink links[PWLINK_COUNT]; +}; + +struct grpc_pollset { + gpr_mu mu; + gpr_atm worker_count; + pollable* active_pollable; + bool kicked_without_poller; + grpc_closure* shutdown_closure; + bool already_shutdown; + grpc_pollset_worker* root_worker; + int containing_pollset_set_count; +}; + +/******************************************************************************* + * Pollset-set Declarations + */ + +struct grpc_pollset_set { + gpr_refcount refs; + gpr_mu mu; + grpc_pollset_set* parent; + + size_t pollset_count; + size_t pollset_capacity; + grpc_pollset** pollsets; + + size_t fd_count; + size_t fd_capacity; + grpc_fd** fds; +}; + +/******************************************************************************* + * Common helpers + */ + +static bool append_error(grpc_error** composite, grpc_error* error, + const char* desc) { + if (error == GRPC_ERROR_NONE) return true; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); + } + *composite = grpc_error_add_child(*composite, error); + return false; +} + +/******************************************************************************* + * Fd Definitions + */ + +/* 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. + */ + +static grpc_fd* fd_freelist = nullptr; +static gpr_mu fd_freelist_mu; + +#ifndef NDEBUG +#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) { + if (grpc_trace_fd_refcount.enabled()) { + gpr_log(GPR_DEBUG, + "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%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); +} + +#ifndef NDEBUG +#define INVALIDATE_FD(fd) invalidate_fd(fd) +/* Since an fd is never really destroyed (i.e gpr_free() is not called), it is + * hard to cases where fd fields are accessed even after calling fd_destroy(). + * The following invalidates fd fields to make catching such errors easier */ +static void invalidate_fd(grpc_fd* fd) { + fd->fd = -1; + fd->salt = -1; + gpr_atm_no_barrier_store(&fd->refst, -1); + memset(&fd->orphan_mu, -1, sizeof(fd->orphan_mu)); + memset(&fd->pollable_mu, -1, sizeof(fd->pollable_mu)); + fd->pollable_obj = nullptr; + fd->on_done_closure = nullptr; + memset(&fd->iomgr_object, -1, sizeof(fd->iomgr_object)); + fd->track_err = false; +} +#else +#define INVALIDATE_FD(fd) +#endif + +/* Uninitialize and add to the freelist */ +static void fd_destroy(void* arg, grpc_error* error) { + grpc_fd* fd = static_cast(arg); + grpc_iomgr_unregister_object(&fd->iomgr_object); + POLLABLE_UNREF(fd->pollable_obj, "fd_pollable"); + gpr_mu_destroy(&fd->pollable_mu); + gpr_mu_destroy(&fd->orphan_mu); + + fd->read_closure->DestroyEvent(); + fd->write_closure->DestroyEvent(); + fd->error_closure->DestroyEvent(); + + INVALIDATE_FD(fd); + + /* Add the fd to the freelist */ + gpr_mu_lock(&fd_freelist_mu); + fd->freelist_next = fd_freelist; + fd_freelist = fd; + gpr_mu_unlock(&fd_freelist_mu); +} + +#ifndef NDEBUG +static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file, + int line) { + if (grpc_trace_fd_refcount.enabled()) { + gpr_log(GPR_DEBUG, + "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%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) { +#endif + gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); + if (old == n) { + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(fd_destroy, fd, grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + } else { + GPR_ASSERT(old > n); + } +} + +static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } + +static void fd_global_shutdown(void) { + // TODO(guantaol): We don't have a reasonable explanation about this + // lock()/unlock() pattern. It can be a valid barrier if there is at most one + // pending lock() at this point. Otherwise, there is still a possibility of + // use-after-free race. Need to reason about the code and/or clean it up. + gpr_mu_lock(&fd_freelist_mu); + gpr_mu_unlock(&fd_freelist_mu); + while (fd_freelist != nullptr) { + grpc_fd* fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + gpr_free(fd); + } + gpr_mu_destroy(&fd_freelist_mu); +} + +static grpc_fd* fd_create(int fd, const char* name, bool track_err) { + grpc_fd* new_fd = nullptr; + + gpr_mu_lock(&fd_freelist_mu); + if (fd_freelist != nullptr) { + new_fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + } + gpr_mu_unlock(&fd_freelist_mu); + + if (new_fd == nullptr) { + new_fd = static_cast(gpr_malloc(sizeof(grpc_fd))); + new_fd->read_closure.Init(); + new_fd->write_closure.Init(); + new_fd->error_closure.Init(); + } + + new_fd->fd = fd; + new_fd->salt = gpr_atm_no_barrier_fetch_add(&g_fd_salt, 1); + gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); + gpr_mu_init(&new_fd->orphan_mu); + gpr_mu_init(&new_fd->pollable_mu); + new_fd->pollable_obj = nullptr; + new_fd->read_closure->InitEvent(); + new_fd->write_closure->InitEvent(); + new_fd->error_closure->InitEvent(); + new_fd->freelist_next = nullptr; + new_fd->on_done_closure = nullptr; + + char* fd_name; + gpr_asprintf(&fd_name, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); +#ifndef NDEBUG + if (grpc_trace_fd_refcount.enabled()) { + gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name); + } +#endif + gpr_free(fd_name); + + new_fd->track_err = track_err; + return new_fd; +} + +static int fd_wrapped_fd(grpc_fd* fd) { + int ret_fd = fd->fd; + return (gpr_atm_acq_load(&fd->refst) & 1) ? ret_fd : -1; +} + +static void fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd, + const char* reason) { + bool is_fd_closed = false; + + gpr_mu_lock(&fd->orphan_mu); + + // Get the fd->pollable_obj and set the owner_orphaned on that pollable to + // true so that the pollable will no longer access its owner_fd field. + gpr_mu_lock(&fd->pollable_mu); + pollable* pollable_obj = fd->pollable_obj; + gpr_mu_unlock(&fd->pollable_mu); + + if (pollable_obj) { + gpr_mu_lock(&pollable_obj->owner_orphan_mu); + pollable_obj->owner_orphaned = true; + } + + fd->on_done_closure = on_done; + + /* If release_fd is not NULL, we should be relinquishing control of the file + descriptor fd->fd (but we still own the grpc_fd structure). */ + if (release_fd != nullptr) { + *release_fd = fd->fd; + } else { + close(fd->fd); + is_fd_closed = true; + } + + // TODO(sreek): handle fd removal (where is_fd_closed=false) + if (!is_fd_closed) { + GRPC_FD_TRACE("epoll_fd %p (%d) was orphaned but not closed.", fd, fd->fd); + } + + /* Remove the active status but keep referenced. We want this grpc_fd struct + to be alive (and not added to freelist) until the end of this function */ + REF_BY(fd, 1, reason); + + GRPC_CLOSURE_SCHED(fd->on_done_closure, GRPC_ERROR_NONE); + + if (pollable_obj) { + gpr_mu_unlock(&pollable_obj->owner_orphan_mu); + } + + gpr_mu_unlock(&fd->orphan_mu); + + UNREF_BY(fd, 2, reason); /* Drop the reference */ +} + +static bool fd_is_shutdown(grpc_fd* fd) { + return fd->read_closure->IsShutdown(); +} + +/* Might be called multiple times */ +static void fd_shutdown(grpc_fd* fd, grpc_error* why) { + if (fd->read_closure->SetShutdown(GRPC_ERROR_REF(why))) { + if (shutdown(fd->fd, SHUT_RDWR)) { + if (errno != ENOTCONN) { + gpr_log(GPR_ERROR, "Error shutting down fd %d. errno: %d", + grpc_fd_wrapped_fd(fd), errno); + } + } + fd->write_closure->SetShutdown(GRPC_ERROR_REF(why)); + fd->error_closure->SetShutdown(GRPC_ERROR_REF(why)); + } + GRPC_ERROR_UNREF(why); +} + +static void fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) { + fd->read_closure->NotifyOn(closure); +} + +static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) { + fd->write_closure->NotifyOn(closure); +} + +static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) { + fd->error_closure->NotifyOn(closure); +} + +/******************************************************************************* + * Pollable Definitions + */ + +static grpc_error* pollable_create(pollable_type type, pollable** p) { + *p = nullptr; + + int epfd = epoll_create1(EPOLL_CLOEXEC); + if (epfd == -1) { + return GRPC_OS_ERROR(errno, "epoll_create1"); + } + GRPC_FD_TRACE("Pollable_create: created epfd: %d (type: %d)", epfd, type); + *p = static_cast(gpr_malloc(sizeof(**p))); + grpc_error* err = grpc_wakeup_fd_init(&(*p)->wakeup); + if (err != GRPC_ERROR_NONE) { + GRPC_FD_TRACE( + "Pollable_create: closed epfd: %d (type: %d). wakeupfd_init error", + epfd, type); + close(epfd); + gpr_free(*p); + *p = nullptr; + return err; + } + struct epoll_event ev; + ev.events = static_cast(EPOLLIN | EPOLLET); + ev.data.ptr = (void*)(1 | (intptr_t) & (*p)->wakeup); + if (epoll_ctl(epfd, EPOLL_CTL_ADD, (*p)->wakeup.read_fd, &ev) != 0) { + err = GRPC_OS_ERROR(errno, "epoll_ctl"); + GRPC_FD_TRACE( + "Pollable_create: closed epfd: %d (type: %d). epoll_ctl error", epfd, + type); + close(epfd); + grpc_wakeup_fd_destroy(&(*p)->wakeup); + gpr_free(*p); + *p = nullptr; + return err; + } + + (*p)->type = type; + gpr_ref_init(&(*p)->refs, 1); + gpr_mu_init(&(*p)->mu); + (*p)->epfd = epfd; + (*p)->owner_fd = nullptr; + gpr_mu_init(&(*p)->owner_orphan_mu); + (*p)->owner_orphaned = false; + (*p)->pollset_set = nullptr; + (*p)->next = (*p)->prev = *p; + (*p)->root_worker = nullptr; + (*p)->event_cursor = 0; + (*p)->event_count = 0; + (*p)->fd_cache_size = 0; + (*p)->fd_cache_counter = 0; + return GRPC_ERROR_NONE; +} + +#ifdef NDEBUG +static pollable* pollable_ref(pollable* p) { +#else +static pollable* pollable_ref(pollable* p, int line, const char* reason) { + if (grpc_trace_pollable_refcount.enabled()) { + int r = static_cast gpr_atm_no_barrier_load(&p->refs.count); + gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG, + "POLLABLE:%p ref %d->%d %s", p, r, r + 1, reason); + } +#endif + gpr_ref(&p->refs); + return p; +} + +#ifdef NDEBUG +static void pollable_unref(pollable* p) { +#else +static void pollable_unref(pollable* p, int line, const char* reason) { + if (p == nullptr) return; + if (grpc_trace_pollable_refcount.enabled()) { + int r = static_cast gpr_atm_no_barrier_load(&p->refs.count); + gpr_log(__FILE__, line, GPR_LOG_SEVERITY_DEBUG, + "POLLABLE:%p unref %d->%d %s", p, r, r - 1, reason); + } +#endif + if (p != nullptr && gpr_unref(&p->refs)) { + GRPC_FD_TRACE("pollable_unref: Closing epfd: %d", p->epfd); + close(p->epfd); + grpc_wakeup_fd_destroy(&p->wakeup); + gpr_mu_destroy(&p->owner_orphan_mu); + gpr_free(p); + } +} + +static grpc_error* pollable_add_fd(pollable* p, grpc_fd* fd) { + grpc_error* error = GRPC_ERROR_NONE; + static const char* err_desc = "pollable_add_fd"; + const int epfd = p->epfd; + gpr_mu_lock(&p->mu); + p->fd_cache_counter++; + + // Handle the case of overflow for our cache counter by + // reseting the recency-counter on all cache objects + if (p->fd_cache_counter == 0) { + for (int i = 0; i < p->fd_cache_size; i++) { + p->fd_cache[i].last_used = 0; + } + } + + int lru_idx = 0; + for (int i = 0; i < p->fd_cache_size; i++) { + if (p->fd_cache[i].fd == fd->fd && p->fd_cache[i].salt == fd->salt) { + GRPC_STATS_INC_POLLSET_FD_CACHE_HITS(); + p->fd_cache[i].last_used = p->fd_cache_counter; + gpr_mu_unlock(&p->mu); + return GRPC_ERROR_NONE; + } else if (p->fd_cache[i].last_used < p->fd_cache[lru_idx].last_used) { + lru_idx = i; + } + } + + // Add to cache + if (p->fd_cache_size < MAX_FDS_IN_CACHE) { + lru_idx = p->fd_cache_size; + p->fd_cache_size++; + } + p->fd_cache[lru_idx].fd = fd->fd; + p->fd_cache[lru_idx].salt = fd->salt; + p->fd_cache[lru_idx].last_used = p->fd_cache_counter; + gpr_mu_unlock(&p->mu); + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "add fd %p (%d) to pollable %p", fd, fd->fd, p); + } + + struct epoll_event ev_fd; + ev_fd.events = + static_cast(EPOLLET | EPOLLIN | EPOLLOUT | EPOLLEXCLUSIVE); + /* Use the second least significant bit of ev_fd.data.ptr to store track_err + * to avoid synchronization issues when accessing it after receiving an event. + * Accessing fd would be a data race there because the fd might have been + * returned to the free list at that point. */ + ev_fd.data.ptr = reinterpret_cast(reinterpret_cast(fd) | + (fd->track_err ? 2 : 0)); + GRPC_STATS_INC_SYSCALL_EPOLL_CTL(); + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd->fd, &ev_fd) != 0) { + switch (errno) { + case EEXIST: + break; + default: + append_error(&error, GRPC_OS_ERROR(errno, "epoll_ctl"), err_desc); + } + } + + return error; +} + +/******************************************************************************* + * Pollset Definitions + */ + +GPR_TLS_DECL(g_current_thread_pollset); +GPR_TLS_DECL(g_current_thread_worker); + +/* Global state management */ +static grpc_error* pollset_global_init(void) { + gpr_tls_init(&g_current_thread_pollset); + gpr_tls_init(&g_current_thread_worker); + return pollable_create(PO_EMPTY, &g_empty_pollable); +} + +static void pollset_global_shutdown(void) { + POLLABLE_UNREF(g_empty_pollable, "g_empty_pollable"); + gpr_tls_destroy(&g_current_thread_pollset); + gpr_tls_destroy(&g_current_thread_worker); +} + +/* pollset->mu must be held while calling this function */ +static void pollset_maybe_finish_shutdown(grpc_pollset* pollset) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, + "PS:%p (pollable:%p) maybe_finish_shutdown sc=%p (target:!NULL) " + "rw=%p (target:NULL) cpsc=%d (target:0)", + pollset, pollset->active_pollable, pollset->shutdown_closure, + pollset->root_worker, pollset->containing_pollset_set_count); + } + if (pollset->shutdown_closure != nullptr && pollset->root_worker == nullptr && + pollset->containing_pollset_set_count == 0) { + GPR_TIMER_MARK("pollset_finish_shutdown", 0); + GRPC_CLOSURE_SCHED(pollset->shutdown_closure, GRPC_ERROR_NONE); + pollset->shutdown_closure = nullptr; + pollset->already_shutdown = true; + } +} + +/* pollset->mu must be held before calling this function, + * pollset->active_pollable->mu & specific_worker->pollable_obj->mu must not be + * held */ +static grpc_error* kick_one_worker(grpc_pollset_worker* specific_worker) { + GPR_TIMER_SCOPE("kick_one_worker", 0); + pollable* p = specific_worker->pollable_obj; + grpc_core::MutexLock lock(&p->mu); + GPR_ASSERT(specific_worker != nullptr); + if (specific_worker->kicked) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p kicked_specific_but_already_kicked", p); + } + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(); + return GRPC_ERROR_NONE; + } + if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p kicked_specific_but_awake", p); + } + GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(); + specific_worker->kicked = true; + return GRPC_ERROR_NONE; + } + if (specific_worker == p->root_worker) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p kicked_specific_via_wakeup_fd", p); + } + specific_worker->kicked = true; + grpc_error* error = grpc_wakeup_fd_wakeup(&p->wakeup); + return error; + } + if (specific_worker->initialized_cv) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p kicked_specific_via_cv", p); + } + specific_worker->kicked = true; + gpr_cv_signal(&specific_worker->cv); + return GRPC_ERROR_NONE; + } + // we can get here during end_worker after removing specific_worker from the + // pollable list but before removing it from the pollset list + return GRPC_ERROR_NONE; +} + +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + GPR_TIMER_SCOPE("pollset_kick", 0); + GRPC_STATS_INC_POLLSET_KICK(); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, + "PS:%p kick %p tls_pollset=%p tls_worker=%p pollset.root_worker=%p", + pollset, specific_worker, + (void*)gpr_tls_get(&g_current_thread_pollset), + (void*)gpr_tls_get(&g_current_thread_worker), pollset->root_worker); + } + if (specific_worker == nullptr) { + if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) { + if (pollset->root_worker == nullptr) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p kicked_any_without_poller", pollset); + } + GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(); + pollset->kicked_without_poller = true; + return GRPC_ERROR_NONE; + } else { + // We've been asked to kick a poller, but we haven't been told which one + // ... any will do + // We look at the pollset worker list because: + // 1. the pollable list may include workers from other pollers, so we'd + // need to do an O(N) search + // 2. we'd additionally need to take the pollable lock, which we've so + // far avoided + // Now, we would prefer to wake a poller in cv_wait, and not in + // epoll_wait (since the latter would imply the need to do an additional + // wakeup) + // We know that if a worker is at the root of a pollable, it's (likely) + // also the root of a pollset, and we know that if a worker is NOT at + // the root of a pollset, it's (likely) not at the root of a pollable, + // so we take our chances and choose the SECOND worker enqueued against + // the pollset as a worker that's likely to be in cv_wait + return kick_one_worker( + pollset->root_worker->links[PWLINK_POLLSET].next); + } + } else { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p kicked_any_but_awake", pollset); + } + GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(); + return GRPC_ERROR_NONE; + } + } else { + return kick_one_worker(specific_worker); + } +} + +static grpc_error* pollset_kick_all(grpc_pollset* pollset) { + GPR_TIMER_SCOPE("pollset_kick_all", 0); + grpc_error* error = GRPC_ERROR_NONE; + const char* err_desc = "pollset_kick_all"; + grpc_pollset_worker* w = pollset->root_worker; + if (w != nullptr) { + do { + GRPC_STATS_INC_POLLSET_KICK(); + append_error(&error, kick_one_worker(w), err_desc); + w = w->links[PWLINK_POLLSET].next; + } while (w != pollset->root_worker); + } + return error; +} + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + gpr_mu_init(&pollset->mu); + gpr_atm_no_barrier_store(&pollset->worker_count, 0); + pollset->active_pollable = POLLABLE_REF(g_empty_pollable, "pollset"); + pollset->kicked_without_poller = false; + pollset->shutdown_closure = nullptr; + pollset->already_shutdown = false; + pollset->root_worker = nullptr; + pollset->containing_pollset_set_count = 0; + *mu = &pollset->mu; +} + +static int poll_deadline_to_millis_timeout(grpc_millis millis) { + if (millis == GRPC_MILLIS_INF_FUTURE) return -1; + grpc_millis delta = millis - grpc_core::ExecCtx::Get()->Now(); + if (delta > INT_MAX) + return INT_MAX; + else if (delta < 0) + return 0; + else + return static_cast(delta); +} + +static void fd_become_readable(grpc_fd* fd) { fd->read_closure->SetReady(); } + +static void fd_become_writable(grpc_fd* fd) { fd->write_closure->SetReady(); } + +static void fd_has_errors(grpc_fd* fd) { fd->error_closure->SetReady(); } + +/* Get the pollable_obj attached to this fd. If none is attached, create a new + * pollable object (of type PO_FD), attach it to the fd and return it + * + * Note that if a pollable object is already attached to the fd, it may be of + * either PO_FD or PO_MULTI type */ +static grpc_error* get_fd_pollable(grpc_fd* fd, pollable** p) { + gpr_mu_lock(&fd->pollable_mu); + grpc_error* error = GRPC_ERROR_NONE; + static const char* err_desc = "get_fd_pollable"; + if (fd->pollable_obj == nullptr) { + if (append_error(&error, pollable_create(PO_FD, &fd->pollable_obj), + err_desc)) { + fd->pollable_obj->owner_fd = fd; + if (!append_error(&error, pollable_add_fd(fd->pollable_obj, fd), + err_desc)) { + POLLABLE_UNREF(fd->pollable_obj, "fd_pollable"); + fd->pollable_obj = nullptr; + } + } + } + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(fd->pollable_obj != nullptr); + *p = POLLABLE_REF(fd->pollable_obj, "pollset"); + } else { + GPR_ASSERT(fd->pollable_obj == nullptr); + *p = nullptr; + } + gpr_mu_unlock(&fd->pollable_mu); + return error; +} + +/* pollset->po.mu lock must be held by the caller before calling this */ +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + GPR_TIMER_SCOPE("pollset_shutdown", 0); + GPR_ASSERT(pollset->shutdown_closure == nullptr); + pollset->shutdown_closure = closure; + GRPC_LOG_IF_ERROR("pollset_shutdown", pollset_kick_all(pollset)); + pollset_maybe_finish_shutdown(pollset); +} + +static grpc_error* pollable_process_events(grpc_pollset* pollset, + pollable* pollable_obj, bool drain) { + GPR_TIMER_SCOPE("pollable_process_events", 0); + static const char* err_desc = "pollset_process_events"; + // Use a simple heuristic to determine how many fd events to process + // per loop iteration. (events/workers) + int handle_count = 1; + int worker_count = gpr_atm_no_barrier_load(&pollset->worker_count); + GPR_ASSERT(worker_count > 0); + handle_count = + (pollable_obj->event_count - pollable_obj->event_cursor) / worker_count; + if (handle_count == 0) { + handle_count = 1; + } else if (handle_count > MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) { + handle_count = MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL; + } + grpc_error* error = GRPC_ERROR_NONE; + for (int i = 0; (drain || i < handle_count) && + pollable_obj->event_cursor != pollable_obj->event_count; + i++) { + int n = pollable_obj->event_cursor++; + struct epoll_event* ev = &pollable_obj->events[n]; + void* data_ptr = ev->data.ptr; + if (1 & (intptr_t)data_ptr) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p got pollset_wakeup %p", pollset, data_ptr); + } + append_error(&error, + grpc_wakeup_fd_consume_wakeup( + (grpc_wakeup_fd*)((~static_cast(1)) & + (intptr_t)data_ptr)), + err_desc); + } else { + grpc_fd* fd = + reinterpret_cast(reinterpret_cast(data_ptr) & ~2); + bool track_err = reinterpret_cast(data_ptr) & 2; + bool cancel = (ev->events & EPOLLHUP) != 0; + bool error = (ev->events & EPOLLERR) != 0; + bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0; + bool write_ev = (ev->events & EPOLLOUT) != 0; + bool err_fallback = error && !track_err; + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, + "PS:%p got fd %p: cancel=%d read=%d " + "write=%d", + pollset, fd, cancel, read_ev, write_ev); + } + if (error && !err_fallback) { + fd_has_errors(fd); + } + if (read_ev || cancel || err_fallback) { + fd_become_readable(fd); + } + if (write_ev || cancel || err_fallback) { + fd_become_writable(fd); + } + } + } + + return error; +} + +/* pollset_shutdown is guaranteed to be called before pollset_destroy. */ +static void pollset_destroy(grpc_pollset* pollset) { + POLLABLE_UNREF(pollset->active_pollable, "pollset"); + pollset->active_pollable = nullptr; + gpr_mu_destroy(&pollset->mu); +} + +static grpc_error* pollable_epoll(pollable* p, grpc_millis deadline) { + GPR_TIMER_SCOPE("pollable_epoll", 0); + int timeout = poll_deadline_to_millis_timeout(deadline); + + if (grpc_polling_trace.enabled()) { + char* desc = pollable_desc(p); + gpr_log(GPR_INFO, "POLLABLE:%p[%s] poll for %dms", p, desc, timeout); + gpr_free(desc); + } + + if (timeout != 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; + } + int r; + do { + GRPC_STATS_INC_SYSCALL_POLL(); + r = epoll_wait(p->epfd, p->events, MAX_EPOLL_EVENTS, timeout); + } while (r < 0 && errno == EINTR); + if (timeout != 0) { + GRPC_SCHEDULING_END_BLOCKING_REGION; + } + + if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait"); + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "POLLABLE:%p got %d events", p, r); + } + + p->event_cursor = 0; + p->event_count = r; + + return GRPC_ERROR_NONE; +} + +/* Return true if first in list */ +static bool worker_insert(grpc_pollset_worker** root_worker, + grpc_pollset_worker* worker, pwlinks link) { + if (*root_worker == nullptr) { + *root_worker = worker; + worker->links[link].next = worker->links[link].prev = worker; + return true; + } else { + worker->links[link].next = *root_worker; + worker->links[link].prev = worker->links[link].next->links[link].prev; + worker->links[link].next->links[link].prev = worker; + worker->links[link].prev->links[link].next = worker; + return false; + } +} + +/* returns the new root IFF the root changed */ +typedef enum { WRR_NEW_ROOT, WRR_EMPTIED, WRR_REMOVED } worker_remove_result; + +static worker_remove_result worker_remove(grpc_pollset_worker** root_worker, + grpc_pollset_worker* worker, + pwlinks link) { + if (worker == *root_worker) { + if (worker == worker->links[link].next) { + *root_worker = nullptr; + return WRR_EMPTIED; + } else { + *root_worker = worker->links[link].next; + worker->links[link].prev->links[link].next = worker->links[link].next; + worker->links[link].next->links[link].prev = worker->links[link].prev; + return WRR_NEW_ROOT; + } + } else { + worker->links[link].prev->links[link].next = worker->links[link].next; + worker->links[link].next->links[link].prev = worker->links[link].prev; + return WRR_REMOVED; + } +} + +/* Return true if this thread should poll */ +static bool begin_worker(grpc_pollset* pollset, grpc_pollset_worker* worker, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GPR_TIMER_SCOPE("begin_worker", 0); + bool do_poll = + (pollset->shutdown_closure == nullptr && !pollset->already_shutdown); + gpr_atm_no_barrier_fetch_add(&pollset->worker_count, 1); + if (worker_hdl != nullptr) *worker_hdl = worker; + worker->initialized_cv = false; + worker->kicked = false; + worker->pollset = pollset; + worker->pollable_obj = + POLLABLE_REF(pollset->active_pollable, "pollset_worker"); + worker_insert(&pollset->root_worker, worker, PWLINK_POLLSET); + gpr_mu_lock(&worker->pollable_obj->mu); + if (!worker_insert(&worker->pollable_obj->root_worker, worker, + PWLINK_POLLABLE)) { + worker->initialized_cv = true; + gpr_cv_init(&worker->cv); + gpr_mu_unlock(&pollset->mu); + if (grpc_polling_trace.enabled() && + worker->pollable_obj->root_worker != worker) { + gpr_log(GPR_INFO, "PS:%p wait %p w=%p for %dms", pollset, + worker->pollable_obj, worker, + poll_deadline_to_millis_timeout(deadline)); + } + while (do_poll && worker->pollable_obj->root_worker != worker) { + if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->mu, + grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p timeout_wait %p w=%p", pollset, + worker->pollable_obj, worker); + } + do_poll = false; + } else if (worker->kicked) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PS:%p wakeup %p w=%p", pollset, + worker->pollable_obj, worker); + } + do_poll = false; + } else if (grpc_polling_trace.enabled() && + worker->pollable_obj->root_worker != worker) { + gpr_log(GPR_INFO, "PS:%p spurious_wakeup %p w=%p", pollset, + worker->pollable_obj, worker); + } + } + grpc_core::ExecCtx::Get()->InvalidateNow(); + } else { + gpr_mu_unlock(&pollset->mu); + } + gpr_mu_unlock(&worker->pollable_obj->mu); + + return do_poll; +} + +static void end_worker(grpc_pollset* pollset, grpc_pollset_worker* worker, + grpc_pollset_worker** worker_hdl) { + GPR_TIMER_SCOPE("end_worker", 0); + gpr_mu_lock(&pollset->mu); + gpr_mu_lock(&worker->pollable_obj->mu); + switch (worker_remove(&worker->pollable_obj->root_worker, worker, + PWLINK_POLLABLE)) { + case WRR_NEW_ROOT: { + // wakeup new poller + grpc_pollset_worker* new_root = worker->pollable_obj->root_worker; + GPR_ASSERT(new_root->initialized_cv); + gpr_cv_signal(&new_root->cv); + break; + } + case WRR_EMPTIED: + if (pollset->active_pollable != worker->pollable_obj) { + // pollable no longer being polled: flush events + pollable_process_events(pollset, worker->pollable_obj, true); + } + break; + case WRR_REMOVED: + break; + } + gpr_mu_unlock(&worker->pollable_obj->mu); + POLLABLE_UNREF(worker->pollable_obj, "pollset_worker"); + if (worker_remove(&pollset->root_worker, worker, PWLINK_POLLSET) == + WRR_EMPTIED) { + pollset_maybe_finish_shutdown(pollset); + } + if (worker->initialized_cv) { + gpr_cv_destroy(&worker->cv); + } + gpr_atm_no_barrier_fetch_add(&pollset->worker_count, -1); +} + +#ifndef NDEBUG +static long gettid(void) { return syscall(__NR_gettid); } +#endif + +/* pollset->mu lock must be held by the caller before calling this. + The function pollset_work() may temporarily release the lock (pollset->po.mu) + during the course of its execution but it will always re-acquire the lock and + ensure that it is held by the time the function returns */ +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GPR_TIMER_SCOPE("pollset_work", 0); +#ifdef GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP + grpc_pollset_worker* worker = + (grpc_pollset_worker*)gpr_malloc(sizeof(*worker)); +#define WORKER_PTR (worker) +#else + grpc_pollset_worker worker; +#define WORKER_PTR (&worker) +#endif +#ifndef NDEBUG + WORKER_PTR->originator = gettid(); +#endif + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, + "PS:%p work hdl=%p worker=%p now=%" PRId64 " deadline=%" PRId64 + " kwp=%d pollable=%p", + pollset, worker_hdl, WORKER_PTR, grpc_core::ExecCtx::Get()->Now(), + deadline, pollset->kicked_without_poller, pollset->active_pollable); + } + static const char* err_desc = "pollset_work"; + grpc_error* error = GRPC_ERROR_NONE; + if (pollset->kicked_without_poller) { + pollset->kicked_without_poller = false; + } else { + if (begin_worker(pollset, WORKER_PTR, worker_hdl, deadline)) { + gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); + gpr_tls_set(&g_current_thread_worker, (intptr_t)WORKER_PTR); + if (WORKER_PTR->pollable_obj->event_cursor == + WORKER_PTR->pollable_obj->event_count) { + append_error(&error, pollable_epoll(WORKER_PTR->pollable_obj, deadline), + err_desc); + } + append_error( + &error, + pollable_process_events(pollset, WORKER_PTR->pollable_obj, false), + err_desc); + grpc_core::ExecCtx::Get()->Flush(); + gpr_tls_set(&g_current_thread_pollset, 0); + gpr_tls_set(&g_current_thread_worker, 0); + } + end_worker(pollset, WORKER_PTR, worker_hdl); + } +#ifdef GRPC_EPOLLEX_CREATE_WORKERS_ON_HEAP + gpr_free(worker); +#endif +#undef WORKER_PTR + return error; +} + +static grpc_error* pollset_transition_pollable_from_empty_to_fd_locked( + grpc_pollset* pollset, grpc_fd* fd) { + static const char* err_desc = "pollset_transition_pollable_from_empty_to_fd"; + grpc_error* error = GRPC_ERROR_NONE; + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, + "PS:%p add fd %p (%d); transition pollable from empty to fd", + pollset, fd, fd->fd); + } + append_error(&error, pollset_kick_all(pollset), err_desc); + POLLABLE_UNREF(pollset->active_pollable, "pollset"); + append_error(&error, get_fd_pollable(fd, &pollset->active_pollable), + err_desc); + return error; +} + +static grpc_error* pollset_transition_pollable_from_fd_to_multi_locked( + grpc_pollset* pollset, grpc_fd* and_add_fd) { + static const char* err_desc = "pollset_transition_pollable_from_fd_to_multi"; + grpc_error* error = GRPC_ERROR_NONE; + if (grpc_polling_trace.enabled()) { + gpr_log( + GPR_INFO, + "PS:%p add fd %p (%d); transition pollable from fd %p to multipoller", + pollset, and_add_fd, and_add_fd ? and_add_fd->fd : -1, + pollset->active_pollable->owner_fd); + } + append_error(&error, pollset_kick_all(pollset), err_desc); + grpc_fd* initial_fd = pollset->active_pollable->owner_fd; + POLLABLE_UNREF(pollset->active_pollable, "pollset"); + pollset->active_pollable = nullptr; + if (append_error(&error, pollable_create(PO_MULTI, &pollset->active_pollable), + err_desc)) { + append_error(&error, pollable_add_fd(pollset->active_pollable, initial_fd), + err_desc); + if (and_add_fd != nullptr) { + append_error(&error, + pollable_add_fd(pollset->active_pollable, and_add_fd), + err_desc); + } + } + return error; +} + +/* expects pollsets locked, flag whether fd is locked or not */ +static grpc_error* pollset_add_fd_locked(grpc_pollset* pollset, grpc_fd* fd) { + grpc_error* error = GRPC_ERROR_NONE; + pollable* po_at_start = + POLLABLE_REF(pollset->active_pollable, "pollset_add_fd"); + switch (pollset->active_pollable->type) { + case PO_EMPTY: + /* empty pollable --> single fd pollable */ + error = pollset_transition_pollable_from_empty_to_fd_locked(pollset, fd); + break; + case PO_FD: + gpr_mu_lock(&po_at_start->owner_orphan_mu); + if (po_at_start->owner_orphaned) { + error = + pollset_transition_pollable_from_empty_to_fd_locked(pollset, fd); + } else { + /* fd --> multipoller */ + error = + pollset_transition_pollable_from_fd_to_multi_locked(pollset, fd); + } + gpr_mu_unlock(&po_at_start->owner_orphan_mu); + break; + case PO_MULTI: + error = pollable_add_fd(pollset->active_pollable, fd); + break; + } + if (error != GRPC_ERROR_NONE) { + POLLABLE_UNREF(pollset->active_pollable, "pollset"); + pollset->active_pollable = po_at_start; + } else { + POLLABLE_UNREF(po_at_start, "pollset_add_fd"); + } + return error; +} + +static grpc_error* pollset_as_multipollable_locked(grpc_pollset* pollset, + pollable** pollable_obj) { + grpc_error* error = GRPC_ERROR_NONE; + pollable* po_at_start = + POLLABLE_REF(pollset->active_pollable, "pollset_as_multipollable"); + switch (pollset->active_pollable->type) { + case PO_EMPTY: + POLLABLE_UNREF(pollset->active_pollable, "pollset"); + error = pollable_create(PO_MULTI, &pollset->active_pollable); + /* Any workers currently polling on this pollset must now be woked up so + * that they can pick up the new active_pollable */ + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, + "PS:%p active pollable transition from empty to multi", + pollset); + } + static const char* err_desc = + "pollset_as_multipollable_locked: empty -> multi"; + append_error(&error, pollset_kick_all(pollset), err_desc); + break; + case PO_FD: + gpr_mu_lock(&po_at_start->owner_orphan_mu); + if (po_at_start->owner_orphaned) { + // Unlock before Unref'ing the pollable + gpr_mu_unlock(&po_at_start->owner_orphan_mu); + POLLABLE_UNREF(pollset->active_pollable, "pollset"); + error = pollable_create(PO_MULTI, &pollset->active_pollable); + } else { + error = pollset_transition_pollable_from_fd_to_multi_locked(pollset, + nullptr); + gpr_mu_unlock(&po_at_start->owner_orphan_mu); + } + break; + case PO_MULTI: + break; + } + if (error != GRPC_ERROR_NONE) { + POLLABLE_UNREF(pollset->active_pollable, "pollset"); + pollset->active_pollable = po_at_start; + *pollable_obj = nullptr; + } else { + *pollable_obj = POLLABLE_REF(pollset->active_pollable, "pollset_set"); + POLLABLE_UNREF(po_at_start, "pollset_as_multipollable"); + } + return error; +} + +static void pollset_add_fd(grpc_pollset* pollset, grpc_fd* fd) { + GPR_TIMER_SCOPE("pollset_add_fd", 0); + gpr_mu_lock(&pollset->mu); + grpc_error* error = pollset_add_fd_locked(pollset, fd); + gpr_mu_unlock(&pollset->mu); + GRPC_LOG_IF_ERROR("pollset_add_fd", error); +} + +/******************************************************************************* + * Pollset-set Definitions + */ + +static grpc_pollset_set* pss_lock_adam(grpc_pollset_set* pss) { + gpr_mu_lock(&pss->mu); + while (pss->parent != nullptr) { + gpr_mu_unlock(&pss->mu); + pss = pss->parent; + gpr_mu_lock(&pss->mu); + } + return pss; +} + +static grpc_pollset_set* pollset_set_create(void) { + grpc_pollset_set* pss = + static_cast(gpr_zalloc(sizeof(*pss))); + gpr_mu_init(&pss->mu); + gpr_ref_init(&pss->refs, 1); + return pss; +} + +static void pollset_set_unref(grpc_pollset_set* pss) { + if (pss == nullptr) return; + if (!gpr_unref(&pss->refs)) return; + pollset_set_unref(pss->parent); + gpr_mu_destroy(&pss->mu); + for (size_t i = 0; i < pss->pollset_count; i++) { + gpr_mu_lock(&pss->pollsets[i]->mu); + if (0 == --pss->pollsets[i]->containing_pollset_set_count) { + pollset_maybe_finish_shutdown(pss->pollsets[i]); + } + gpr_mu_unlock(&pss->pollsets[i]->mu); + } + for (size_t i = 0; i < pss->fd_count; i++) { + UNREF_BY(pss->fds[i], 2, "pollset_set"); + } + gpr_free(pss->pollsets); + gpr_free(pss->fds); + gpr_free(pss); +} + +static void pollset_set_add_fd(grpc_pollset_set* pss, grpc_fd* fd) { + GPR_TIMER_SCOPE("pollset_set_add_fd", 0); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PSS:%p: add fd %p (%d)", pss, fd, fd->fd); + } + grpc_error* error = GRPC_ERROR_NONE; + static const char* err_desc = "pollset_set_add_fd"; + pss = pss_lock_adam(pss); + for (size_t i = 0; i < pss->pollset_count; i++) { + append_error(&error, pollable_add_fd(pss->pollsets[i]->active_pollable, fd), + err_desc); + } + if (pss->fd_count == pss->fd_capacity) { + pss->fd_capacity = GPR_MAX(pss->fd_capacity * 2, 8); + pss->fds = static_cast( + gpr_realloc(pss->fds, pss->fd_capacity * sizeof(*pss->fds))); + } + REF_BY(fd, 2, "pollset_set"); + pss->fds[pss->fd_count++] = fd; + gpr_mu_unlock(&pss->mu); + + GRPC_LOG_IF_ERROR(err_desc, error); +} + +static void pollset_set_del_fd(grpc_pollset_set* pss, grpc_fd* fd) { + GPR_TIMER_SCOPE("pollset_set_del_fd", 0); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PSS:%p: del fd %p", pss, fd); + } + pss = pss_lock_adam(pss); + size_t i; + for (i = 0; i < pss->fd_count; i++) { + if (pss->fds[i] == fd) { + UNREF_BY(fd, 2, "pollset_set"); + break; + } + } + GPR_ASSERT(i != pss->fd_count); + for (; i < pss->fd_count - 1; i++) { + pss->fds[i] = pss->fds[i + 1]; + } + pss->fd_count--; + gpr_mu_unlock(&pss->mu); +} + +static void pollset_set_del_pollset(grpc_pollset_set* pss, grpc_pollset* ps) { + GPR_TIMER_SCOPE("pollset_set_del_pollset", 0); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PSS:%p: del pollset %p", pss, ps); + } + pss = pss_lock_adam(pss); + size_t i; + for (i = 0; i < pss->pollset_count; i++) { + if (pss->pollsets[i] == ps) { + break; + } + } + GPR_ASSERT(i != pss->pollset_count); + for (; i < pss->pollset_count - 1; i++) { + pss->pollsets[i] = pss->pollsets[i + 1]; + } + pss->pollset_count--; + gpr_mu_unlock(&pss->mu); + gpr_mu_lock(&ps->mu); + if (0 == --ps->containing_pollset_set_count) { + pollset_maybe_finish_shutdown(ps); + } + gpr_mu_unlock(&ps->mu); +} + +// add all fds to pollables, and output a new array of unorphaned out_fds +// assumes pollsets are multipollable +static grpc_error* add_fds_to_pollsets(grpc_fd** fds, size_t fd_count, + grpc_pollset** pollsets, + size_t pollset_count, + const char* err_desc, grpc_fd** out_fds, + size_t* out_fd_count) { + GPR_TIMER_SCOPE("add_fds_to_pollsets", 0); + grpc_error* error = GRPC_ERROR_NONE; + for (size_t i = 0; i < fd_count; i++) { + gpr_mu_lock(&fds[i]->orphan_mu); + if ((gpr_atm_no_barrier_load(&fds[i]->refst) & 1) == 0) { + gpr_mu_unlock(&fds[i]->orphan_mu); + UNREF_BY(fds[i], 2, "pollset_set"); + } else { + for (size_t j = 0; j < pollset_count; j++) { + append_error(&error, + pollable_add_fd(pollsets[j]->active_pollable, fds[i]), + err_desc); + } + gpr_mu_unlock(&fds[i]->orphan_mu); + out_fds[(*out_fd_count)++] = fds[i]; + } + } + return error; +} + +static void pollset_set_add_pollset(grpc_pollset_set* pss, grpc_pollset* ps) { + GPR_TIMER_SCOPE("pollset_set_add_pollset", 0); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PSS:%p: add pollset %p", pss, ps); + } + grpc_error* error = GRPC_ERROR_NONE; + static const char* err_desc = "pollset_set_add_pollset"; + pollable* pollable_obj = nullptr; + gpr_mu_lock(&ps->mu); + if (!GRPC_LOG_IF_ERROR(err_desc, + pollset_as_multipollable_locked(ps, &pollable_obj))) { + GPR_ASSERT(pollable_obj == nullptr); + gpr_mu_unlock(&ps->mu); + return; + } + ps->containing_pollset_set_count++; + gpr_mu_unlock(&ps->mu); + pss = pss_lock_adam(pss); + size_t initial_fd_count = pss->fd_count; + pss->fd_count = 0; + append_error(&error, + add_fds_to_pollsets(pss->fds, initial_fd_count, &ps, 1, err_desc, + pss->fds, &pss->fd_count), + err_desc); + if (pss->pollset_count == pss->pollset_capacity) { + pss->pollset_capacity = GPR_MAX(pss->pollset_capacity * 2, 8); + pss->pollsets = static_cast(gpr_realloc( + pss->pollsets, pss->pollset_capacity * sizeof(*pss->pollsets))); + } + pss->pollsets[pss->pollset_count++] = ps; + gpr_mu_unlock(&pss->mu); + POLLABLE_UNREF(pollable_obj, "pollset_set"); + + GRPC_LOG_IF_ERROR(err_desc, error); +} + +static void pollset_set_add_pollset_set(grpc_pollset_set* a, + grpc_pollset_set* b) { + GPR_TIMER_SCOPE("pollset_set_add_pollset_set", 0); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PSS: merge (%p, %p)", a, b); + } + grpc_error* error = GRPC_ERROR_NONE; + static const char* err_desc = "pollset_set_add_fd"; + for (;;) { + if (a == b) { + // pollset ancestors are the same: nothing to do + return; + } + if (a > b) { + GPR_SWAP(grpc_pollset_set*, a, b); + } + gpr_mu* a_mu = &a->mu; + gpr_mu* b_mu = &b->mu; + gpr_mu_lock(a_mu); + gpr_mu_lock(b_mu); + if (a->parent != nullptr) { + a = a->parent; + } else if (b->parent != nullptr) { + b = b->parent; + } else { + break; // exit loop, both pollsets locked + } + gpr_mu_unlock(a_mu); + gpr_mu_unlock(b_mu); + } + // try to do the least copying possible + // TODO(sreek): there's probably a better heuristic here + const size_t a_size = a->fd_count + a->pollset_count; + const size_t b_size = b->fd_count + b->pollset_count; + if (b_size > a_size) { + GPR_SWAP(grpc_pollset_set*, a, b); + } + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "PSS: parent %p to %p", b, a); + } + gpr_ref(&a->refs); + b->parent = a; + if (a->fd_capacity < a->fd_count + b->fd_count) { + a->fd_capacity = GPR_MAX(2 * a->fd_capacity, a->fd_count + b->fd_count); + a->fds = static_cast( + gpr_realloc(a->fds, a->fd_capacity * sizeof(*a->fds))); + } + size_t initial_a_fd_count = a->fd_count; + a->fd_count = 0; + append_error( + &error, + add_fds_to_pollsets(a->fds, initial_a_fd_count, b->pollsets, + b->pollset_count, "merge_a2b", a->fds, &a->fd_count), + err_desc); + append_error( + &error, + add_fds_to_pollsets(b->fds, b->fd_count, a->pollsets, a->pollset_count, + "merge_b2a", a->fds, &a->fd_count), + err_desc); + if (a->pollset_capacity < a->pollset_count + b->pollset_count) { + a->pollset_capacity = + GPR_MAX(2 * a->pollset_capacity, a->pollset_count + b->pollset_count); + a->pollsets = static_cast( + gpr_realloc(a->pollsets, a->pollset_capacity * sizeof(*a->pollsets))); + } + if (b->pollset_count > 0) { + memcpy(a->pollsets + a->pollset_count, b->pollsets, + b->pollset_count * sizeof(*b->pollsets)); + } + a->pollset_count += b->pollset_count; + gpr_free(b->fds); + gpr_free(b->pollsets); + b->fds = nullptr; + b->pollsets = nullptr; + b->fd_count = b->fd_capacity = b->pollset_count = b->pollset_capacity = 0; + gpr_mu_unlock(&a->mu); + gpr_mu_unlock(&b->mu); +} + +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +/******************************************************************************* + * Event engine binding + */ + +static void shutdown_background_closure(void) {} + +static void shutdown_engine(void) { + fd_global_shutdown(); + pollset_global_shutdown(); +} + +static const grpc_event_engine_vtable vtable = { + sizeof(grpc_pollset), + true, + false, + + fd_create, + fd_wrapped_fd, + fd_orphan, + fd_shutdown, + fd_notify_on_read, + fd_notify_on_write, + fd_notify_on_error, + fd_become_readable, + fd_become_writable, + fd_has_errors, + fd_is_shutdown, + + pollset_init, + pollset_shutdown, + pollset_destroy, + pollset_work, + pollset_kick, + pollset_add_fd, + + pollset_set_create, + pollset_set_unref, // destroy ==> unref 1 public ref + pollset_set_add_pollset, + pollset_set_del_pollset, + pollset_set_add_pollset_set, + pollset_set_del_pollset_set, + pollset_set_add_fd, + pollset_set_del_fd, + + shutdown_background_closure, + shutdown_engine, +}; + +const grpc_event_engine_vtable* grpc_init_epollex_linux( + bool explicitly_requested) { + if (!grpc_has_wakeup_fd()) { + gpr_log(GPR_ERROR, "Skipping epollex because of no wakeup fd."); + return nullptr; + } + + if (!grpc_is_epollexclusive_available()) { + gpr_log(GPR_INFO, "Skipping epollex because it is not supported."); + return nullptr; + } + + fd_global_init(); + + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + pollset_global_shutdown(); + fd_global_shutdown(); + return nullptr; + } + + return &vtable; +} + +#else /* defined(GRPC_LINUX_EPOLL_CREATE1) */ +#if defined(GRPC_POSIX_SOCKET_EV_EPOLLEX) +#include "src/core/lib/iomgr/ev_epollex_linux.h" +/* If GRPC_LINUX_EPOLL_CREATE1 is not defined, it means + epoll_create1 is not available. Return NULL */ +const grpc_event_engine_vtable* grpc_init_epollex_linux( + bool explicitly_requested) { + return nullptr; +} +#endif /* defined(GRPC_POSIX_SOCKET_EV_EPOLLEX) */ + +#endif /* !defined(GRPC_LINUX_EPOLL_CREATE1) */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.h b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.h new file mode 100644 index 00000000000000..e70ba72a7db1cb --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_epollex_linux.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H +#define GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H + +#include + +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/port.h" + +const grpc_event_engine_vtable* grpc_init_epollex_linux( + bool explicitly_requested); + +#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.cc b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.cc new file mode 100644 index 00000000000000..67cbfbbd021467 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.cc @@ -0,0 +1,1883 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_EV_POLL + +#include "src/core/lib/iomgr/ev_poll_posix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/murmur_hash.h" +#include "src/core/lib/gpr/tls.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/block_annotate.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/wakeup_fd_cv.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" + +#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1) + +/******************************************************************************* + * FD declarations + */ +typedef struct grpc_fd_watcher { + struct grpc_fd_watcher* next; + struct grpc_fd_watcher* prev; + grpc_pollset* pollset; + grpc_pollset_worker* worker; + grpc_fd* fd; +} grpc_fd_watcher; + +typedef struct grpc_cached_wakeup_fd grpc_cached_wakeup_fd; + +/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */ +struct grpc_fork_fd_list { + /* Only one of fd or cached_wakeup_fd will be set. The unused field will be + set to nullptr. */ + grpc_fd* fd; + grpc_cached_wakeup_fd* cached_wakeup_fd; + + grpc_fork_fd_list* next; + grpc_fork_fd_list* prev; +}; + +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 mu; + int shutdown; + int closed; + int released; + gpr_atm pollhup; + grpc_error* shutdown_error; + + /* 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. */ + grpc_fd_watcher inactive_watcher_root; + grpc_fd_watcher* read_watcher; + grpc_fd_watcher* write_watcher; + + grpc_closure* read_closure; + grpc_closure* write_closure; + + grpc_closure* on_done_closure; + + grpc_iomgr_object iomgr_object; + + /* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */ + grpc_fork_fd_list* fork_fd_list; +}; + +/* True when GRPC_ENABLE_FORK_SUPPORT=1. We do not support fork with poll-cv */ +static bool track_fds_for_fork = false; + +/* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */ +static grpc_fork_fd_list* fork_fd_list_head = nullptr; +static gpr_mu fork_fd_list_mu; + +/* 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 */ +static uint32_t fd_begin_poll(grpc_fd* fd, grpc_pollset* pollset, + grpc_pollset_worker* worker, uint32_t read_mask, + uint32_t write_mask, grpc_fd_watcher* rec); +/* Complete polling previously started with fd_begin_poll + MUST NOT be called with a pollset lock taken + if got_read or got_write are 1, also does the become_{readable,writable} as + appropriate. */ +static void fd_end_poll(grpc_fd_watcher* rec, int got_read, int got_write); + +/* Return 1 if this fd is orphaned, 0 otherwise */ +static bool fd_is_orphaned(grpc_fd* fd); + +#ifndef NDEBUG +static void fd_ref(grpc_fd* fd, const char* reason, const char* file, int line); +static void fd_unref(grpc_fd* fd, const char* reason, const char* file, + int line); +#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__) +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__) +#else +static void fd_ref(grpc_fd* fd); +static void fd_unref(grpc_fd* fd); +#define GRPC_FD_REF(fd, reason) fd_ref(fd) +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd) +#endif + +#define CLOSURE_NOT_READY ((grpc_closure*)0) +#define CLOSURE_READY ((grpc_closure*)1) + +/******************************************************************************* + * pollset declarations + */ + +typedef struct grpc_cached_wakeup_fd { + grpc_wakeup_fd fd; + struct grpc_cached_wakeup_fd* next; + + /* Only used when GRPC_ENABLE_FORK_SUPPORT=1 */ + grpc_fork_fd_list* fork_fd_list; +} grpc_cached_wakeup_fd; + +struct grpc_pollset_worker { + grpc_cached_wakeup_fd* wakeup_fd; + int reevaluate_polling_on_wakeup; + int kicked_specifically; + struct grpc_pollset_worker* next; + struct grpc_pollset_worker* prev; +}; + +struct grpc_pollset { + gpr_mu mu; + grpc_pollset_worker root_worker; + int shutting_down; + int called_shutdown; + int kicked_without_pollers; + grpc_closure* shutdown_done; + int pollset_set_count; + /* all polled fds */ + size_t fd_count; + size_t fd_capacity; + grpc_fd** fds; + /* Local cache of eventfds for workers */ + grpc_cached_wakeup_fd* local_wakeup_cache; +}; + +/* Add an fd to a pollset */ +static void pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd); + +static void pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd); + +/* 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 */ +static int poll_deadline_to_millis_timeout(grpc_millis deadline); + +/* Allow kick to wakeup the currently polling worker */ +#define GRPC_POLLSET_CAN_KICK_SELF 1 +/* Force the wakee to repoll when awoken */ +#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2 +/* As per pollset_kick, with an extended set of flags (defined above) + -- mostly for fd_posix's use. */ +static grpc_error* pollset_kick_ext(grpc_pollset* p, + grpc_pollset_worker* specific_worker, + uint32_t flags) GRPC_MUST_USE_RESULT; + +/* Return 1 if the pollset has active threads in pollset_work (pollset must + * be locked) */ +static bool pollset_has_workers(grpc_pollset* pollset); + +/******************************************************************************* + * pollset_set definitions + */ + +struct grpc_pollset_set { + gpr_mu mu; + + size_t pollset_count; + size_t pollset_capacity; + grpc_pollset** pollsets; + + size_t pollset_set_count; + size_t pollset_set_capacity; + struct grpc_pollset_set** pollset_sets; + + size_t fd_count; + size_t fd_capacity; + grpc_fd** fds; +}; + +/******************************************************************************* + * condition variable polling definitions + */ + +#define POLLCV_THREAD_GRACE_MS 1000 +#define CV_POLL_PERIOD_MS 1000 +#define CV_DEFAULT_TABLE_SIZE 16 + +typedef struct poll_result { + gpr_refcount refcount; + grpc_cv_node* watchers; + int watchcount; + struct pollfd* fds; + nfds_t nfds; + int retval; + int err; + int completed; +} poll_result; + +typedef struct poll_args { + grpc_core::Thread poller_thd; + gpr_cv trigger; + int trigger_set; + bool harvestable; + gpr_cv harvest; + bool joinable; + gpr_cv join; + struct pollfd* fds; + nfds_t nfds; + poll_result* result; + struct poll_args* next; + struct poll_args* prev; +} poll_args; + +// This is a 2-tiered cache, we mantain a hash table +// of active poll calls, so we can wait on the result +// of that call. We also maintain freelists of inactive +// poll args and of dead poller threads. +typedef struct poll_hash_table { + poll_args* free_pollers; + poll_args** active_pollers; + poll_args* dead_pollers; + unsigned int size; + unsigned int count; +} poll_hash_table; + +// TODO(kpayson64): Eliminate use of global non-POD variables +poll_hash_table poll_cache; +grpc_cv_fd_table g_cvfds; + +/******************************************************************************* + * functions to track opened fds. No-ops unless track_fds_for_fork is true. + */ + +static void fork_fd_list_remove_node(grpc_fork_fd_list* node) { + if (track_fds_for_fork) { + gpr_mu_lock(&fork_fd_list_mu); + if (fork_fd_list_head == node) { + fork_fd_list_head = node->next; + } + if (node->prev != nullptr) { + node->prev->next = node->next; + } + if (node->next != nullptr) { + node->next->prev = node->prev; + } + gpr_free(node); + gpr_mu_unlock(&fork_fd_list_mu); + } +} + +static void fork_fd_list_add_node(grpc_fork_fd_list* node) { + gpr_mu_lock(&fork_fd_list_mu); + node->next = fork_fd_list_head; + node->prev = nullptr; + if (fork_fd_list_head != nullptr) { + fork_fd_list_head->prev = node; + } + fork_fd_list_head = node; + gpr_mu_unlock(&fork_fd_list_mu); +} + +static void fork_fd_list_add_grpc_fd(grpc_fd* fd) { + if (track_fds_for_fork) { + fd->fork_fd_list = + static_cast(gpr_malloc(sizeof(grpc_fork_fd_list))); + fd->fork_fd_list->fd = fd; + fd->fork_fd_list->cached_wakeup_fd = nullptr; + fork_fd_list_add_node(fd->fork_fd_list); + } +} + +static void fork_fd_list_add_wakeup_fd(grpc_cached_wakeup_fd* fd) { + if (track_fds_for_fork) { + fd->fork_fd_list = + static_cast(gpr_malloc(sizeof(grpc_fork_fd_list))); + fd->fork_fd_list->cached_wakeup_fd = fd; + fd->fork_fd_list->fd = nullptr; + fork_fd_list_add_node(fd->fork_fd_list); + } +} + + /******************************************************************************* + * fd_posix.c + */ + +#ifndef NDEBUG +#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) { + if (grpc_trace_fd_refcount.enabled()) { + gpr_log(GPR_DEBUG, + "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%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); +} + +#ifndef NDEBUG +static void unref_by(grpc_fd* fd, int n, const char* reason, const char* file, + int line) { + if (grpc_trace_fd_refcount.enabled()) { + gpr_log(GPR_DEBUG, + "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%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) { +#endif + gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); + if (old == n) { + gpr_mu_destroy(&fd->mu); + grpc_iomgr_unregister_object(&fd->iomgr_object); + fork_fd_list_remove_node(fd->fork_fd_list); + if (fd->shutdown) GRPC_ERROR_UNREF(fd->shutdown_error); + gpr_free(fd); + } else { + GPR_ASSERT(old > n); + } +} + +static grpc_fd* fd_create(int fd, const char* name, bool track_err) { + GPR_DEBUG_ASSERT(track_err == false); + grpc_fd* r = static_cast(gpr_malloc(sizeof(*r))); + gpr_mu_init(&r->mu); + gpr_atm_rel_store(&r->refst, 1); + r->shutdown = 0; + r->read_closure = CLOSURE_NOT_READY; + r->write_closure = CLOSURE_NOT_READY; + r->fd = fd; + r->inactive_watcher_root.next = r->inactive_watcher_root.prev = + &r->inactive_watcher_root; + r->read_watcher = r->write_watcher = nullptr; + r->on_done_closure = nullptr; + r->closed = 0; + r->released = 0; + gpr_atm_no_barrier_store(&r->pollhup, 0); + + char* name2; + gpr_asprintf(&name2, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&r->iomgr_object, name2); + gpr_free(name2); + fork_fd_list_add_grpc_fd(r); + return r; +} + +static bool fd_is_orphaned(grpc_fd* fd) { + return (gpr_atm_acq_load(&fd->refst) & 1) == 0; +} + +static grpc_error* pollset_kick_locked(grpc_fd_watcher* watcher) { + gpr_mu_lock(&watcher->pollset->mu); + GPR_ASSERT(watcher->worker); + grpc_error* err = pollset_kick_ext(watcher->pollset, watcher->worker, + GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP); + gpr_mu_unlock(&watcher->pollset->mu); + return err; +} + +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); + } else if (fd->read_watcher) { + pollset_kick_locked(fd->read_watcher); + } else if (fd->write_watcher) { + pollset_kick_locked(fd->write_watcher); + } +} + +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); + } + if (fd->read_watcher) { + pollset_kick_locked(fd->read_watcher); + } + if (fd->write_watcher && fd->write_watcher != fd->read_watcher) { + pollset_kick_locked(fd->write_watcher); + } +} + +static int has_watchers(grpc_fd* fd) { + return fd->read_watcher != nullptr || fd->write_watcher != nullptr || + fd->inactive_watcher_root.next != &fd->inactive_watcher_root; +} + +static void close_fd_locked(grpc_fd* fd) { + fd->closed = 1; + if (!fd->released) { + close(fd->fd); + } + GRPC_CLOSURE_SCHED(fd->on_done_closure, GRPC_ERROR_NONE); +} + +static int fd_wrapped_fd(grpc_fd* fd) { + if (fd->released || fd->closed) { + return -1; + } else { + return fd->fd; + } +} + +static void fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd, + const char* reason) { + fd->on_done_closure = on_done; + fd->released = release_fd != nullptr; + if (release_fd != nullptr) { + *release_fd = fd->fd; + fd->released = true; + } + gpr_mu_lock(&fd->mu); + REF_BY(fd, 1, reason); /* remove active status, but keep referenced */ + if (!has_watchers(fd)) { + close_fd_locked(fd); + } else { + wake_all_watchers_locked(fd); + } + gpr_mu_unlock(&fd->mu); + UNREF_BY(fd, 2, reason); /* drop the reference */ +} + +/* increment refcount by two to avoid changing the orphan bit */ +#ifndef NDEBUG +static void fd_ref(grpc_fd* fd, const char* reason, const char* file, + int line) { + ref_by(fd, 2, reason, file, line); +} + +static void fd_unref(grpc_fd* fd, const char* reason, const char* file, + int line) { + unref_by(fd, 2, reason, file, line); +} +#else +static void fd_ref(grpc_fd* fd) { ref_by(fd, 2); } + +static void fd_unref(grpc_fd* fd) { unref_by(fd, 2); } +#endif + +static grpc_error* fd_shutdown_error(grpc_fd* fd) { + if (!fd->shutdown) { + return GRPC_ERROR_NONE; + } else { + return grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "FD shutdown", &fd->shutdown_error, 1), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + } +} + +static void notify_on_locked(grpc_fd* fd, grpc_closure** st, + grpc_closure* closure) { + if (fd->shutdown || gpr_atm_no_barrier_load(&fd->pollhup)) { + GRPC_CLOSURE_SCHED( + closure, grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("FD shutdown"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } else if (*st == CLOSURE_NOT_READY) { + /* not ready ==> switch to a waiting state by setting the closure */ + *st = closure; + } else if (*st == CLOSURE_READY) { + /* already ready ==> queue the closure to run immediately */ + *st = CLOSURE_NOT_READY; + GRPC_CLOSURE_SCHED(closure, fd_shutdown_error(fd)); + maybe_wake_one_watcher_locked(fd); + } else { + /* 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(); + } +} + +/* returns 1 if state becomes not ready */ +static int set_ready_locked(grpc_fd* fd, grpc_closure** st) { + if (*st == CLOSURE_READY) { + /* duplicate ready ==> ignore */ + return 0; + } else if (*st == CLOSURE_NOT_READY) { + /* not ready, and not waiting ==> flag ready */ + *st = CLOSURE_READY; + return 0; + } else { + /* waiting ==> queue closure */ + GRPC_CLOSURE_SCHED(*st, fd_shutdown_error(fd)); + *st = CLOSURE_NOT_READY; + return 1; + } +} + +static void fd_shutdown(grpc_fd* fd, grpc_error* why) { + gpr_mu_lock(&fd->mu); + /* only shutdown once */ + if (!fd->shutdown) { + fd->shutdown = 1; + fd->shutdown_error = why; + /* signal read/write closed to OS so that future operations fail */ + shutdown(fd->fd, SHUT_RDWR); + set_ready_locked(fd, &fd->read_closure); + set_ready_locked(fd, &fd->write_closure); + } else { + GRPC_ERROR_UNREF(why); + } + gpr_mu_unlock(&fd->mu); +} + +static bool fd_is_shutdown(grpc_fd* fd) { + gpr_mu_lock(&fd->mu); + bool r = fd->shutdown; + gpr_mu_unlock(&fd->mu); + return r; +} + +static void fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) { + gpr_mu_lock(&fd->mu); + notify_on_locked(fd, &fd->read_closure, closure); + gpr_mu_unlock(&fd->mu); +} + +static void fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) { + gpr_mu_lock(&fd->mu); + notify_on_locked(fd, &fd->write_closure, closure); + gpr_mu_unlock(&fd->mu); +} + +static void fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_ERROR, "Polling engine does not support tracking errors."); + } + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_CANCELLED); +} + +static void fd_set_readable(grpc_fd* fd) { + gpr_mu_lock(&fd->mu); + set_ready_locked(fd, &fd->read_closure); + gpr_mu_unlock(&fd->mu); +} + +static void fd_set_writable(grpc_fd* fd) { + gpr_mu_lock(&fd->mu); + set_ready_locked(fd, &fd->write_closure); + gpr_mu_unlock(&fd->mu); +} + +static void fd_set_error(grpc_fd* fd) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_ERROR, "Polling engine does not support tracking errors."); + } +} + +static uint32_t fd_begin_poll(grpc_fd* fd, grpc_pollset* pollset, + grpc_pollset_worker* worker, uint32_t read_mask, + uint32_t write_mask, grpc_fd_watcher* watcher) { + uint32_t mask = 0; + grpc_closure* cur; + int requested; + /* keep track of pollers that have requested our events, in case they change + */ + GRPC_FD_REF(fd, "poll"); + + gpr_mu_lock(&fd->mu); + + /* if we are shutdown, then don't add to the watcher set */ + if (fd->shutdown) { + watcher->fd = nullptr; + watcher->pollset = nullptr; + watcher->worker = nullptr; + gpr_mu_unlock(&fd->mu); + GRPC_FD_UNREF(fd, "poll"); + return 0; + } + + /* if there is nobody polling for read, but we need to, then start doing so */ + cur = fd->read_closure; + requested = cur != CLOSURE_READY; + if (read_mask && fd->read_watcher == nullptr && requested) { + fd->read_watcher = watcher; + mask |= read_mask; + } + /* if there is nobody polling for write, but we need to, then start doing so + */ + cur = fd->write_closure; + requested = cur != CLOSURE_READY; + if (write_mask && fd->write_watcher == nullptr && requested) { + fd->write_watcher = watcher; + mask |= write_mask; + } + /* if not polling, remember this watcher in case we need someone to later */ + if (mask == 0 && worker != nullptr) { + watcher->next = &fd->inactive_watcher_root; + watcher->prev = watcher->next->prev; + watcher->next->prev = watcher->prev->next = watcher; + } + watcher->pollset = pollset; + watcher->worker = worker; + watcher->fd = fd; + gpr_mu_unlock(&fd->mu); + + return mask; +} + +static void 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 == nullptr) { + return; + } + + gpr_mu_lock(&fd->mu); + + if (watcher == fd->read_watcher) { + /* remove read watcher, kick if we still need a read */ + was_polling = 1; + if (!got_read) { + kick = 1; + } + fd->read_watcher = nullptr; + } + if (watcher == fd->write_watcher) { + /* remove write watcher, kick if we still need a write */ + was_polling = 1; + if (!got_write) { + kick = 1; + } + fd->write_watcher = nullptr; + } + if (!was_polling && watcher->worker != nullptr) { + /* remove from inactive list */ + watcher->next->prev = watcher->prev; + watcher->prev->next = watcher->next; + } + if (got_read) { + if (set_ready_locked(fd, &fd->read_closure)) { + kick = 1; + } + } + if (got_write) { + if (set_ready_locked(fd, &fd->write_closure)) { + kick = 1; + } + } + if (kick) { + maybe_wake_one_watcher_locked(fd); + } + if (fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) { + close_fd_locked(fd); + } + gpr_mu_unlock(&fd->mu); + + GRPC_FD_UNREF(fd, "poll"); +} + +/******************************************************************************* + * pollset_posix.c + */ + +GPR_TLS_DECL(g_current_thread_poller); +GPR_TLS_DECL(g_current_thread_worker); + +static void remove_worker(grpc_pollset* p, grpc_pollset_worker* worker) { + worker->prev->next = worker->next; + worker->next->prev = worker->prev; +} + +static bool pollset_has_workers(grpc_pollset* p) { + return p->root_worker.next != &p->root_worker; +} + +static bool pollset_in_pollset_sets(grpc_pollset* p) { + return p->pollset_set_count; +} + +static bool pollset_has_observers(grpc_pollset* p) { + return pollset_has_workers(p) || pollset_in_pollset_sets(p); +} + +static grpc_pollset_worker* pop_front_worker(grpc_pollset* p) { + if (pollset_has_workers(p)) { + grpc_pollset_worker* w = p->root_worker.next; + remove_worker(p, w); + return w; + } else { + return nullptr; + } +} + +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; +} + +static void kick_append_error(grpc_error** composite, grpc_error* error) { + if (error == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Kick Failure"); + } + *composite = grpc_error_add_child(*composite, error); +} + +static grpc_error* pollset_kick_ext(grpc_pollset* p, + grpc_pollset_worker* specific_worker, + uint32_t flags) { + GPR_TIMER_SCOPE("pollset_kick_ext", 0); + grpc_error* error = GRPC_ERROR_NONE; + GRPC_STATS_INC_POLLSET_KICK(); + + /* pollset->mu already held */ + if (specific_worker != nullptr) { + if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { + GPR_TIMER_SCOPE("pollset_kick_ext.broadcast", 0); + GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); + for (specific_worker = p->root_worker.next; + specific_worker != &p->root_worker; + specific_worker = specific_worker->next) { + kick_append_error( + &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } + p->kicked_without_pollers = true; + } else if (gpr_tls_get(&g_current_thread_worker) != + (intptr_t)specific_worker) { + GPR_TIMER_MARK("different_thread_worker", 0); + if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { + specific_worker->reevaluate_polling_on_wakeup = true; + } + specific_worker->kicked_specifically = true; + kick_append_error(&error, + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) { + GPR_TIMER_MARK("kick_yoself", 0); + if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { + specific_worker->reevaluate_polling_on_wakeup = true; + } + specific_worker->kicked_specifically = true; + kick_append_error(&error, + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } + } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) { + GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); + GPR_TIMER_MARK("kick_anonymous", 0); + specific_worker = pop_front_worker(p); + if (specific_worker != nullptr) { + if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) { + GPR_TIMER_MARK("kick_anonymous_not_self", 0); + push_back_worker(p, specific_worker); + specific_worker = pop_front_worker(p); + if ((flags & GRPC_POLLSET_CAN_KICK_SELF) == 0 && + gpr_tls_get(&g_current_thread_worker) == + (intptr_t)specific_worker) { + push_back_worker(p, specific_worker); + specific_worker = nullptr; + } + } + if (specific_worker != nullptr) { + GPR_TIMER_MARK("finally_kick", 0); + push_back_worker(p, specific_worker); + kick_append_error( + &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } + } else { + GPR_TIMER_MARK("kicked_no_pollers", 0); + p->kicked_without_pollers = true; + } + } + + GRPC_LOG_IF_ERROR("pollset_kick_ext", GRPC_ERROR_REF(error)); + return error; +} + +static grpc_error* pollset_kick(grpc_pollset* p, + grpc_pollset_worker* specific_worker) { + return pollset_kick_ext(p, specific_worker, 0); +} + +/* global state management */ + +static grpc_error* pollset_global_init(void) { + gpr_tls_init(&g_current_thread_poller); + gpr_tls_init(&g_current_thread_worker); + return GRPC_ERROR_NONE; +} + +static void pollset_global_shutdown(void) { + gpr_tls_destroy(&g_current_thread_poller); + gpr_tls_destroy(&g_current_thread_worker); +} + +/* main interface */ + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + gpr_mu_init(&pollset->mu); + *mu = &pollset->mu; + pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; + pollset->shutting_down = 0; + pollset->called_shutdown = 0; + pollset->kicked_without_pollers = 0; + pollset->local_wakeup_cache = nullptr; + pollset->kicked_without_pollers = 0; + pollset->fd_count = 0; + pollset->fd_capacity = 0; + pollset->fds = nullptr; + pollset->pollset_set_count = 0; +} + +static void pollset_destroy(grpc_pollset* pollset) { + GPR_ASSERT(!pollset_has_workers(pollset)); + while (pollset->local_wakeup_cache) { + grpc_cached_wakeup_fd* next = pollset->local_wakeup_cache->next; + fork_fd_list_remove_node(pollset->local_wakeup_cache->fork_fd_list); + grpc_wakeup_fd_destroy(&pollset->local_wakeup_cache->fd); + gpr_free(pollset->local_wakeup_cache); + pollset->local_wakeup_cache = next; + } + gpr_free(pollset->fds); + gpr_mu_destroy(&pollset->mu); +} + +static void pollset_add_fd(grpc_pollset* pollset, grpc_fd* fd) { + gpr_mu_lock(&pollset->mu); + size_t i; + /* TODO(ctiller): this is O(num_fds^2); maybe switch to a hash set here */ + for (i = 0; i < pollset->fd_count; i++) { + if (pollset->fds[i] == fd) goto exit; + } + if (pollset->fd_count == pollset->fd_capacity) { + pollset->fd_capacity = + GPR_MAX(pollset->fd_capacity + 8, pollset->fd_count * 3 / 2); + pollset->fds = static_cast( + gpr_realloc(pollset->fds, sizeof(grpc_fd*) * pollset->fd_capacity)); + } + pollset->fds[pollset->fd_count++] = fd; + GRPC_FD_REF(fd, "multipoller"); + pollset_kick(pollset, nullptr); +exit: + gpr_mu_unlock(&pollset->mu); +} + +static void finish_shutdown(grpc_pollset* pollset) { + size_t i; + for (i = 0; i < pollset->fd_count; i++) { + GRPC_FD_UNREF(pollset->fds[i], "multipoller"); + } + pollset->fd_count = 0; + GRPC_CLOSURE_SCHED(pollset->shutdown_done, GRPC_ERROR_NONE); +} + +static void work_combine_error(grpc_error** composite, grpc_error* error) { + if (error == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("pollset_work"); + } + *composite = grpc_error_add_child(*composite, error); +} + +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GPR_TIMER_SCOPE("pollset_work", 0); + grpc_pollset_worker worker; + if (worker_hdl) *worker_hdl = &worker; + grpc_error* error = GRPC_ERROR_NONE; + + /* Avoid malloc for small number of elements. */ + enum { inline_elements = 96 }; + struct pollfd pollfd_space[inline_elements]; + struct grpc_fd_watcher watcher_space[inline_elements]; + + /* pollset->mu already held */ + int added_worker = 0; + int locked = 1; + int queued_work = 0; + int keep_polling = 0; + /* this must happen before we (potentially) drop pollset->mu */ + worker.next = worker.prev = nullptr; + worker.reevaluate_polling_on_wakeup = 0; + if (pollset->local_wakeup_cache != nullptr) { + worker.wakeup_fd = pollset->local_wakeup_cache; + pollset->local_wakeup_cache = worker.wakeup_fd->next; + } else { + worker.wakeup_fd = static_cast( + gpr_malloc(sizeof(*worker.wakeup_fd))); + error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd); + fork_fd_list_add_wakeup_fd(worker.wakeup_fd); + if (error != GRPC_ERROR_NONE) { + GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); + return error; + } + } + worker.kicked_specifically = 0; + /* If we're shutting down then we don't execute any extended work */ + if (pollset->shutting_down) { + GPR_TIMER_MARK("pollset_work.shutting_down", 0); + goto done; + } + /* Start polling, and keep doing so while we're being asked to + re-evaluate our pollers (this allows poll() based pollers to + ensure they don't miss wakeups) */ + keep_polling = 1; + gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset); + while (keep_polling) { + keep_polling = 0; + if (!pollset->kicked_without_pollers || + deadline <= grpc_core::ExecCtx::Get()->Now()) { + if (!added_worker) { + push_front_worker(pollset, &worker); + added_worker = 1; + gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); + } + GPR_TIMER_SCOPE("maybe_work_and_unlock", 0); +#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR) +#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR) + + int timeout; + int r; + size_t i, fd_count; + nfds_t pfd_count; + grpc_fd_watcher* watchers; + struct pollfd* pfds; + + timeout = poll_deadline_to_millis_timeout(deadline); + + if (pollset->fd_count + 2 <= inline_elements) { + pfds = pollfd_space; + watchers = watcher_space; + } else { + /* Allocate one buffer to hold both pfds and watchers arrays */ + const size_t pfd_size = sizeof(*pfds) * (pollset->fd_count + 2); + const size_t watch_size = sizeof(*watchers) * (pollset->fd_count + 2); + void* buf = gpr_malloc(pfd_size + watch_size); + pfds = static_cast(buf); + watchers = static_cast( + (void*)(static_cast(buf) + pfd_size)); + } + + fd_count = 0; + pfd_count = 1; + pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker.wakeup_fd->fd); + pfds[0].events = POLLIN; + pfds[0].revents = 0; + for (i = 0; i < pollset->fd_count; i++) { + if (fd_is_orphaned(pollset->fds[i]) || + gpr_atm_no_barrier_load(&pollset->fds[i]->pollhup) == 1) { + GRPC_FD_UNREF(pollset->fds[i], "multipoller"); + } else { + pollset->fds[fd_count++] = pollset->fds[i]; + watchers[pfd_count].fd = pollset->fds[i]; + GRPC_FD_REF(watchers[pfd_count].fd, "multipoller_start"); + pfds[pfd_count].fd = pollset->fds[i]->fd; + pfds[pfd_count].revents = 0; + pfd_count++; + } + } + pollset->fd_count = fd_count; + gpr_mu_unlock(&pollset->mu); + + for (i = 1; i < pfd_count; i++) { + grpc_fd* fd = watchers[i].fd; + pfds[i].events = static_cast( + fd_begin_poll(fd, pollset, &worker, POLLIN, POLLOUT, &watchers[i])); + GRPC_FD_UNREF(fd, "multipoller_start"); + } + + /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid + even going into the blocking annotation if possible */ + GRPC_SCHEDULING_START_BLOCKING_REGION; + GRPC_STATS_INC_SYSCALL_POLL(); + r = grpc_poll_function(pfds, pfd_count, timeout); + GRPC_SCHEDULING_END_BLOCKING_REGION; + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "%p poll=%d", pollset, r); + } + + if (r < 0) { + if (errno != EINTR) { + work_combine_error(&error, GRPC_OS_ERROR(errno, "poll")); + } + + for (i = 1; i < pfd_count; i++) { + if (watchers[i].fd == nullptr) { + fd_end_poll(&watchers[i], 0, 0); + } else { + // Wake up all the file descriptors, if we have an invalid one + // we can identify it on the next pollset_work() + fd_end_poll(&watchers[i], 1, 1); + } + } + } else if (r == 0) { + for (i = 1; i < pfd_count; i++) { + fd_end_poll(&watchers[i], 0, 0); + } + } else { + if (pfds[0].revents & POLLIN_CHECK) { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "%p: got_wakeup", pollset); + } + work_combine_error( + &error, grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd)); + } + for (i = 1; i < pfd_count; i++) { + if (watchers[i].fd == nullptr) { + fd_end_poll(&watchers[i], 0, 0); + } else { + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_INFO, "%p got_event: %d r:%d w:%d [%d]", pollset, + pfds[i].fd, (pfds[i].revents & POLLIN_CHECK) != 0, + (pfds[i].revents & POLLOUT_CHECK) != 0, pfds[i].revents); + } + /* This is a mitigation to prevent poll() from spinning on a + ** POLLHUP https://github.com/grpc/grpc/pull/13665 + */ + if (pfds[i].revents & POLLHUP) { + gpr_atm_no_barrier_store(&watchers[i].fd->pollhup, 1); + } + fd_end_poll(&watchers[i], pfds[i].revents & POLLIN_CHECK, + pfds[i].revents & POLLOUT_CHECK); + } + } + } + + if (pfds != pollfd_space) { + /* pfds and watchers are in the same memory block pointed to by pfds */ + gpr_free(pfds); + } + + locked = 0; + } else { + GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); + pollset->kicked_without_pollers = 0; + } + /* Finished execution - start cleaning up. + Note that we may arrive here from outside the enclosing while() loop. + In that case we won't loop though as we haven't added worker to the + worker list, which means nobody could ask us to re-evaluate polling). */ + done: + if (!locked) { + queued_work |= grpc_core::ExecCtx::Get()->Flush(); + gpr_mu_lock(&pollset->mu); + locked = 1; + } + /* If we're forced to re-evaluate polling (via pollset_kick with + GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force + a loop */ + if (worker.reevaluate_polling_on_wakeup && error == GRPC_ERROR_NONE) { + worker.reevaluate_polling_on_wakeup = 0; + pollset->kicked_without_pollers = 0; + if (queued_work || worker.kicked_specifically) { + /* If there's queued work on the list, then set the deadline to be + immediate so we get back out of the polling loop quickly */ + deadline = 0; + } + keep_polling = 1; + } + } + gpr_tls_set(&g_current_thread_poller, 0); + if (added_worker) { + remove_worker(pollset, &worker); + gpr_tls_set(&g_current_thread_worker, 0); + } + /* release wakeup fd to the local pool */ + worker.wakeup_fd->next = pollset->local_wakeup_cache; + pollset->local_wakeup_cache = worker.wakeup_fd; + /* check shutdown conditions */ + if (pollset->shutting_down) { + if (pollset_has_workers(pollset)) { + pollset_kick(pollset, nullptr); + } else if (!pollset->called_shutdown && !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + gpr_mu_unlock(&pollset->mu); + finish_shutdown(pollset); + grpc_core::ExecCtx::Get()->Flush(); + /* Continuing to access pollset here is safe -- it is the caller's + * responsibility to not destroy when it has outstanding calls to + * pollset_work. + * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */ + gpr_mu_lock(&pollset->mu); + } + } + if (worker_hdl) *worker_hdl = nullptr; + GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); + return error; +} + +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + GPR_ASSERT(!pollset->shutting_down); + pollset->shutting_down = 1; + pollset->shutdown_done = closure; + pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); + if (!pollset->called_shutdown && !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + finish_shutdown(pollset); + } +} + +static int poll_deadline_to_millis_timeout(grpc_millis deadline) { + if (deadline == GRPC_MILLIS_INF_FUTURE) return -1; + if (deadline == 0) return 0; + grpc_millis n = deadline - grpc_core::ExecCtx::Get()->Now(); + if (n < 0) return 0; + if (n > INT_MAX) return -1; + return static_cast(n); +} + +/******************************************************************************* + * pollset_set_posix.c + */ + +static grpc_pollset_set* pollset_set_create(void) { + grpc_pollset_set* pollset_set = + static_cast(gpr_zalloc(sizeof(*pollset_set))); + gpr_mu_init(&pollset_set->mu); + return pollset_set; +} + +static void 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_set"); + } + for (i = 0; i < pollset_set->pollset_count; i++) { + grpc_pollset* pollset = pollset_set->pollsets[i]; + gpr_mu_lock(&pollset->mu); + pollset->pollset_set_count--; + /* check shutdown */ + if (pollset->shutting_down && !pollset->called_shutdown && + !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + gpr_mu_unlock(&pollset->mu); + finish_shutdown(pollset); + } else { + gpr_mu_unlock(&pollset->mu); + } + } + gpr_free(pollset_set->pollsets); + gpr_free(pollset_set->pollset_sets); + gpr_free(pollset_set->fds); + gpr_free(pollset_set); +} + +static void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { + size_t i, j; + gpr_mu_lock(&pollset->mu); + pollset->pollset_set_count++; + gpr_mu_unlock(&pollset->mu); + 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 = static_cast(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 (fd_is_orphaned(pollset_set->fds[i])) { + GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set"); + } else { + 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); +} + +static void 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); + gpr_mu_lock(&pollset->mu); + pollset->pollset_set_count--; + /* check shutdown */ + if (pollset->shutting_down && !pollset->called_shutdown && + !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + gpr_mu_unlock(&pollset->mu); + finish_shutdown(pollset); + } else { + gpr_mu_unlock(&pollset->mu); + } +} + +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + size_t i, j; + gpr_mu_lock(&bag->mu); + if (bag->pollset_set_count == bag->pollset_set_capacity) { + bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity); + bag->pollset_sets = static_cast( + gpr_realloc(bag->pollset_sets, + bag->pollset_set_capacity * sizeof(*bag->pollset_sets))); + } + bag->pollset_sets[bag->pollset_set_count++] = item; + for (i = 0, j = 0; i < bag->fd_count; i++) { + if (fd_is_orphaned(bag->fds[i])) { + GRPC_FD_UNREF(bag->fds[i], "pollset_set"); + } else { + pollset_set_add_fd(item, bag->fds[i]); + bag->fds[j++] = bag->fds[i]; + } + } + bag->fd_count = j; + gpr_mu_unlock(&bag->mu); +} + +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + size_t i; + gpr_mu_lock(&bag->mu); + for (i = 0; i < bag->pollset_set_count; i++) { + if (bag->pollset_sets[i] == item) { + bag->pollset_set_count--; + GPR_SWAP(grpc_pollset_set*, bag->pollset_sets[i], + bag->pollset_sets[bag->pollset_set_count]); + break; + } + } + gpr_mu_unlock(&bag->mu); +} + +static void 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 = static_cast( + 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++) { + pollset_add_fd(pollset_set->pollsets[i], fd); + } + for (i = 0; i < pollset_set->pollset_set_count; i++) { + pollset_set_add_fd(pollset_set->pollset_sets[i], fd); + } + gpr_mu_unlock(&pollset_set->mu); +} + +static void 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; + } + } + for (i = 0; i < pollset_set->pollset_set_count; i++) { + pollset_set_del_fd(pollset_set->pollset_sets[i], fd); + } + gpr_mu_unlock(&pollset_set->mu); +} + +/******************************************************************************* + * Condition Variable polling extensions + */ + +static void run_poll(void* args); +static void cache_poller_locked(poll_args* args); +static void cache_harvest_locked(); + +static void cache_insert_locked(poll_args* args) { + uint32_t key = gpr_murmur_hash3(args->fds, args->nfds * sizeof(struct pollfd), + 0xDEADBEEF); + key = key % poll_cache.size; + if (poll_cache.active_pollers[key]) { + poll_cache.active_pollers[key]->prev = args; + } + args->next = poll_cache.active_pollers[key]; + args->prev = nullptr; + poll_cache.active_pollers[key] = args; + poll_cache.count++; +} + +static void init_result(poll_args* pargs) { + pargs->result = static_cast(gpr_malloc(sizeof(poll_result))); + gpr_ref_init(&pargs->result->refcount, 1); + pargs->result->watchers = nullptr; + pargs->result->watchcount = 0; + pargs->result->fds = static_cast( + gpr_malloc(sizeof(struct pollfd) * pargs->nfds)); + memcpy(pargs->result->fds, pargs->fds, sizeof(struct pollfd) * pargs->nfds); + pargs->result->nfds = pargs->nfds; + pargs->result->retval = 0; + pargs->result->err = 0; + pargs->result->completed = 0; +} + +// Creates a poll_args object for a given arguments to poll(). +// This object may return a poll_args in the cache. +static poll_args* get_poller_locked(struct pollfd* fds, nfds_t count) { + uint32_t key = + gpr_murmur_hash3(fds, count * sizeof(struct pollfd), 0xDEADBEEF); + key = key % poll_cache.size; + poll_args* curr = poll_cache.active_pollers[key]; + while (curr) { + if (curr->nfds == count && + memcmp(curr->fds, fds, count * sizeof(struct pollfd)) == 0) { + gpr_free(fds); + return curr; + } + curr = curr->next; + } + + if (poll_cache.free_pollers) { + poll_args* pargs = poll_cache.free_pollers; + poll_cache.free_pollers = pargs->next; + if (poll_cache.free_pollers) { + poll_cache.free_pollers->prev = nullptr; + } + pargs->fds = fds; + pargs->nfds = count; + pargs->next = nullptr; + pargs->prev = nullptr; + init_result(pargs); + cache_poller_locked(pargs); + return pargs; + } + + poll_args* pargs = + static_cast(gpr_malloc(sizeof(struct poll_args))); + gpr_cv_init(&pargs->trigger); + gpr_cv_init(&pargs->harvest); + gpr_cv_init(&pargs->join); + pargs->harvestable = false; + pargs->joinable = false; + pargs->fds = fds; + pargs->nfds = count; + pargs->next = nullptr; + pargs->prev = nullptr; + pargs->trigger_set = 0; + init_result(pargs); + cache_poller_locked(pargs); + gpr_ref(&g_cvfds.pollcount); + pargs->poller_thd = grpc_core::Thread("grpc_poller", &run_poll, pargs); + pargs->poller_thd.Start(); + return pargs; +} + +static void cache_delete_locked(poll_args* args) { + if (!args->prev) { + uint32_t key = gpr_murmur_hash3( + args->fds, args->nfds * sizeof(struct pollfd), 0xDEADBEEF); + key = key % poll_cache.size; + GPR_ASSERT(poll_cache.active_pollers[key] == args); + poll_cache.active_pollers[key] = args->next; + } else { + args->prev->next = args->next; + } + + if (args->next) { + args->next->prev = args->prev; + } + + poll_cache.count--; + if (poll_cache.free_pollers) { + poll_cache.free_pollers->prev = args; + } + args->prev = nullptr; + args->next = poll_cache.free_pollers; + gpr_free(args->fds); + poll_cache.free_pollers = args; +} + +static void cache_poller_locked(poll_args* args) { + if (poll_cache.count + 1 > poll_cache.size / 2) { + poll_args** old_active_pollers = poll_cache.active_pollers; + poll_cache.size = poll_cache.size * 2; + poll_cache.count = 0; + poll_cache.active_pollers = + static_cast(gpr_malloc(sizeof(void*) * poll_cache.size)); + for (unsigned int i = 0; i < poll_cache.size; i++) { + poll_cache.active_pollers[i] = nullptr; + } + for (unsigned int i = 0; i < poll_cache.size / 2; i++) { + poll_args* curr = old_active_pollers[i]; + poll_args* next = nullptr; + while (curr) { + next = curr->next; + cache_insert_locked(curr); + curr = next; + } + } + gpr_free(old_active_pollers); + } + + cache_insert_locked(args); +} + +static void cache_destroy_locked(poll_args* args) { + if (args->next) { + args->next->prev = args->prev; + } + + if (args->prev) { + args->prev->next = args->next; + } else { + poll_cache.free_pollers = args->next; + } + + // Now move this args to the dead poller list for later join + if (poll_cache.dead_pollers != nullptr) { + poll_cache.dead_pollers->prev = args; + } + args->prev = nullptr; + args->next = poll_cache.dead_pollers; + poll_cache.dead_pollers = args; +} + +static void cache_harvest_locked() { + while (poll_cache.dead_pollers) { + poll_args* args = poll_cache.dead_pollers; + poll_cache.dead_pollers = poll_cache.dead_pollers->next; + // Keep the list consistent in case new dead pollers get added when we + // release the lock below to wait on joining + if (poll_cache.dead_pollers) { + poll_cache.dead_pollers->prev = nullptr; + } + args->harvestable = true; + gpr_cv_signal(&args->harvest); + while (!args->joinable) { + gpr_cv_wait(&args->join, &g_cvfds.mu, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + args->poller_thd.Join(); + gpr_free(args); + } +} + +static void decref_poll_result(poll_result* res) { + if (gpr_unref(&res->refcount)) { + GPR_ASSERT(!res->watchers); + gpr_free(res->fds); + gpr_free(res); + } +} + +void remove_cvn(grpc_cv_node** head, grpc_cv_node* target) { + if (target->next) { + target->next->prev = target->prev; + } + + if (target->prev) { + target->prev->next = target->next; + } else { + *head = target->next; + } +} + +gpr_timespec thread_grace; + +// Poll in a background thread +static void run_poll(void* args) { + poll_args* pargs = static_cast(args); + while (1) { + poll_result* result = pargs->result; + int retval = g_cvfds.poll(result->fds, result->nfds, CV_POLL_PERIOD_MS); + gpr_mu_lock(&g_cvfds.mu); + cache_harvest_locked(); + if (retval != 0) { + result->completed = 1; + result->retval = retval; + result->err = errno; + grpc_cv_node* watcher = result->watchers; + while (watcher) { + gpr_cv_signal(watcher->cv); + watcher = watcher->next; + } + } + if (result->watchcount == 0 || result->completed) { + cache_delete_locked(pargs); + decref_poll_result(result); + // Leave this polling thread alive for a grace period to do another poll() + // op + gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC); + deadline = gpr_time_add(deadline, thread_grace); + pargs->trigger_set = 0; + gpr_cv_wait(&pargs->trigger, &g_cvfds.mu, deadline); + cache_harvest_locked(); + if (!pargs->trigger_set) { + cache_destroy_locked(pargs); + break; + } + } + gpr_mu_unlock(&g_cvfds.mu); + } + + if (gpr_unref(&g_cvfds.pollcount)) { + gpr_cv_signal(&g_cvfds.shutdown_cv); + } + while (!pargs->harvestable) { + gpr_cv_wait(&pargs->harvest, &g_cvfds.mu, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + pargs->joinable = true; + gpr_cv_signal(&pargs->join); + gpr_mu_unlock(&g_cvfds.mu); +} + +// This function overrides poll() to handle condition variable wakeup fds +static int cvfd_poll(struct pollfd* fds, nfds_t nfds, int timeout) { + if (timeout == 0) { + // Don't bother using background threads for polling if timeout is 0, + // poll-cv might not wait for a poll to return otherwise. + // https://github.com/grpc/grpc/issues/13298 + return poll(fds, nfds, 0); + } + unsigned int i; + int res, idx; + grpc_cv_node* pollcv; + int skip_poll = 0; + nfds_t nsockfds = 0; + poll_result* result = nullptr; + gpr_mu_lock(&g_cvfds.mu); + cache_harvest_locked(); + pollcv = static_cast(gpr_malloc(sizeof(grpc_cv_node))); + pollcv->next = nullptr; + gpr_cv pollcv_cv; + gpr_cv_init(&pollcv_cv); + pollcv->cv = &pollcv_cv; + grpc_cv_node* fd_cvs = + static_cast(gpr_malloc(nfds * sizeof(grpc_cv_node))); + + for (i = 0; i < nfds; i++) { + fds[i].revents = 0; + if (fds[i].fd < 0 && (fds[i].events & POLLIN)) { + idx = GRPC_FD_TO_IDX(fds[i].fd); + fd_cvs[i].cv = &pollcv_cv; + fd_cvs[i].prev = nullptr; + fd_cvs[i].next = g_cvfds.cvfds[idx].cvs; + if (g_cvfds.cvfds[idx].cvs) { + g_cvfds.cvfds[idx].cvs->prev = &(fd_cvs[i]); + } + g_cvfds.cvfds[idx].cvs = &(fd_cvs[i]); + // Don't bother polling if a wakeup fd is ready + if (g_cvfds.cvfds[idx].is_set) { + skip_poll = 1; + } + } else if (fds[i].fd >= 0) { + nsockfds++; + } + } + + gpr_timespec deadline = gpr_now(GPR_CLOCK_MONOTONIC); + if (timeout < 0) { + deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + } else { + deadline = + gpr_time_add(deadline, gpr_time_from_millis(timeout, GPR_TIMESPAN)); + } + + res = 0; + if (!skip_poll && nsockfds > 0) { + struct pollfd* pollfds = static_cast( + gpr_malloc(sizeof(struct pollfd) * nsockfds)); + idx = 0; + for (i = 0; i < nfds; i++) { + if (fds[i].fd >= 0) { + pollfds[idx].fd = fds[i].fd; + pollfds[idx].events = fds[i].events; + pollfds[idx].revents = 0; + idx++; + } + } + poll_args* pargs = get_poller_locked(pollfds, nsockfds); + result = pargs->result; + pollcv->next = result->watchers; + pollcv->prev = nullptr; + if (result->watchers) { + result->watchers->prev = pollcv; + } + result->watchers = pollcv; + result->watchcount++; + gpr_ref(&result->refcount); + + pargs->trigger_set = 1; + gpr_cv_signal(&pargs->trigger); + gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + cache_harvest_locked(); + res = result->retval; + errno = result->err; + result->watchcount--; + remove_cvn(&result->watchers, pollcv); + } else if (!skip_poll) { + gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + cache_harvest_locked(); + } + + idx = 0; + for (i = 0; i < nfds; i++) { + if (fds[i].fd < 0 && (fds[i].events & POLLIN)) { + remove_cvn(&g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].cvs, &(fd_cvs[i])); + if (g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].is_set) { + fds[i].revents = POLLIN; + if (res >= 0) res++; + } + } else if (!skip_poll && fds[i].fd >= 0 && result->completed) { + fds[i].revents = result->fds[idx].revents; + idx++; + } + } + + gpr_free(fd_cvs); + gpr_free(pollcv); + if (result) { + decref_poll_result(result); + } + + gpr_mu_unlock(&g_cvfds.mu); + + return res; +} + +static void global_cv_fd_table_init() { + gpr_mu_init(&g_cvfds.mu); + gpr_mu_lock(&g_cvfds.mu); + gpr_cv_init(&g_cvfds.shutdown_cv); + gpr_ref_init(&g_cvfds.pollcount, 1); + g_cvfds.size = CV_DEFAULT_TABLE_SIZE; + g_cvfds.cvfds = static_cast( + gpr_malloc(sizeof(grpc_fd_node) * CV_DEFAULT_TABLE_SIZE)); + g_cvfds.free_fds = nullptr; + thread_grace = gpr_time_from_millis(POLLCV_THREAD_GRACE_MS, GPR_TIMESPAN); + for (int i = 0; i < CV_DEFAULT_TABLE_SIZE; i++) { + g_cvfds.cvfds[i].is_set = 0; + g_cvfds.cvfds[i].cvs = nullptr; + g_cvfds.cvfds[i].next_free = g_cvfds.free_fds; + g_cvfds.free_fds = &g_cvfds.cvfds[i]; + } + // Override the poll function with one that supports cvfds + g_cvfds.poll = grpc_poll_function; + grpc_poll_function = &cvfd_poll; + + // Initialize the cache + poll_cache.size = 32; + poll_cache.count = 0; + poll_cache.free_pollers = nullptr; + poll_cache.active_pollers = + static_cast(gpr_malloc(sizeof(void*) * 32)); + for (unsigned int i = 0; i < poll_cache.size; i++) { + poll_cache.active_pollers[i] = nullptr; + } + poll_cache.dead_pollers = nullptr; + + gpr_mu_unlock(&g_cvfds.mu); +} + +static void global_cv_fd_table_shutdown() { + gpr_mu_lock(&g_cvfds.mu); + // Attempt to wait for all abandoned poll() threads to terminate + // Not doing so will result in reported memory leaks + if (!gpr_unref(&g_cvfds.pollcount)) { + int res = gpr_cv_wait(&g_cvfds.shutdown_cv, &g_cvfds.mu, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_seconds(3, GPR_TIMESPAN))); + GPR_ASSERT(res == 0); + } + gpr_cv_destroy(&g_cvfds.shutdown_cv); + grpc_poll_function = g_cvfds.poll; + gpr_free(g_cvfds.cvfds); + + cache_harvest_locked(); + gpr_free(poll_cache.active_pollers); + + gpr_mu_unlock(&g_cvfds.mu); + gpr_mu_destroy(&g_cvfds.mu); +} + +/******************************************************************************* + * event engine binding + */ + +static void shutdown_background_closure(void) {} + +static void shutdown_engine(void) { + pollset_global_shutdown(); + if (grpc_cv_wakeup_fds_enabled()) { + global_cv_fd_table_shutdown(); + } + if (track_fds_for_fork) { + gpr_mu_destroy(&fork_fd_list_mu); + grpc_core::Fork::SetResetChildPollingEngineFunc(nullptr); + } +} + +static const grpc_event_engine_vtable vtable = { + sizeof(grpc_pollset), + false, + false, + + fd_create, + fd_wrapped_fd, + fd_orphan, + fd_shutdown, + fd_notify_on_read, + fd_notify_on_write, + fd_notify_on_error, + fd_set_readable, + fd_set_writable, + fd_set_error, + fd_is_shutdown, + + pollset_init, + pollset_shutdown, + pollset_destroy, + pollset_work, + pollset_kick, + pollset_add_fd, + + pollset_set_create, + pollset_set_destroy, + pollset_set_add_pollset, + pollset_set_del_pollset, + pollset_set_add_pollset_set, + pollset_set_del_pollset_set, + pollset_set_add_fd, + pollset_set_del_fd, + + shutdown_background_closure, + shutdown_engine, +}; + +/* Called by the child process's post-fork handler to close open fds, including + * worker wakeup fds. This allows gRPC to shutdown in the child process without + * interfering with connections or RPCs ongoing in the parent. */ +static void reset_event_manager_on_fork() { + gpr_mu_lock(&fork_fd_list_mu); + while (fork_fd_list_head != nullptr) { + if (fork_fd_list_head->fd != nullptr) { + close(fork_fd_list_head->fd->fd); + fork_fd_list_head->fd->fd = -1; + } else { + close(fork_fd_list_head->cached_wakeup_fd->fd.read_fd); + fork_fd_list_head->cached_wakeup_fd->fd.read_fd = -1; + close(fork_fd_list_head->cached_wakeup_fd->fd.write_fd); + fork_fd_list_head->cached_wakeup_fd->fd.write_fd = -1; + } + fork_fd_list_head = fork_fd_list_head->next; + } + gpr_mu_unlock(&fork_fd_list_mu); +} + +const grpc_event_engine_vtable* grpc_init_poll_posix(bool explicit_request) { + if (!grpc_has_wakeup_fd()) { + gpr_log(GPR_ERROR, "Skipping poll because of no wakeup fd."); + return nullptr; + } + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + return nullptr; + } + if (grpc_core::Fork::Enabled()) { + track_fds_for_fork = true; + gpr_mu_init(&fork_fd_list_mu); + grpc_core::Fork::SetResetChildPollingEngineFunc( + reset_event_manager_on_fork); + } + return &vtable; +} + +const grpc_event_engine_vtable* grpc_init_poll_cv_posix(bool explicit_request) { + global_cv_fd_table_init(); + grpc_enable_cv_wakeup_fds(1); + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + global_cv_fd_table_shutdown(); + grpc_enable_cv_wakeup_fds(0); + return nullptr; + } + return &vtable; +} + +#endif /* GRPC_POSIX_SOCKET_EV_POLL */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.h b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.h new file mode 100644 index 00000000000000..ab3cd9029e6bd9 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_poll_posix.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H +#define GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H + +#include + +#include "src/core/lib/iomgr/ev_posix.h" + +const grpc_event_engine_vtable* grpc_init_poll_posix(bool explicit_request); +const grpc_event_engine_vtable* grpc_init_poll_cv_posix(bool explicit_request); + +#endif /* GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_posix.cc b/third_party/grpc/src/core/lib/iomgr/ev_posix.cc new file mode 100644 index 00000000000000..32d1b6c43ef5a4 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_posix.cc @@ -0,0 +1,406 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_EV + +#include "src/core/lib/iomgr/ev_posix.h" + +#include + +#include +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/ev_epoll1_linux.h" +#include "src/core/lib/iomgr/ev_epollex_linux.h" +#include "src/core/lib/iomgr/ev_poll_posix.h" +#include "src/core/lib/iomgr/internal_errqueue.h" + +grpc_core::TraceFlag grpc_polling_trace(false, + "polling"); /* Disabled by default */ + +/* Traces fd create/close operations */ +grpc_core::TraceFlag grpc_fd_trace(false, "fd_trace"); +grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount"); +grpc_core::DebugOnlyTraceFlag grpc_polling_api_trace(false, "polling_api"); + +#ifndef NDEBUG + +// Polling API trace only enabled in debug builds +#define GRPC_POLLING_API_TRACE(format, ...) \ + if (grpc_polling_api_trace.enabled()) { \ + gpr_log(GPR_INFO, "(polling-api) " format, __VA_ARGS__); \ + } +#else +#define GRPC_POLLING_API_TRACE(...) +#endif + +/** Default poll() function - a pointer so that it can be overridden by some + * tests */ +#ifndef GPR_AIX +grpc_poll_function_type grpc_poll_function = poll; +#else +int aix_poll(struct pollfd fds[], nfds_t nfds, int timeout) { + return poll(fds, nfds, timeout); +} +grpc_poll_function_type grpc_poll_function = aix_poll; +#endif + +grpc_wakeup_fd grpc_global_wakeup_fd; + +static const grpc_event_engine_vtable* g_event_engine = nullptr; +static const char* g_poll_strategy_name = nullptr; + +typedef const grpc_event_engine_vtable* (*event_engine_factory_fn)( + bool explicit_request); + +typedef struct { + const char* name; + event_engine_factory_fn factory; +} event_engine_factory; + +namespace { + +grpc_poll_function_type real_poll_function; + +int dummy_poll(struct pollfd fds[], nfds_t nfds, int timeout) { + if (timeout == 0) { + return real_poll_function(fds, nfds, 0); + } else { + gpr_log(GPR_ERROR, "Attempted a blocking poll when declared non-polling."); + GPR_ASSERT(false); + return -1; + } +} + +const grpc_event_engine_vtable* init_non_polling(bool explicit_request) { + if (!explicit_request) { + return nullptr; + } + // return the simplest engine as a dummy but also override the poller + auto ret = grpc_init_poll_posix(explicit_request); + real_poll_function = grpc_poll_function; + grpc_poll_function = dummy_poll; + + return ret; +} +} // namespace + +#define ENGINE_HEAD_CUSTOM "head_custom" +#define ENGINE_TAIL_CUSTOM "tail_custom" + +// The global array of event-engine factories. Each entry is a pair with a name +// and an event-engine generator function (nullptr if there is no generator +// registered for this name). The middle entries are the engines predefined by +// open-source gRPC. The head entries represent an opportunity for specific +// high-priority custom pollers to be added by the initializer plugins of +// custom-built gRPC libraries. The tail entries represent the same, but for +// low-priority custom pollers. The actual poller selected is either the first +// available one in the list if no specific poller is requested, or the first +// specific poller that is requested by name in the GRPC_POLL_STRATEGY +// environment variable if that variable is set (which should be a +// comma-separated list of one or more event engine names) +static event_engine_factory g_factories[] = { + {ENGINE_HEAD_CUSTOM, nullptr}, {ENGINE_HEAD_CUSTOM, nullptr}, + {ENGINE_HEAD_CUSTOM, nullptr}, {ENGINE_HEAD_CUSTOM, nullptr}, + {"epollex", grpc_init_epollex_linux}, {"epoll1", grpc_init_epoll1_linux}, + {"poll", grpc_init_poll_posix}, {"poll-cv", grpc_init_poll_cv_posix}, + {"none", init_non_polling}, {ENGINE_TAIL_CUSTOM, nullptr}, + {ENGINE_TAIL_CUSTOM, nullptr}, {ENGINE_TAIL_CUSTOM, nullptr}, + {ENGINE_TAIL_CUSTOM, nullptr}, +}; + +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; + size_t len; + GPR_ASSERT(end >= beg); + len = static_cast(end - beg); + s = static_cast(gpr_malloc(len + 1)); + memcpy(s, beg, len); + s[len] = 0; + *ss = static_cast(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 == nullptr) { + add(s, s + strlen(s), ss, ns); + } else { + add(s, c, ss, ns); + split(c + 1, ss, ns); + } +} + +static bool is(const char* want, const char* have) { + return 0 == strcmp(want, "all") || 0 == strcmp(want, have); +} + +static void try_engine(const char* engine) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) { + if (g_factories[i].factory != nullptr && is(engine, g_factories[i].name)) { + if ((g_event_engine = g_factories[i].factory( + 0 == strcmp(engine, g_factories[i].name)))) { + g_poll_strategy_name = g_factories[i].name; + gpr_log(GPR_DEBUG, "Using polling engine: %s", g_factories[i].name); + return; + } + } + } +} + +/* Call this before calling grpc_event_engine_init() */ +void grpc_register_event_engine_factory(const char* name, + event_engine_factory_fn factory, + bool add_at_head) { + const char* custom_match = + add_at_head ? ENGINE_HEAD_CUSTOM : ENGINE_TAIL_CUSTOM; + + // Overwrite an existing registration if already registered + for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) { + if (0 == strcmp(name, g_factories[i].name)) { + g_factories[i].factory = factory; + return; + } + } + + // Otherwise fill in an available custom slot + for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) { + if (0 == strcmp(g_factories[i].name, custom_match)) { + g_factories[i].name = name; + g_factories[i].factory = factory; + return; + } + } + + // Otherwise fail + GPR_ASSERT(false); +} + +/* Call this only after calling grpc_event_engine_init() */ +const char* grpc_get_poll_strategy_name() { return g_poll_strategy_name; } + +void grpc_event_engine_init(void) { + char* s = gpr_getenv("GRPC_POLL_STRATEGY"); + if (s == nullptr) { + s = gpr_strdup("all"); + } + + char** strings = nullptr; + size_t nstrings = 0; + split(s, &strings, &nstrings); + + for (size_t i = 0; g_event_engine == nullptr && i < nstrings; i++) { + try_engine(strings[i]); + } + + for (size_t i = 0; i < nstrings; i++) { + gpr_free(strings[i]); + } + gpr_free(strings); + + if (g_event_engine == nullptr) { + gpr_log(GPR_ERROR, "No event engine could be initialized from %s", s); + abort(); + } + gpr_free(s); +} + +void grpc_event_engine_shutdown(void) { + g_event_engine->shutdown_engine(); + g_event_engine = nullptr; +} + +bool grpc_event_engine_can_track_errors(void) { + /* Only track errors if platform supports errqueue. */ + if (grpc_core::kernel_supports_errqueue()) { + return g_event_engine->can_track_err; + } + return false; +} + +bool grpc_event_engine_run_in_background(void) { + return g_event_engine->run_in_background; +} + +grpc_fd* grpc_fd_create(int fd, const char* name, bool track_err) { + GRPC_POLLING_API_TRACE("fd_create(%d, %s, %d)", fd, name, track_err); + GRPC_FD_TRACE("fd_create(%d, %s, %d)", fd, name, track_err); + return g_event_engine->fd_create( + fd, name, track_err && grpc_event_engine_can_track_errors()); +} + +int grpc_fd_wrapped_fd(grpc_fd* fd) { + return g_event_engine->fd_wrapped_fd(fd); +} + +void grpc_fd_orphan(grpc_fd* fd, grpc_closure* on_done, int* release_fd, + const char* reason) { + GRPC_POLLING_API_TRACE("fd_orphan(%d, %p, %p, %s)", grpc_fd_wrapped_fd(fd), + on_done, release_fd, reason); + GRPC_FD_TRACE("grpc_fd_orphan, fd:%d closed", grpc_fd_wrapped_fd(fd)); + + g_event_engine->fd_orphan(fd, on_done, release_fd, reason); +} + +void grpc_fd_shutdown(grpc_fd* fd, grpc_error* why) { + GRPC_POLLING_API_TRACE("fd_shutdown(%d)", grpc_fd_wrapped_fd(fd)); + GRPC_FD_TRACE("fd_shutdown(%d)", grpc_fd_wrapped_fd(fd)); + g_event_engine->fd_shutdown(fd, why); +} + +bool grpc_fd_is_shutdown(grpc_fd* fd) { + return g_event_engine->fd_is_shutdown(fd); +} + +void grpc_fd_notify_on_read(grpc_fd* fd, grpc_closure* closure) { + g_event_engine->fd_notify_on_read(fd, closure); +} + +void grpc_fd_notify_on_write(grpc_fd* fd, grpc_closure* closure) { + g_event_engine->fd_notify_on_write(fd, closure); +} + +void grpc_fd_notify_on_error(grpc_fd* fd, grpc_closure* closure) { + g_event_engine->fd_notify_on_error(fd, closure); +} + +void grpc_fd_set_readable(grpc_fd* fd) { g_event_engine->fd_set_readable(fd); } + +void grpc_fd_set_writable(grpc_fd* fd) { g_event_engine->fd_set_writable(fd); } + +void grpc_fd_set_error(grpc_fd* fd) { g_event_engine->fd_set_error(fd); } + +static size_t pollset_size(void) { return g_event_engine->pollset_size; } + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + GRPC_POLLING_API_TRACE("pollset_init(%p)", pollset); + g_event_engine->pollset_init(pollset, mu); +} + +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + GRPC_POLLING_API_TRACE("pollset_shutdown(%p)", pollset); + g_event_engine->pollset_shutdown(pollset, closure); +} + +static void pollset_destroy(grpc_pollset* pollset) { + GRPC_POLLING_API_TRACE("pollset_destroy(%p)", pollset); + g_event_engine->pollset_destroy(pollset); +} + +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) { + GRPC_POLLING_API_TRACE("pollset_work(%p, %" PRId64 ") begin", pollset, + deadline); + grpc_error* err = g_event_engine->pollset_work(pollset, worker, deadline); + GRPC_POLLING_API_TRACE("pollset_work(%p, %" PRId64 ") end", pollset, + deadline); + return err; +} + +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + GRPC_POLLING_API_TRACE("pollset_kick(%p, %p)", pollset, specific_worker); + return g_event_engine->pollset_kick(pollset, specific_worker); +} + +void grpc_pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd) { + GRPC_POLLING_API_TRACE("pollset_add_fd(%p, %d)", pollset, + grpc_fd_wrapped_fd(fd)); + g_event_engine->pollset_add_fd(pollset, fd); +} + +void pollset_global_init() {} +void pollset_global_shutdown() {} + +grpc_pollset_vtable grpc_posix_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + +static grpc_pollset_set* pollset_set_create(void) { + grpc_pollset_set* pss = g_event_engine->pollset_set_create(); + GRPC_POLLING_API_TRACE("pollset_set_create(%p)", pss); + return pss; +} + +static void pollset_set_destroy(grpc_pollset_set* pollset_set) { + GRPC_POLLING_API_TRACE("pollset_set_destroy(%p)", pollset_set); + g_event_engine->pollset_set_destroy(pollset_set); +} + +static void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { + GRPC_POLLING_API_TRACE("pollset_set_add_pollset(%p, %p)", pollset_set, + pollset); + g_event_engine->pollset_set_add_pollset(pollset_set, pollset); +} + +static void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { + GRPC_POLLING_API_TRACE("pollset_set_del_pollset(%p, %p)", pollset_set, + pollset); + g_event_engine->pollset_set_del_pollset(pollset_set, pollset); +} + +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + GRPC_POLLING_API_TRACE("pollset_set_add_pollset_set(%p, %p)", bag, item); + g_event_engine->pollset_set_add_pollset_set(bag, item); +} + +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + GRPC_POLLING_API_TRACE("pollset_set_del_pollset_set(%p, %p)", bag, item); + g_event_engine->pollset_set_del_pollset_set(bag, item); +} + +grpc_pollset_set_vtable grpc_posix_pollset_set_vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; + +void grpc_pollset_set_add_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) { + GRPC_POLLING_API_TRACE("pollset_set_add_fd(%p, %d)", pollset_set, + grpc_fd_wrapped_fd(fd)); + g_event_engine->pollset_set_add_fd(pollset_set, fd); +} + +void grpc_pollset_set_del_fd(grpc_pollset_set* pollset_set, grpc_fd* fd) { + GRPC_POLLING_API_TRACE("pollset_set_del_fd(%p, %d)", pollset_set, + grpc_fd_wrapped_fd(fd)); + g_event_engine->pollset_set_del_fd(pollset_set, fd); +} + +void grpc_shutdown_background_closure(void) { + g_event_engine->shutdown_background_closure(); +} + +#endif // GRPC_POSIX_SOCKET_EV diff --git a/third_party/grpc/src/core/lib/iomgr/ev_posix.h b/third_party/grpc/src/core/lib/iomgr/ev_posix.h new file mode 100644 index 00000000000000..812c7a0f0f8150 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_posix.h @@ -0,0 +1,191 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_EV_POSIX_H +#define GRPC_CORE_LIB_IOMGR_EV_POSIX_H + +#include + +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +extern grpc_core::TraceFlag grpc_fd_trace; /* Disabled by default */ +extern grpc_core::TraceFlag grpc_polling_trace; /* Disabled by default */ + +#define GRPC_FD_TRACE(format, ...) \ + if (grpc_fd_trace.enabled()) { \ + gpr_log(GPR_INFO, "(fd-trace) " format, __VA_ARGS__); \ + } + +typedef struct grpc_fd grpc_fd; + +typedef struct grpc_event_engine_vtable { + size_t pollset_size; + bool can_track_err; + bool run_in_background; + + grpc_fd* (*fd_create)(int fd, const char* name, bool track_err); + int (*fd_wrapped_fd)(grpc_fd* fd); + void (*fd_orphan)(grpc_fd* fd, grpc_closure* on_done, int* release_fd, + const char* reason); + void (*fd_shutdown)(grpc_fd* fd, grpc_error* why); + void (*fd_notify_on_read)(grpc_fd* fd, grpc_closure* closure); + void (*fd_notify_on_write)(grpc_fd* fd, grpc_closure* closure); + void (*fd_notify_on_error)(grpc_fd* fd, grpc_closure* closure); + void (*fd_set_readable)(grpc_fd* fd); + void (*fd_set_writable)(grpc_fd* fd); + void (*fd_set_error)(grpc_fd* fd); + bool (*fd_is_shutdown)(grpc_fd* fd); + + void (*pollset_init)(grpc_pollset* pollset, gpr_mu** mu); + void (*pollset_shutdown)(grpc_pollset* pollset, grpc_closure* closure); + void (*pollset_destroy)(grpc_pollset* pollset); + grpc_error* (*pollset_work)(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline); + grpc_error* (*pollset_kick)(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker); + void (*pollset_add_fd)(grpc_pollset* pollset, struct grpc_fd* fd); + + grpc_pollset_set* (*pollset_set_create)(void); + void (*pollset_set_destroy)(grpc_pollset_set* pollset_set); + void (*pollset_set_add_pollset)(grpc_pollset_set* pollset_set, + grpc_pollset* pollset); + void (*pollset_set_del_pollset)(grpc_pollset_set* pollset_set, + grpc_pollset* pollset); + void (*pollset_set_add_pollset_set)(grpc_pollset_set* bag, + grpc_pollset_set* item); + void (*pollset_set_del_pollset_set)(grpc_pollset_set* bag, + grpc_pollset_set* item); + void (*pollset_set_add_fd)(grpc_pollset_set* pollset_set, grpc_fd* fd); + void (*pollset_set_del_fd)(grpc_pollset_set* pollset_set, grpc_fd* fd); + + void (*shutdown_background_closure)(void); + void (*shutdown_engine)(void); +} grpc_event_engine_vtable; + +/* register a new event engine factory */ +void grpc_register_event_engine_factory( + const char* name, const grpc_event_engine_vtable* (*factory)(bool), + bool add_at_head); + +void grpc_event_engine_init(void); +void grpc_event_engine_shutdown(void); + +/* Return the name of the poll strategy */ +const char* grpc_get_poll_strategy_name(); + +/* Returns true if polling engine can track errors separately, false otherwise. + * If this is true, fd can be created with track_err set. After this, error + * events will be reported using fd_notify_on_error. If it is not set, errors + * will continue to be reported through fd_notify_on_read and + * fd_notify_on_write. + */ +bool grpc_event_engine_can_track_errors(); + +/* Returns true if polling engine runs in the background, false otherwise. + * Currently only 'epollbg' runs in the background. + */ +bool grpc_event_engine_run_in_background(); + +/* Create a wrapped file descriptor. + Requires fd is a non-blocking file descriptor. + \a track_err if true means that error events would be tracked separately + using grpc_fd_notify_on_error. Currently, valid only for linux systems. + This takes ownership of closing fd. */ +grpc_fd* grpc_fd_create(int fd, const char* name, bool track_err); + +/* Return the wrapped fd, or -1 if it has been released or closed. */ +int grpc_fd_wrapped_fd(grpc_fd* fd); + +/* 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. + If release_fd is not NULL, it's set to fd and fd will not be closed. + 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_closure* on_done, int* release_fd, + const char* reason); + +/* Has grpc_fd_shutdown been called on an fd? */ +bool grpc_fd_is_shutdown(grpc_fd* fd); + +/* Cause any current and future callbacks to fail. */ +void grpc_fd_shutdown(grpc_fd* fd, grpc_error* why); + +/* 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_closure* closure); + +/* Exactly the same semantics as above, except based on writable events. */ +void grpc_fd_notify_on_write(grpc_fd* fd, grpc_closure* closure); + +/* Exactly the same semantics as above, except based on error events. track_err + * needs to have been set on grpc_fd_create */ +void grpc_fd_notify_on_error(grpc_fd* fd, grpc_closure* closure); + +/* Forcibly set the fd to be readable, resulting in the closure registered with + * grpc_fd_notify_on_read being invoked. + */ +void grpc_fd_set_readable(grpc_fd* fd); + +/* Forcibly set the fd to be writable, resulting in the closure registered with + * grpc_fd_notify_on_write being invoked. + */ +void grpc_fd_set_writable(grpc_fd* fd); + +/* Forcibly set the fd to have errored, resulting in the closure registered with + * grpc_fd_notify_on_error being invoked. + */ +void grpc_fd_set_error(grpc_fd* fd); + +/* pollset_posix functions */ + +/* Add an fd to a pollset */ +void grpc_pollset_add_fd(grpc_pollset* pollset, struct grpc_fd* fd); + +/* pollset_set_posix functions */ + +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); + +/* Shut down all the closures registered in the background poller. */ +void grpc_shutdown_background_closure(); + +/* 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_CORE_LIB_IOMGR_EV_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/ev_windows.cc b/third_party/grpc/src/core/lib/iomgr/ev_windows.cc new file mode 100644 index 00000000000000..32c62b7a762802 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/ev_windows.cc @@ -0,0 +1,30 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/debug/trace.h" + +grpc_core::TraceFlag grpc_polling_trace(false, + "polling"); /* Disabled by default */ + +#endif // GRPC_WINSOCK_SOCKET diff --git a/third_party/grpc/src/core/lib/iomgr/exec_ctx.cc b/third_party/grpc/src/core/lib/iomgr/exec_ctx.cc new file mode 100644 index 00000000000000..683dd2f649330b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/exec_ctx.cc @@ -0,0 +1,166 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" + +#include +#include + +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/profiling/timers.h" + +static void exec_ctx_run(grpc_closure* closure, grpc_error* error) { +#ifndef NDEBUG + closure->scheduled = false; + if (grpc_trace_closure.enabled()) { + gpr_log(GPR_DEBUG, "running closure %p: created [%s:%d]: %s [%s:%d]", + closure, closure->file_created, closure->line_created, + closure->run ? "run" : "scheduled", closure->file_initiated, + closure->line_initiated); + } +#endif + closure->cb(closure->cb_arg, error); +#ifndef NDEBUG + if (grpc_trace_closure.enabled()) { + gpr_log(GPR_DEBUG, "closure %p finished", closure); + } +#endif + GRPC_ERROR_UNREF(error); +} + +static void exec_ctx_sched(grpc_closure* closure, grpc_error* error) { + grpc_closure_list_append(grpc_core::ExecCtx::Get()->closure_list(), closure, + error); +} + +static gpr_timespec g_start_time; + +// For debug of the timer manager crash only. +// TODO (mxyan): remove after bug is fixed. +#ifdef GRPC_DEBUG_TIMER_MANAGER +extern int64_t g_start_time_sec; +extern int64_t g_start_time_nsec; +#endif // GRPC_DEBUG_TIMER_MANAGER + +static grpc_millis timespec_to_millis_round_down(gpr_timespec ts) { + ts = gpr_time_sub(ts, g_start_time); + double x = GPR_MS_PER_SEC * static_cast(ts.tv_sec) + + static_cast(ts.tv_nsec) / GPR_NS_PER_MS; + if (x < 0) return 0; + if (x > GRPC_MILLIS_INF_FUTURE) return GRPC_MILLIS_INF_FUTURE; + return static_cast(x); +} + +static grpc_millis timespec_to_millis_round_up(gpr_timespec ts) { + ts = gpr_time_sub(ts, g_start_time); + double x = GPR_MS_PER_SEC * static_cast(ts.tv_sec) + + static_cast(ts.tv_nsec) / GPR_NS_PER_MS + + static_cast(GPR_NS_PER_SEC - 1) / + static_cast(GPR_NS_PER_SEC); + if (x < 0) return 0; + if (x > GRPC_MILLIS_INF_FUTURE) return GRPC_MILLIS_INF_FUTURE; + return static_cast(x); +} + +gpr_timespec grpc_millis_to_timespec(grpc_millis millis, + gpr_clock_type clock_type) { + // special-case infinities as grpc_millis can be 32bit on some platforms + // while gpr_time_from_millis always takes an int64_t. + if (millis == GRPC_MILLIS_INF_FUTURE) { + return gpr_inf_future(clock_type); + } + if (millis == GRPC_MILLIS_INF_PAST) { + return gpr_inf_past(clock_type); + } + + if (clock_type == GPR_TIMESPAN) { + return gpr_time_from_millis(millis, GPR_TIMESPAN); + } + return gpr_time_add(gpr_convert_clock_type(g_start_time, clock_type), + gpr_time_from_millis(millis, GPR_TIMESPAN)); +} + +grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec ts) { + return timespec_to_millis_round_down( + gpr_convert_clock_type(ts, g_start_time.clock_type)); +} + +grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec ts) { + return timespec_to_millis_round_up( + gpr_convert_clock_type(ts, g_start_time.clock_type)); +} + +static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = { + exec_ctx_run, exec_ctx_sched, "exec_ctx"}; +static grpc_closure_scheduler exec_ctx_scheduler = {&exec_ctx_scheduler_vtable}; +grpc_closure_scheduler* grpc_schedule_on_exec_ctx = &exec_ctx_scheduler; + +namespace grpc_core { +GPR_TLS_CLASS_DEF(ExecCtx::exec_ctx_); + +// WARNING: for testing purposes only! +void ExecCtx::TestOnlyGlobalInit(gpr_timespec new_val) { + g_start_time = new_val; + gpr_tls_init(&exec_ctx_); +} + +void ExecCtx::GlobalInit(void) { + g_start_time = gpr_now(GPR_CLOCK_MONOTONIC); + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. +#ifdef GRPC_DEBUG_TIMER_MANAGER + g_start_time_sec = g_start_time.tv_sec; + g_start_time_nsec = g_start_time.tv_nsec; +#endif + gpr_tls_init(&exec_ctx_); +} + +bool ExecCtx::Flush() { + bool did_something = 0; + GPR_TIMER_SCOPE("grpc_exec_ctx_flush", 0); + for (;;) { + if (!grpc_closure_list_empty(closure_list_)) { + grpc_closure* c = closure_list_.head; + closure_list_.head = closure_list_.tail = nullptr; + while (c != nullptr) { + grpc_closure* next = c->next_data.next; + grpc_error* error = c->error_data.error; + did_something = true; + exec_ctx_run(c, error); + c = next; + } + } else if (!grpc_combiner_continue_exec_ctx()) { + break; + } + } + GPR_ASSERT(combiner_data_.active_combiner == nullptr); + return did_something; +} + +grpc_millis ExecCtx::Now() { + if (!now_is_valid_) { + now_ = timespec_to_millis_round_down(gpr_now(GPR_CLOCK_MONOTONIC)); + now_is_valid_ = true; + } + return now_; +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/iomgr/exec_ctx.h b/third_party/grpc/src/core/lib/iomgr/exec_ctx.h new file mode 100644 index 00000000000000..e90eb54cd3533e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/exec_ctx.h @@ -0,0 +1,231 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_EXEC_CTX_H +#define GRPC_CORE_LIB_IOMGR_EXEC_CTX_H + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/tls.h" +#include "src/core/lib/gprpp/fork.h" +#include "src/core/lib/iomgr/closure.h" + +typedef int64_t grpc_millis; + +#define GRPC_MILLIS_INF_FUTURE INT64_MAX +#define GRPC_MILLIS_INF_PAST INT64_MIN + +/** A workqueue represents a list of work to be executed asynchronously. + Forward declared here to avoid a circular dependency with workqueue.h. */ +typedef struct grpc_workqueue grpc_workqueue; +typedef struct grpc_combiner grpc_combiner; + +/* This exec_ctx is ready to return: either pre-populated, or cached as soon as + the finish_check returns true */ +#define GRPC_EXEC_CTX_FLAG_IS_FINISHED 1 +/* The exec_ctx's thread is (potentially) owned by a call or channel: care + should be given to not delete said call/channel from this exec_ctx */ +#define GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP 2 +/* This exec ctx was initialized by an internal thread, and should not + be counted by fork handlers */ +#define GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD 4 + +extern grpc_closure_scheduler* grpc_schedule_on_exec_ctx; + +gpr_timespec grpc_millis_to_timespec(grpc_millis millis, gpr_clock_type clock); +grpc_millis grpc_timespec_to_millis_round_down(gpr_timespec timespec); +grpc_millis grpc_timespec_to_millis_round_up(gpr_timespec timespec); + +namespace grpc_core { +/** Execution context. + * A bag of data that collects information along a callstack. + * It is created on the stack at public API entry points, and stored internally + * as a thread-local variable. + * + * Generally, to create an exec_ctx instance, add the following line at the top + * of the public API entry point or at the start of a thread's work function : + * + * grpc_core::ExecCtx exec_ctx; + * + * Access the created ExecCtx instance using : + * grpc_core::ExecCtx::Get() + * + * Specific responsibilities (this may grow in the future): + * - track a list of work that needs to be delayed until the top of the + * call stack (this provides a convenient mechanism to run callbacks + * without worrying about locking issues) + * - provide a decision maker (via IsReadyToFinish) that provides a + * signal as to whether a borrowed thread should continue to do work or + * should actively try to finish up and get this thread back to its owner + * + * CONVENTIONS: + * - Instance of this must ALWAYS be constructed on the stack, never + * heap allocated. + * - Exactly one instance of ExecCtx must be created per thread. Instances must + * always be called exec_ctx. + * - Do not pass exec_ctx as a parameter to a function. Always access it using + * grpc_core::ExecCtx::Get(). + */ +class ExecCtx { + public: + /** Default Constructor */ + + ExecCtx() : flags_(GRPC_EXEC_CTX_FLAG_IS_FINISHED) { + grpc_core::Fork::IncExecCtxCount(); + Set(this); + } + + /** Parameterised Constructor */ + ExecCtx(uintptr_t fl) : flags_(fl) { + if (!(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD & flags_)) { + grpc_core::Fork::IncExecCtxCount(); + } + Set(this); + } + + /** Destructor */ + virtual ~ExecCtx() { + flags_ |= GRPC_EXEC_CTX_FLAG_IS_FINISHED; + Flush(); + Set(last_exec_ctx_); + if (!(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD & flags_)) { + grpc_core::Fork::DecExecCtxCount(); + } + } + + /** Disallow copy and assignment operators */ + ExecCtx(const ExecCtx&) = delete; + ExecCtx& operator=(const ExecCtx&) = delete; + + unsigned starting_cpu() const { return starting_cpu_; } + + struct CombinerData { + /* currently active combiner: updated only via combiner.c */ + grpc_combiner* active_combiner; + /* last active combiner in the active combiner list */ + grpc_combiner* last_combiner; + }; + + /** Only to be used by grpc-combiner code */ + CombinerData* combiner_data() { return &combiner_data_; } + + /** Return pointer to grpc_closure_list */ + grpc_closure_list* closure_list() { return &closure_list_; } + + /** Return flags */ + uintptr_t flags() { return flags_; } + + /** Checks if there is work to be done */ + bool HasWork() { + return combiner_data_.active_combiner != nullptr || + !grpc_closure_list_empty(closure_list_); + } + + /** Flush any work that has been enqueued onto this grpc_exec_ctx. + * Caller must guarantee that no interfering locks are held. + * Returns true if work was performed, false otherwise. + */ + bool Flush(); + + /** Returns true if we'd like to leave this execution context as soon as + * possible: useful for deciding whether to do something more or not + * depending on outside context. + */ + bool IsReadyToFinish() { + if ((flags_ & GRPC_EXEC_CTX_FLAG_IS_FINISHED) == 0) { + if (CheckReadyToFinish()) { + flags_ |= GRPC_EXEC_CTX_FLAG_IS_FINISHED; + return true; + } + return false; + } else { + return true; + } + } + + /** Returns the stored current time relative to start if valid, + * otherwise refreshes the stored time, sets it valid and returns the new + * value. + */ + grpc_millis Now(); + + /** Invalidates the stored time value. A new time value will be set on calling + * Now(). + */ + void InvalidateNow() { now_is_valid_ = false; } + + /** To be used only by shutdown code in iomgr */ + void SetNowIomgrShutdown() { + now_ = GRPC_MILLIS_INF_FUTURE; + now_is_valid_ = true; + } + + /** To be used only for testing. + * Sets the now value. + */ + void TestOnlySetNow(grpc_millis new_val) { + now_ = new_val; + now_is_valid_ = true; + } + + static void TestOnlyGlobalInit(gpr_timespec new_val); + + /** Global initialization for ExecCtx. Called by iomgr. */ + static void GlobalInit(void); + + /** Global shutdown for ExecCtx. Called by iomgr. */ + static void GlobalShutdown(void) { gpr_tls_destroy(&exec_ctx_); } + + /** Gets pointer to current exec_ctx. */ + static ExecCtx* Get() { + return reinterpret_cast(gpr_tls_get(&exec_ctx_)); + } + + static void Set(ExecCtx* exec_ctx) { + gpr_tls_set(&exec_ctx_, reinterpret_cast(exec_ctx)); + } + + protected: + /** Check if ready to finish. */ + virtual bool CheckReadyToFinish() { return false; } + + /** Disallow delete on ExecCtx. */ + static void operator delete(void* p) { abort(); } + + private: + /** Set exec_ctx_ to exec_ctx. */ + + grpc_closure_list closure_list_ = GRPC_CLOSURE_LIST_INIT; + CombinerData combiner_data_ = {nullptr, nullptr}; + uintptr_t flags_; + + unsigned starting_cpu_ = gpr_cpu_current_cpu(); + + bool now_is_valid_ = false; + grpc_millis now_ = 0; + + GPR_TLS_CLASS_DECL(exec_ctx_); + ExecCtx* last_exec_ctx_ = Get(); +}; +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_IOMGR_EXEC_CTX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/executor.cc b/third_party/grpc/src/core/lib/iomgr/executor.cc new file mode 100644 index 00000000000000..45d96b80eb4a24 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/executor.cc @@ -0,0 +1,433 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/executor.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/tls.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +#define MAX_DEPTH 2 + +#define EXECUTOR_TRACE(format, ...) \ + if (executor_trace.enabled()) { \ + gpr_log(GPR_INFO, "EXECUTOR " format, __VA_ARGS__); \ + } + +#define EXECUTOR_TRACE0(str) \ + if (executor_trace.enabled()) { \ + gpr_log(GPR_INFO, "EXECUTOR " str); \ + } + +grpc_core::TraceFlag executor_trace(false, "executor"); + +GPR_TLS_DECL(g_this_thread_state); + +GrpcExecutor::GrpcExecutor(const char* name) : name_(name) { + adding_thread_lock_ = GPR_SPINLOCK_STATIC_INITIALIZER; + gpr_atm_rel_store(&num_threads_, 0); + max_threads_ = GPR_MAX(1, 2 * gpr_cpu_num_cores()); +} + +void GrpcExecutor::Init() { SetThreading(true); } + +size_t GrpcExecutor::RunClosures(const char* executor_name, + grpc_closure_list list) { + size_t n = 0; + + grpc_closure* c = list.head; + while (c != nullptr) { + grpc_closure* next = c->next_data.next; + grpc_error* error = c->error_data.error; +#ifndef NDEBUG + EXECUTOR_TRACE("(%s) run %p [created by %s:%d]", executor_name, c, + c->file_created, c->line_created); + c->scheduled = false; +#else + EXECUTOR_TRACE("(%s) run %p", executor_name, c); +#endif + c->cb(c->cb_arg, error); + GRPC_ERROR_UNREF(error); + c = next; + n++; + grpc_core::ExecCtx::Get()->Flush(); + } + + return n; +} + +bool GrpcExecutor::IsThreaded() const { + return gpr_atm_acq_load(&num_threads_) > 0; +} + +void GrpcExecutor::SetThreading(bool threading) { + gpr_atm curr_num_threads = gpr_atm_acq_load(&num_threads_); + EXECUTOR_TRACE("(%s) SetThreading(%d) begin", name_, threading); + + if (threading) { + if (curr_num_threads > 0) { + EXECUTOR_TRACE("(%s) SetThreading(true). curr_num_threads == 0", name_); + return; + } + + GPR_ASSERT(num_threads_ == 0); + gpr_atm_rel_store(&num_threads_, 1); + gpr_tls_init(&g_this_thread_state); + thd_state_ = static_cast( + gpr_zalloc(sizeof(ThreadState) * max_threads_)); + + for (size_t i = 0; i < max_threads_; i++) { + gpr_mu_init(&thd_state_[i].mu); + gpr_cv_init(&thd_state_[i].cv); + thd_state_[i].id = i; + thd_state_[i].name = name_; + thd_state_[i].thd = grpc_core::Thread(); + thd_state_[i].elems = GRPC_CLOSURE_LIST_INIT; + } + + thd_state_[0].thd = + grpc_core::Thread(name_, &GrpcExecutor::ThreadMain, &thd_state_[0]); + thd_state_[0].thd.Start(); + } else { // !threading + if (curr_num_threads == 0) { + EXECUTOR_TRACE("(%s) SetThreading(false). curr_num_threads == 0", name_); + return; + } + + for (size_t i = 0; i < max_threads_; i++) { + gpr_mu_lock(&thd_state_[i].mu); + thd_state_[i].shutdown = true; + gpr_cv_signal(&thd_state_[i].cv); + gpr_mu_unlock(&thd_state_[i].mu); + } + + /* Ensure no thread is adding a new thread. Once this is past, then no + * thread will try to add a new one either (since shutdown is true) */ + gpr_spinlock_lock(&adding_thread_lock_); + gpr_spinlock_unlock(&adding_thread_lock_); + + curr_num_threads = gpr_atm_no_barrier_load(&num_threads_); + for (gpr_atm i = 0; i < curr_num_threads; i++) { + thd_state_[i].thd.Join(); + EXECUTOR_TRACE("(%s) Thread %" PRIdPTR " of %" PRIdPTR " joined", name_, + i + 1, curr_num_threads); + } + + gpr_atm_rel_store(&num_threads_, 0); + for (size_t i = 0; i < max_threads_; i++) { + gpr_mu_destroy(&thd_state_[i].mu); + gpr_cv_destroy(&thd_state_[i].cv); + RunClosures(thd_state_[i].name, thd_state_[i].elems); + } + + gpr_free(thd_state_); + gpr_tls_destroy(&g_this_thread_state); + } + + EXECUTOR_TRACE("(%s) SetThreading(%d) done", name_, threading); +} + +void GrpcExecutor::Shutdown() { SetThreading(false); } + +void GrpcExecutor::ThreadMain(void* arg) { + ThreadState* ts = static_cast(arg); + gpr_tls_set(&g_this_thread_state, reinterpret_cast(ts)); + + grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD); + + size_t subtract_depth = 0; + for (;;) { + EXECUTOR_TRACE("(%s) [%" PRIdPTR "]: step (sub_depth=%" PRIdPTR ")", + ts->name, ts->id, subtract_depth); + + gpr_mu_lock(&ts->mu); + ts->depth -= subtract_depth; + // Wait for closures to be enqueued or for the executor to be shutdown + while (grpc_closure_list_empty(ts->elems) && !ts->shutdown) { + ts->queued_long_job = false; + gpr_cv_wait(&ts->cv, &ts->mu, gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + + if (ts->shutdown) { + EXECUTOR_TRACE("(%s) [%" PRIdPTR "]: shutdown", ts->name, ts->id); + gpr_mu_unlock(&ts->mu); + break; + } + + GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED(); + grpc_closure_list closures = ts->elems; + ts->elems = GRPC_CLOSURE_LIST_INIT; + gpr_mu_unlock(&ts->mu); + + EXECUTOR_TRACE("(%s) [%" PRIdPTR "]: execute", ts->name, ts->id); + + grpc_core::ExecCtx::Get()->InvalidateNow(); + subtract_depth = RunClosures(ts->name, closures); + } +} + +void GrpcExecutor::Enqueue(grpc_closure* closure, grpc_error* error, + bool is_short) { + bool retry_push; + if (is_short) { + GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS(); + } else { + GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS(); + } + + do { + retry_push = false; + size_t cur_thread_count = + static_cast(gpr_atm_acq_load(&num_threads_)); + + // If the number of threads is zero(i.e either the executor is not threaded + // or already shutdown), then queue the closure on the exec context itself + if (cur_thread_count == 0) { +#ifndef NDEBUG + EXECUTOR_TRACE("(%s) schedule %p (created %s:%d) inline", name_, closure, + closure->file_created, closure->line_created); +#else + EXECUTOR_TRACE("(%s) schedule %p inline", name_, closure); +#endif + grpc_closure_list_append(grpc_core::ExecCtx::Get()->closure_list(), + closure, error); + return; + } + + ThreadState* ts = (ThreadState*)gpr_tls_get(&g_this_thread_state); + if (ts == nullptr) { + ts = &thd_state_[GPR_HASH_POINTER(grpc_core::ExecCtx::Get(), + cur_thread_count)]; + } else { + GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF(); + } + + ThreadState* orig_ts = ts; + bool try_new_thread = false; + + for (;;) { +#ifndef NDEBUG + EXECUTOR_TRACE( + "(%s) try to schedule %p (%s) (created %s:%d) to thread " + "%" PRIdPTR, + name_, closure, is_short ? "short" : "long", closure->file_created, + closure->line_created, ts->id); +#else + EXECUTOR_TRACE("(%s) try to schedule %p (%s) to thread %" PRIdPTR, name_, + closure, is_short ? "short" : "long", ts->id); +#endif + + gpr_mu_lock(&ts->mu); + if (ts->queued_long_job) { + // if there's a long job queued, we never queue anything else to this + // queue (since long jobs can take 'infinite' time and we need to + // guarantee no starvation). Spin through queues and try again + gpr_mu_unlock(&ts->mu); + size_t idx = ts->id; + ts = &thd_state_[(idx + 1) % cur_thread_count]; + if (ts == orig_ts) { + // We cycled through all the threads. Retry enqueue again by creating + // a new thread + // + // TODO (sreek): There is a potential issue here. We are + // unconditionally setting try_new_thread to true here. What if the + // executor is shutdown OR if cur_thread_count is already equal to + // max_threads ? + // (Fortunately, this is not an issue yet (as of july 2018) because + // there is only one instance of long job in gRPC and hence we will + // not hit this code path) + retry_push = true; + try_new_thread = true; + break; + } + + continue; // Try the next thread-state + } + + // == Found the thread state (i.e thread) to enqueue this closure! == + + // Also, if this thread has been waiting for closures, wake it up. + // - If grpc_closure_list_empty() is true and the Executor is not + // shutdown, it means that the thread must be waiting in ThreadMain() + // - Note that gpr_cv_signal() won't immediately wakeup the thread. That + // happens after we release the mutex &ts->mu a few lines below + if (grpc_closure_list_empty(ts->elems) && !ts->shutdown) { + GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED(); + gpr_cv_signal(&ts->cv); + } + + grpc_closure_list_append(&ts->elems, closure, error); + + // If we already queued more than MAX_DEPTH number of closures on this + // thread, use this as a hint to create more threads + ts->depth++; + try_new_thread = ts->depth > MAX_DEPTH && + cur_thread_count < max_threads_ && !ts->shutdown; + + ts->queued_long_job = !is_short; + + gpr_mu_unlock(&ts->mu); + break; + } + + if (try_new_thread && gpr_spinlock_trylock(&adding_thread_lock_)) { + cur_thread_count = static_cast(gpr_atm_acq_load(&num_threads_)); + if (cur_thread_count < max_threads_) { + // Increment num_threads (safe to do a store instead of a cas because we + // always increment num_threads under the 'adding_thread_lock') + gpr_atm_rel_store(&num_threads_, cur_thread_count + 1); + + thd_state_[cur_thread_count].thd = grpc_core::Thread( + name_, &GrpcExecutor::ThreadMain, &thd_state_[cur_thread_count]); + thd_state_[cur_thread_count].thd.Start(); + } + gpr_spinlock_unlock(&adding_thread_lock_); + } + + if (retry_push) { + GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES(); + } + } while (retry_push); +} + +static GrpcExecutor* executors[GRPC_NUM_EXECUTORS]; + +void default_enqueue_short(grpc_closure* closure, grpc_error* error) { + executors[GRPC_DEFAULT_EXECUTOR]->Enqueue(closure, error, + true /* is_short */); +} + +void default_enqueue_long(grpc_closure* closure, grpc_error* error) { + executors[GRPC_DEFAULT_EXECUTOR]->Enqueue(closure, error, + false /* is_short */); +} + +void resolver_enqueue_short(grpc_closure* closure, grpc_error* error) { + executors[GRPC_RESOLVER_EXECUTOR]->Enqueue(closure, error, + true /* is_short */); +} + +void resolver_enqueue_long(grpc_closure* closure, grpc_error* error) { + executors[GRPC_RESOLVER_EXECUTOR]->Enqueue(closure, error, + false /* is_short */); +} + +static const grpc_closure_scheduler_vtable + vtables_[GRPC_NUM_EXECUTORS][GRPC_NUM_EXECUTOR_JOB_TYPES] = { + {{&default_enqueue_short, &default_enqueue_short, "def-ex-short"}, + {&default_enqueue_long, &default_enqueue_long, "def-ex-long"}}, + {{&resolver_enqueue_short, &resolver_enqueue_short, "res-ex-short"}, + {&resolver_enqueue_long, &resolver_enqueue_long, "res-ex-long"}}}; + +static grpc_closure_scheduler + schedulers_[GRPC_NUM_EXECUTORS][GRPC_NUM_EXECUTOR_JOB_TYPES] = { + {{&vtables_[GRPC_DEFAULT_EXECUTOR][GRPC_EXECUTOR_SHORT]}, + {&vtables_[GRPC_DEFAULT_EXECUTOR][GRPC_EXECUTOR_LONG]}}, + {{&vtables_[GRPC_RESOLVER_EXECUTOR][GRPC_EXECUTOR_SHORT]}, + {&vtables_[GRPC_RESOLVER_EXECUTOR][GRPC_EXECUTOR_LONG]}}}; + +// grpc_executor_init() and grpc_executor_shutdown() functions are called in the +// the grpc_init() and grpc_shutdown() code paths which are protected by a +// global mutex. So it is okay to assume that these functions are thread-safe +void grpc_executor_init() { + EXECUTOR_TRACE0("grpc_executor_init() enter"); + + // Return if grpc_executor_init() is already called earlier + if (executors[GRPC_DEFAULT_EXECUTOR] != nullptr) { + GPR_ASSERT(executors[GRPC_RESOLVER_EXECUTOR] != nullptr); + return; + } + + executors[GRPC_DEFAULT_EXECUTOR] = + grpc_core::New("default-executor"); + executors[GRPC_RESOLVER_EXECUTOR] = + grpc_core::New("resolver-executor"); + + executors[GRPC_DEFAULT_EXECUTOR]->Init(); + executors[GRPC_RESOLVER_EXECUTOR]->Init(); + + EXECUTOR_TRACE0("grpc_executor_init() done"); +} + +grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorType executor_type, + GrpcExecutorJobType job_type) { + return &schedulers_[executor_type][job_type]; +} + +grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorJobType job_type) { + return grpc_executor_scheduler(GRPC_DEFAULT_EXECUTOR, job_type); +} + +void grpc_executor_shutdown() { + EXECUTOR_TRACE0("grpc_executor_shutdown() enter"); + + // Return if grpc_executor_shutdown() is already called earlier + if (executors[GRPC_DEFAULT_EXECUTOR] == nullptr) { + GPR_ASSERT(executors[GRPC_RESOLVER_EXECUTOR] == nullptr); + return; + } + + executors[GRPC_DEFAULT_EXECUTOR]->Shutdown(); + executors[GRPC_RESOLVER_EXECUTOR]->Shutdown(); + + // Delete the executor objects. + // + // NOTE: It is important to call Shutdown() on all executors first before + // calling Delete() because it is possible for one executor (that is not + // shutdown yet) to call Enqueue() on a different executor which is already + // shutdown. This is legal and in such cases, the Enqueue() operation + // effectively "fails" and enqueues that closure on the calling thread's + // exec_ctx. + // + // By ensuring that all executors are shutdown first, we are also ensuring + // that no thread is active across all executors. + + grpc_core::Delete(executors[GRPC_DEFAULT_EXECUTOR]); + grpc_core::Delete(executors[GRPC_RESOLVER_EXECUTOR]); + executors[GRPC_DEFAULT_EXECUTOR] = nullptr; + executors[GRPC_RESOLVER_EXECUTOR] = nullptr; + + EXECUTOR_TRACE0("grpc_executor_shutdown() done"); +} + +bool grpc_executor_is_threaded(GrpcExecutorType executor_type) { + GPR_ASSERT(executor_type < GRPC_NUM_EXECUTORS); + return executors[executor_type]->IsThreaded(); +} + +bool grpc_executor_is_threaded() { + return grpc_executor_is_threaded(GRPC_DEFAULT_EXECUTOR); +} + +void grpc_executor_set_threading(bool enable) { + EXECUTOR_TRACE("grpc_executor_set_threading(%d) called", enable); + for (int i = 0; i < GRPC_NUM_EXECUTORS; i++) { + executors[i]->SetThreading(enable); + } +} diff --git a/third_party/grpc/src/core/lib/iomgr/executor.h b/third_party/grpc/src/core/lib/iomgr/executor.h new file mode 100644 index 00000000000000..8829138c5fa71e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/executor.h @@ -0,0 +1,116 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_EXECUTOR_H +#define GRPC_CORE_LIB_IOMGR_EXECUTOR_H + +#include + +#include "src/core/lib/gpr/spinlock.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/closure.h" + +typedef struct { + gpr_mu mu; + size_t id; // For debugging purposes + const char* name; // Thread state name + gpr_cv cv; + grpc_closure_list elems; + size_t depth; // Number of closures in the closure list + bool shutdown; + bool queued_long_job; + grpc_core::Thread thd; +} ThreadState; + +typedef enum { + GRPC_EXECUTOR_SHORT = 0, + GRPC_EXECUTOR_LONG, + GRPC_NUM_EXECUTOR_JOB_TYPES // Add new values above this +} GrpcExecutorJobType; + +class GrpcExecutor { + public: + GrpcExecutor(const char* executor_name); + + void Init(); + + /** Is the executor multi-threaded? */ + bool IsThreaded() const; + + /* Enable/disable threading - must be called after Init and Shutdown() */ + void SetThreading(bool threading); + + /** Shutdown the executor, running all pending work as part of the call */ + void Shutdown(); + + /** Enqueue the closure onto the executor. is_short is true if the closure is + * a short job (i.e expected to not block and complete quickly) */ + void Enqueue(grpc_closure* closure, grpc_error* error, bool is_short); + + private: + static size_t RunClosures(const char* executor_name, grpc_closure_list list); + static void ThreadMain(void* arg); + + const char* name_; + ThreadState* thd_state_; + size_t max_threads_; + gpr_atm num_threads_; + gpr_spinlock adding_thread_lock_; +}; + +// == Global executor functions == + +typedef enum { + GRPC_DEFAULT_EXECUTOR = 0, + GRPC_RESOLVER_EXECUTOR, + + GRPC_NUM_EXECUTORS // Add new values above this +} GrpcExecutorType; + +// TODO(sreek): Currently we have two executors (available globally): The +// default executor and the resolver executor. +// +// Some of the functions below operate on the DEFAULT executor only while some +// operate of ALL the executors. This is a bit confusing and should be cleaned +// up in future (where we make all the following functions take executor_type +// and/or job_type) + +// Initialize ALL the executors +void grpc_executor_init(); + +// Shutdown ALL the executors +void grpc_executor_shutdown(); + +// Set the threading mode for ALL the executors +void grpc_executor_set_threading(bool enable); + +// Get the DEFAULT executor scheduler for the given job_type +grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorJobType job_type); + +// Get the executor scheduler for a given executor_type and a job_type +grpc_closure_scheduler* grpc_executor_scheduler(GrpcExecutorType executor_type, + GrpcExecutorJobType job_type); + +// Return if a given executor is running in threaded mode (i.e if +// grpc_executor_set_threading(true) was called previously on that executor) +bool grpc_executor_is_threaded(GrpcExecutorType executor_type); + +// Return if the DEFAULT executor is threaded +bool grpc_executor_is_threaded(); + +#endif /* GRPC_CORE_LIB_IOMGR_EXECUTOR_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/fork_posix.cc b/third_party/grpc/src/core/lib/iomgr/fork_posix.cc new file mode 100644 index 00000000000000..05ecd2a49b79f0 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/fork_posix.cc @@ -0,0 +1,112 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_FORK + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gprpp/fork.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/timer_manager.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +/* + * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK + * AROUND VERY SPECIFIC USE CASES. + */ + +namespace { +bool skipped_handler = true; +bool registered_handlers = false; +} // namespace + +void grpc_prefork() { + grpc_core::ExecCtx exec_ctx; + skipped_handler = true; + if (!grpc_is_initialized()) { + return; + } + if (!grpc_core::Fork::Enabled()) { + gpr_log(GPR_ERROR, + "Fork support not enabled; try running with the " + "environment variable GRPC_ENABLE_FORK_SUPPORT=1"); + return; + } + if (strcmp(grpc_get_poll_strategy_name(), "epoll1") != 0 && + strcmp(grpc_get_poll_strategy_name(), "poll") != 0) { + gpr_log(GPR_INFO, + "Fork support is only compatible with the epoll1 and poll polling " + "strategies"); + } + if (!grpc_core::Fork::BlockExecCtx()) { + gpr_log(GPR_INFO, + "Other threads are currently calling into gRPC, skipping fork() " + "handlers"); + return; + } + grpc_timer_manager_set_threading(false); + grpc_executor_set_threading(false); + grpc_core::ExecCtx::Get()->Flush(); + grpc_core::Fork::AwaitThreads(); + skipped_handler = false; +} + +void grpc_postfork_parent() { + if (!skipped_handler) { + grpc_core::Fork::AllowExecCtx(); + grpc_core::ExecCtx exec_ctx; + grpc_timer_manager_set_threading(true); + grpc_executor_set_threading(true); + } +} + +void grpc_postfork_child() { + if (!skipped_handler) { + grpc_core::Fork::AllowExecCtx(); + grpc_core::ExecCtx exec_ctx; + grpc_core::Fork::child_postfork_func reset_polling_engine = + grpc_core::Fork::GetResetChildPollingEngineFunc(); + if (reset_polling_engine != nullptr) { + reset_polling_engine(); + } + grpc_timer_manager_set_threading(true); + grpc_executor_set_threading(true); + } +} + +void grpc_fork_handlers_auto_register() { + if (grpc_core::Fork::Enabled() & !registered_handlers) { +#ifdef GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK + pthread_atfork(grpc_prefork, grpc_postfork_parent, grpc_postfork_child); + registered_handlers = true; +#endif // GRPC_POSIX_FORK_ALLOW_PTHREAD_ATFORK + } +} + +#endif // GRPC_POSIX_FORK diff --git a/third_party/grpc/src/core/lib/iomgr/fork_windows.cc b/third_party/grpc/src/core/lib/iomgr/fork_windows.cc new file mode 100644 index 00000000000000..798f671bdffba6 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/fork_windows.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifndef GRPC_POSIX_FORK + +#include +#include + +/* + * NOTE: FORKING IS NOT GENERALLY SUPPORTED, THIS IS ONLY INTENDED TO WORK + * AROUND VERY SPECIFIC USE CASES. + */ + +void grpc_prefork() { gpr_log(GPR_ERROR, "Forking not supported on Windows"); } + +void grpc_postfork_parent() {} + +void grpc_postfork_child() {} + +void grpc_fork_handlers_auto_register() {} + +#endif // GRPC_POSIX_FORK diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname.h b/third_party/grpc/src/core/lib/iomgr/gethostname.h new file mode 100644 index 00000000000000..9f10b4afa78b78 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/gethostname.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H +#define GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H + +// Returns the hostname of the local machine. +// Caller takes ownership of result. +char* grpc_gethostname(); + +#endif /* GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname_fallback.cc b/third_party/grpc/src/core/lib/iomgr/gethostname_fallback.cc new file mode 100644 index 00000000000000..65ae818723abc6 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/gethostname_fallback.cc @@ -0,0 +1,30 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/gethostname.h" +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_GETHOSTNAME_FALLBACK + +#include + +char* grpc_gethostname() { return NULL; } + +#endif // GRPC_GETHOSTNAME_FALLBACK diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname_host_name_max.cc b/third_party/grpc/src/core/lib/iomgr/gethostname_host_name_max.cc new file mode 100644 index 00000000000000..79f5daa8f3269d --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/gethostname_host_name_max.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/gethostname.h" +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_HOST_NAME_MAX + +#include +#include + +#include + +char* grpc_gethostname() { + char* hostname = static_cast(gpr_malloc(HOST_NAME_MAX)); + if (gethostname(hostname, HOST_NAME_MAX) != 0) { + gpr_free(hostname); + return nullptr; + } + return hostname; +} + +#endif // GRPC_POSIX_HOST_NAME_MAX diff --git a/third_party/grpc/src/core/lib/iomgr/gethostname_sysconf.cc b/third_party/grpc/src/core/lib/iomgr/gethostname_sysconf.cc new file mode 100644 index 00000000000000..92c5de3338eae9 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/gethostname_sysconf.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/gethostname.h" +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SYSCONF + +#include + +#include + +char* grpc_gethostname() { + size_t host_name_max = (size_t)sysconf(_SC_HOST_NAME_MAX); + char* hostname = (char*)gpr_malloc(host_name_max); + if (gethostname(hostname, host_name_max) != 0) { + gpr_free(hostname); + return nullptr; + } + return hostname; +} + +#endif // GRPC_POSIX_SYSCONF diff --git a/third_party/grpc/src/core/lib/iomgr/gevent_util.h b/third_party/grpc/src/core/lib/iomgr/gevent_util.h new file mode 100644 index 00000000000000..de30543e3a37d7 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/gevent_util.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_GEVENT_UTIL_H +#define GRPC_CORE_LIB_IOMGR_GEVENT_UTIL_H + +#include + +#include +#include +#include "src/core/lib/iomgr/error.h" + +// These are only used by the gRPC Python extension for gevent +// support. They are easier to define here (rather than in Cython) +// because Cython doesn't handle #defines well. + +grpc_error* grpc_socket_error(char* error) { + return grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); +} + +char* grpc_slice_buffer_start(grpc_slice_buffer* buffer, int i) { + return (char*)GRPC_SLICE_START_PTR(buffer->slices[i]); +} + +int grpc_slice_buffer_length(grpc_slice_buffer* buffer, int i) { + return GRPC_SLICE_LENGTH(buffer->slices[i]); +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/internal_errqueue.cc b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.cc new file mode 100644 index 00000000000000..982d709f094f56 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include +#include "src/core/lib/iomgr/internal_errqueue.h" + +#ifdef GRPC_POSIX_SOCKET_TCP + +#include +#include +#include +#include + +namespace grpc_core { +static bool errqueue_supported = false; + +bool kernel_supports_errqueue() { return errqueue_supported; } + +void grpc_errqueue_init() { +/* Both-compile time and run-time linux kernel versions should be atleast 4.0.0 + */ +#ifdef LINUX_VERSION_CODE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + struct utsname buffer; + if (uname(&buffer) != 0) { + gpr_log(GPR_ERROR, "uname: %s", strerror(errno)); + return; + } + char* release = buffer.release; + if (release == nullptr) { + return; + } + + if (strtol(release, nullptr, 10) >= 4) { + errqueue_supported = true; + } else { + gpr_log(GPR_DEBUG, "ERRQUEUE support not enabled"); + } +#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(4, 0, 0) */ +#endif /* LINUX_VERSION_CODE */ +} +} /* namespace grpc_core */ + +#else + +namespace grpc_core { +void grpc_errqueue_init() {} +} /* namespace grpc_core */ + +#endif /* GRPC_POSIX_SOCKET_TCP */ diff --git a/third_party/grpc/src/core/lib/iomgr/internal_errqueue.h b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.h new file mode 100644 index 00000000000000..f8644c2536cd81 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/internal_errqueue.h @@ -0,0 +1,89 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* This file contains constants defined in and + * so as to allow collecting network timestamps in the + * kernel. This file allows tcp_posix.cc to compile on platforms that do not + * have and . + */ + +#ifndef GRPC_CORE_LIB_IOMGR_INTERNAL_ERRQUEUE_H +#define GRPC_CORE_LIB_IOMGR_INTERNAL_ERRQUEUE_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_TCP + +#include +#include + +#ifdef GRPC_LINUX_ERRQUEUE +#include +#include +#include +#endif /* GRPC_LINUX_ERRQUEUE */ + +namespace grpc_core { + +#ifdef GRPC_LINUX_ERRQUEUE + +/* Redefining scm_timestamping in the same way that defines + * it, so that code compiles on systems that don't have it. */ +struct scm_timestamping { + struct timespec ts[3]; +}; +/* Also redefine timestamp types */ +/* The timestamp type for when the driver passed skb to NIC, or HW. */ +constexpr int SCM_TSTAMP_SND = 0; +/* The timestamp type for when data entered the packet scheduler. */ +constexpr int SCM_TSTAMP_SCHED = 1; +/* The timestamp type for when data acknowledged by peer. */ +constexpr int SCM_TSTAMP_ACK = 2; +/* Redefine required constants from */ +constexpr uint32_t SOF_TIMESTAMPING_TX_SOFTWARE = 1u << 1; +constexpr uint32_t SOF_TIMESTAMPING_SOFTWARE = 1u << 4; +constexpr uint32_t SOF_TIMESTAMPING_OPT_ID = 1u << 7; +constexpr uint32_t SOF_TIMESTAMPING_TX_SCHED = 1u << 8; +constexpr uint32_t SOF_TIMESTAMPING_TX_ACK = 1u << 9; +constexpr uint32_t SOF_TIMESTAMPING_OPT_TSONLY = 1u << 11; + +constexpr uint32_t kTimestampingSocketOptions = SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_OPT_ID | + SOF_TIMESTAMPING_OPT_TSONLY; +constexpr uint32_t kTimestampingRecordingOptions = + SOF_TIMESTAMPING_TX_SCHED | SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_TX_ACK; +#endif /* GRPC_LINUX_ERRQUEUE */ + +/* Returns true if kernel is capable of supporting errqueue and timestamping. + * Currently allowing only linux kernels above 4.0.0 + */ +bool kernel_supports_errqueue(); + +} /* namespace grpc_core */ + +#endif /* GRPC_POSIX_SOCKET_TCP */ + +namespace grpc_core { +/* Initializes errqueue support */ +void grpc_errqueue_init(); +} /* namespace grpc_core */ + +#endif /* GRPC_CORE_LIB_IOMGR_INTERNAL_ERRQUEUE_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/iocp_windows.cc b/third_party/grpc/src/core/lib/iomgr/iocp_windows.cc new file mode 100644 index 00000000000000..ad325fe2156924 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iocp_windows.cc @@ -0,0 +1,157 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include +#include + +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/timer.h" + +static ULONG g_iocp_kick_token; +static OVERLAPPED g_iocp_custom_overlap; + +static gpr_atm g_custom_events = 0; + +static HANDLE g_iocp; + +static DWORD deadline_to_millis_timeout(grpc_millis deadline) { + if (deadline == GRPC_MILLIS_INF_FUTURE) { + return INFINITE; + } + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + if (deadline < now) return 0; + grpc_millis timeout = deadline - now; + if (timeout > std::numeric_limits::max()) return INFINITE; + return static_cast(deadline - now); +} + +grpc_iocp_work_status grpc_iocp_work(grpc_millis deadline) { + BOOL success; + DWORD bytes = 0; + DWORD flags = 0; + ULONG_PTR completion_key; + LPOVERLAPPED overlapped; + grpc_winsocket* socket; + grpc_winsocket_callback_info* info; + GRPC_STATS_INC_SYSCALL_POLL(); + success = + GetQueuedCompletionStatus(g_iocp, &bytes, &completion_key, &overlapped, + deadline_to_millis_timeout(deadline)); + grpc_core::ExecCtx::Get()->InvalidateNow(); + if (success == 0 && overlapped == NULL) { + return GRPC_IOCP_WORK_TIMEOUT; + } + 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 GRPC_IOCP_WORK_KICK; + } + 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 { + abort(); + } + if (socket->shutdown_called) { + info->bytes_transfered = 0; + info->wsa_error = WSA_OPERATION_ABORTED; + } else { + success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, + FALSE, &flags); + info->bytes_transfered = bytes; + info->wsa_error = success ? 0 : WSAGetLastError(); + } + GPR_ASSERT(overlapped == &info->overlapped); + grpc_socket_become_ready(socket, info); + return GRPC_IOCP_WORK_WORK; +} + +void grpc_iocp_init(void) { + g_iocp = + CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0); + GPR_ASSERT(g_iocp); +} + +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_flush(void) { + grpc_core::ExecCtx exec_ctx; + grpc_iocp_work_status work_status; + + do { + work_status = grpc_iocp_work(GRPC_MILLIS_INF_PAST); + } while (work_status == GRPC_IOCP_WORK_KICK || + grpc_core::ExecCtx::Get()->Flush()); +} + +void grpc_iocp_shutdown(void) { + grpc_core::ExecCtx exec_ctx; + while (gpr_atm_acq_load(&g_custom_events)) { + grpc_iocp_work(GRPC_MILLIS_INF_FUTURE); + grpc_core::ExecCtx::Get()->Flush(); + } + + GPR_ASSERT(CloseHandle(g_iocp)); +} + +void grpc_iocp_add_socket(grpc_winsocket* socket) { + HANDLE ret; + if (socket->added_to_iocp) return; + ret = CreateIoCompletionPort((HANDLE)socket->socket, g_iocp, + (uintptr_t)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); +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/lib/iomgr/iocp_windows.h b/third_party/grpc/src/core/lib/iomgr/iocp_windows.h new file mode 100644 index 00000000000000..68d9de61582147 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iocp_windows.h @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H +#define GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H + +#include + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/socket_windows.h" + +typedef enum { + GRPC_IOCP_WORK_WORK, + GRPC_IOCP_WORK_TIMEOUT, + GRPC_IOCP_WORK_KICK +} grpc_iocp_work_status; + +grpc_iocp_work_status grpc_iocp_work(grpc_millis deadline); +void grpc_iocp_init(void); +void grpc_iocp_kick(void); +void grpc_iocp_flush(void); +void grpc_iocp_shutdown(void); +void grpc_iocp_add_socket(grpc_winsocket*); + +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr.cc b/third_party/grpc/src/core/lib/iomgr/iomgr.cc new file mode 100644 index 00000000000000..eb29973514f9ab --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr.cc @@ -0,0 +1,187 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/iomgr.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/buffer_list.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/internal_errqueue.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +static gpr_mu g_mu; +static gpr_cv g_rcv; +static int g_shutdown; +static grpc_iomgr_object g_root_object; + +void grpc_iomgr_init() { + grpc_core::ExecCtx exec_ctx; + grpc_determine_iomgr_platform(); + g_shutdown = 0; + gpr_mu_init(&g_mu); + gpr_cv_init(&g_rcv); + grpc_executor_init(); + grpc_timer_list_init(); + g_root_object.next = g_root_object.prev = &g_root_object; + g_root_object.name = (char*)"root"; + grpc_network_status_init(); + grpc_iomgr_platform_init(); + grpc_core::grpc_errqueue_init(); +} + +void grpc_iomgr_start() { grpc_timer_manager_init(); } + +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; +} + +size_t grpc_iomgr_count_objects_for_testing(void) { return count_objects(); } + +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() { + 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); + + { + grpc_timer_manager_shutdown(); + grpc_iomgr_platform_flush(); + grpc_executor_shutdown(); + + gpr_mu_lock(&g_mu); + g_shutdown = 1; + while (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_root_object.next != &g_root_object) { + gpr_log(GPR_DEBUG, + "Waiting for %" PRIuPTR " iomgr objects to be destroyed", + count_objects()); + } + last_warning_time = gpr_now(GPR_CLOCK_REALTIME); + } + grpc_core::ExecCtx::Get()->SetNowIomgrShutdown(); + if (grpc_timer_check(nullptr) == GRPC_TIMERS_FIRED) { + gpr_mu_unlock(&g_mu); + grpc_core::ExecCtx::Get()->Flush(); + grpc_iomgr_platform_flush(); + gpr_mu_lock(&g_mu); + continue; + } + if (g_root_object.next != &g_root_object) { + if (grpc_iomgr_abort_on_leaks()) { + gpr_log(GPR_DEBUG, + "Failed to free %" PRIuPTR + " iomgr objects before shutdown deadline: " + "memory leaks are likely", + count_objects()); + dump_objects("LEAKED"); + abort(); + } + gpr_timespec short_deadline = + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(100, GPR_TIMESPAN)); + if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline)) { + if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > + 0) { + if (g_root_object.next != &g_root_object) { + gpr_log(GPR_DEBUG, + "Failed to free %" PRIuPTR + " iomgr objects before shutdown deadline: " + "memory leaks are likely", + count_objects()); + dump_objects("LEAKED"); + } + break; + } + } + } + } + gpr_mu_unlock(&g_mu); + grpc_timer_list_shutdown(); + grpc_core::ExecCtx::Get()->Flush(); + } + + /* ensure all threads have left g_mu */ + gpr_mu_lock(&g_mu); + gpr_mu_unlock(&g_mu); + + grpc_iomgr_platform_shutdown(); + grpc_network_status_shutdown(); + gpr_mu_destroy(&g_mu); + gpr_cv_destroy(&g_rcv); +} + +void grpc_iomgr_shutdown_background_closure() { + grpc_iomgr_platform_shutdown_background_closure(); +} + +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); +} + +bool grpc_iomgr_abort_on_leaks(void) { + char* env = gpr_getenv("GRPC_ABORT_ON_LEAKS"); + bool should_we = gpr_is_true(env); + gpr_free(env); + return should_we; +} diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr.h b/third_party/grpc/src/core/lib/iomgr/iomgr.h new file mode 100644 index 00000000000000..8ea9289e068673 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_H +#define GRPC_CORE_LIB_IOMGR_IOMGR_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +/** Initializes the iomgr. */ +void grpc_iomgr_init(); + +/** Starts any background threads for iomgr. */ +void grpc_iomgr_start(); + +/** Signals the intention to shutdown the iomgr. Expects to be able to flush + * exec_ctx. */ +void grpc_iomgr_shutdown(); + +/** Signals the intention to shutdown all the closures registered in the + * background poller. */ +void grpc_iomgr_shutdown_background_closure(); + +/* Exposed only for testing */ +size_t grpc_iomgr_count_objects_for_testing(); + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_custom.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.cc new file mode 100644 index 00000000000000..4b112c9097f6b5 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.cc @@ -0,0 +1,65 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/pollset_set_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" + +gpr_thd_id g_init_thread; + +static void iomgr_platform_init(void) { + grpc_core::ExecCtx exec_ctx; + grpc_executor_set_threading(false); + g_init_thread = gpr_thd_currentid(); + grpc_pollset_global_init(); +} +static void iomgr_platform_flush(void) {} +static void iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } +static void iomgr_platform_shutdown_background_closure(void) {} + +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown, + iomgr_platform_shutdown_background_closure}; + +void grpc_custom_iomgr_init(grpc_socket_vtable* socket, + grpc_custom_resolver_vtable* resolver, + grpc_custom_timer_vtable* timer, + grpc_custom_poller_vtable* poller) { + grpc_custom_endpoint_init(socket); + grpc_custom_timer_init(timer); + grpc_custom_pollset_init(poller); + grpc_custom_pollset_set_init(); + grpc_custom_resolver_init(resolver); + grpc_set_iomgr_platform_vtable(&vtable); +} + +#ifdef GRPC_CUSTOM_SOCKET +grpc_iomgr_platform_vtable* grpc_default_iomgr_platform_vtable() { + return &vtable; +} +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_custom.h b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.h new file mode 100644 index 00000000000000..57cc2f9b9232d2 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_custom.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H + +#include + +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer_custom.h" + +#include + +/* The thread ID of the thread on which grpc was initialized. Used to verify + * that all calls into the custom iomgr are made on that same thread */ +extern gpr_thd_id g_init_thread; + +#ifdef GRPC_CUSTOM_IOMGR_THREAD_CHECK +#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD() \ + GPR_ASSERT(gpr_thd_currentid() == g_init_thread) +#else +#define GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD() +#endif /* GRPC_CUSTOM_IOMGR_THREAD_CHECK */ + +void grpc_custom_iomgr_init(grpc_socket_vtable* socket, + grpc_custom_resolver_vtable* resolver, + grpc_custom_timer_vtable* timer, + grpc_custom_poller_vtable* poller); + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_CUSTOM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_internal.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.cc new file mode 100644 index 00000000000000..b6c9211865defc --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +static grpc_iomgr_platform_vtable* iomgr_platform_vtable = nullptr; + +void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable) { + iomgr_platform_vtable = vtable; +} + +void grpc_determine_iomgr_platform() { + if (iomgr_platform_vtable == nullptr) { + grpc_set_default_iomgr_platform(); + } +} + +void grpc_iomgr_platform_init() { iomgr_platform_vtable->init(); } + +void grpc_iomgr_platform_flush() { iomgr_platform_vtable->flush(); } + +void grpc_iomgr_platform_shutdown() { iomgr_platform_vtable->shutdown(); } + +void grpc_iomgr_platform_shutdown_background_closure() { + iomgr_platform_vtable->shutdown_background_closure(); +} diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_internal.h b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.h new file mode 100644 index 00000000000000..bca7409907fba2 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_internal.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H +#define GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H + +#include + +#include + +#include "src/core/lib/iomgr/iomgr.h" + +typedef struct grpc_iomgr_object { + char* name; + struct grpc_iomgr_object* next; + struct grpc_iomgr_object* prev; +} grpc_iomgr_object; + +typedef struct grpc_iomgr_platform_vtable { + void (*init)(void); + void (*flush)(void); + void (*shutdown)(void); + void (*shutdown_background_closure)(void); +} grpc_iomgr_platform_vtable; + +void grpc_iomgr_register_object(grpc_iomgr_object* obj, const char* name); +void grpc_iomgr_unregister_object(grpc_iomgr_object* obj); + +void grpc_determine_iomgr_platform(); + +void grpc_set_iomgr_platform_vtable(grpc_iomgr_platform_vtable* vtable); + +void grpc_set_default_iomgr_platform(); + +void grpc_iomgr_platform_init(void); +/** flush any globally queued work from iomgr */ +void grpc_iomgr_platform_flush(void); +/** tear down all platform specific global iomgr structures */ +void grpc_iomgr_platform_shutdown(void); + +/** shut down all the closures registered in the background poller */ +void grpc_iomgr_platform_shutdown_background_closure(void); + +bool grpc_iomgr_abort_on_leaks(void); + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_posix.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.cc new file mode 100644 index 00000000000000..9386adf060def0 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_IOMGR + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_tcp_server_vtable grpc_posix_tcp_server_vtable; +extern grpc_tcp_client_vtable grpc_posix_tcp_client_vtable; +extern grpc_timer_vtable grpc_generic_timer_vtable; +extern grpc_pollset_vtable grpc_posix_pollset_vtable; +extern grpc_pollset_set_vtable grpc_posix_pollset_set_vtable; +extern grpc_address_resolver_vtable grpc_posix_resolver_vtable; + +static void iomgr_platform_init(void) { + grpc_wakeup_fd_global_init(); + grpc_event_engine_init(); +} + +static void iomgr_platform_flush(void) {} + +static void iomgr_platform_shutdown(void) { + grpc_event_engine_shutdown(); + grpc_wakeup_fd_global_destroy(); +} + +static void iomgr_platform_shutdown_background_closure(void) { + grpc_shutdown_background_closure(); +} + +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown, + iomgr_platform_shutdown_background_closure}; + +void grpc_set_default_iomgr_platform() { + grpc_set_tcp_client_impl(&grpc_posix_tcp_client_vtable); + grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable); + grpc_set_timer_impl(&grpc_generic_timer_vtable); + grpc_set_pollset_vtable(&grpc_posix_pollset_vtable); + grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable); + grpc_set_resolver_impl(&grpc_posix_resolver_vtable); + grpc_set_iomgr_platform_vtable(&vtable); +} + +#endif /* GRPC_POSIX_SOCKET_IOMGR */ diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_posix.h b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.h new file mode 100644 index 00000000000000..54ec46e1bb0f6c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_posix.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H +#define GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H + +#include + +#include "src/core/lib/iomgr/iomgr_internal.h" + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_posix_cfstream.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_posix_cfstream.cc new file mode 100644 index 00000000000000..552ef4309c84b2 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_posix_cfstream.cc @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_CFSTREAM_IOMGR + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/timer.h" + +static const char* grpc_cfstream_env_var = "grpc_cfstream"; + +extern grpc_tcp_server_vtable grpc_posix_tcp_server_vtable; +extern grpc_tcp_client_vtable grpc_posix_tcp_client_vtable; +extern grpc_tcp_client_vtable grpc_cfstream_client_vtable; +extern grpc_timer_vtable grpc_generic_timer_vtable; +extern grpc_pollset_vtable grpc_posix_pollset_vtable; +extern grpc_pollset_set_vtable grpc_posix_pollset_set_vtable; +extern grpc_address_resolver_vtable grpc_posix_resolver_vtable; + +static void iomgr_platform_init(void) { + grpc_wakeup_fd_global_init(); + grpc_event_engine_init(); +} + +static void iomgr_platform_flush(void) {} + +static void iomgr_platform_shutdown(void) { + grpc_event_engine_shutdown(); + grpc_wakeup_fd_global_destroy(); +} + +static void iomgr_platform_shutdown_background_closure(void) { + grpc_shutdown_background_closure(); +} + +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown, + iomgr_platform_shutdown_background_closure}; + +void grpc_set_default_iomgr_platform() { + char* enable_cfstream = getenv(grpc_cfstream_env_var); + grpc_tcp_client_vtable* client_vtable = &grpc_posix_tcp_client_vtable; + if (enable_cfstream != nullptr && enable_cfstream[0] == '1') { + client_vtable = &grpc_cfstream_client_vtable; + } + grpc_set_tcp_client_impl(client_vtable); + grpc_set_tcp_server_impl(&grpc_posix_tcp_server_vtable); + grpc_set_timer_impl(&grpc_generic_timer_vtable); + grpc_set_pollset_vtable(&grpc_posix_pollset_vtable); + grpc_set_pollset_set_vtable(&grpc_posix_pollset_set_vtable); + grpc_set_resolver_impl(&grpc_posix_resolver_vtable); + grpc_set_iomgr_platform_vtable(&vtable); +} + +#endif /* GRPC_CFSTREAM_IOMGR */ diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_uv.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_uv.cc new file mode 100644 index 00000000000000..4a984446dbac9c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_uv.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#if defined(GRPC_CUSTOM_SOCKET) && defined(GRPC_UV) + +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer_custom.h" + +extern grpc_socket_vtable grpc_uv_socket_vtable; +extern grpc_custom_resolver_vtable uv_resolver_vtable; +extern grpc_custom_timer_vtable uv_timer_vtable; +extern grpc_custom_poller_vtable uv_pollset_vtable; + +void grpc_set_default_iomgr_platform() { + grpc_custom_iomgr_init(&grpc_uv_socket_vtable, &uv_resolver_vtable, + &uv_timer_vtable, &uv_pollset_vtable); +} +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/iomgr_windows.cc b/third_party/grpc/src/core/lib/iomgr/iomgr_windows.cc new file mode 100644 index 00000000000000..24ef0dba7b4d56 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/iomgr_windows.cc @@ -0,0 +1,90 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr_windows.h" + +#include + +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_tcp_server_vtable grpc_windows_tcp_server_vtable; +extern grpc_tcp_client_vtable grpc_windows_tcp_client_vtable; +extern grpc_timer_vtable grpc_generic_timer_vtable; +extern grpc_pollset_vtable grpc_windows_pollset_vtable; +extern grpc_pollset_set_vtable grpc_windows_pollset_set_vtable; +extern grpc_address_resolver_vtable grpc_windows_resolver_vtable; + +/* 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); +} + +static void iomgr_platform_init(void) { + winsock_init(); + grpc_iocp_init(); + grpc_pollset_global_init(); +} + +static void iomgr_platform_flush(void) { grpc_iocp_flush(); } + +static void iomgr_platform_shutdown(void) { + grpc_pollset_global_shutdown(); + grpc_iocp_shutdown(); + winsock_shutdown(); +} + +static void iomgr_platform_shutdown_background_closure(void) {} + +static grpc_iomgr_platform_vtable vtable = { + iomgr_platform_init, iomgr_platform_flush, iomgr_platform_shutdown, + iomgr_platform_shutdown_background_closure}; + +void grpc_set_default_iomgr_platform() { + grpc_set_tcp_client_impl(&grpc_windows_tcp_client_vtable); + grpc_set_tcp_server_impl(&grpc_windows_tcp_server_vtable); + grpc_set_timer_impl(&grpc_generic_timer_vtable); + grpc_set_pollset_vtable(&grpc_windows_pollset_vtable); + grpc_set_pollset_set_vtable(&grpc_windows_pollset_set_vtable); + grpc_set_resolver_impl(&grpc_windows_resolver_vtable); + grpc_set_iomgr_platform_vtable(&vtable); +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.cc b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.cc new file mode 100644 index 00000000000000..8df6a66e6b3527 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.cc @@ -0,0 +1,105 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/is_epollexclusive_available.h" + +#ifdef GRPC_LINUX_EPOLL_CREATE1 + +#include + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/sys_epoll_wrapper.h" + +/* This polling engine is only relevant on linux kernels supporting epoll() */ +bool grpc_is_epollexclusive_available(void) { + static bool logged_why_not = false; + + int fd = epoll_create1(EPOLL_CLOEXEC); + if (fd < 0) { + if (!logged_why_not) { + gpr_log(GPR_DEBUG, + "epoll_create1 failed with error: %d. Not using epollex polling " + "engine.", + fd); + logged_why_not = true; + } + return false; + } + int evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (evfd < 0) { + if (!logged_why_not) { + gpr_log(GPR_DEBUG, + "eventfd failed with error: %d. Not using epollex polling " + "engine.", + fd); + logged_why_not = true; + } + close(fd); + return false; + } + struct epoll_event ev; + /* choose events that should cause an error on + EPOLLEXCLUSIVE enabled kernels - specifically the combination of + EPOLLONESHOT and EPOLLEXCLUSIVE */ + ev.events = + static_cast(EPOLLET | EPOLLIN | EPOLLEXCLUSIVE | EPOLLONESHOT); + ev.data.ptr = nullptr; + if (epoll_ctl(fd, EPOLL_CTL_ADD, evfd, &ev) != 0) { + if (errno != EINVAL) { + if (!logged_why_not) { + gpr_log( + GPR_ERROR, + "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT failed with error: " + "%d. Not using epollex polling engine.", + errno); + logged_why_not = true; + } + close(fd); + close(evfd); + return false; + } + } else { + if (!logged_why_not) { + gpr_log(GPR_DEBUG, + "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT succeeded. This is " + "evidence of no EPOLLEXCLUSIVE support. Not using " + "epollex polling engine."); + logged_why_not = true; + } + close(fd); + close(evfd); + return false; + } + close(evfd); + close(fd); + return true; +} + +#else + +bool grpc_is_epollexclusive_available(void) { return false; } + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.h b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.h new file mode 100644 index 00000000000000..8a44113c3f748c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/is_epollexclusive_available.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H +#define GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool grpc_is_epollexclusive_available(void); + +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/load_file.cc b/third_party/grpc/src/core/lib/iomgr/load_file.cc new file mode 100644 index 00000000000000..f6431d0f1caf53 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/load_file.cc @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/load_file.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/block_annotate.h" + +grpc_error* grpc_load_file(const char* filename, int add_null_terminator, + grpc_slice* output) { + unsigned char* contents = nullptr; + size_t contents_size = 0; + grpc_slice result = grpc_empty_slice(); + FILE* file; + size_t bytes_read = 0; + grpc_error* error = GRPC_ERROR_NONE; + + GRPC_SCHEDULING_START_BLOCKING_REGION; + file = fopen(filename, "rb"); + if (file == nullptr) { + error = GRPC_OS_ERROR(errno, "fopen"); + goto end; + } + fseek(file, 0, SEEK_END); + /* Converting to size_t on the assumption that it will not fail */ + contents_size = static_cast(ftell(file)); + fseek(file, 0, SEEK_SET); + contents = static_cast( + gpr_malloc(contents_size + (add_null_terminator ? 1 : 0))); + bytes_read = fread(contents, 1, contents_size, file); + if (bytes_read < contents_size) { + error = GRPC_OS_ERROR(errno, "fread"); + GPR_ASSERT(ferror(file)); + goto end; + } + if (add_null_terminator) { + contents[contents_size++] = 0; + } + result = grpc_slice_new(contents, contents_size, gpr_free); + +end: + *output = result; + if (file != nullptr) fclose(file); + if (error != GRPC_ERROR_NONE) { + grpc_error* error_out = + grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to load file", &error, 1), + GRPC_ERROR_STR_FILENAME, + grpc_slice_from_copied_string( + filename)); // TODO(ncteisen), always static? + GRPC_ERROR_UNREF(error); + error = error_out; + } + GRPC_SCHEDULING_END_BLOCKING_REGION_NO_EXEC_CTX; + return error; +} diff --git a/third_party/grpc/src/core/lib/iomgr/load_file.h b/third_party/grpc/src/core/lib/iomgr/load_file.h new file mode 100644 index 00000000000000..1cb2b5de731212 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/load_file.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_LOAD_FILE_H +#define GRPC_CORE_LIB_IOMGR_LOAD_FILE_H + +#include + +#include + +#include + +#include "src/core/lib/iomgr/error.h" + +/* Loads the content of a file into a slice. add_null_terminator will add + a NULL terminator if non-zero. */ +grpc_error* grpc_load_file(const char* filename, int add_null_terminator, + grpc_slice* slice); + +#endif /* GRPC_CORE_LIB_IOMGR_LOAD_FILE_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/lockfree_event.cc b/third_party/grpc/src/core/lib/iomgr/lockfree_event.cc new file mode 100644 index 00000000000000..085fea40a4080e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/lockfree_event.cc @@ -0,0 +1,254 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/lockfree_event.h" + +#include + +#include "src/core/lib/debug/trace.h" + +extern grpc_core::TraceFlag grpc_polling_trace; + +/* 'state' holds the to call when the fd is readable or writable respectively. + It can contain one of the following values: + kClosureReady : The fd has an I/O event of interest but there is no + closure yet to execute + + kClosureNotReady : The fd has no I/O event of interest + + closure ptr : The closure to be executed when the fd has an I/O + event of interest + + shutdown_error | kShutdownBit : + 'shutdown_error' field ORed with kShutdownBit. + This indicates that the fd is shutdown. Since all + memory allocations are word-aligned, the lower two + bits of the shutdown_error pointer are always 0. So + it is safe to OR these with kShutdownBit + + Valid state transitions: + + <-----3------ kClosureNotReady -----1-------> kClosureReady + | | ^ | ^ | | + | | | | | | | + | +--------------4----------+ 6 +---------2---------------+ | + | | | + | v | + +-----5-------> [shutdown_error | kShutdownBit] <-------7---------+ + + For 1, 4 : See SetReady() function + For 2, 3 : See NotifyOn() function + For 5,6,7: See SetShutdown() function */ + +namespace grpc_core { + +LockfreeEvent::LockfreeEvent() { InitEvent(); } + +void LockfreeEvent::InitEvent() { + /* Perform an atomic store to start the state machine. + + Note carefully that LockfreeEvent *MAY* be used whilst in a destroyed + state, while a file descriptor is on a freelist. In such a state it may + be SetReady'd, and so we need to perform an atomic operation here to + ensure no races */ + gpr_atm_no_barrier_store(&state_, kClosureNotReady); +} + +void LockfreeEvent::DestroyEvent() { + gpr_atm curr; + do { + curr = gpr_atm_no_barrier_load(&state_); + if (curr & kShutdownBit) { + GRPC_ERROR_UNREF((grpc_error*)(curr & ~kShutdownBit)); + } else { + GPR_ASSERT(curr == kClosureNotReady || curr == kClosureReady); + } + /* we CAS in a shutdown, no error value here. If this event is interacted + with post-deletion (see the note in the constructor) we want the bit + pattern to prevent error retention in a deleted object */ + } while (!gpr_atm_no_barrier_cas(&state_, curr, + kShutdownBit /* shutdown, no error */)); +} + +void LockfreeEvent::NotifyOn(grpc_closure* closure) { + while (true) { + /* This load needs to be an acquire load because this can be a shutdown + * error that we might need to reference. Adding acquire semantics makes + * sure that the shutdown error has been initialized properly before us + * referencing it. */ + gpr_atm curr = gpr_atm_acq_load(&state_); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_ERROR, "LockfreeEvent::NotifyOn: %p curr=%p closure=%p", this, + (void*)curr, closure); + } + switch (curr) { + case kClosureNotReady: { + /* kClosureNotReady -> . + + We're guaranteed by API that there's an acquire barrier before here, + so there's no need to double-dip and this can be a release-only. + + The release itself pairs with the acquire half of a set_ready full + barrier. */ + if (gpr_atm_rel_cas(&state_, kClosureNotReady, (gpr_atm)closure)) { + return; /* Successful. Return */ + } + + break; /* retry */ + } + + case kClosureReady: { + /* Change the state to kClosureNotReady. Schedule the closure if + successful. If not, the state most likely transitioned to shutdown. + We should retry. + + This can be a no-barrier cas since the state is being transitioned to + kClosureNotReady; set_ready and set_shutdown do not schedule any + closure when transitioning out of CLOSURE_NO_READY state (i.e there + is no other code that needs to 'happen-after' this) */ + if (gpr_atm_no_barrier_cas(&state_, kClosureReady, kClosureNotReady)) { + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + return; /* Successful. Return */ + } + + break; /* retry */ + } + + default: { + /* 'curr' is either a closure or the fd is shutdown(in which case 'curr' + contains a pointer to the shutdown-error). If the fd is shutdown, + schedule the closure with the shutdown error */ + if ((curr & kShutdownBit) > 0) { + grpc_error* shutdown_err = (grpc_error*)(curr & ~kShutdownBit); + GRPC_CLOSURE_SCHED(closure, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "FD Shutdown", &shutdown_err, 1)); + return; + } + + /* There is already a closure!. This indicates a bug in the code */ + gpr_log(GPR_ERROR, + "LockfreeEvent::NotifyOn: notify_on called with a previous " + "callback still pending"); + abort(); + } + } + } + + GPR_UNREACHABLE_CODE(return ); +} + +bool LockfreeEvent::SetShutdown(grpc_error* shutdown_err) { + gpr_atm new_state = (gpr_atm)shutdown_err | kShutdownBit; + + while (true) { + gpr_atm curr = gpr_atm_no_barrier_load(&state_); + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_ERROR, "LockfreeEvent::SetShutdown: %p curr=%p err=%s", + &state_, (void*)curr, grpc_error_string(shutdown_err)); + } + switch (curr) { + case kClosureReady: + case kClosureNotReady: + /* Need a full barrier here so that the initial load in notify_on + doesn't need a barrier */ + if (gpr_atm_full_cas(&state_, curr, new_state)) { + return true; /* early out */ + } + break; /* retry */ + + default: { + /* 'curr' is either a closure or the fd is already shutdown */ + + /* If fd is already shutdown, we are done */ + if ((curr & kShutdownBit) > 0) { + GRPC_ERROR_UNREF(shutdown_err); + return false; + } + + /* Fd is not shutdown. Schedule the closure and move the state to + shutdown state. + Needs an acquire to pair with setting the closure (and get a + happens-after on that edge), and a release to pair with anything + loading the shutdown state. */ + if (gpr_atm_full_cas(&state_, curr, new_state)) { + GRPC_CLOSURE_SCHED((grpc_closure*)curr, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "FD Shutdown", &shutdown_err, 1)); + return true; + } + + /* 'curr' was a closure but now changed to a different state. We will + have to retry */ + break; + } + } + } + + GPR_UNREACHABLE_CODE(return false); +} + +void LockfreeEvent::SetReady() { + while (true) { + gpr_atm curr = gpr_atm_no_barrier_load(&state_); + + if (grpc_polling_trace.enabled()) { + gpr_log(GPR_ERROR, "LockfreeEvent::SetReady: %p curr=%p", &state_, + (void*)curr); + } + + switch (curr) { + case kClosureReady: { + /* Already ready. We are done here */ + return; + } + + case kClosureNotReady: { + /* No barrier required as we're transitioning to a state that does not + involve a closure */ + if (gpr_atm_no_barrier_cas(&state_, kClosureNotReady, kClosureReady)) { + return; /* early out */ + } + break; /* retry */ + } + + default: { + /* 'curr' is either a closure or the fd is shutdown */ + if ((curr & kShutdownBit) > 0) { + /* The fd is shutdown. Do nothing */ + return; + } + /* Full cas: acquire pairs with this cas' release in the event of a + spurious set_ready; release pairs with this or the acquire in + notify_on (or set_shutdown) */ + else if (gpr_atm_full_cas(&state_, curr, kClosureNotReady)) { + GRPC_CLOSURE_SCHED((grpc_closure*)curr, GRPC_ERROR_NONE); + return; + } + /* else the state changed again (only possible by either a racing + set_ready or set_shutdown functions. In both these cases, the closure + would have been scheduled for execution. So we are done here */ + return; + } + } + } +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/iomgr/lockfree_event.h b/third_party/grpc/src/core/lib/iomgr/lockfree_event.h new file mode 100644 index 00000000000000..d6a6c226bc07aa --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/lockfree_event.h @@ -0,0 +1,72 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H +#define GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H + +/* Lock free event notification for file descriptors */ + +#include + +#include + +#include "src/core/lib/iomgr/closure.h" + +namespace grpc_core { + +class LockfreeEvent { + public: + LockfreeEvent(); + + LockfreeEvent(const LockfreeEvent&) = delete; + LockfreeEvent& operator=(const LockfreeEvent&) = delete; + + // These methods are used to initialize and destroy the internal state. These + // cannot be done in constructor and destructor because SetReady may be called + // when the event is destroyed and put in a freelist. + void InitEvent(); + void DestroyEvent(); + + // Returns true if fd has been shutdown, false otherwise. + bool IsShutdown() const { + return (gpr_atm_no_barrier_load(&state_) & kShutdownBit) != 0; + } + + // Schedules \a closure when the event is received (see SetReady()) or the + // shutdown state has been set. Note that the event may have already been + // received, in which case the closure would be scheduled immediately. + // If the shutdown state has already been set, then \a closure is scheduled + // with the shutdown error. + void NotifyOn(grpc_closure* closure); + + // Sets the shutdown state. If a closure had been provided by NotifyOn and has + // not yet been scheduled, it will be scheduled with \a error. + bool SetShutdown(grpc_error* error); + + // Signals that the event has been received. + void SetReady(); + + private: + enum State { kClosureNotReady = 0, kClosureReady = 2, kShutdownBit = 1 }; + + gpr_atm state_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/nameser.h b/third_party/grpc/src/core/lib/iomgr/nameser.h new file mode 100644 index 00000000000000..22a00cdab8b1f6 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/nameser.h @@ -0,0 +1,106 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_NAMESER_H +#define GRPC_CORE_LIB_IOMGR_NAMESER_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_HAVE_ARPA_NAMESER + +#include + +#else /* GRPC_HAVE_ARPA_NAMESER */ + +typedef enum __ns_class { + ns_c_invalid = 0, /* Cookie. */ + ns_c_in = 1, /* Internet. */ + ns_c_2 = 2, /* unallocated/unsupported. */ + ns_c_chaos = 3, /* MIT Chaos-net. */ + ns_c_hs = 4, /* MIT Hesiod. */ + /* Query class values which do not appear in resource records */ + ns_c_none = 254, /* for prereq. sections in update requests */ + ns_c_any = 255, /* Wildcard match. */ + ns_c_max = 65536 +} ns_class; + +typedef enum __ns_type { + ns_t_invalid = 0, /* Cookie. */ + ns_t_a = 1, /* Host address. */ + ns_t_ns = 2, /* Authoritative server. */ + ns_t_md = 3, /* Mail destination. */ + ns_t_mf = 4, /* Mail forwarder. */ + ns_t_cname = 5, /* Canonical name. */ + ns_t_soa = 6, /* Start of authority zone. */ + ns_t_mb = 7, /* Mailbox domain name. */ + ns_t_mg = 8, /* Mail group member. */ + ns_t_mr = 9, /* Mail rename name. */ + ns_t_null = 10, /* Null resource record. */ + ns_t_wks = 11, /* Well known service. */ + ns_t_ptr = 12, /* Domain name pointer. */ + ns_t_hinfo = 13, /* Host information. */ + ns_t_minfo = 14, /* Mailbox information. */ + ns_t_mx = 15, /* Mail routing information. */ + ns_t_txt = 16, /* Text strings. */ + ns_t_rp = 17, /* Responsible person. */ + ns_t_afsdb = 18, /* AFS cell database. */ + ns_t_x25 = 19, /* X_25 calling address. */ + ns_t_isdn = 20, /* ISDN calling address. */ + ns_t_rt = 21, /* Router. */ + ns_t_nsap = 22, /* NSAP address. */ + ns_t_nsap_ptr = 23, /* Reverse NSAP lookup (deprecated). */ + ns_t_sig = 24, /* Security signature. */ + ns_t_key = 25, /* Security key. */ + ns_t_px = 26, /* X.400 mail mapping. */ + ns_t_gpos = 27, /* Geographical position (withdrawn). */ + ns_t_aaaa = 28, /* Ip6 Address. */ + ns_t_loc = 29, /* Location Information. */ + ns_t_nxt = 30, /* Next domain (security). */ + ns_t_eid = 31, /* Endpoint identifier. */ + ns_t_nimloc = 32, /* Nimrod Locator. */ + ns_t_srv = 33, /* Server Selection. */ + ns_t_atma = 34, /* ATM Address */ + ns_t_naptr = 35, /* Naming Authority PoinTeR */ + ns_t_kx = 36, /* Key Exchange */ + ns_t_cert = 37, /* Certification record */ + ns_t_a6 = 38, /* IPv6 address (deprecates AAAA) */ + ns_t_dname = 39, /* Non-terminal DNAME (for IPv6) */ + ns_t_sink = 40, /* Kitchen sink (experimentatl) */ + ns_t_opt = 41, /* EDNS0 option (meta-RR) */ + ns_t_apl = 42, /* Address prefix list (RFC3123) */ + ns_t_ds = 43, /* Delegation Signer (RFC4034) */ + ns_t_sshfp = 44, /* SSH Key Fingerprint (RFC4255) */ + ns_t_rrsig = 46, /* Resource Record Signature (RFC4034) */ + ns_t_nsec = 47, /* Next Secure (RFC4034) */ + ns_t_dnskey = 48, /* DNS Public Key (RFC4034) */ + ns_t_tkey = 249, /* Transaction key */ + ns_t_tsig = 250, /* Transaction signature. */ + ns_t_ixfr = 251, /* Incremental zone transfer. */ + ns_t_axfr = 252, /* Transfer zone of authority. */ + ns_t_mailb = 253, /* Transfer mailbox records. */ + ns_t_maila = 254, /* Transfer mail agent records. */ + ns_t_any = 255, /* Wildcard match. */ + ns_t_zxfr = 256, /* BIND-specific, nonstandard. */ + ns_t_max = 65536 +} ns_type; + +#endif /* GRPC_HAVE_ARPA_NAMESER */ + +#endif /* GRPC_CORE_LIB_IOMGR_NAMESER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/network_status_tracker.cc b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.cc new file mode 100644 index 00000000000000..d4b7f4a57d1239 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/network_status_tracker.h" + +void grpc_network_status_shutdown(void) {} + +void grpc_network_status_init(void) { + // TODO(makarandd): Install callback with OS to monitor network status. +} + +void grpc_destroy_network_status_monitor() {} + +void grpc_network_status_register_endpoint(grpc_endpoint* ep) { (void)ep; } + +void grpc_network_status_unregister_endpoint(grpc_endpoint* ep) { (void)ep; } + +void grpc_network_status_shutdown_all_endpoints() {} diff --git a/third_party/grpc/src/core/lib/iomgr/network_status_tracker.h b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.h new file mode 100644 index 00000000000000..198877f60f8991 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/network_status_tracker.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H +#define GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H +#include + +#include "src/core/lib/iomgr/endpoint.h" + +void grpc_network_status_init(void); +void grpc_network_status_shutdown(void); + +void grpc_network_status_register_endpoint(grpc_endpoint* ep); +void grpc_network_status_unregister_endpoint(grpc_endpoint* ep); +void grpc_network_status_shutdown_all_endpoints(); + +#endif /* GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/polling_entity.cc b/third_party/grpc/src/core/lib/iomgr/polling_entity.cc new file mode 100644 index 00000000000000..dea07cae5372e9 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/polling_entity.cc @@ -0,0 +1,96 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/lib/iomgr/polling_entity.h" + +grpc_polling_entity grpc_polling_entity_create_from_pollset_set( + grpc_pollset_set* pollset_set) { + grpc_polling_entity pollent; + pollent.pollent.pollset_set = pollset_set; + pollent.tag = GRPC_POLLS_POLLSET_SET; + return pollent; +} + +grpc_polling_entity grpc_polling_entity_create_from_pollset( + grpc_pollset* pollset) { + grpc_polling_entity pollent; + pollent.pollent.pollset = pollset; + pollent.tag = GRPC_POLLS_POLLSET; + return pollent; +} + +grpc_pollset* grpc_polling_entity_pollset(grpc_polling_entity* pollent) { + if (pollent->tag == GRPC_POLLS_POLLSET) { + return pollent->pollent.pollset; + } + return nullptr; +} + +grpc_pollset_set* grpc_polling_entity_pollset_set( + grpc_polling_entity* pollent) { + if (pollent->tag == GRPC_POLLS_POLLSET_SET) { + return pollent->pollent.pollset_set; + } + return nullptr; +} + +bool grpc_polling_entity_is_empty(const grpc_polling_entity* pollent) { + return pollent->tag == GRPC_POLLS_NONE; +} + +void grpc_polling_entity_add_to_pollset_set(grpc_polling_entity* pollent, + grpc_pollset_set* pss_dst) { + if (pollent->tag == GRPC_POLLS_POLLSET) { + // CFStream does not use file destriptors. When CFStream is used, the fd + // pollset is possible to be null. + if (pollent->pollent.pollset != nullptr) { + grpc_pollset_set_add_pollset(pss_dst, pollent->pollent.pollset); + } + } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) { + GPR_ASSERT(pollent->pollent.pollset_set != nullptr); + grpc_pollset_set_add_pollset_set(pss_dst, pollent->pollent.pollset_set); + } else { + gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag); + abort(); + } +} + +void grpc_polling_entity_del_from_pollset_set(grpc_polling_entity* pollent, + grpc_pollset_set* pss_dst) { + if (pollent->tag == GRPC_POLLS_POLLSET) { +#ifdef GRPC_CFSTREAM + if (pollent->pollent.pollset != nullptr) { + grpc_pollset_set_del_pollset(pss_dst, pollent->pollent.pollset); + } +#else + GPR_ASSERT(pollent->pollent.pollset != nullptr); + grpc_pollset_set_del_pollset(pss_dst, pollent->pollent.pollset); +#endif + } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) { + GPR_ASSERT(pollent->pollent.pollset_set != nullptr); + grpc_pollset_set_del_pollset_set(pss_dst, pollent->pollent.pollset_set); + } else { + gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag); + abort(); + } +} diff --git a/third_party/grpc/src/core/lib/iomgr/polling_entity.h b/third_party/grpc/src/core/lib/iomgr/polling_entity.h new file mode 100644 index 00000000000000..6f4c5bdd665bb0 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/polling_entity.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H +#define GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H + +#include + +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_set.h" + +typedef enum grpc_pollset_tag { + GRPC_POLLS_NONE, + GRPC_POLLS_POLLSET, + GRPC_POLLS_POLLSET_SET +} grpc_pollset_tag; + +/* A grpc_polling_entity is a pollset-or-pollset_set container. It allows + * functions that accept a pollset XOR a pollset_set to do so through an + * abstract interface. No ownership is taken. */ + +struct grpc_polling_entity { + union { + grpc_pollset* pollset = nullptr; + grpc_pollset_set* pollset_set; + } pollent; + grpc_pollset_tag tag = GRPC_POLLS_NONE; +}; + +grpc_polling_entity grpc_polling_entity_create_from_pollset_set( + grpc_pollset_set* pollset_set); +grpc_polling_entity grpc_polling_entity_create_from_pollset( + grpc_pollset* pollset); + +/** If \a pollent contains a pollset, return it. Otherwise, return NULL */ +grpc_pollset* grpc_polling_entity_pollset(grpc_polling_entity* pollent); + +/** If \a pollent contains a pollset_set, return it. Otherwise, return NULL */ +grpc_pollset_set* grpc_polling_entity_pollset_set(grpc_polling_entity* pollent); + +bool grpc_polling_entity_is_empty(const grpc_polling_entity* pollent); + +/** Add the pollset or pollset_set in \a pollent to the destination pollset_set + * \a * pss_dst */ +void grpc_polling_entity_add_to_pollset_set(grpc_polling_entity* pollent, + grpc_pollset_set* pss_dst); + +/** Delete the pollset or pollset_set in \a pollent from the destination + * pollset_set \a * pss_dst */ +void grpc_polling_entity_del_from_pollset_set(grpc_polling_entity* pollent, + grpc_pollset_set* pss_dst); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset.cc b/third_party/grpc/src/core/lib/iomgr/pollset.cc new file mode 100644 index 00000000000000..ebfef1dbc7626c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset.cc @@ -0,0 +1,56 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/pollset.h" + +grpc_pollset_vtable* grpc_pollset_impl; + +void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable) { + grpc_pollset_impl = vtable; +} + +void grpc_pollset_global_init() { grpc_pollset_impl->global_init(); } + +void grpc_pollset_global_shutdown() { grpc_pollset_impl->global_shutdown(); } + +void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + grpc_pollset_impl->init(pollset, mu); +} + +void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + grpc_pollset_impl->shutdown(pollset, closure); +} + +void grpc_pollset_destroy(grpc_pollset* pollset) { + grpc_pollset_impl->destroy(pollset); +} + +grpc_error* grpc_pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) { + return grpc_pollset_impl->work(pollset, worker, deadline); +} + +grpc_error* grpc_pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + return grpc_pollset_impl->kick(pollset, specific_worker); +} + +size_t grpc_pollset_size(void) { return grpc_pollset_impl->pollset_size(); } diff --git a/third_party/grpc/src/core/lib/iomgr/pollset.h b/third_party/grpc/src/core/lib/iomgr/pollset.h new file mode 100644 index 00000000000000..28472b360d64c4 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_H + +#include + +#include +#include + +#include "src/core/lib/iomgr/exec_ctx.h" + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount; + +/* 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 */ + +typedef struct grpc_pollset grpc_pollset; +typedef struct grpc_pollset_worker grpc_pollset_worker; + +typedef struct grpc_pollset_vtable { + void (*global_init)(void); + void (*global_shutdown)(void); + void (*init)(grpc_pollset* pollset, gpr_mu** mu); + void (*shutdown)(grpc_pollset* pollset, grpc_closure* closure); + void (*destroy)(grpc_pollset* pollset); + grpc_error* (*work)(grpc_pollset* pollset, grpc_pollset_worker** worker, + grpc_millis deadline); + grpc_error* (*kick)(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker); + size_t (*pollset_size)(void); +} grpc_pollset_vtable; + +void grpc_set_pollset_vtable(grpc_pollset_vtable* vtable); + +void grpc_pollset_global_init(void); +void grpc_pollset_global_shutdown(void); + +size_t grpc_pollset_size(void); +/* Initialize a pollset: assumes *pollset contains all zeros */ +void grpc_pollset_init(grpc_pollset* pollset, gpr_mu** mu); +/* Begin shutting down the pollset, and call closure when done. + * pollset's mutex must be held */ +void grpc_pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure); +void grpc_pollset_destroy(grpc_pollset* pollset); + +/* Do some work on a pollset. + May involve invoking asynchronous callbacks, or actually polling file + descriptors. + Requires pollset's mutex locked. + May unlock its mutex 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 + pollset's mutex is released for the first time by grpc_pollset_work + and it is guaranteed that it will not be released by grpc_pollset_work + AFTER worker has been destroyed. + + It's legal for worker to be NULL: in that case, this specific thread can not + be directly woken with a kick, but maybe be indirectly (with a kick against + the pollset as a whole). + + Tries not to block past deadline. + May call grpc_closure_list_run on grpc_closure_list, without holding the + pollset + lock */ +grpc_error* grpc_pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) GRPC_MUST_USE_RESULT; + +/* Break one polling thread out of polling work for this pollset. + If specific_worker is non-NULL, then kick that worker. */ +grpc_error* grpc_pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) + GRPC_MUST_USE_RESULT; + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_custom.cc b/third_party/grpc/src/core/lib/iomgr/pollset_custom.cc new file mode 100644 index 00000000000000..70e8a4596faa8c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_custom.cc @@ -0,0 +1,106 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_custom.h" +#include "src/core/lib/iomgr/timer.h" + +#include "src/core/lib/debug/trace.h" + +static grpc_custom_poller_vtable* poller_vtable; + +struct grpc_pollset { + gpr_mu mu; +}; + +static size_t pollset_size() { return sizeof(grpc_pollset); } + +static void pollset_global_init() { poller_vtable->init(); } + +static void pollset_global_shutdown() { poller_vtable->shutdown(); } + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_init(&pollset->mu); + *mu = &pollset->mu; +} + +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); +} + +static void pollset_destroy(grpc_pollset* pollset) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_destroy(&pollset->mu); +} + +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_mu_unlock(&pollset->mu); + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + grpc_millis timeout = 0; + if (deadline > now) { + timeout = deadline - now; + } + // We yield here because the poll() call might yield + // control back to the application + grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get(); + grpc_core::ExecCtx::Set(nullptr); + poller_vtable->poll(static_cast(timeout)); + grpc_core::ExecCtx::Set(curr); + grpc_core::ExecCtx::Get()->InvalidateNow(); + if (grpc_core::ExecCtx::Get()->HasWork()) { + grpc_core::ExecCtx::Get()->Flush(); + } + gpr_mu_lock(&pollset->mu); + return GRPC_ERROR_NONE; +} + +static grpc_error* pollset_kick(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + poller_vtable->kick(); + return GRPC_ERROR_NONE; +} + +grpc_pollset_vtable custom_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + +void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable) { + poller_vtable = vtable; + grpc_set_pollset_vtable(&custom_pollset_vtable); +} diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_custom.h b/third_party/grpc/src/core/lib/iomgr/pollset_custom.h new file mode 100644 index 00000000000000..9e2027f7f4a69b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_custom.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H + +#include + +#include + +typedef struct grpc_custom_poller_vtable { + void (*init)(); + void (*poll)(size_t timeout_ms); + void (*kick)(); + void (*shutdown)(); +} grpc_custom_poller_vtable; + +void grpc_custom_pollset_init(grpc_custom_poller_vtable* vtable); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_CUSTOM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set.cc b/third_party/grpc/src/core/lib/iomgr/pollset_set.cc new file mode 100644 index 00000000000000..42a647a7376589 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_set.cc @@ -0,0 +1,55 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/pollset_set.h" + +grpc_pollset_set_vtable* grpc_pollset_set_impl; + +void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable) { + grpc_pollset_set_impl = vtable; +} + +grpc_pollset_set* grpc_pollset_set_create() { + return grpc_pollset_set_impl->create(); +} + +void grpc_pollset_set_destroy(grpc_pollset_set* pollset_set) { + grpc_pollset_set_impl->destroy(pollset_set); +} + +void grpc_pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { + grpc_pollset_set_impl->add_pollset(pollset_set, pollset); +} + +void grpc_pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) { + grpc_pollset_set_impl->del_pollset(pollset_set, pollset); +} + +void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + grpc_pollset_set_impl->add_pollset_set(bag, item); +} + +void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) { + grpc_pollset_set_impl->del_pollset_set(bag, item); +} diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set.h b/third_party/grpc/src/core/lib/iomgr/pollset_set.h new file mode 100644 index 00000000000000..d3355b8ff8ce5b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_set.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_H + +#include + +#include "src/core/lib/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. */ + +typedef struct grpc_pollset_set grpc_pollset_set; + +typedef struct grpc_pollset_set_vtable { + grpc_pollset_set* (*create)(void); + void (*destroy)(grpc_pollset_set* pollset_set); + void (*add_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset); + void (*del_pollset)(grpc_pollset_set* pollset_set, grpc_pollset* pollset); + void (*add_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item); + void (*del_pollset_set)(grpc_pollset_set* bag, grpc_pollset_set* item); +} grpc_pollset_set_vtable; + +void grpc_set_pollset_set_vtable(grpc_pollset_set_vtable* vtable); + +grpc_pollset_set* grpc_pollset_set_create(void); +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); +void grpc_pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item); +void grpc_pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.cc b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.cc new file mode 100644 index 00000000000000..b1ee66020d9fc0 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/pollset_set.h" + +grpc_pollset_set* pollset_set_create(void) { + return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +} + +void pollset_set_destroy(grpc_pollset_set* pollset_set) {} + +void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +static grpc_pollset_set_vtable vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; + +void grpc_custom_pollset_set_init() { grpc_set_pollset_set_vtable(&vtable); } diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.h b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.h new file mode 100644 index 00000000000000..80e19a1fef95fd --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_custom.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H + +#include + +void grpc_custom_pollset_set_init(); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_CUSTOM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.cc b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.cc new file mode 100644 index 00000000000000..bb9e7f5d28dbe7 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.cc @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/pollset_set_windows.h" + +static grpc_pollset_set* pollset_set_create(void) { + return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +} + +static void pollset_set_destroy(grpc_pollset_set* pollset_set) {} + +static void pollset_set_add_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +static void pollset_set_del_pollset(grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +static void pollset_set_add_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +static void pollset_set_del_pollset_set(grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +grpc_pollset_set_vtable grpc_windows_pollset_set_vtable = { + pollset_set_create, pollset_set_destroy, + pollset_set_add_pollset, pollset_set_del_pollset, + pollset_set_add_pollset_set, pollset_set_del_pollset_set}; + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.h b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.h new file mode 100644 index 00000000000000..5ac9d1823bd6f1 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_set_windows.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H + +#include + +#include "src/core/lib/iomgr/pollset_set.h" + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_uv.cc b/third_party/grpc/src/core/lib/iomgr/pollset_uv.cc new file mode 100644 index 00000000000000..bade6eae6c521a --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_uv.cc @@ -0,0 +1,93 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include +#include +#include "src/core/lib/iomgr/pollset_custom.h" + +#include + +/* Indicates that grpc_pollset_work should run an iteration of the UV loop + before running callbacks. This defaults to 1, and should be disabled if + grpc_pollset_work will be called within the callstack of uv_run */ +int grpc_pollset_work_run_loop = 1; + +static bool g_kicked = false; + +typedef struct uv_poller_handle { + uv_timer_t poll_timer; + uv_timer_t kick_timer; + int refs; +} uv_poller_handle; + +static uv_poller_handle* g_handle; + +static void init() { + g_handle = (uv_poller_handle*)gpr_malloc(sizeof(uv_poller_handle)); + g_handle->refs = 2; + uv_timer_init(uv_default_loop(), &g_handle->poll_timer); + uv_timer_init(uv_default_loop(), &g_handle->kick_timer); +} + +static void empty_timer_cb(uv_timer_t* handle) {} + +static void kick_timer_cb(uv_timer_t* handle) { g_kicked = false; } + +static void run_loop(size_t timeout) { + if (grpc_pollset_work_run_loop) { + if (timeout == 0) { + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } else { + uv_timer_start(&g_handle->poll_timer, empty_timer_cb, timeout, 0); + uv_run(uv_default_loop(), UV_RUN_ONCE); + uv_timer_stop(&g_handle->poll_timer); + } + } +} + +static void kick() { + if (!g_kicked) { + g_kicked = true; + uv_timer_start(&g_handle->kick_timer, kick_timer_cb, 0, 0); + } +} + +static void close_timer_cb(uv_handle_t* handle) { + g_handle->refs--; + if (g_handle->refs == 0) { + gpr_free(g_handle); + } +} + +static void shutdown() { + uv_close((uv_handle_t*)&g_handle->poll_timer, close_timer_cb); + uv_close((uv_handle_t*)&g_handle->kick_timer, close_timer_cb); + if (grpc_pollset_work_run_loop) { + GPR_ASSERT(uv_run(uv_default_loop(), UV_RUN_DEFAULT) == 0); + } +} + +grpc_custom_poller_vtable uv_pollset_vtable = {init, run_loop, kick, shutdown}; + +#endif /* GRPC_UV */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_uv.h b/third_party/grpc/src/core/lib/iomgr/pollset_uv.h new file mode 100644 index 00000000000000..de82bcc1d31865 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_uv.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_UV_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_UV_H + +extern int grpc_pollset_work_run_loop; + +typedef struct grpc_custom_poller_vtable { + void (*init)(void); + void (*run_loop)(int blocking); +} grpc_custom_poller_vtable; + +void grpc_custom_pollset_global_init(grpc_custom_poller_vtable* vtable); +void grpc_custom_pollset_global_shutdown(void); + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_UV_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_windows.cc b/third_party/grpc/src/core/lib/iomgr/pollset_windows.cc new file mode 100644 index 00000000000000..e9a808d8ad9641 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_windows.cc @@ -0,0 +1,229 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include + +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_windows.h" + +#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker*)1) + +grpc_core::DebugOnlyTraceFlag grpc_trace_fd_refcount(false, "fd_refcount"); + +gpr_mu grpc_polling_mu; +static grpc_pollset_worker* g_active_poller; +static grpc_pollset_worker g_global_root_worker; + +static void pollset_global_init(void) { + gpr_mu_init(&grpc_polling_mu); + g_active_poller = NULL; + g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = + g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = + &g_global_root_worker; +} + +static void pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } + +static void remove_worker(grpc_pollset_worker* worker, + grpc_pollset_worker_link_type type) { + worker->links[type].prev->links[type].next = worker->links[type].next; + worker->links[type].next->links[type].prev = worker->links[type].prev; + worker->links[type].next = worker->links[type].prev = worker; +} + +static int has_workers(grpc_pollset_worker* root, + grpc_pollset_worker_link_type type) { + return root->links[type].next != root; +} + +static grpc_pollset_worker* pop_front_worker( + grpc_pollset_worker* root, grpc_pollset_worker_link_type type) { + if (has_workers(root, type)) { + grpc_pollset_worker* w = root->links[type].next; + remove_worker(w, type); + return w; + } else { + return NULL; + } +} + +static void push_front_worker(grpc_pollset_worker* root, + grpc_pollset_worker_link_type type, + grpc_pollset_worker* worker) { + worker->links[type].prev = root; + worker->links[type].next = worker->links[type].prev->links[type].next; + worker->links[type].prev->links[type].next = + worker->links[type].next->links[type].prev = worker; +} + +static size_t pollset_size(void) { return sizeof(grpc_pollset); } + +/* 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. */ + +static void pollset_init(grpc_pollset* pollset, gpr_mu** mu) { + *mu = &grpc_polling_mu; + pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = + pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = + &pollset->root_worker; +} + +static void pollset_shutdown(grpc_pollset* pollset, grpc_closure* closure) { + pollset->shutting_down = 1; + grpc_pollset_kick(pollset, GRPC_POLLSET_KICK_BROADCAST); + if (!pollset->is_iocp_worker) { + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + } else { + pollset->on_shutdown = closure; + } +} + +static void pollset_destroy(grpc_pollset* pollset) {} + +static grpc_error* pollset_work(grpc_pollset* pollset, + grpc_pollset_worker** worker_hdl, + grpc_millis deadline) { + grpc_pollset_worker worker; + if (worker_hdl) *worker_hdl = &worker; + + int added_worker = 0; + worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = + worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = + worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = + worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = NULL; + worker.kicked = 0; + worker.pollset = pollset; + gpr_cv_init(&worker.cv); + if (!pollset->kicked_without_pollers && !pollset->shutting_down) { + if (g_active_poller == NULL) { + grpc_pollset_worker* next_worker; + /* become poller */ + pollset->is_iocp_worker = 1; + g_active_poller = &worker; + gpr_mu_unlock(&grpc_polling_mu); + grpc_iocp_work(deadline); + grpc_core::ExecCtx::Get()->Flush(); + gpr_mu_lock(&grpc_polling_mu); + pollset->is_iocp_worker = 0; + g_active_poller = NULL; + /* try to get a worker from this pollsets worker list */ + next_worker = pop_front_worker(&pollset->root_worker, + GRPC_POLLSET_WORKER_LINK_POLLSET); + if (next_worker == NULL) { + /* try to get a worker from the global list */ + next_worker = pop_front_worker(&g_global_root_worker, + GRPC_POLLSET_WORKER_LINK_GLOBAL); + } + if (next_worker != NULL) { + next_worker->kicked = 1; + gpr_cv_signal(&next_worker->cv); + } + + if (pollset->shutting_down && pollset->on_shutdown != NULL) { + GRPC_CLOSURE_SCHED(pollset->on_shutdown, GRPC_ERROR_NONE); + pollset->on_shutdown = NULL; + } + goto done; + } + push_front_worker(&g_global_root_worker, GRPC_POLLSET_WORKER_LINK_GLOBAL, + &worker); + push_front_worker(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET, + &worker); + added_worker = 1; + while (!worker.kicked) { + if (gpr_cv_wait(&worker.cv, &grpc_polling_mu, + grpc_millis_to_timespec(deadline, GPR_CLOCK_REALTIME))) { + grpc_core::ExecCtx::Get()->InvalidateNow(); + break; + } + grpc_core::ExecCtx::Get()->InvalidateNow(); + } + } else { + pollset->kicked_without_pollers = 0; + } +done: + if (!grpc_closure_list_empty(*grpc_core::ExecCtx::Get()->closure_list())) { + gpr_mu_unlock(&grpc_polling_mu); + grpc_core::ExecCtx::Get()->Flush(); + gpr_mu_lock(&grpc_polling_mu); + } + if (added_worker) { + remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_GLOBAL); + remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_POLLSET); + } + gpr_cv_destroy(&worker.cv); + if (worker_hdl) *worker_hdl = NULL; + return GRPC_ERROR_NONE; +} + +static grpc_error* 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.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next; + specific_worker != &p->root_worker; + specific_worker = + specific_worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next) { + specific_worker->kicked = 1; + gpr_cv_signal(&specific_worker->cv); + } + p->kicked_without_pollers = 1; + if (p->is_iocp_worker) { + grpc_iocp_kick(); + } + } else { + if (p->is_iocp_worker && g_active_poller == specific_worker) { + grpc_iocp_kick(); + } else { + specific_worker->kicked = 1; + gpr_cv_signal(&specific_worker->cv); + } + } + } else { + specific_worker = + pop_front_worker(&p->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET); + if (specific_worker != NULL) { + grpc_pollset_kick(p, specific_worker); + } else if (p->is_iocp_worker) { + grpc_iocp_kick(); + } else { + p->kicked_without_pollers = 1; + } + } + return GRPC_ERROR_NONE; +} + +grpc_pollset_vtable grpc_windows_pollset_vtable = { + pollset_global_init, pollset_global_shutdown, + pollset_init, pollset_shutdown, + pollset_destroy, pollset_work, + pollset_kick, pollset_size}; + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/lib/iomgr/pollset_windows.h b/third_party/grpc/src/core/lib/iomgr/pollset_windows.h new file mode 100644 index 00000000000000..e89758c6941535 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/pollset_windows.h @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H +#define GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H + +#include + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET +#include "src/core/lib/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 enum { + GRPC_POLLSET_WORKER_LINK_POLLSET = 0, + GRPC_POLLSET_WORKER_LINK_GLOBAL, + GRPC_POLLSET_WORKER_LINK_TYPES +} grpc_pollset_worker_link_type; + +typedef struct grpc_pollset_worker_link { + struct grpc_pollset_worker* next; + struct grpc_pollset_worker* prev; +} grpc_pollset_worker_link; + +struct grpc_pollset; +typedef struct grpc_pollset grpc_pollset; + +typedef struct grpc_pollset_worker { + gpr_cv cv; + int kicked; + struct grpc_pollset* pollset; + grpc_pollset_worker_link links[GRPC_POLLSET_WORKER_LINK_TYPES]; +} grpc_pollset_worker; + +struct grpc_pollset { + int shutting_down; + int kicked_without_pollers; + int is_iocp_worker; + grpc_pollset_worker root_worker; + grpc_closure* on_shutdown; +}; + +void grpc_pollset_global_init(void); +void grpc_pollset_global_shutdown(void); + +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/port.h b/third_party/grpc/src/core/lib/iomgr/port.h new file mode 100644 index 00000000000000..c8046b21dcd810 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/port.h @@ -0,0 +1,205 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifndef GRPC_CORE_LIB_IOMGR_PORT_H +#define GRPC_CORE_LIB_IOMGR_PORT_H + +#ifdef GRPC_UV +#ifndef GRPC_CUSTOM_SOCKET +#define GRPC_CUSTOM_SOCKET +#endif +#endif +#if defined(GRPC_CUSTOM_SOCKET) +// Do Nothing +#elif defined(GPR_MANYLINUX1) +#define GRPC_HAVE_ARPA_NAMESER 1 +#define GRPC_HAVE_IFADDRS 1 +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_MSG_NOSIGNAL 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_POSIX_FORK 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#define GRPC_LINUX_EPOLL 1 +#elif defined(GPR_WINDOWS) +#define GRPC_WINSOCK_SOCKET 1 +#define GRPC_WINDOWS_SOCKETUTILS 1 +#elif defined(GPR_ANDROID) +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_MSG_NOSIGNAL 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_LINUX_EVENTFD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#elif defined(GPR_LINUX) +#define GRPC_HAVE_ARPA_NAMESER 1 +#define GRPC_HAVE_IFADDRS 1 +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_IP_PKTINFO 1 +#define GRPC_HAVE_MSG_NOSIGNAL 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#ifdef LINUX_VERSION_CODE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) +#define GRPC_LINUX_ERRQUEUE 1 +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) */ +#endif /* LINUX_VERSION_CODE */ +#define GRPC_LINUX_MULTIPOLL_WITH_EPOLL 1 +#define GRPC_POSIX_FORK 1 +#define GRPC_POSIX_HOST_NAME_MAX 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#ifdef __GLIBC_PREREQ +#if __GLIBC_PREREQ(2, 4) +#define GRPC_LINUX_EPOLL 1 +#endif +#if __GLIBC_PREREQ(2, 9) +#define GRPC_LINUX_EPOLL_CREATE1 1 +#define GRPC_LINUX_EVENTFD 1 +#endif +#if __GLIBC_PREREQ(2, 10) +#define GRPC_LINUX_SOCKETUTILS 1 +#endif +#endif +#ifdef LINUX_VERSION_CODE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#define GRPC_HAVE_TCP_USER_TIMEOUT +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) */ +#endif /* LINUX_VERSION_CODE */ +#ifndef __GLIBC__ +#define GRPC_LINUX_EPOLL 1 +#define GRPC_LINUX_EPOLL_CREATE1 1 +#define GRPC_LINUX_EVENTFD 1 +#define GRPC_MSG_IOVLEN_TYPE int +#endif +#ifndef GRPC_LINUX_EVENTFD +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#endif +#ifndef GRPC_LINUX_SOCKETUTILS +#define GRPC_POSIX_SOCKETUTILS +#endif +#elif defined(GPR_APPLE) +#define GRPC_HAVE_ARPA_NAMESER 1 +#define GRPC_HAVE_IFADDRS 1 +#define GRPC_HAVE_SO_NOSIGPIPE 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_MSG_IOVLEN_TYPE int +#define GRPC_POSIX_FORK 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#ifdef GRPC_CFSTREAM +#define GRPC_CFSTREAM_IOMGR 1 +#define GRPC_CFSTREAM_CLIENT 1 +#define GRPC_CFSTREAM_ENDPOINT 1 +#define GRPC_POSIX_SOCKET_ARES_EV_DRIVER 1 +#define GRPC_POSIX_SOCKET_EV 1 +#define GRPC_POSIX_SOCKET_EV_EPOLL1 1 +#define GRPC_POSIX_SOCKET_EV_EPOLLEX 1 +#define GRPC_POSIX_SOCKET_EV_POLL 1 +#define GRPC_POSIX_SOCKET_RESOLVE_ADDRESS 1 +#define GRPC_POSIX_SOCKET_SOCKADDR 1 +#define GRPC_POSIX_SOCKET_SOCKET_FACTORY 1 +#define GRPC_POSIX_SOCKET_TCP 1 +#define GRPC_POSIX_SOCKET_TCP_CLIENT 1 +#define GRPC_POSIX_SOCKET_TCP_SERVER 1 +#define GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON 1 +#define GRPC_POSIX_SOCKET_UTILS_COMMON 1 +#else +#define GRPC_POSIX_SOCKET 1 +#endif +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_SYSCONF 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#elif defined(GPR_FREEBSD) +#define GRPC_HAVE_ARPA_NAMESER 1 +#define GRPC_HAVE_IFADDRS 1 +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_SO_NOSIGPIPE 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_POSIX_FORK 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#elif defined(GPR_OPENBSD) +#define GRPC_HAVE_IFADDRS 1 +#define GRPC_HAVE_IPV6_RECVPKTINFO 1 +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#elif defined(GPR_SOLARIS) +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#elif defined(GPR_AIX) +#define GRPC_HAVE_UNIX_SOCKET 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#elif defined(GPR_NACL) +#define GRPC_HAVE_ARPA_NAMESER 1 +#define GRPC_POSIX_NO_SPECIAL_WAKEUP_FD 1 +#define GRPC_POSIX_SOCKET 1 +#define GRPC_POSIX_SOCKETUTILS 1 +#define GRPC_POSIX_WAKEUP_FD 1 +#elif !defined(GPR_NO_AUTODETECT_PLATFORM) +#error "Platform not recognized" +#endif + +#if defined(GRPC_POSIX_SOCKET) + defined(GRPC_WINSOCK_SOCKET) + \ + defined(GRPC_CUSTOM_SOCKET) + defined(GRPC_CFSTREAM) != \ + 1 +#error \ + "Must define exactly one of GRPC_POSIX_SOCKET, GRPC_WINSOCK_SOCKET, GRPC_CUSTOM_SOCKET" +#endif + +#ifdef GRPC_POSIX_SOCKET +#define GRPC_POSIX_SOCKET_ARES_EV_DRIVER 1 +#define GRPC_POSIX_SOCKET_EV 1 +#define GRPC_POSIX_SOCKET_EV_EPOLLEX 1 +#define GRPC_POSIX_SOCKET_EV_POLL 1 +#define GRPC_POSIX_SOCKET_EV_EPOLL1 1 +#define GRPC_POSIX_SOCKET_IOMGR 1 +#define GRPC_POSIX_SOCKET_RESOLVE_ADDRESS 1 +#define GRPC_POSIX_SOCKET_SOCKADDR 1 +#define GRPC_POSIX_SOCKET_SOCKET_FACTORY 1 +#define GRPC_POSIX_SOCKET_TCP 1 +#define GRPC_POSIX_SOCKET_TCP_CLIENT 1 +#define GRPC_POSIX_SOCKET_TCP_SERVER 1 +#define GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON 1 +#define GRPC_POSIX_SOCKET_UTILS_COMMON 1 +#endif + +#if defined(GRPC_POSIX_HOST_NAME_MAX) && defined(GRPC_POSIX_SYSCONF) +#error "Cannot define both GRPC_POSIX_HOST_NAME_MAX and GRPC_POSIX_SYSCONF" +#endif +#if !defined(GRPC_POSIX_HOST_NAME_MAX) && !defined(GRPC_POSIX_SYSCONF) +#define GRPC_GETHOSTNAME_FALLBACK 1 +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_PORT_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address.cc new file mode 100644 index 00000000000000..f2a4676369a9fa --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resolve_address.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include "src/core/lib/iomgr/resolve_address.h" + +grpc_address_resolver_vtable* grpc_resolve_address_impl; + +void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable) { + grpc_resolve_address_impl = vtable; +} + +void grpc_resolve_address(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses) { + grpc_resolve_address_impl->resolve_address( + addr, default_port, interested_parties, on_done, addresses); +} + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addrs) { + if (addrs != nullptr) { + gpr_free(addrs->addrs); + } + gpr_free(addrs); +} + +grpc_error* grpc_blocking_resolve_address(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses) { + return grpc_resolve_address_impl->blocking_resolve_address(name, default_port, + addresses); +} diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address.h b/third_party/grpc/src/core/lib/iomgr/resolve_address.h new file mode 100644 index 00000000000000..7016ffc31aac14 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resolve_address.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H +#define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H + +#include + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV +#include +#endif + +#ifdef GRPC_WINSOCK_SOCKET +#include +#endif + +#if defined(GRPC_POSIX_SOCKET) || defined(GRPC_CFSTREAM) +#include +#endif + +#include "src/core/lib/iomgr/pollset_set.h" + +#define GRPC_MAX_SOCKADDR_SIZE 128 + +typedef struct { + char addr[GRPC_MAX_SOCKADDR_SIZE]; + socklen_t len; +} grpc_resolved_address; + +typedef struct { + size_t naddrs; + grpc_resolved_address* addrs; +} grpc_resolved_addresses; + +typedef struct grpc_address_resolver_vtable { + void (*resolve_address)(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses); + grpc_error* (*blocking_resolve_address)(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses); +} grpc_address_resolver_vtable; + +void grpc_set_resolver_impl(grpc_address_resolver_vtable* vtable); + +/* Asynchronously resolve addr. Use default_port if a port isn't designated + in addr, otherwise use the port in addr. */ +/* TODO(apolcyn): add a timeout here */ +void grpc_resolve_address(const char* addr, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses); + +/* Destroy resolved addresses */ +void grpc_resolved_addresses_destroy(grpc_resolved_addresses* addresses); + +/* Resolve addr in a blocking fashion. On success, + result must be freed with grpc_resolved_addresses_destroy. */ +grpc_error* grpc_blocking_resolve_address(const char* name, + const char* default_port, + grpc_resolved_addresses** addresses); + +#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.cc new file mode 100644 index 00000000000000..9cf7817f66ec44 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.cc @@ -0,0 +1,187 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include +#include + +#include +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" + +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include + +typedef struct grpc_custom_resolver { + grpc_closure* on_done; + grpc_resolved_addresses** addresses; + char* host; + char* port; +} grpc_custom_resolver; + +static grpc_custom_resolver_vtable* resolve_address_vtable = nullptr; + +static int retry_named_port_failure(grpc_custom_resolver* r, + grpc_resolved_addresses** res) { + // This loop is copied from resolve_address_posix.c + const char* svc[][2] = {{"http", "80"}, {"https", "443"}}; + for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) { + if (strcmp(r->port, svc[i][0]) == 0) { + gpr_free(r->port); + r->port = gpr_strdup(svc[i][1]); + if (res) { + grpc_error* error = + resolve_address_vtable->resolve(r->host, r->port, res); + if (error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(error); + return 0; + } + } else { + resolve_address_vtable->resolve_async(r, r->host, r->port); + } + return 1; + } + } + return 0; +} + +void grpc_custom_resolve_callback(grpc_custom_resolver* r, + grpc_resolved_addresses* result, + grpc_error* error) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_core::ExecCtx exec_ctx; + if (error == GRPC_ERROR_NONE) { + *r->addresses = result; + } else if (retry_named_port_failure(r, nullptr)) { + return; + } + if (r->on_done) { + GRPC_CLOSURE_SCHED(r->on_done, error); + } + gpr_free(r->host); + gpr_free(r->port); + gpr_free(r); +} + +static grpc_error* try_split_host_port(const char* name, + const char* default_port, char** host, + char** port) { + /* parse name, splitting it into host and port parts */ + grpc_error* error; + gpr_split_host_port(name, host, port); + if (*host == nullptr) { + char* msg; + gpr_asprintf(&msg, "unparseable host:port: '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + if (*port == nullptr) { + // TODO(murgatroid99): add tests for this case + if (default_port == nullptr) { + char* msg; + gpr_asprintf(&msg, "no port in name '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + *port = gpr_strdup(default_port); + } + return GRPC_ERROR_NONE; +} + +static grpc_error* blocking_resolve_address_impl( + const char* name, const char* default_port, + grpc_resolved_addresses** addresses) { + char* host; + char* port; + grpc_error* err; + + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + gpr_free(host); + gpr_free(port); + return err; + } + + /* Call getaddrinfo */ + grpc_custom_resolver resolver; + resolver.host = host; + resolver.port = port; + + grpc_resolved_addresses* addrs; + grpc_core::ExecCtx* curr = grpc_core::ExecCtx::Get(); + grpc_core::ExecCtx::Set(nullptr); + err = resolve_address_vtable->resolve(host, port, &addrs); + if (err != GRPC_ERROR_NONE) { + if (retry_named_port_failure(&resolver, &addrs)) { + GRPC_ERROR_UNREF(err); + err = GRPC_ERROR_NONE; + } + } + grpc_core::ExecCtx::Set(curr); + if (err == GRPC_ERROR_NONE) { + *addresses = addrs; + } + gpr_free(resolver.host); + gpr_free(resolver.port); + return err; +} + +static void resolve_address_impl(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) { + grpc_custom_resolver* r = nullptr; + char* host = nullptr; + char* port = nullptr; + grpc_error* err; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(on_done, err); + gpr_free(host); + gpr_free(port); + return; + } + r = (grpc_custom_resolver*)gpr_malloc(sizeof(grpc_custom_resolver)); + r->on_done = on_done; + r->addresses = addrs; + r->host = host; + r->port = port; + + /* Call getaddrinfo */ + resolve_address_vtable->resolve_async(r, r->host, r->port); +} + +static grpc_address_resolver_vtable custom_resolver_vtable = { + resolve_address_impl, blocking_resolve_address_impl}; + +void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl) { + resolve_address_vtable = impl; + grpc_set_resolver_impl(&custom_resolver_vtable); +} diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.h b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.h new file mode 100644 index 00000000000000..e0c6714087aba9 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_custom.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" + +typedef struct grpc_custom_resolver grpc_custom_resolver; + +typedef struct grpc_custom_resolver_vtable { + grpc_error* (*resolve)(char* host, char* port, grpc_resolved_addresses** res); + void (*resolve_async)(grpc_custom_resolver* resolver, char* host, char* port); +} grpc_custom_resolver_vtable; + +void grpc_custom_resolve_callback(grpc_custom_resolver* resolver, + grpc_resolved_addresses* result, + grpc_error* error); + +/* Internal APIs */ +void grpc_custom_resolver_init(grpc_custom_resolver_vtable* impl); + +#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_CUSTOM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_posix.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address_posix.cc new file mode 100644 index 00000000000000..c285d7eca6693a --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_posix.cc @@ -0,0 +1,181 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" +#ifdef GRPC_POSIX_SOCKET_RESOLVE_ADDRESS + +#include "src/core/lib/iomgr/sockaddr.h" + +#include "src/core/lib/iomgr/resolve_address.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/block_annotate.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +static grpc_error* posix_blocking_resolve_address( + const char* name, const char* default_port, + grpc_resolved_addresses** addresses) { + grpc_core::ExecCtx exec_ctx; + struct addrinfo hints; + struct addrinfo *result = nullptr, *resp; + char* host; + char* port; + int s; + size_t i; + grpc_error* err; + + if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' && + name[4] == ':' && name[5] != 0) { + return grpc_resolve_unix_domain_address(name + 5, addresses); + } + + /* parse name, splitting it into host and port parts */ + gpr_split_host_port(name, &host, &port); + if (host == nullptr) { + err = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto done; + } + if (port == nullptr) { + if (default_port == nullptr) { + err = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(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 */ + + GRPC_SCHEDULING_START_BLOCKING_REGION; + s = getaddrinfo(host, port, &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; + + if (s != 0) { + /* Retry if well-known service name is recognized */ + const char* svc[][2] = {{"http", "80"}, {"https", "443"}}; + for (i = 0; i < GPR_ARRAY_SIZE(svc); i++) { + if (strcmp(port, svc[i][0]) == 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; + s = getaddrinfo(host, svc[i][1], &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; + break; + } + } + } + + if (s != 0) { + err = grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("OS Error"), + GRPC_ERROR_INT_ERRNO, s), + GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(gai_strerror(s))), + GRPC_ERROR_STR_SYSCALL, + grpc_slice_from_static_string("getaddrinfo")), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto done; + } + + /* Success path: set addrs non-NULL, fill it in */ + *addresses = static_cast( + gpr_malloc(sizeof(grpc_resolved_addresses))); + (*addresses)->naddrs = 0; + for (resp = result; resp != nullptr; resp = resp->ai_next) { + (*addresses)->naddrs++; + } + (*addresses)->addrs = static_cast( + gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs)); + i = 0; + for (resp = result; resp != nullptr; resp = resp->ai_next) { + memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + (*addresses)->addrs[i].len = resp->ai_addrlen; + i++; + } + err = GRPC_ERROR_NONE; + +done: + gpr_free(host); + gpr_free(port); + if (result) { + freeaddrinfo(result); + } + return err; +} + +typedef struct { + char* name; + char* default_port; + grpc_closure* on_done; + grpc_resolved_addresses** addrs_out; + grpc_closure request_closure; + void* arg; +} request; + +/* Callback to be passed to grpc_executor to asynch-ify + * grpc_blocking_resolve_address */ +static void do_request_thread(void* rp, grpc_error* error) { + request* r = static_cast(rp); + GRPC_CLOSURE_SCHED(r->on_done, grpc_blocking_resolve_address( + r->name, r->default_port, r->addrs_out)); + gpr_free(r->name); + gpr_free(r->default_port); + gpr_free(r); +} + +static void posix_resolve_address(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addrs) { + request* r = static_cast(gpr_malloc(sizeof(request))); + GRPC_CLOSURE_INIT( + &r->request_closure, do_request_thread, r, + grpc_executor_scheduler(GRPC_RESOLVER_EXECUTOR, GRPC_EXECUTOR_SHORT)); + r->name = gpr_strdup(name); + r->default_port = gpr_strdup(default_port); + r->on_done = on_done; + r->addrs_out = addrs; + GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE); +} + +grpc_address_resolver_vtable grpc_posix_resolver_vtable = { + posix_resolve_address, posix_blocking_resolve_address}; +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/resolve_address_windows.cc b/third_party/grpc/src/core/lib/iomgr/resolve_address_windows.cc new file mode 100644 index 00000000000000..3e977dca2da25d --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resolve_address_windows.cc @@ -0,0 +1,166 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr.h" + +#include "src/core/lib/iomgr/resolve_address.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/block_annotate.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +typedef struct { + char* name; + char* default_port; + grpc_closure request_closure; + grpc_closure* on_done; + grpc_resolved_addresses** addresses; +} request; + +static grpc_error* windows_blocking_resolve_address( + const char* name, const char* default_port, + grpc_resolved_addresses** addresses) { + grpc_core::ExecCtx exec_ctx; + struct addrinfo hints; + struct addrinfo *result = NULL, *resp; + char* host; + char* port; + int s; + size_t i; + grpc_error* error = GRPC_ERROR_NONE; + + /* parse name, splitting it into host and port parts */ + gpr_split_host_port(name, &host, &port); + if (host == NULL) { + char* msg; + gpr_asprintf(&msg, "unparseable host:port: '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + goto done; + } + if (port == NULL) { + if (default_port == NULL) { + char* msg; + gpr_asprintf(&msg, "no port in name '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + 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 */ + + GRPC_SCHEDULING_START_BLOCKING_REGION; + s = getaddrinfo(host, port, &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; + if (s != 0) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "getaddrinfo"); + goto done; + } + + /* Success path: set addrs non-NULL, fill it in */ + (*addresses) = + (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses)->naddrs = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + (*addresses)->naddrs++; + } + (*addresses)->addrs = (grpc_resolved_address*)gpr_malloc( + sizeof(grpc_resolved_address) * (*addresses)->naddrs); + i = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + (*addresses)->addrs[i].len = resp->ai_addrlen; + i++; + } + + { + for (i = 0; i < (*addresses)->naddrs; i++) { + char* buf; + grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); + gpr_free(buf); + } + } + +done: + gpr_free(host); + gpr_free(port); + if (result) { + freeaddrinfo(result); + } + return error; +} + +/* Callback to be passed to grpc_executor to asynch-ify + * grpc_blocking_resolve_address */ +static void do_request_thread(void* rp, grpc_error* error) { + request* r = (request*)rp; + if (error == GRPC_ERROR_NONE) { + error = + grpc_blocking_resolve_address(r->name, r->default_port, r->addresses); + } else { + GRPC_ERROR_REF(error); + } + GRPC_CLOSURE_SCHED(r->on_done, error); + gpr_free(r->name); + gpr_free(r->default_port); + gpr_free(r); +} + +static void windows_resolve_address(const char* name, const char* default_port, + grpc_pollset_set* interested_parties, + grpc_closure* on_done, + grpc_resolved_addresses** addresses) { + request* r = (request*)gpr_malloc(sizeof(request)); + GRPC_CLOSURE_INIT( + &r->request_closure, do_request_thread, r, + grpc_executor_scheduler(GRPC_RESOLVER_EXECUTOR, GRPC_EXECUTOR_SHORT)); + r->name = gpr_strdup(name); + r->default_port = gpr_strdup(default_port); + r->on_done = on_done; + r->addresses = addresses; + GRPC_CLOSURE_SCHED(&r->request_closure, GRPC_ERROR_NONE); +} + +grpc_address_resolver_vtable grpc_windows_resolver_vtable = { + windows_resolve_address, windows_blocking_resolve_address}; +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/resource_quota.cc b/third_party/grpc/src/core/lib/iomgr/resource_quota.cc new file mode 100644 index 00000000000000..61c366098e1520 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resource_quota.cc @@ -0,0 +1,1003 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/resource_quota.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/combiner.h" + +grpc_core::TraceFlag grpc_resource_quota_trace(false, "resource_quota"); + +#define MEMORY_USAGE_ESTIMATION_MAX 65536 + +/* Internal linked list pointers for a resource user */ +typedef struct { + grpc_resource_user* next; + grpc_resource_user* prev; +} grpc_resource_user_link; + +/* Resource users are kept in (potentially) several intrusive linked lists + at once. These are the list names. */ +typedef enum { + /* Resource users that are waiting for an allocation */ + GRPC_RULIST_AWAITING_ALLOCATION, + /* Resource users that have free memory available for internal reclamation */ + GRPC_RULIST_NON_EMPTY_FREE_POOL, + /* Resource users that have published a benign reclamation is available */ + GRPC_RULIST_RECLAIMER_BENIGN, + /* Resource users that have published a destructive reclamation is + available */ + GRPC_RULIST_RECLAIMER_DESTRUCTIVE, + /* Number of lists: must be last */ + GRPC_RULIST_COUNT +} grpc_rulist; + +struct grpc_resource_user { + /* The quota this resource user consumes from */ + grpc_resource_quota* resource_quota; + + /* Closure to schedule an allocation under the resource quota combiner lock */ + grpc_closure allocate_closure; + /* Closure to publish a non empty free pool under the resource quota combiner + lock */ + grpc_closure add_to_free_pool_closure; + + /* one ref for each ref call (released by grpc_resource_user_unref), and one + ref for each byte allocated (released by grpc_resource_user_free) */ + gpr_atm refs; + /* is this resource user unlocked? starts at 0, increases for each shutdown + call */ + gpr_atm shutdown; + + gpr_mu mu; + /* The amount of memory (in bytes) this user has cached for its own use: to + avoid quota contention, each resource user can keep some memory in + addition to what it is immediately using (e.g., for caching), and the quota + can pull it back under memory pressure. + This value can become negative if more memory has been requested than + existed in the free pool, at which point the quota is consulted to bring + this value non-negative (asynchronously). */ + int64_t free_pool; + /* A list of closures to call once free_pool becomes non-negative - ie when + all outstanding allocations have been granted. */ + grpc_closure_list on_allocated; + /* True if we are currently trying to allocate from the quota, false if not */ + bool allocating; + /* The amount of memory (in bytes) that has been requested from this user + * asynchronously but hasn't been granted yet. */ + int64_t outstanding_allocations; + /* True if we are currently trying to add ourselves to the non-free quota + list, false otherwise */ + bool added_to_free_pool; + + /* The number of threads currently allocated to this resource user */ + gpr_atm num_threads_allocated; + + /* Reclaimers: index 0 is the benign reclaimer, 1 is the destructive reclaimer + */ + grpc_closure* reclaimers[2]; + /* Reclaimers just posted: once we're in the combiner lock, we'll move them + to the array above */ + grpc_closure* new_reclaimers[2]; + /* Trampoline closures to finish reclamation and re-enter the quota combiner + lock */ + grpc_closure post_reclaimer_closure[2]; + + /* Closure to execute under the quota combiner to de-register and shutdown the + resource user */ + grpc_closure destroy_closure; + + /* Links in the various grpc_rulist lists */ + grpc_resource_user_link links[GRPC_RULIST_COUNT]; + + /* The name of this resource user, for debugging/tracing */ + char* name; +}; + +struct grpc_resource_quota { + /* refcount */ + gpr_refcount refs; + + /* estimate of current memory usage + scaled to the range [0..RESOURCE_USAGE_ESTIMATION_MAX] */ + gpr_atm memory_usage_estimation; + + /* Master combiner lock: all activity on a quota executes under this combiner + * (so no mutex is needed for this data structure) */ + grpc_combiner* combiner; + /* Size of the resource quota */ + int64_t size; + /* Amount of free memory in the resource quota */ + int64_t free_pool; + /* Used size of memory in the resource quota. Updated as soon as the resource + * users start to allocate or free the memory. */ + gpr_atm used; + + gpr_atm last_size; + + /* Mutex to protect max_threads and num_threads_allocated */ + /* Note: We could have used gpr_atm for max_threads and num_threads_allocated + * and avoid having this mutex; but in that case, each invocation of the + * function grpc_resource_user_allocate_threads() would have had to do at + * least two atomic loads (for max_threads and num_threads_allocated) followed + * by a CAS (on num_threads_allocated). + * Moreover, we expect grpc_resource_user_allocate_threads() to be often + * called concurrently thereby increasing the chances of failing the CAS + * operation. This additional complexity is not worth the tiny perf gain we + * may (or may not) have by using atomics */ + gpr_mu thread_count_mu; + + /* Max number of threads allowed */ + int max_threads; + + /* Number of threads currently allocated via this resource_quota object */ + int num_threads_allocated; + + /* Has rq_step been scheduled to occur? */ + bool step_scheduled; + + /* Are we currently reclaiming memory */ + bool reclaiming; + + /* Closure around rq_step */ + grpc_closure rq_step_closure; + + /* Closure around rq_reclamation_done */ + grpc_closure rq_reclamation_done_closure; + + /* This is only really usable for debugging: it's always a stale pointer, but + a stale pointer that might just be fresh enough to guide us to where the + reclamation system is stuck */ + grpc_closure* debug_only_last_initiated_reclaimer; + grpc_resource_user* debug_only_last_reclaimer_resource_user; + + /* Roots of all resource user lists */ + grpc_resource_user* roots[GRPC_RULIST_COUNT]; + + char* name; +}; + +static void ru_unref_by(grpc_resource_user* resource_user, gpr_atm amount); + +/******************************************************************************* + * list management + */ + +static void rulist_add_head(grpc_resource_user* resource_user, + grpc_rulist list) { + grpc_resource_quota* resource_quota = resource_user->resource_quota; + grpc_resource_user** root = &resource_quota->roots[list]; + if (*root == nullptr) { + *root = resource_user; + resource_user->links[list].next = resource_user->links[list].prev = + resource_user; + } else { + resource_user->links[list].next = *root; + resource_user->links[list].prev = (*root)->links[list].prev; + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev->links[list].next = resource_user; + *root = resource_user; + } +} + +static void rulist_add_tail(grpc_resource_user* resource_user, + grpc_rulist list) { + grpc_resource_quota* resource_quota = resource_user->resource_quota; + grpc_resource_user** root = &resource_quota->roots[list]; + if (*root == nullptr) { + *root = resource_user; + resource_user->links[list].next = resource_user->links[list].prev = + resource_user; + } else { + resource_user->links[list].next = (*root)->links[list].next; + resource_user->links[list].prev = *root; + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev->links[list].next = resource_user; + } +} + +static bool rulist_empty(grpc_resource_quota* resource_quota, + grpc_rulist list) { + return resource_quota->roots[list] == nullptr; +} + +static grpc_resource_user* rulist_pop_head(grpc_resource_quota* resource_quota, + grpc_rulist list) { + grpc_resource_user** root = &resource_quota->roots[list]; + grpc_resource_user* resource_user = *root; + if (resource_user == nullptr) { + return nullptr; + } + if (resource_user->links[list].next == resource_user) { + *root = nullptr; + } else { + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev; + resource_user->links[list].prev->links[list].next = + resource_user->links[list].next; + *root = resource_user->links[list].next; + } + resource_user->links[list].next = resource_user->links[list].prev = nullptr; + return resource_user; +} + +static void rulist_remove(grpc_resource_user* resource_user, grpc_rulist list) { + if (resource_user->links[list].next == nullptr) return; + grpc_resource_quota* resource_quota = resource_user->resource_quota; + if (resource_quota->roots[list] == resource_user) { + resource_quota->roots[list] = resource_user->links[list].next; + if (resource_quota->roots[list] == resource_user) { + resource_quota->roots[list] = nullptr; + } + } + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev; + resource_user->links[list].prev->links[list].next = + resource_user->links[list].next; + resource_user->links[list].next = resource_user->links[list].prev = nullptr; +} + +/******************************************************************************* + * resource quota state machine + */ + +static bool rq_alloc(grpc_resource_quota* resource_quota); +static bool rq_reclaim_from_per_user_free_pool( + grpc_resource_quota* resource_quota); +static bool rq_reclaim(grpc_resource_quota* resource_quota, bool destructive); + +static void rq_step(void* rq, grpc_error* error) { + grpc_resource_quota* resource_quota = static_cast(rq); + resource_quota->step_scheduled = false; + do { + if (rq_alloc(resource_quota)) goto done; + } while (rq_reclaim_from_per_user_free_pool(resource_quota)); + + if (!rq_reclaim(resource_quota, false)) { + rq_reclaim(resource_quota, true); + } + +done: + grpc_resource_quota_unref_internal(resource_quota); +} + +static void rq_step_sched(grpc_resource_quota* resource_quota) { + if (resource_quota->step_scheduled) return; + resource_quota->step_scheduled = true; + grpc_resource_quota_ref_internal(resource_quota); + GRPC_CLOSURE_SCHED(&resource_quota->rq_step_closure, GRPC_ERROR_NONE); +} + +/* update the atomically available resource estimate - use no barriers since + timeliness of delivery really doesn't matter much */ +static void rq_update_estimate(grpc_resource_quota* resource_quota) { + gpr_atm memory_usage_estimation = MEMORY_USAGE_ESTIMATION_MAX; + if (resource_quota->size != 0) { + memory_usage_estimation = + GPR_CLAMP((gpr_atm)((1.0 - ((double)resource_quota->free_pool) / + ((double)resource_quota->size)) * + MEMORY_USAGE_ESTIMATION_MAX), + 0, MEMORY_USAGE_ESTIMATION_MAX); + } + gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, + memory_usage_estimation); +} + +/* returns true if all allocations are completed */ +static bool rq_alloc(grpc_resource_quota* resource_quota) { + grpc_resource_user* resource_user; + while ((resource_user = rulist_pop_head(resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION))) { + gpr_mu_lock(&resource_user->mu); + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, + "RQ: check allocation for user %p shutdown=%" PRIdPTR + " free_pool=%" PRId64, + resource_user, gpr_atm_no_barrier_load(&resource_user->shutdown), + resource_user->free_pool); + } + if (gpr_atm_no_barrier_load(&resource_user->shutdown)) { + resource_user->allocating = false; + grpc_closure_list_fail_all( + &resource_user->on_allocated, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource user shutdown")); + int64_t aborted_allocations = resource_user->outstanding_allocations; + resource_user->outstanding_allocations = 0; + resource_user->free_pool += aborted_allocations; + GRPC_CLOSURE_LIST_SCHED(&resource_user->on_allocated); + gpr_mu_unlock(&resource_user->mu); + ru_unref_by(resource_user, static_cast(aborted_allocations)); + continue; + } + if (resource_user->free_pool < 0 && + -resource_user->free_pool <= resource_quota->free_pool) { + int64_t amt = -resource_user->free_pool; + resource_user->free_pool = 0; + resource_quota->free_pool -= amt; + rq_update_estimate(resource_quota); + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, + "RQ %s %s: grant alloc %" PRId64 + " bytes; rq_free_pool -> %" PRId64, + resource_quota->name, resource_user->name, amt, + resource_quota->free_pool); + } + } else if (grpc_resource_quota_trace.enabled() && + resource_user->free_pool >= 0) { + gpr_log(GPR_INFO, "RQ %s %s: discard already satisfied alloc request", + resource_quota->name, resource_user->name); + } + if (resource_user->free_pool >= 0) { + resource_user->allocating = false; + resource_user->outstanding_allocations = 0; + GRPC_CLOSURE_LIST_SCHED(&resource_user->on_allocated); + gpr_mu_unlock(&resource_user->mu); + } else { + rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION); + gpr_mu_unlock(&resource_user->mu); + return false; + } + } + return true; +} + +/* returns true if any memory could be reclaimed from buffers */ +static bool rq_reclaim_from_per_user_free_pool( + grpc_resource_quota* resource_quota) { + grpc_resource_user* resource_user; + while ((resource_user = rulist_pop_head(resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL))) { + gpr_mu_lock(&resource_user->mu); + resource_user->added_to_free_pool = false; + if (resource_user->free_pool > 0) { + int64_t amt = resource_user->free_pool; + resource_user->free_pool = 0; + resource_quota->free_pool += amt; + rq_update_estimate(resource_quota); + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, + "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64 + " bytes; rq_free_pool -> %" PRId64, + resource_quota->name, resource_user->name, amt, + resource_quota->free_pool); + } + gpr_mu_unlock(&resource_user->mu); + return true; + } else { + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, + "RQ %s %s: failed to reclaim_from_per_user_free_pool; " + "free_pool = %" PRId64 "; rq_free_pool = %" PRId64, + resource_quota->name, resource_user->name, + resource_user->free_pool, resource_quota->free_pool); + } + gpr_mu_unlock(&resource_user->mu); + } + } + return false; +} + +/* returns true if reclamation is proceeding */ +static bool rq_reclaim(grpc_resource_quota* resource_quota, bool destructive) { + if (resource_quota->reclaiming) return true; + grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE + : GRPC_RULIST_RECLAIMER_BENIGN; + grpc_resource_user* resource_user = rulist_pop_head(resource_quota, list); + if (resource_user == nullptr) return false; + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, "RQ %s %s: initiate %s reclamation", resource_quota->name, + resource_user->name, destructive ? "destructive" : "benign"); + } + resource_quota->reclaiming = true; + grpc_resource_quota_ref_internal(resource_quota); + grpc_closure* c = resource_user->reclaimers[destructive]; + GPR_ASSERT(c); + resource_quota->debug_only_last_reclaimer_resource_user = resource_user; + resource_quota->debug_only_last_initiated_reclaimer = c; + resource_user->reclaimers[destructive] = nullptr; + GRPC_CLOSURE_SCHED(c, GRPC_ERROR_NONE); + return true; +} + +/******************************************************************************* + * ru_slice: a slice implementation that is backed by a grpc_resource_user + */ + +typedef struct { + grpc_slice_refcount base; + gpr_refcount refs; + grpc_resource_user* resource_user; + size_t size; +} ru_slice_refcount; + +static void ru_slice_ref(void* p) { + ru_slice_refcount* rc = static_cast(p); + gpr_ref(&rc->refs); +} + +static void ru_slice_unref(void* p) { + ru_slice_refcount* rc = static_cast(p); + if (gpr_unref(&rc->refs)) { + grpc_resource_user_free(rc->resource_user, rc->size); + gpr_free(rc); + } +} + +static const grpc_slice_refcount_vtable ru_slice_vtable = { + ru_slice_ref, ru_slice_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +static grpc_slice ru_slice_create(grpc_resource_user* resource_user, + size_t size) { + ru_slice_refcount* rc = static_cast( + gpr_malloc(sizeof(ru_slice_refcount) + size)); + rc->base.vtable = &ru_slice_vtable; + rc->base.sub_refcount = &rc->base; + gpr_ref_init(&rc->refs, 1); + rc->resource_user = resource_user; + rc->size = size; + grpc_slice slice; + slice.refcount = &rc->base; + slice.data.refcounted.bytes = reinterpret_cast(rc + 1); + slice.data.refcounted.length = size; + return slice; +} + +/******************************************************************************* + * grpc_resource_quota internal implementation: resource user manipulation under + * the combiner + */ + +static void ru_allocate(void* ru, grpc_error* error) { + grpc_resource_user* resource_user = static_cast(ru); + if (rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION)) { + rq_step_sched(resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION); +} + +static void ru_add_to_free_pool(void* ru, grpc_error* error) { + grpc_resource_user* resource_user = static_cast(ru); + if (!rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL)) { + rq_step_sched(resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL); +} + +static bool ru_post_reclaimer(grpc_resource_user* resource_user, + bool destructive) { + grpc_closure* closure = resource_user->new_reclaimers[destructive]; + GPR_ASSERT(closure != nullptr); + resource_user->new_reclaimers[destructive] = nullptr; + GPR_ASSERT(resource_user->reclaimers[destructive] == nullptr); + if (gpr_atm_acq_load(&resource_user->shutdown) > 0) { + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_CANCELLED); + return false; + } + resource_user->reclaimers[destructive] = closure; + return true; +} + +static void ru_post_benign_reclaimer(void* ru, grpc_error* error) { + grpc_resource_user* resource_user = static_cast(ru); + if (!ru_post_reclaimer(resource_user, false)) return; + if (!rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_RECLAIMER_BENIGN)) { + rq_step_sched(resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN); +} + +static void ru_post_destructive_reclaimer(void* ru, grpc_error* error) { + grpc_resource_user* resource_user = static_cast(ru); + if (!ru_post_reclaimer(resource_user, true)) return; + if (!rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_RECLAIMER_BENIGN) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) { + rq_step_sched(resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE); +} + +static void ru_shutdown(void* ru, grpc_error* error) { + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, "RU shutdown %p", ru); + } + grpc_resource_user* resource_user = static_cast(ru); + gpr_mu_lock(&resource_user->mu); + GRPC_CLOSURE_SCHED(resource_user->reclaimers[0], GRPC_ERROR_CANCELLED); + GRPC_CLOSURE_SCHED(resource_user->reclaimers[1], GRPC_ERROR_CANCELLED); + resource_user->reclaimers[0] = nullptr; + resource_user->reclaimers[1] = nullptr; + rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_BENIGN); + rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE); + if (resource_user->allocating) { + rq_step_sched(resource_user->resource_quota); + } + gpr_mu_unlock(&resource_user->mu); +} + +static void ru_destroy(void* ru, grpc_error* error) { + grpc_resource_user* resource_user = static_cast(ru); + GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->refs) == 0); + // Free all the remaining thread quota + grpc_resource_user_free_threads(resource_user, + static_cast(gpr_atm_no_barrier_load( + &resource_user->num_threads_allocated))); + + for (int i = 0; i < GRPC_RULIST_COUNT; i++) { + rulist_remove(resource_user, static_cast(i)); + } + GRPC_CLOSURE_SCHED(resource_user->reclaimers[0], GRPC_ERROR_CANCELLED); + GRPC_CLOSURE_SCHED(resource_user->reclaimers[1], GRPC_ERROR_CANCELLED); + if (resource_user->free_pool != 0) { + resource_user->resource_quota->free_pool += resource_user->free_pool; + rq_step_sched(resource_user->resource_quota); + } + grpc_resource_quota_unref_internal(resource_user->resource_quota); + gpr_mu_destroy(&resource_user->mu); + gpr_free(resource_user->name); + gpr_free(resource_user); +} + +static void ru_allocated_slices(void* arg, grpc_error* error) { + grpc_resource_user_slice_allocator* slice_allocator = + static_cast(arg); + if (error == GRPC_ERROR_NONE) { + for (size_t i = 0; i < slice_allocator->count; i++) { + grpc_slice_buffer_add_indexed( + slice_allocator->dest, ru_slice_create(slice_allocator->resource_user, + slice_allocator->length)); + } + } + GRPC_CLOSURE_RUN(&slice_allocator->on_done, GRPC_ERROR_REF(error)); +} + +/******************************************************************************* + * grpc_resource_quota internal implementation: quota manipulation under the + * combiner + */ + +typedef struct { + int64_t size; + grpc_resource_quota* resource_quota; + grpc_closure closure; +} rq_resize_args; + +static void rq_resize(void* args, grpc_error* error) { + rq_resize_args* a = static_cast(args); + int64_t delta = a->size - a->resource_quota->size; + a->resource_quota->size += delta; + a->resource_quota->free_pool += delta; + rq_update_estimate(a->resource_quota); + rq_step_sched(a->resource_quota); + grpc_resource_quota_unref_internal(a->resource_quota); + gpr_free(a); +} + +static void rq_reclamation_done(void* rq, grpc_error* error) { + grpc_resource_quota* resource_quota = static_cast(rq); + resource_quota->reclaiming = false; + rq_step_sched(resource_quota); + grpc_resource_quota_unref_internal(resource_quota); +} + +/******************************************************************************* + * grpc_resource_quota api + */ + +/* Public API */ +grpc_resource_quota* grpc_resource_quota_create(const char* name) { + grpc_resource_quota* resource_quota = + static_cast(gpr_malloc(sizeof(*resource_quota))); + gpr_ref_init(&resource_quota->refs, 1); + resource_quota->combiner = grpc_combiner_create(); + resource_quota->free_pool = INT64_MAX; + resource_quota->size = INT64_MAX; + resource_quota->used = 0; + gpr_atm_no_barrier_store(&resource_quota->last_size, GPR_ATM_MAX); + gpr_mu_init(&resource_quota->thread_count_mu); + resource_quota->max_threads = INT_MAX; + resource_quota->num_threads_allocated = 0; + resource_quota->step_scheduled = false; + resource_quota->reclaiming = false; + gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, 0); + if (name != nullptr) { + resource_quota->name = gpr_strdup(name); + } else { + gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR, + (intptr_t)resource_quota); + } + GRPC_CLOSURE_INIT(&resource_quota->rq_step_closure, rq_step, resource_quota, + grpc_combiner_finally_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_quota->rq_reclamation_done_closure, + rq_reclamation_done, resource_quota, + grpc_combiner_scheduler(resource_quota->combiner)); + for (int i = 0; i < GRPC_RULIST_COUNT; i++) { + resource_quota->roots[i] = nullptr; + } + return resource_quota; +} + +void grpc_resource_quota_unref_internal(grpc_resource_quota* resource_quota) { + if (gpr_unref(&resource_quota->refs)) { + // No outstanding thread quota + GPR_ASSERT(resource_quota->num_threads_allocated == 0); + GRPC_COMBINER_UNREF(resource_quota->combiner, "resource_quota"); + gpr_free(resource_quota->name); + gpr_mu_destroy(&resource_quota->thread_count_mu); + gpr_free(resource_quota); + } +} + +/* Public API */ +void grpc_resource_quota_unref(grpc_resource_quota* resource_quota) { + grpc_core::ExecCtx exec_ctx; + grpc_resource_quota_unref_internal(resource_quota); +} + +grpc_resource_quota* grpc_resource_quota_ref_internal( + grpc_resource_quota* resource_quota) { + gpr_ref(&resource_quota->refs); + return resource_quota; +} + +/* Public API */ +void grpc_resource_quota_ref(grpc_resource_quota* resource_quota) { + grpc_resource_quota_ref_internal(resource_quota); +} + +double grpc_resource_quota_get_memory_pressure( + grpc_resource_quota* resource_quota) { + return (static_cast(gpr_atm_no_barrier_load( + &resource_quota->memory_usage_estimation))) / + (static_cast(MEMORY_USAGE_ESTIMATION_MAX)); +} + +/* Public API */ +void grpc_resource_quota_set_max_threads(grpc_resource_quota* resource_quota, + int new_max_threads) { + GPR_ASSERT(new_max_threads >= 0); + gpr_mu_lock(&resource_quota->thread_count_mu); + resource_quota->max_threads = new_max_threads; + gpr_mu_unlock(&resource_quota->thread_count_mu); +} + +/* Public API */ +void grpc_resource_quota_resize(grpc_resource_quota* resource_quota, + size_t size) { + grpc_core::ExecCtx exec_ctx; + rq_resize_args* a = static_cast(gpr_malloc(sizeof(*a))); + a->resource_quota = grpc_resource_quota_ref_internal(resource_quota); + a->size = static_cast(size); + gpr_atm_no_barrier_store(&resource_quota->last_size, + (gpr_atm)GPR_MIN((size_t)GPR_ATM_MAX, size)); + GRPC_CLOSURE_INIT(&a->closure, rq_resize, a, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&a->closure, GRPC_ERROR_NONE); +} + +size_t grpc_resource_quota_peek_size(grpc_resource_quota* resource_quota) { + return static_cast( + gpr_atm_no_barrier_load(&resource_quota->last_size)); +} + +/******************************************************************************* + * grpc_resource_user channel args api + */ + +grpc_resource_quota* grpc_resource_quota_from_channel_args( + const grpc_channel_args* channel_args, bool create) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + if (channel_args->args[i].type == GRPC_ARG_POINTER) { + return grpc_resource_quota_ref_internal( + static_cast( + channel_args->args[i].value.pointer.p)); + } else { + gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer"); + } + } + } + return create ? grpc_resource_quota_create(nullptr) : nullptr; +} + +static void* rq_copy(void* rq) { + grpc_resource_quota_ref(static_cast(rq)); + return rq; +} + +static void rq_destroy(void* rq) { + grpc_resource_quota_unref_internal(static_cast(rq)); +} + +static int rq_cmp(void* a, void* b) { return GPR_ICMP(a, b); } + +const grpc_arg_pointer_vtable* grpc_resource_quota_arg_vtable(void) { + static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp}; + return &vtable; +} + +/******************************************************************************* + * grpc_resource_user api + */ + +grpc_resource_user* grpc_resource_user_create( + grpc_resource_quota* resource_quota, const char* name) { + grpc_resource_user* resource_user = + static_cast(gpr_malloc(sizeof(*resource_user))); + resource_user->resource_quota = + grpc_resource_quota_ref_internal(resource_quota); + GRPC_CLOSURE_INIT(&resource_user->allocate_closure, &ru_allocate, + resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->add_to_free_pool_closure, + &ru_add_to_free_pool, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[0], + &ru_post_benign_reclaimer, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[1], + &ru_post_destructive_reclaimer, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->destroy_closure, &ru_destroy, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + gpr_mu_init(&resource_user->mu); + gpr_atm_rel_store(&resource_user->refs, 1); + gpr_atm_rel_store(&resource_user->shutdown, 0); + resource_user->free_pool = 0; + grpc_closure_list_init(&resource_user->on_allocated); + resource_user->allocating = false; + resource_user->added_to_free_pool = false; + gpr_atm_no_barrier_store(&resource_user->num_threads_allocated, 0); + resource_user->reclaimers[0] = nullptr; + resource_user->reclaimers[1] = nullptr; + resource_user->new_reclaimers[0] = nullptr; + resource_user->new_reclaimers[1] = nullptr; + resource_user->outstanding_allocations = 0; + for (int i = 0; i < GRPC_RULIST_COUNT; i++) { + resource_user->links[i].next = resource_user->links[i].prev = nullptr; + } + if (name != nullptr) { + resource_user->name = gpr_strdup(name); + } else { + gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR, + (intptr_t)resource_user); + } + return resource_user; +} + +grpc_resource_quota* grpc_resource_user_quota( + grpc_resource_user* resource_user) { + return resource_user->resource_quota; +} + +static void ru_ref_by(grpc_resource_user* resource_user, gpr_atm amount) { + GPR_ASSERT(amount > 0); + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&resource_user->refs, amount) != 0); +} + +static void ru_unref_by(grpc_resource_user* resource_user, gpr_atm amount) { + GPR_ASSERT(amount > 0); + gpr_atm old = gpr_atm_full_fetch_add(&resource_user->refs, -amount); + GPR_ASSERT(old >= amount); + if (old == amount) { + GRPC_CLOSURE_SCHED(&resource_user->destroy_closure, GRPC_ERROR_NONE); + } +} + +void grpc_resource_user_ref(grpc_resource_user* resource_user) { + ru_ref_by(resource_user, 1); +} + +void grpc_resource_user_unref(grpc_resource_user* resource_user) { + ru_unref_by(resource_user, 1); +} + +void grpc_resource_user_shutdown(grpc_resource_user* resource_user) { + if (gpr_atm_full_fetch_add(&resource_user->shutdown, 1) == 0) { + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE( + ru_shutdown, resource_user, + grpc_combiner_scheduler(resource_user->resource_quota->combiner)), + GRPC_ERROR_NONE); + } +} + +bool grpc_resource_user_allocate_threads(grpc_resource_user* resource_user, + int thread_count) { + GPR_ASSERT(thread_count >= 0); + bool is_success = false; + gpr_mu_lock(&resource_user->resource_quota->thread_count_mu); + grpc_resource_quota* rq = resource_user->resource_quota; + if (rq->num_threads_allocated + thread_count <= rq->max_threads) { + rq->num_threads_allocated += thread_count; + gpr_atm_no_barrier_fetch_add(&resource_user->num_threads_allocated, + thread_count); + is_success = true; + } + gpr_mu_unlock(&resource_user->resource_quota->thread_count_mu); + return is_success; +} + +void grpc_resource_user_free_threads(grpc_resource_user* resource_user, + int thread_count) { + GPR_ASSERT(thread_count >= 0); + gpr_mu_lock(&resource_user->resource_quota->thread_count_mu); + grpc_resource_quota* rq = resource_user->resource_quota; + rq->num_threads_allocated -= thread_count; + int old_count = static_cast(gpr_atm_no_barrier_fetch_add( + &resource_user->num_threads_allocated, -thread_count)); + if (old_count < thread_count || rq->num_threads_allocated < 0) { + gpr_log(GPR_ERROR, + "Releasing more threads (%d) than currently allocated (rq threads: " + "%d, ru threads: %d)", + thread_count, rq->num_threads_allocated + thread_count, old_count); + abort(); + } + gpr_mu_unlock(&resource_user->resource_quota->thread_count_mu); +} + +static void resource_user_alloc_locked(grpc_resource_user* resource_user, + size_t size, + grpc_closure* optional_on_done) { + ru_ref_by(resource_user, static_cast(size)); + resource_user->free_pool -= static_cast(size); + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64, + resource_user->resource_quota->name, resource_user->name, size, + resource_user->free_pool); + } + if (resource_user->free_pool < 0) { + if (optional_on_done != nullptr) { + resource_user->outstanding_allocations += static_cast(size); + grpc_closure_list_append(&resource_user->on_allocated, optional_on_done, + GRPC_ERROR_NONE); + } + if (!resource_user->allocating) { + resource_user->allocating = true; + GRPC_CLOSURE_SCHED(&resource_user->allocate_closure, GRPC_ERROR_NONE); + } + } else { + GRPC_CLOSURE_SCHED(optional_on_done, GRPC_ERROR_NONE); + } +} + +bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user, + size_t size) { + if (gpr_atm_no_barrier_load(&resource_user->shutdown)) return false; + gpr_mu_lock(&resource_user->mu); + grpc_resource_quota* resource_quota = resource_user->resource_quota; + bool cas_success; + do { + gpr_atm used = gpr_atm_no_barrier_load(&resource_quota->used); + gpr_atm new_used = used + size; + if (static_cast(new_used) > + grpc_resource_quota_peek_size(resource_quota)) { + gpr_mu_unlock(&resource_user->mu); + return false; + } + cas_success = gpr_atm_full_cas(&resource_quota->used, used, new_used); + } while (!cas_success); + resource_user_alloc_locked(resource_user, size, nullptr); + gpr_mu_unlock(&resource_user->mu); + return true; +} + +void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size, + grpc_closure* optional_on_done) { + // TODO(juanlishen): Maybe return immediately if shutting down. Deferring this + // because some tests become flaky after the change. + gpr_mu_lock(&resource_user->mu); + grpc_resource_quota* resource_quota = resource_user->resource_quota; + gpr_atm_no_barrier_fetch_add(&resource_quota->used, size); + resource_user_alloc_locked(resource_user, size, optional_on_done); + gpr_mu_unlock(&resource_user->mu); +} + +void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size) { + gpr_mu_lock(&resource_user->mu); + grpc_resource_quota* resource_quota = resource_user->resource_quota; + gpr_atm prior = gpr_atm_no_barrier_fetch_add(&resource_quota->used, -size); + GPR_ASSERT(prior >= static_cast(size)); + bool was_zero_or_negative = resource_user->free_pool <= 0; + resource_user->free_pool += static_cast(size); + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64, + resource_user->resource_quota->name, resource_user->name, size, + resource_user->free_pool); + } + bool is_bigger_than_zero = resource_user->free_pool > 0; + if (is_bigger_than_zero && was_zero_or_negative && + !resource_user->added_to_free_pool) { + resource_user->added_to_free_pool = true; + GRPC_CLOSURE_SCHED(&resource_user->add_to_free_pool_closure, + GRPC_ERROR_NONE); + } + gpr_mu_unlock(&resource_user->mu); + ru_unref_by(resource_user, static_cast(size)); +} + +void grpc_resource_user_post_reclaimer(grpc_resource_user* resource_user, + bool destructive, + grpc_closure* closure) { + GPR_ASSERT(resource_user->new_reclaimers[destructive] == nullptr); + resource_user->new_reclaimers[destructive] = closure; + GRPC_CLOSURE_SCHED(&resource_user->post_reclaimer_closure[destructive], + GRPC_ERROR_NONE); +} + +void grpc_resource_user_finish_reclamation(grpc_resource_user* resource_user) { + if (grpc_resource_quota_trace.enabled()) { + gpr_log(GPR_INFO, "RQ %s %s: reclamation complete", + resource_user->resource_quota->name, resource_user->name); + } + GRPC_CLOSURE_SCHED( + &resource_user->resource_quota->rq_reclamation_done_closure, + GRPC_ERROR_NONE); +} + +void grpc_resource_user_slice_allocator_init( + grpc_resource_user_slice_allocator* slice_allocator, + grpc_resource_user* resource_user, grpc_iomgr_cb_func cb, void* p) { + GRPC_CLOSURE_INIT(&slice_allocator->on_allocated, ru_allocated_slices, + slice_allocator, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&slice_allocator->on_done, cb, p, + grpc_schedule_on_exec_ctx); + slice_allocator->resource_user = resource_user; +} + +void grpc_resource_user_alloc_slices( + grpc_resource_user_slice_allocator* slice_allocator, size_t length, + size_t count, grpc_slice_buffer* dest) { + if (gpr_atm_no_barrier_load(&slice_allocator->resource_user->shutdown)) { + GRPC_CLOSURE_SCHED( + &slice_allocator->on_allocated, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resource user shutdown")); + return; + } + slice_allocator->length = length; + slice_allocator->count = count; + slice_allocator->dest = dest; + grpc_resource_user_alloc(slice_allocator->resource_user, count * length, + &slice_allocator->on_allocated); +} diff --git a/third_party/grpc/src/core/lib/iomgr/resource_quota.h b/third_party/grpc/src/core/lib/iomgr/resource_quota.h new file mode 100644 index 00000000000000..1c79b52e3fa74f --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/resource_quota.h @@ -0,0 +1,173 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H +#define GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H + +#include + +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/closure.h" + +/** \file Tracks resource usage against a pool. + + The current implementation tracks only memory usage, but in the future + this may be extended to (for example) threads and file descriptors. + + A grpc_resource_quota represents the pooled resources, and + grpc_resource_user instances attach to the quota and consume those + resources. They also offer a vector for reclamation: if we become + resource constrained, grpc_resource_user instances are asked (in turn) to + free up whatever they can so that the system as a whole can make progress. + + There are three kinds of reclamation that take place, in order of increasing + invasiveness: + - an internal reclamation, where cached resource at the resource user level + is returned to the quota + - a benign reclamation phase, whereby resources that are in use but are not + helping anything make progress are reclaimed + - a destructive reclamation, whereby resources that are helping something + make progress may be enacted so that at least one part of the system can + complete. + + Only one reclamation will be outstanding for a given quota at a given time. + On each reclamation attempt, the kinds of reclamation are tried in order of + increasing invasiveness, stopping at the first one that succeeds. Thus, on a + given reclamation attempt, if internal and benign reclamation both fail, it + will wind up doing a destructive reclamation. However, the next reclamation + attempt may then be able to get what it needs via internal or benign + reclamation, due to resources that may have been freed up by the destructive + reclamation in the previous attempt. + + Future work will be to expose the current resource pressure so that back + pressure can be applied to avoid reclamation phases starting. + + Resource users own references to resource quotas, and resource quotas + maintain lists of users (which users arrange to leave before they are + destroyed) */ + +extern grpc_core::TraceFlag grpc_resource_quota_trace; + +// TODO(juanlishen): This is a hack. We need to do real accounting instead of +// hard coding. +constexpr size_t GRPC_RESOURCE_QUOTA_CALL_SIZE = 15 * 1024; +constexpr size_t GRPC_RESOURCE_QUOTA_CHANNEL_SIZE = 50 * 1024; + +grpc_resource_quota* grpc_resource_quota_ref_internal( + grpc_resource_quota* resource_quota); +void grpc_resource_quota_unref_internal(grpc_resource_quota* resource_quota); +grpc_resource_quota* grpc_resource_quota_from_channel_args( + const grpc_channel_args* channel_args, bool create = true); + +/* Return a number indicating current memory pressure: + 0.0 ==> no memory usage + 1.0 ==> maximum memory usage */ +double grpc_resource_quota_get_memory_pressure( + grpc_resource_quota* resource_quota); + +size_t grpc_resource_quota_peek_size(grpc_resource_quota* resource_quota); + +typedef struct grpc_resource_user grpc_resource_user; + +grpc_resource_user* grpc_resource_user_create( + grpc_resource_quota* resource_quota, const char* name); + +/* Returns a borrowed reference to the underlying resource quota for this + resource user. */ +grpc_resource_quota* grpc_resource_user_quota( + grpc_resource_user* resource_user); + +void grpc_resource_user_ref(grpc_resource_user* resource_user); +void grpc_resource_user_unref(grpc_resource_user* resource_user); +void grpc_resource_user_shutdown(grpc_resource_user* resource_user); + +/* Attempts to get quota from the resource_user to create 'thread_count' number + * of threads. Returns true if successful (i.e the caller is now free to create + * 'thread_count' number of threads) or false if quota is not available */ +bool grpc_resource_user_allocate_threads(grpc_resource_user* resource_user, + int thread_count); +/* Releases 'thread_count' worth of quota back to the resource user. The quota + * should have been previously obtained successfully by calling + * grpc_resource_user_allocate_threads(). + * + * Note: There need not be an exact one-to-one correspondence between + * grpc_resource_user_allocate_threads() and grpc_resource_user_free_threads() + * calls. The only requirement is that the number of threads allocated should + * all be eventually released */ +void grpc_resource_user_free_threads(grpc_resource_user* resource_user, + int thread_count); + +/* Allocates from the resource user 'size' worth of memory if this won't exceed + * the resource quota's total size. Returns whether the allocation is done + * successfully. If allocated successfully, the memory should be freed by the + * caller eventually. */ +bool grpc_resource_user_safe_alloc(grpc_resource_user* resource_user, + size_t size); +/* Allocates from the resource user 'size' worth of memory. + * If optional_on_done is NULL, then allocate immediately. This may push the + * quota over-limit, at which point reclamation will kick in. The caller is + * always responsible to free the memory eventually. + * If optional_on_done is non-NULL, it will be scheduled without error when the + * allocation has been granted by the quota, and the caller is responsible to + * free the memory eventually. Or it may be scheduled with an error, in which + * case the caller fails to allocate the memory and shouldn't free the memory. + */ +void grpc_resource_user_alloc(grpc_resource_user* resource_user, size_t size, + grpc_closure* optional_on_done); +/* Release memory back to the quota */ +void grpc_resource_user_free(grpc_resource_user* resource_user, size_t size); +/* Post a memory reclaimer to the resource user. Only one benign and one + destructive reclaimer can be posted at once. When executed, the reclaimer + MUST call grpc_resource_user_finish_reclamation before it completes, to + return control to the resource quota. */ +void grpc_resource_user_post_reclaimer(grpc_resource_user* resource_user, + bool destructive, grpc_closure* closure); +/* Finish a reclamation step */ +void grpc_resource_user_finish_reclamation(grpc_resource_user* resource_user); + +/* Helper to allocate slices from a resource user */ +typedef struct grpc_resource_user_slice_allocator { + /* Closure for when a resource user allocation completes */ + grpc_closure on_allocated; + /* Closure to call when slices have been allocated */ + grpc_closure on_done; + /* Length of slices to allocate on the current request */ + size_t length; + /* Number of slices to allocate on the current request */ + size_t count; + /* Destination for slices to allocate on the current request */ + grpc_slice_buffer* dest; + /* Parent resource user */ + grpc_resource_user* resource_user; +} grpc_resource_user_slice_allocator; + +/* Initialize a slice allocator. + When an allocation is completed, calls \a cb with arg \p. */ +void grpc_resource_user_slice_allocator_init( + grpc_resource_user_slice_allocator* slice_allocator, + grpc_resource_user* resource_user, grpc_iomgr_cb_func cb, void* p); + +/* Allocate \a count slices of length \a length into \a dest. Only one request + can be outstanding at a time. */ +void grpc_resource_user_alloc_slices( + grpc_resource_user_slice_allocator* slice_allocator, size_t length, + size_t count, grpc_slice_buffer* dest); + +#endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr.h b/third_party/grpc/src/core/lib/iomgr/sockaddr.h new file mode 100644 index 00000000000000..5edf735cd18a9e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/sockaddr.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* This header transitively includes other headers that care about include + * order, so it should be included first. As a consequence, it should not be + * included in any other header. */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_H +#define GRPC_CORE_LIB_IOMGR_SOCKADDR_H + +#include + +#include "src/core/lib/iomgr/sockaddr_custom.h" +#include "src/core/lib/iomgr/sockaddr_posix.h" +#include "src/core/lib/iomgr/sockaddr_windows.h" + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_custom.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_custom.h new file mode 100644 index 00000000000000..d85cc504d3ecaa --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_custom.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H + +#include + +#include +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include + +// TODO(kpayson) It would be nice to abstract this so we don't +// depend on anything uv specific +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + +#endif // GRPC_UV + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_CUSTOM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_posix.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_posix.h new file mode 100644 index 00000000000000..3cedd9082d76f7 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_posix.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H +#define GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_SOCKADDR +#include +#include +#include +#include +#include +#include + +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.cc b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.cc new file mode 100644 index 00000000000000..0839bdfef2db64 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.cc @@ -0,0 +1,299 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +static const uint8_t kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0xff, 0xff}; + +int grpc_sockaddr_is_v4mapped(const grpc_resolved_address* resolved_addr, + grpc_resolved_address* resolved_addr4_out) { + GPR_ASSERT(resolved_addr != resolved_addr4_out); + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + grpc_sockaddr_in* addr4_out = + resolved_addr4_out == nullptr + ? nullptr + : reinterpret_cast(resolved_addr4_out->addr); + if (addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast(addr); + if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix, + sizeof(kV4MappedPrefix)) == 0) { + if (resolved_addr4_out != nullptr) { + /* Normalize ::ffff:0.0.0.0/96 to IPv4. */ + memset(resolved_addr4_out, 0, sizeof(*resolved_addr4_out)); + addr4_out->sin_family = GRPC_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; + resolved_addr4_out->len = + static_cast(sizeof(grpc_sockaddr_in)); + } + return 1; + } + } + return 0; +} + +int grpc_sockaddr_to_v4mapped(const grpc_resolved_address* resolved_addr, + grpc_resolved_address* resolved_addr6_out) { + GPR_ASSERT(resolved_addr != resolved_addr6_out); + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + grpc_sockaddr_in6* addr6_out = + reinterpret_cast(resolved_addr6_out->addr); + if (addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast(addr); + memset(resolved_addr6_out, 0, sizeof(*resolved_addr6_out)); + addr6_out->sin6_family = GRPC_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; + resolved_addr6_out->len = static_cast(sizeof(grpc_sockaddr_in6)); + return 1; + } + return 0; +} + +int grpc_sockaddr_is_wildcard(const grpc_resolved_address* resolved_addr, + int* port_out) { + const grpc_sockaddr* addr; + grpc_resolved_address addr4_normalized; + if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr4_normalized)) { + resolved_addr = &addr4_normalized; + } + addr = reinterpret_cast(resolved_addr->addr); + if (addr->sa_family == GRPC_AF_INET) { + /* Check for 0.0.0.0 */ + const grpc_sockaddr_in* addr4 = + reinterpret_cast(addr); + if (addr4->sin_addr.s_addr != 0) { + return 0; + } + *port_out = grpc_ntohs(addr4->sin_port); + return 1; + } else if (addr->sa_family == GRPC_AF_INET6) { + /* Check for :: */ + const grpc_sockaddr_in6* addr6 = + reinterpret_cast(addr); + int i; + for (i = 0; i < 16; i++) { + if (addr6->sin6_addr.s6_addr[i] != 0) { + return 0; + } + } + *port_out = grpc_ntohs(addr6->sin6_port); + return 1; + } else { + return 0; + } +} + +void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address* wild4_out, + grpc_resolved_address* wild6_out) { + grpc_sockaddr_make_wildcard4(port, wild4_out); + grpc_sockaddr_make_wildcard6(port, wild6_out); +} + +void grpc_sockaddr_make_wildcard4(int port, + grpc_resolved_address* resolved_wild_out) { + grpc_sockaddr_in* wild_out = + reinterpret_cast(resolved_wild_out->addr); + GPR_ASSERT(port >= 0 && port < 65536); + memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); + wild_out->sin_family = GRPC_AF_INET; + wild_out->sin_port = grpc_htons(static_cast(port)); + resolved_wild_out->len = static_cast(sizeof(grpc_sockaddr_in)); +} + +void grpc_sockaddr_make_wildcard6(int port, + grpc_resolved_address* resolved_wild_out) { + grpc_sockaddr_in6* wild_out = + reinterpret_cast(resolved_wild_out->addr); + GPR_ASSERT(port >= 0 && port < 65536); + memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); + wild_out->sin6_family = GRPC_AF_INET6; + wild_out->sin6_port = grpc_htons(static_cast(port)); + resolved_wild_out->len = static_cast(sizeof(grpc_sockaddr_in6)); +} + +int grpc_sockaddr_to_string(char** out, + const grpc_resolved_address* resolved_addr, + int normalize) { + const grpc_sockaddr* addr; + const int save_errno = errno; + grpc_resolved_address addr_normalized; + char ntop_buf[GRPC_INET6_ADDRSTRLEN]; + const void* ip = nullptr; + int port = 0; + uint32_t sin6_scope_id = 0; + int ret; + + *out = nullptr; + if (normalize && grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { + resolved_addr = &addr_normalized; + } + addr = reinterpret_cast(resolved_addr->addr); + if (addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast(addr); + ip = &addr4->sin_addr; + port = grpc_ntohs(addr4->sin_port); + } else if (addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast(addr); + ip = &addr6->sin6_addr; + port = grpc_ntohs(addr6->sin6_port); + sin6_scope_id = addr6->sin6_scope_id; + } + if (ip != nullptr && grpc_inet_ntop(addr->sa_family, ip, ntop_buf, + sizeof(ntop_buf)) != nullptr) { + if (sin6_scope_id != 0) { + char* host_with_scope; + /* Enclose sin6_scope_id with the format defined in RFC 6784 section 2. */ + gpr_asprintf(&host_with_scope, "%s%%25%" PRIu32, ntop_buf, sin6_scope_id); + ret = gpr_join_host_port(out, host_with_scope, port); + gpr_free(host_with_scope); + } else { + 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; +} + +void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port) { + grpc_sockaddr_in6* addr6 = (grpc_sockaddr_in6*)out->addr; + grpc_sockaddr_in* addr4 = (grpc_sockaddr_in*)out->addr; + + if (grpc_inet_pton(GRPC_AF_INET6, addr, &addr6->sin6_addr) == 1) { + addr6->sin6_family = GRPC_AF_INET6; + addr6->sin6_flowinfo = 0; + addr6->sin6_scope_id = 0; + out->len = sizeof(grpc_sockaddr_in6); + } else if (grpc_inet_pton(GRPC_AF_INET, addr, &addr4->sin_addr) == 1) { + addr4->sin_family = GRPC_AF_INET; + out->len = sizeof(grpc_sockaddr_in); + } else { + GPR_ASSERT(0); + } + grpc_sockaddr_set_port(out, port); +} + +char* grpc_sockaddr_to_uri(const grpc_resolved_address* resolved_addr) { + if (resolved_addr->len == 0) return nullptr; + grpc_resolved_address addr_normalized; + if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { + resolved_addr = &addr_normalized; + } + const char* scheme = grpc_sockaddr_get_uri_scheme(resolved_addr); + if (scheme == nullptr || strcmp("unix", scheme) == 0) { + return grpc_sockaddr_to_uri_unix_if_possible(resolved_addr); + } + char* path = nullptr; + char* uri_str = nullptr; + if (grpc_sockaddr_to_string(&path, resolved_addr, + false /* suppress errors */) && + scheme != nullptr) { + gpr_asprintf(&uri_str, "%s:%s", scheme, path); + } + gpr_free(path); + return uri_str != nullptr ? uri_str : nullptr; +} + +const char* grpc_sockaddr_get_uri_scheme( + const grpc_resolved_address* resolved_addr) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + switch (addr->sa_family) { + case GRPC_AF_INET: + return "ipv4"; + case GRPC_AF_INET6: + return "ipv6"; + case GRPC_AF_UNIX: + return "unix"; + } + return nullptr; +} + +int grpc_sockaddr_get_family(const grpc_resolved_address* resolved_addr) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + return addr->sa_family; +} + +int grpc_sockaddr_get_port(const grpc_resolved_address* resolved_addr) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + switch (addr->sa_family) { + case GRPC_AF_INET: + return grpc_ntohs(((grpc_sockaddr_in*)addr)->sin_port); + case GRPC_AF_INET6: + return grpc_ntohs(((grpc_sockaddr_in6*)addr)->sin6_port); + default: + if (grpc_is_unix_socket(resolved_addr)) { + return 1; + } + gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_get_port", + addr->sa_family); + return 0; + } +} + +int grpc_sockaddr_set_port(const grpc_resolved_address* resolved_addr, + int port) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + switch (addr->sa_family) { + case GRPC_AF_INET: + GPR_ASSERT(port >= 0 && port < 65536); + ((grpc_sockaddr_in*)addr)->sin_port = + grpc_htons(static_cast(port)); + return 1; + case GRPC_AF_INET6: + GPR_ASSERT(port >= 0 && port < 65536); + ((grpc_sockaddr_in6*)addr)->sin6_port = + grpc_htons(static_cast(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/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.h new file mode 100644 index 00000000000000..a4e90a73ab34e1 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_utils.h @@ -0,0 +1,84 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H +#define GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H + +#include + +#include "src/core/lib/iomgr/resolve_address.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 grpc_resolved_address* addr, + grpc_resolved_address* 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 grpc_resolved_address* addr, + grpc_resolved_address* 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 grpc_resolved_address* addr, int* port_out); + +/* Writes 0.0.0.0:port and [::]:port to separate sockaddrs. */ +void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address* wild4_out, + grpc_resolved_address* wild6_out); + +/* Writes 0.0.0.0:port. */ +void grpc_sockaddr_make_wildcard4(int port, grpc_resolved_address* wild_out); + +/* Writes [::]:port. */ +void grpc_sockaddr_make_wildcard6(int port, grpc_resolved_address* wild_out); + +/* Return the IP port number of a sockaddr */ +int grpc_sockaddr_get_port(const grpc_resolved_address* addr); + +/* Set IP port number of a sockaddr */ +int grpc_sockaddr_set_port(const grpc_resolved_address* 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 grpc_resolved_address* addr, + int normalize); + +void grpc_string_to_sockaddr(grpc_resolved_address* out, char* addr, int port); + +/* Returns the URI string corresponding to \a addr */ +char* grpc_sockaddr_to_uri(const grpc_resolved_address* addr); + +/* Returns the URI scheme corresponding to \a addr */ +const char* grpc_sockaddr_get_uri_scheme(const grpc_resolved_address* addr); + +int grpc_sockaddr_get_family(const grpc_resolved_address* resolved_addr); + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/sockaddr_windows.h b/third_party/grpc/src/core/lib/iomgr/sockaddr_windows.h new file mode 100644 index 00000000000000..4d637251a18efb --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/sockaddr_windows.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H +#define GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include +#include + +// must be included after the above +#include + +typedef struct sockaddr grpc_sockaddr; +typedef struct sockaddr_in grpc_sockaddr_in; +typedef struct in_addr grpc_in_addr; +typedef struct sockaddr_in6 grpc_sockaddr_in6; +typedef struct in6_addr grpc_in6_addr; + +#define GRPC_INET_ADDRSTRLEN INET_ADDRSTRLEN +#define GRPC_INET6_ADDRSTRLEN INET6_ADDRSTRLEN + +#define GRPC_SOCK_STREAM SOCK_STREAM +#define GRPC_SOCK_DGRAM SOCK_DGRAM + +#define GRPC_AF_UNSPEC AF_UNSPEC +#define GRPC_AF_UNIX AF_UNIX +#define GRPC_AF_INET AF_INET +#define GRPC_AF_INET6 AF_INET6 + +#define GRPC_AI_PASSIVE AI_PASSIVE + +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_WINDOWS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.cc b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.cc new file mode 100644 index 00000000000000..57137769c83fc6 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_SOCKET_FACTORY + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/socket_factory_posix.h" + +#include +#include + +void grpc_socket_factory_init(grpc_socket_factory* factory, + const grpc_socket_factory_vtable* vtable) { + factory->vtable = vtable; + gpr_ref_init(&factory->refcount, 1); +} + +int grpc_socket_factory_socket(grpc_socket_factory* factory, int domain, + int type, int protocol) { + return factory->vtable->socket(factory, domain, type, protocol); +} + +int grpc_socket_factory_bind(grpc_socket_factory* factory, int sockfd, + const grpc_resolved_address* addr) { + return factory->vtable->bind(factory, sockfd, addr); +} + +int grpc_socket_factory_compare(grpc_socket_factory* a, + grpc_socket_factory* b) { + int c = GPR_ICMP(a, b); + if (c != 0) { + grpc_socket_factory* sma = a; + grpc_socket_factory* smb = b; + c = GPR_ICMP(sma->vtable, smb->vtable); + if (c == 0) { + c = sma->vtable->compare(sma, smb); + } + } + return c; +} + +grpc_socket_factory* grpc_socket_factory_ref(grpc_socket_factory* factory) { + gpr_ref(&factory->refcount); + return factory; +} + +void grpc_socket_factory_unref(grpc_socket_factory* factory) { + if (gpr_unref(&factory->refcount)) { + factory->vtable->destroy(factory); + } +} + +static void* socket_factory_arg_copy(void* p) { + return grpc_socket_factory_ref(static_cast(p)); +} + +static void socket_factory_arg_destroy(void* p) { + grpc_socket_factory_unref(static_cast(p)); +} + +static int socket_factory_cmp(void* a, void* b) { + return grpc_socket_factory_compare(static_cast(a), + static_cast(b)); +} + +static const grpc_arg_pointer_vtable socket_factory_arg_vtable = { + socket_factory_arg_copy, socket_factory_arg_destroy, socket_factory_cmp}; + +grpc_arg grpc_socket_factory_to_arg(grpc_socket_factory* factory) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_SOCKET_FACTORY, + factory, &socket_factory_arg_vtable); +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.h b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.h new file mode 100644 index 00000000000000..9a52f4ea4efe4b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_factory_posix.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H +#define GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H + +#include + +#include +#include +#include "src/core/lib/iomgr/resolve_address.h" + +/** The virtual table of grpc_socket_factory */ +typedef struct { + /** Replacement for socket(2) */ + int (*socket)(grpc_socket_factory* factory, int domain, int type, + int protocol); + /** Replacement for bind(2) */ + int (*bind)(grpc_socket_factory* factory, int sockfd, + const grpc_resolved_address* addr); + /** Compare socket factory \a a and \a b */ + int (*compare)(grpc_socket_factory* a, grpc_socket_factory* b); + /** Destroys the socket factory instance */ + void (*destroy)(grpc_socket_factory* factory); +} grpc_socket_factory_vtable; + +/** The Socket Factory interface allows changes on socket options */ +struct grpc_socket_factory { + const grpc_socket_factory_vtable* vtable; + gpr_refcount refcount; +}; + +/** called by concrete implementations to initialize the base struct */ +void grpc_socket_factory_init(grpc_socket_factory* factory, + const grpc_socket_factory_vtable* vtable); + +/** Wrap \a factory as a grpc_arg */ +grpc_arg grpc_socket_factory_to_arg(grpc_socket_factory* factory); + +/** Perform the equivalent of a socket(2) operation using \a factory */ +int grpc_socket_factory_socket(grpc_socket_factory* factory, int domain, + int type, int protocol); + +/** Perform the equivalent of a bind(2) operation using \a factory */ +int grpc_socket_factory_bind(grpc_socket_factory* factory, int sockfd, + const grpc_resolved_address* addr); + +/** Compare if \a a and \a b are the same factory or have same settings */ +int grpc_socket_factory_compare(grpc_socket_factory* a, grpc_socket_factory* b); + +grpc_socket_factory* grpc_socket_factory_ref(grpc_socket_factory* factory); +void grpc_socket_factory_unref(grpc_socket_factory* factory); + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_FACTORY_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_mutator.cc b/third_party/grpc/src/core/lib/iomgr/socket_mutator.cc new file mode 100644 index 00000000000000..a448c9f61c3c95 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_mutator.cc @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/socket_mutator.h" + +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/useful.h" + +void grpc_socket_mutator_init(grpc_socket_mutator* mutator, + const grpc_socket_mutator_vtable* vtable) { + mutator->vtable = vtable; + gpr_ref_init(&mutator->refcount, 1); +} + +grpc_socket_mutator* grpc_socket_mutator_ref(grpc_socket_mutator* mutator) { + gpr_ref(&mutator->refcount); + return mutator; +} + +bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd) { + return mutator->vtable->mutate_fd(fd, mutator); +} + +int grpc_socket_mutator_compare(grpc_socket_mutator* a, + grpc_socket_mutator* b) { + int c = GPR_ICMP(a, b); + if (c != 0) { + grpc_socket_mutator* sma = a; + grpc_socket_mutator* smb = b; + c = GPR_ICMP(sma->vtable, smb->vtable); + if (c == 0) { + c = sma->vtable->compare(sma, smb); + } + } + return c; +} + +void grpc_socket_mutator_unref(grpc_socket_mutator* mutator) { + if (gpr_unref(&mutator->refcount)) { + mutator->vtable->destroy(mutator); + } +} + +static void* socket_mutator_arg_copy(void* p) { + return grpc_socket_mutator_ref(static_cast(p)); +} + +static void socket_mutator_arg_destroy(void* p) { + grpc_socket_mutator_unref(static_cast(p)); +} + +static int socket_mutator_cmp(void* a, void* b) { + return grpc_socket_mutator_compare(static_cast(a), + static_cast(b)); +} + +static const grpc_arg_pointer_vtable socket_mutator_arg_vtable = { + socket_mutator_arg_copy, socket_mutator_arg_destroy, socket_mutator_cmp}; + +grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator* mutator) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_SOCKET_MUTATOR, + mutator, &socket_mutator_arg_vtable); +} diff --git a/third_party/grpc/src/core/lib/iomgr/socket_mutator.h b/third_party/grpc/src/core/lib/iomgr/socket_mutator.h new file mode 100644 index 00000000000000..8742a3ba61367d --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_mutator.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H +#define GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H + +#include + +#include +#include + +#include + +/** The virtual table of grpc_socket_mutator */ +typedef struct { + /** Mutates the socket options of \a fd */ + bool (*mutate_fd)(int fd, grpc_socket_mutator* mutator); + /** Compare socket mutator \a a and \a b */ + int (*compare)(grpc_socket_mutator* a, grpc_socket_mutator* b); + /** Destroys the socket mutator instance */ + void (*destroy)(grpc_socket_mutator* mutator); +} grpc_socket_mutator_vtable; + +/** The Socket Mutator interface allows changes on socket options */ +struct grpc_socket_mutator { + const grpc_socket_mutator_vtable* vtable; + gpr_refcount refcount; +}; + +/** called by concrete implementations to initialize the base struct */ +void grpc_socket_mutator_init(grpc_socket_mutator* mutator, + const grpc_socket_mutator_vtable* vtable); + +/** Wrap \a mutator as a grpc_arg */ +grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator* mutator); + +/** Perform the file descriptor mutation operation of \a mutator on \a fd */ +bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator* mutator, int fd); + +/** Compare if \a a and \a b are the same mutator or have same settings */ +int grpc_socket_mutator_compare(grpc_socket_mutator* a, grpc_socket_mutator* b); + +grpc_socket_mutator* grpc_socket_mutator_ref(grpc_socket_mutator* mutator); +void grpc_socket_mutator_unref(grpc_socket_mutator* mutator); + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_MUTATOR_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils.h b/third_party/grpc/src/core/lib/iomgr/socket_utils.h new file mode 100644 index 00000000000000..14bb081e935a8e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_utils.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H +#define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H + +#include + +#include + +// TODO(juanlishen): The following functions might be simple enough to implement +// ourselves, so that they don't cause any portability hassle. + +/* A wrapper for htons on POSIX and Windows */ +uint16_t grpc_htons(uint16_t hostshort); + +/* A wrapper for ntohs on POSIX and WINDOWS */ +uint16_t grpc_ntohs(uint16_t netshort); + +/* A wrapper for htonl on POSIX and Windows */ +uint32_t grpc_htonl(uint32_t hostlong); + +/* A wrapper for ntohl on POSIX and WINDOWS */ +uint32_t grpc_ntohl(uint32_t netlong); + +/* A wrapper for inet_pton on POSIX and WINDOWS */ +int grpc_inet_pton(int af, const char* src, void* dst); + +/* A wrapper for inet_ntop on POSIX systems and InetNtop on Windows systems */ +const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size); + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc new file mode 100644 index 00000000000000..4c337a05210342 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_common_posix.cc @@ -0,0 +1,451 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_UTILS_COMMON + +#include "src/core/lib/iomgr/socket_utils.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +/* set a socket to non blocking mode */ +grpc_error* grpc_set_socket_nonblocking(int fd, int non_blocking) { + int oldflags = fcntl(fd, F_GETFL, 0); + if (oldflags < 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + if (non_blocking) { + oldflags |= O_NONBLOCK; + } else { + oldflags &= ~O_NONBLOCK; + } + + if (fcntl(fd, F_SETFL, oldflags) != 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_set_socket_no_sigpipe_if_possible(int fd) { +#ifdef GRPC_HAVE_SO_NOSIGPIPE + int val = 1; + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)"); + } + if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)"); + } + if ((newval != 0) != (val != 0)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_NOSIGPIPE"); + } +#endif + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_set_socket_ip_pktinfo_if_possible(int fd) { +#ifdef GRPC_HAVE_IP_PKTINFO + int get_local_ip = 1; + if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip, + sizeof(get_local_ip))) { + return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)"); + } +#endif + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) { +#ifdef GRPC_HAVE_IPV6_RECVPKTINFO + int get_local_ip = 1; + if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, + sizeof(get_local_ip))) { + return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)"); + } +#endif + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_set_socket_sndbuf(int fd, int buffer_size_bytes) { + return 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size_bytes, + sizeof(buffer_size_bytes)) + ? GRPC_ERROR_NONE + : GRPC_OS_ERROR(errno, "setsockopt(SO_SNDBUF)"); +} + +grpc_error* grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes) { + return 0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size_bytes, + sizeof(buffer_size_bytes)) + ? GRPC_ERROR_NONE + : GRPC_OS_ERROR(errno, "setsockopt(SO_RCVBUF)"); +} + +/* set a socket to close on exec */ +grpc_error* grpc_set_socket_cloexec(int fd, int close_on_exec) { + int oldflags = fcntl(fd, F_GETFD, 0); + if (oldflags < 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + if (close_on_exec) { + oldflags |= FD_CLOEXEC; + } else { + oldflags &= ~FD_CLOEXEC; + } + + if (fcntl(fd, F_SETFD, oldflags) != 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + return GRPC_ERROR_NONE; +} + +/* set a socket to reuse old addresses */ +grpc_error* grpc_set_socket_reuse_addr(int fd, int reuse) { + int val = (reuse != 0); + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)"); + } + if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)"); + } + if ((newval != 0) != val) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEADDR"); + } + + return GRPC_ERROR_NONE; +} + +/* set a socket to reuse old addresses */ +grpc_error* grpc_set_socket_reuse_port(int fd, int reuse) { +#ifndef SO_REUSEPORT + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "SO_REUSEPORT unavailable on compiling system"); +#else + int val = (reuse != 0); + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)"); + } + if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)"); + } + if ((newval != 0) != val) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEPORT"); + } + + return GRPC_ERROR_NONE; +#endif +} + +static gpr_once g_probe_so_reuesport_once = GPR_ONCE_INIT; +static int g_support_so_reuseport = false; + +void probe_so_reuseport_once(void) { +#ifndef GPR_MANYLINUX1 + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + /* This might be an ipv6-only environment in which case 'socket(AF_INET,..)' + call would fail. Try creating IPv6 socket in that case */ + s = socket(AF_INET6, SOCK_STREAM, 0); + } + if (s >= 0) { + g_support_so_reuseport = GRPC_LOG_IF_ERROR( + "check for SO_REUSEPORT", grpc_set_socket_reuse_port(s, 1)); + close(s); + } +#endif +} + +bool grpc_is_socket_reuse_port_supported() { + gpr_once_init(&g_probe_so_reuesport_once, probe_so_reuseport_once); + return g_support_so_reuseport; +} + +/* disable nagle */ +grpc_error* grpc_set_socket_low_latency(int fd, int low_latency) { + int val = (low_latency != 0); + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)"); + } + if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)"); + } + if ((newval != 0) != val) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set TCP_NODELAY"); + } + return GRPC_ERROR_NONE; +} + +/* The default values for TCP_USER_TIMEOUT are currently configured to be in + * line with the default values of KEEPALIVE_TIMEOUT as proposed in + * https://github.com/grpc/proposal/blob/master/A18-tcp-user-timeout.md */ +#define DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS 20000 /* 20 seconds */ +#define DEFAULT_SERVER_TCP_USER_TIMEOUT_MS 20000 /* 20 seconds */ + +static int g_default_client_tcp_user_timeout_ms = + DEFAULT_CLIENT_TCP_USER_TIMEOUT_MS; +static int g_default_server_tcp_user_timeout_ms = + DEFAULT_SERVER_TCP_USER_TIMEOUT_MS; +static bool g_default_client_tcp_user_timeout_enabled = false; +static bool g_default_server_tcp_user_timeout_enabled = true; + +void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client) { + if (is_client) { + g_default_client_tcp_user_timeout_enabled = enable; + if (timeout > 0) { + g_default_client_tcp_user_timeout_ms = timeout; + } + } else { + g_default_server_tcp_user_timeout_enabled = enable; + if (timeout > 0) { + g_default_server_tcp_user_timeout_ms = timeout; + } + } +} + +/* Set TCP_USER_TIMEOUT */ +grpc_error* grpc_set_socket_tcp_user_timeout( + int fd, const grpc_channel_args* channel_args, bool is_client) { +#ifdef GRPC_HAVE_TCP_USER_TIMEOUT + bool enable; + int timeout; + if (is_client) { + enable = g_default_client_tcp_user_timeout_enabled; + timeout = g_default_client_tcp_user_timeout_ms; + } else { + enable = g_default_server_tcp_user_timeout_enabled; + timeout = g_default_server_tcp_user_timeout_ms; + } + if (channel_args) { + for (unsigned int i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], grpc_integer_options{0, 1, INT_MAX}); + /* Continue using default if value is 0 */ + if (value == 0) { + continue; + } + /* Disable if value is INT_MAX */ + enable = value != INT_MAX; + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], grpc_integer_options{0, 1, INT_MAX}); + /* Continue using default if value is 0 */ + if (value == 0) { + continue; + } + timeout = value; + } + } + } + if (enable) { + extern grpc_core::TraceFlag grpc_tcp_trace; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "Enabling TCP_USER_TIMEOUT with a timeout of %d ms", + timeout); + } + int newval; + socklen_t len = sizeof(newval); + if (0 != setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, + sizeof(timeout))) { + gpr_log(GPR_ERROR, "setsockopt(TCP_USER_TIMEOUT) %s", strerror(errno)); + return GRPC_ERROR_NONE; + } + if (0 != getsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &newval, &len)) { + gpr_log(GPR_ERROR, "getsockopt(TCP_USER_TIMEOUT) %s", strerror(errno)); + return GRPC_ERROR_NONE; + } + if (newval != timeout) { + /* Do not fail on failing to set TCP_USER_TIMEOUT for now. */ + gpr_log(GPR_ERROR, "Failed to set TCP_USER_TIMEOUT"); + return GRPC_ERROR_NONE; + } + } +#else + extern grpc_core::TraceFlag grpc_tcp_trace; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP_USER_TIMEOUT not supported for this platform"); + } +#endif /* GRPC_HAVE_TCP_USER_TIMEOUT */ + return GRPC_ERROR_NONE; +} + +/* set a socket using a grpc_socket_mutator */ +grpc_error* grpc_set_socket_with_mutator(int fd, grpc_socket_mutator* mutator) { + GPR_ASSERT(mutator); + if (!grpc_socket_mutator_mutate_fd(mutator, fd)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("grpc_socket_mutator failed."); + } + return GRPC_ERROR_NONE; +} + +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 { + grpc_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, reinterpret_cast(&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; + } +} + +static grpc_error* error_for_fd(int fd, const grpc_resolved_address* addr) { + if (fd >= 0) return GRPC_ERROR_NONE; + char* addr_str; + grpc_sockaddr_to_string(&addr_str, addr, 0); + grpc_error* err = grpc_error_set_str(GRPC_OS_ERROR(errno, "socket"), + GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(addr_str)); + gpr_free(addr_str); + return err; +} + +grpc_error* grpc_create_dualstack_socket( + const grpc_resolved_address* resolved_addr, int type, int protocol, + grpc_dualstack_mode* dsmode, int* newfd) { + return grpc_create_dualstack_socket_using_factory( + nullptr, resolved_addr, type, protocol, dsmode, newfd); +} + +static int create_socket(grpc_socket_factory* factory, int domain, int type, + int protocol) { + return (factory != nullptr) + ? grpc_socket_factory_socket(factory, domain, type, protocol) + : socket(domain, type, protocol); +} + +grpc_error* grpc_create_dualstack_socket_using_factory( + grpc_socket_factory* factory, const grpc_resolved_address* resolved_addr, + int type, int protocol, grpc_dualstack_mode* dsmode, int* newfd) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + int family = addr->sa_family; + if (family == AF_INET6) { + if (grpc_ipv6_loopback_available()) { + *newfd = create_socket(factory, family, type, protocol); + } else { + *newfd = -1; + errno = EAFNOSUPPORT; + } + /* Check if we've got a valid dualstack socket. */ + if (*newfd >= 0 && set_socket_dualstack(*newfd)) { + *dsmode = GRPC_DSMODE_DUALSTACK; + return GRPC_ERROR_NONE; + } + /* If this isn't an IPv4 address, then return whatever we've got. */ + if (!grpc_sockaddr_is_v4mapped(resolved_addr, nullptr)) { + *dsmode = GRPC_DSMODE_IPV6; + return error_for_fd(*newfd, resolved_addr); + } + /* Fall back to AF_INET. */ + if (*newfd >= 0) { + close(*newfd); + } + family = AF_INET; + } + *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE; + *newfd = create_socket(factory, family, type, protocol); + return error_for_fd(*newfd, resolved_addr); +} + +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); } + +uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + +const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { + GPR_ASSERT(size <= (socklen_t)-1); + return inet_ntop(af, src, dst, static_cast(size)); +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_linux.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_linux.cc new file mode 100644 index 00000000000000..34f93cc4b0997b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_linux.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_LINUX_SOCKETUTILS + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include + +#include +#include + +int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock, + int cloexec) { + int flags = 0; + flags |= nonblock ? SOCK_NONBLOCK : 0; + flags |= cloexec ? SOCK_CLOEXEC : 0; + return accept4(sockfd, reinterpret_cast(resolved_addr->addr), + &resolved_addr->len, flags); +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.cc new file mode 100644 index 00000000000000..c48da52ffb29ae --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKETUTILS + +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include +#include +#include + +#include +#include "src/core/lib/iomgr/sockaddr.h" + +int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock, + int cloexec) { + int fd, flags; + fd = accept(sockfd, reinterpret_cast(resolved_addr->addr), + &resolved_addr->len); + 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 /* GRPC_POSIX_SOCKETUTILS */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.h b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.h new file mode 100644 index 00000000000000..71a304dc4ef7cb --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_posix.h @@ -0,0 +1,144 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H +#define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H + +#include + +#include "src/core/lib/iomgr/resolve_address.h" + +#include +#include + +#include +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/socket_factory_posix.h" +#include "src/core/lib/iomgr/socket_mutator.h" + +/* a wrapper for accept or accept4 */ +int grpc_accept4(int sockfd, grpc_resolved_address* resolved_addr, int nonblock, + int cloexec); + +/* set a socket to non blocking mode */ +grpc_error* grpc_set_socket_nonblocking(int fd, int non_blocking); + +/* set a socket to close on exec */ +grpc_error* grpc_set_socket_cloexec(int fd, int close_on_exec); + +/* set a socket to reuse old addresses */ +grpc_error* grpc_set_socket_reuse_addr(int fd, int reuse); + +/* return true if SO_REUSEPORT is supported */ +bool grpc_is_socket_reuse_port_supported(); + +/* disable nagle */ +grpc_error* grpc_set_socket_low_latency(int fd, int low_latency); + +/* set SO_REUSEPORT */ +grpc_error* grpc_set_socket_reuse_port(int fd, int reuse); + +/* Configure the default values for TCP_USER_TIMEOUT */ +void config_default_tcp_user_timeout(bool enable, int timeout, bool is_client); + +/* Set TCP_USER_TIMEOUT */ +grpc_error* grpc_set_socket_tcp_user_timeout( + int fd, const grpc_channel_args* channel_args, bool is_client); + +/* 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. + If SO_NO_SIGPIPE is not available, returns 1. */ +grpc_error* grpc_set_socket_no_sigpipe_if_possible(int fd); + +/* Tries to set IP_PKTINFO if available on this platform. + If IP_PKTINFO is not available, returns 1. */ +grpc_error* grpc_set_socket_ip_pktinfo_if_possible(int fd); + +/* Tries to set IPV6_RECVPKTINFO if available on this platform. + If IPV6_RECVPKTINFO is not available, returns 1. */ +grpc_error* grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd); + +/* Tries to set the socket's send buffer to given size. */ +grpc_error* grpc_set_socket_sndbuf(int fd, int buffer_size_bytes); + +/* Tries to set the socket's receive buffer to given size. */ +grpc_error* grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes); + +/* Tries to set the socket using a grpc_socket_mutator */ +grpc_error* grpc_set_socket_with_mutator(int fd, grpc_socket_mutator* mutator); + +/* 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. */ +grpc_error* grpc_create_dualstack_socket(const grpc_resolved_address* addr, + int type, int protocol, + grpc_dualstack_mode* dsmode, + int* newfd); + +/* Same as grpc_create_dualstack_socket(), but use the given socket factory (if + non-null) to create the socket, rather than calling socket() directly. */ +grpc_error* grpc_create_dualstack_socket_using_factory( + grpc_socket_factory* factory, const grpc_resolved_address* addr, int type, + int protocol, grpc_dualstack_mode* dsmode, int* newfd); + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_uv.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_uv.cc new file mode 100644 index 00000000000000..b5f96b52df5361 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_uv.cc @@ -0,0 +1,49 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" + +#include + +#include + +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); } + +uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + +const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { + uv_inet_ntop(af, src, dst, size); + return dst; +} + +#endif /* GRPC_UV */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_utils_windows.cc b/third_party/grpc/src/core/lib/iomgr/socket_utils_windows.cc new file mode 100644 index 00000000000000..9137ab98e6084c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_utils_windows.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINDOWS_SOCKETUTILS + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" + +#include + +uint16_t grpc_htons(uint16_t hostshort) { return htons(hostshort); } + +uint16_t grpc_ntohs(uint16_t netshort) { return ntohs(netshort); } + +uint32_t grpc_htonl(uint32_t hostlong) { return htonl(hostlong); } + +uint32_t grpc_ntohl(uint32_t netlong) { return ntohl(netlong); } + +int grpc_inet_pton(int af, const char* src, void* dst) { + return inet_pton(af, src, dst); +} + +const char* grpc_inet_ntop(int af, const void* src, char* dst, size_t size) { + /* Windows InetNtopA wants a mutable ip pointer */ + return InetNtopA(af, (void*)src, dst, size); +} + +#endif /* GRPC_WINDOWS_SOCKETUTILS */ diff --git a/third_party/grpc/src/core/lib/iomgr/socket_windows.cc b/third_party/grpc/src/core/lib/iomgr/socket_windows.cc new file mode 100644 index 00000000000000..999c6646ad495e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/socket_windows.cc @@ -0,0 +1,184 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include + +// must be included after winsock2.h +#include + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/sockaddr_windows.h" +#include "src/core/lib/iomgr/socket_windows.h" + +grpc_winsocket* grpc_winsocket_create(SOCKET socket, const char* name) { + char* final_name; + grpc_winsocket* r = (grpc_winsocket*)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; +} + +SOCKET grpc_winsocket_wrapped_socket(grpc_winsocket* socket) { + return socket->socket; +} + +/* 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; + + gpr_mu_lock(&winsocket->state_mu); + if (winsocket->shutdown_called) { + gpr_mu_unlock(&winsocket->state_mu); + return; + } + winsocket->shutdown_called = true; + gpr_mu_unlock(&winsocket->state_mu); + + 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_INFO, "Unable to retrieve DisconnectEx pointer : %s", + utf8_message); + gpr_free(utf8_message); + } + closesocket(winsocket->socket); +} + +static void destroy(grpc_winsocket* winsocket) { + grpc_iomgr_unregister_object(&winsocket->iomgr_object); + gpr_mu_destroy(&winsocket->state_mu); + gpr_free(winsocket); +} + +static bool check_destroyable(grpc_winsocket* winsocket) { + return winsocket->destroy_called == true && + winsocket->write_info.closure == NULL && + winsocket->read_info.closure == NULL; +} + +void grpc_winsocket_destroy(grpc_winsocket* winsocket) { + gpr_mu_lock(&winsocket->state_mu); + GPR_ASSERT(!winsocket->destroy_called); + winsocket->destroy_called = true; + bool should_destroy = check_destroyable(winsocket); + gpr_mu_unlock(&winsocket->state_mu); + if (should_destroy) destroy(winsocket); +} + +/* 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, grpc_closure* closure, + grpc_winsocket_callback_info* info) { + GPR_ASSERT(info->closure == NULL); + gpr_mu_lock(&socket->state_mu); + if (info->has_pending_iocp) { + info->has_pending_iocp = 0; + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + } else { + info->closure = closure; + } + gpr_mu_unlock(&socket->state_mu); +} + +void grpc_socket_notify_on_write(grpc_winsocket* socket, + grpc_closure* closure) { + socket_notify_on_iocp(socket, closure, &socket->write_info); +} + +void grpc_socket_notify_on_read(grpc_winsocket* socket, grpc_closure* closure) { + socket_notify_on_iocp(socket, closure, &socket->read_info); +} + +void grpc_socket_become_ready(grpc_winsocket* socket, + grpc_winsocket_callback_info* info) { + GPR_ASSERT(!info->has_pending_iocp); + gpr_mu_lock(&socket->state_mu); + if (info->closure) { + GRPC_CLOSURE_SCHED(info->closure, GRPC_ERROR_NONE); + info->closure = NULL; + } else { + info->has_pending_iocp = 1; + } + bool should_destroy = check_destroyable(socket); + gpr_mu_unlock(&socket->state_mu); + if (should_destroy) destroy(socket); +} + +static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT; +static bool g_ipv6_loopback_available = false; + +static void probe_ipv6_once(void) { + SOCKET s = socket(AF_INET6, SOCK_STREAM, 0); + g_ipv6_loopback_available = 0; + if (s == INVALID_SOCKET) { + gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed."); + } else { + grpc_sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */ + if (bind(s, reinterpret_cast(&addr), sizeof(addr)) == 0) { + g_ipv6_loopback_available = 1; + } else { + gpr_log(GPR_INFO, + "Disabling AF_INET6 sockets because ::1 is not available."); + } + closesocket(s); + } +} + +int grpc_ipv6_loopback_available(void) { + gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once); + return g_ipv6_loopback_available; +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/iomgr/socket_windows.h b/third_party/grpc/src/core/lib/iomgr/socket_windows.h similarity index 60% rename from third_party/grpc/src/core/iomgr/socket_windows.h rename to third_party/grpc/src/core/lib/iomgr/socket_windows.h index dfbfabe1f934cd..46d7d583560461 100644 --- a/third_party/grpc/src/core/iomgr/socket_windows.h +++ b/third_party/grpc/src/core/lib/iomgr/socket_windows.h @@ -1,47 +1,36 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H -#define GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H +#ifndef GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H +#define GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H #include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET #include -#include #include +#include -#include "src/core/iomgr/iomgr_internal.h" -#include "src/core/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/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 @@ -55,7 +44,7 @@ typedef struct grpc_winsocket_callback_info { OVERLAPPED overlapped; /* The callback information for the pending operation. May be empty if the caller hasn't registered a callback yet. */ - grpc_closure *closure; + grpc_closure* closure; /* 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 @@ -81,11 +70,13 @@ typedef struct grpc_winsocket_callback_info { is closer to what happens in posix world. */ typedef struct grpc_winsocket { SOCKET socket; + bool destroy_called; grpc_winsocket_callback_info write_info; grpc_winsocket_callback_info read_info; gpr_mu state_mu; + bool shutdown_called; /* You can't add the same socket twice to the same IO Completion Port. This prevents that. */ @@ -99,13 +90,30 @@ typedef struct 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); +grpc_winsocket* grpc_winsocket_create(SOCKET socket, const char* name); + +SOCKET grpc_winsocket_wrapped_socket(grpc_winsocket* socket); /* Initiate an asynchronous shutdown of the socket. Will call off any pending operation to cancel them. */ -void grpc_winsocket_shutdown(grpc_winsocket *socket); +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); +void grpc_winsocket_destroy(grpc_winsocket* socket); + +void grpc_socket_notify_on_write(grpc_winsocket* winsocket, + grpc_closure* closure); + +void grpc_socket_notify_on_read(grpc_winsocket* winsocket, + grpc_closure* closure); + +void grpc_socket_become_ready(grpc_winsocket* winsocket, + grpc_winsocket_callback_info* ci); + +/* 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. */ +int grpc_ipv6_loopback_available(void); + +#endif -#endif /* GRPC_INTERNAL_CORE_IOMGR_SOCKET_WINDOWS_H */ +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/sys_epoll_wrapper.h b/third_party/grpc/src/core/lib/iomgr/sys_epoll_wrapper.h new file mode 100644 index 00000000000000..d21d853665b17e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/sys_epoll_wrapper.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H +#define GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H + +#include + +#include + +#ifndef EPOLLEXCLUSIVE +#define EPOLLEXCLUSIVE (1 << 28) +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_SYS_EPOLL_WRAPPER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client.cc new file mode 100644 index 00000000000000..6c0ba40781b599 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_client.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/tcp_client.h" + +grpc_tcp_client_vtable* grpc_tcp_client_impl; + +void grpc_tcp_client_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { + grpc_tcp_client_impl->connect(closure, ep, interested_parties, channel_args, + addr, deadline); +} + +void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl) { + grpc_tcp_client_impl = impl; +} diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client.h b/third_party/grpc/src/core/lib/iomgr/tcp_client.h new file mode 100644 index 00000000000000..d209eeb8c23bfa --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_client.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H +#define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H + +#include + +#include +#include +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/iomgr/resolve_address.h" + +typedef struct grpc_tcp_client_vtable { + void (*connect)(grpc_closure* on_connect, grpc_endpoint** endpoint, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, grpc_millis deadline); +} grpc_tcp_client_vtable; + +/* 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(grpc_closure* on_connect, grpc_endpoint** endpoint, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline); + +void grpc_tcp_client_global_init(); + +void grpc_set_tcp_client_impl(grpc_tcp_client_vtable* impl); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_cfstream.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_cfstream.cc new file mode 100644 index 00000000000000..4b21322d74664c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_cfstream.cc @@ -0,0 +1,216 @@ + +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_CFSTREAM_CLIENT + +#include + +#include + +#include +#include +#include + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/iomgr/cfstream_handle.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/endpoint_cfstream.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/error_cfstream.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; + +typedef struct CFStreamConnect { + gpr_mu mu; + gpr_refcount refcount; + + CFReadStreamRef read_stream; + CFWriteStreamRef write_stream; + CFStreamHandle* stream_handle; + + grpc_timer alarm; + grpc_closure on_alarm; + grpc_closure on_open; + + bool read_stream_open; + bool write_stream_open; + bool failed; + + grpc_closure* closure; + grpc_endpoint** endpoint; + int refs; + char* addr_name; + grpc_resource_quota* resource_quota; +} CFStreamConnect; + +static void CFStreamConnectCleanup(CFStreamConnect* connect) { + grpc_resource_quota_unref_internal(connect->resource_quota); + CFSTREAM_HANDLE_UNREF(connect->stream_handle, "async connect clean up"); + CFRelease(connect->read_stream); + CFRelease(connect->write_stream); + gpr_mu_destroy(&connect->mu); + gpr_free(connect->addr_name); + gpr_free(connect); +} + +static void OnAlarm(void* arg, grpc_error* error) { + CFStreamConnect* connect = static_cast(arg); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT :%p OnAlarm, error:%p", connect, error); + } + gpr_mu_lock(&connect->mu); + grpc_closure* closure = connect->closure; + connect->closure = nil; + const bool done = (--connect->refs == 0); + gpr_mu_unlock(&connect->mu); + // Only schedule a callback once, by either OnAlarm or OnOpen. The + // first one issues callback while the second one does cleanup. + if (done) { + CFStreamConnectCleanup(connect); + } else { + grpc_error* error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("connect() timed out"); + GRPC_CLOSURE_SCHED(closure, error); + } +} + +static void OnOpen(void* arg, grpc_error* error) { + CFStreamConnect* connect = static_cast(arg); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT :%p OnOpen, error:%p", connect, error); + } + gpr_mu_lock(&connect->mu); + grpc_timer_cancel(&connect->alarm); + grpc_closure* closure = connect->closure; + connect->closure = nil; + + bool done = (--connect->refs == 0); + grpc_endpoint** endpoint = connect->endpoint; + + // Only schedule a callback once, by either OnAlarm or OnOpen. The + // first one issues callback while the second one does cleanup. + if (done) { + gpr_mu_unlock(&connect->mu); + CFStreamConnectCleanup(connect); + } else { + if (error == GRPC_ERROR_NONE) { + CFErrorRef stream_error = CFReadStreamCopyError(connect->read_stream); + if (stream_error == NULL) { + stream_error = CFWriteStreamCopyError(connect->write_stream); + } + if (stream_error) { + error = GRPC_ERROR_CREATE_FROM_CFERROR(stream_error, "connect() error"); + CFRelease(stream_error); + } + if (error == GRPC_ERROR_NONE) { + *endpoint = grpc_cfstream_endpoint_create( + connect->read_stream, connect->write_stream, connect->addr_name, + connect->resource_quota, connect->stream_handle); + } + } else { + GRPC_ERROR_REF(error); + } + gpr_mu_unlock(&connect->mu); + GRPC_CLOSURE_SCHED(closure, error); + } +} + +static void ParseResolvedAddress(const grpc_resolved_address* addr, + CFStringRef* host, int* port) { + char *host_port, *host_string, *port_string; + grpc_sockaddr_to_string(&host_port, addr, 1); + gpr_split_host_port(host_port, &host_string, &port_string); + *host = CFStringCreateWithCString(NULL, host_string, kCFStringEncodingUTF8); + gpr_free(host_string); + gpr_free(port_string); + gpr_free(host_port); + *port = grpc_sockaddr_get_port(addr); +} + +static void CFStreamClientConnect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* resolved_addr, + grpc_millis deadline) { + CFStreamConnect* connect; + + connect = (CFStreamConnect*)gpr_zalloc(sizeof(CFStreamConnect)); + connect->closure = closure; + connect->endpoint = ep; + connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); + // connect->resource_quota = resource_quota; + connect->refs = 2; // One for the connect operation, one for the timer. + gpr_ref_init(&connect->refcount, 1); + gpr_mu_init(&connect->mu); + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %p, %s: asynchronously connecting", + connect, connect->addr_name); + } + + grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL); + if (channel_args != NULL) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)channel_args->args[i].value.pointer.p); + } + } + } + connect->resource_quota = resource_quota; + + CFReadStreamRef read_stream; + CFWriteStreamRef write_stream; + + CFStringRef host; + int port; + ParseResolvedAddress(resolved_addr, &host, &port); + CFStreamCreatePairWithSocketToHost(NULL, host, port, &read_stream, + &write_stream); + CFRelease(host); + connect->read_stream = read_stream; + connect->write_stream = write_stream; + connect->stream_handle = + CFStreamHandle::CreateStreamHandle(read_stream, write_stream); + GRPC_CLOSURE_INIT(&connect->on_open, OnOpen, static_cast(connect), + grpc_schedule_on_exec_ctx); + connect->stream_handle->NotifyOnOpen(&connect->on_open); + GRPC_CLOSURE_INIT(&connect->on_alarm, OnAlarm, connect, + grpc_schedule_on_exec_ctx); + gpr_mu_lock(&connect->mu); + CFReadStreamOpen(read_stream); + CFWriteStreamOpen(write_stream); + grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm); + gpr_mu_unlock(&connect->mu); +} + +grpc_tcp_client_vtable grpc_cfstream_client_vtable = {CFStreamClientConnect}; + +#endif /* GRPC_CFSTREAM_CLIENT */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_custom.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_custom.cc new file mode 100644 index 00000000000000..73344b18d8bd22 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_custom.cc @@ -0,0 +1,162 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; +extern grpc_socket_vtable* grpc_custom_socket_vtable; + +struct grpc_custom_tcp_connect { + grpc_custom_socket* socket; + grpc_timer alarm; + grpc_closure on_alarm; + grpc_closure* closure; + grpc_endpoint** endpoint; + int refs; + char* addr_name; + grpc_resource_quota* resource_quota; +}; + +static void custom_tcp_connect_cleanup(grpc_custom_tcp_connect* connect) { + grpc_custom_socket* socket = connect->socket; + grpc_resource_quota_unref_internal(connect->resource_quota); + gpr_free(connect->addr_name); + gpr_free(connect); + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } +} + +static void custom_close_callback(grpc_custom_socket* socket) {} + +static void on_alarm(void* acp, grpc_error* error) { + int done; + grpc_custom_socket* socket = (grpc_custom_socket*)acp; + grpc_custom_tcp_connect* connect = socket->connector; + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_alarm: error=%s", + connect->addr_name, str); + } + if (error == GRPC_ERROR_NONE) { + /* error == NONE implies that the timer ran out, and wasn't cancelled. If + it was cancelled, then the handler that cancelled it also should close + the handle, if applicable */ + grpc_custom_socket_vtable->close(socket, custom_close_callback); + } + done = (--connect->refs == 0); + if (done) { + custom_tcp_connect_cleanup(connect); + } +} + +static void custom_connect_callback_internal(grpc_custom_socket* socket, + grpc_error* error) { + grpc_custom_tcp_connect* connect = socket->connector; + int done; + grpc_closure* closure = connect->closure; + grpc_timer_cancel(&connect->alarm); + if (error == GRPC_ERROR_NONE) { + *connect->endpoint = custom_tcp_endpoint_create( + socket, connect->resource_quota, connect->addr_name); + } + done = (--connect->refs == 0); + if (done) { + grpc_core::ExecCtx::Get()->Flush(); + custom_tcp_connect_cleanup(connect); + } + GRPC_CLOSURE_SCHED(closure, error); +} + +static void custom_connect_callback(grpc_custom_socket* socket, + grpc_error* error) { + if (grpc_core::ExecCtx::Get() == nullptr) { + /* If we are being run on a thread which does not have an exec_ctx created + * yet, we should create one. */ + grpc_core::ExecCtx exec_ctx; + custom_connect_callback_internal(socket, error); + } else { + custom_connect_callback_internal(socket, error); + } +} + +static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* resolved_addr, + grpc_millis deadline) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + (void)channel_args; + (void)interested_parties; + grpc_custom_tcp_connect* connect; + grpc_resource_quota* resource_quota = grpc_resource_quota_create(nullptr); + if (channel_args != nullptr) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)channel_args->args[i].value.pointer.p); + } + } + } + grpc_custom_socket* socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + socket->refs = 2; + grpc_custom_socket_vtable->init(socket, GRPC_AF_UNSPEC); + connect = + (grpc_custom_tcp_connect*)gpr_malloc(sizeof(grpc_custom_tcp_connect)); + connect->closure = closure; + connect->endpoint = ep; + connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); + connect->resource_quota = resource_quota; + connect->socket = socket; + socket->connector = connect; + socket->endpoint = nullptr; + socket->listener = nullptr; + connect->refs = 2; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "CLIENT_CONNECT: %p %s: asynchronously connecting", + socket, connect->addr_name); + } + + GRPC_CLOSURE_INIT(&connect->on_alarm, on_alarm, socket, + grpc_schedule_on_exec_ctx); + grpc_timer_init(&connect->alarm, deadline, &connect->on_alarm); + grpc_custom_socket_vtable->connect( + socket, (const grpc_sockaddr*)resolved_addr->addr, resolved_addr->len, + custom_connect_callback); +} + +grpc_tcp_client_vtable custom_tcp_client_vtable = {tcp_connect}; diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.cc new file mode 100644 index 00000000000000..0bff74e88b3758 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.cc @@ -0,0 +1,363 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_TCP_CLIENT + +#include "src/core/lib/iomgr/tcp_client_posix.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_mutator.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/slice/slice_internal.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; + +typedef struct { + gpr_mu mu; + grpc_fd* fd; + grpc_timer alarm; + grpc_closure on_alarm; + int refs; + grpc_closure write_closure; + grpc_pollset_set* interested_parties; + char* addr_str; + grpc_endpoint** ep; + grpc_closure* closure; + grpc_channel_args* channel_args; +} async_connect; + +static grpc_error* prepare_socket(const grpc_resolved_address* addr, int fd, + const grpc_channel_args* channel_args) { + grpc_error* err = GRPC_ERROR_NONE; + + GPR_ASSERT(fd >= 0); + + err = grpc_set_socket_nonblocking(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_cloexec(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + if (!grpc_is_unix_socket(addr)) { + err = grpc_set_socket_low_latency(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_reuse_addr(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_tcp_user_timeout(fd, channel_args, + true /* is_client */); + if (err != GRPC_ERROR_NONE) goto error; + } + err = grpc_set_socket_no_sigpipe_if_possible(fd); + if (err != GRPC_ERROR_NONE) goto error; + if (channel_args) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_SOCKET_MUTATOR)) { + GPR_ASSERT(channel_args->args[i].type == GRPC_ARG_POINTER); + grpc_socket_mutator* mutator = static_cast( + channel_args->args[i].value.pointer.p); + err = grpc_set_socket_with_mutator(fd, mutator); + if (err != GRPC_ERROR_NONE) goto error; + } + } + } + goto done; + +error: + if (fd >= 0) { + close(fd); + } +done: + return err; +} + +static void tc_on_alarm(void* acp, grpc_error* error) { + int done; + async_connect* ac = static_cast(acp); + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str, + str); + } + gpr_mu_lock(&ac->mu); + if (ac->fd != nullptr) { + grpc_fd_shutdown( + ac->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING("connect() timed out")); + } + done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (done) { + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_str); + grpc_channel_args_destroy(ac->channel_args); + gpr_free(ac); + } +} + +grpc_endpoint* grpc_tcp_client_create_from_fd( + grpc_fd* fd, const grpc_channel_args* channel_args, const char* addr_str) { + return grpc_tcp_create(fd, channel_args, addr_str); +} + +static void on_writable(void* acp, grpc_error* error) { + async_connect* ac = static_cast(acp); + int so_error = 0; + socklen_t so_error_size; + int err; + int done; + grpc_endpoint** ep = ac->ep; + grpc_closure* closure = ac->closure; + grpc_fd* fd; + + GRPC_ERROR_REF(error); + + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: on_writable: error=%s", ac->addr_str, + str); + } + + gpr_mu_lock(&ac->mu); + GPR_ASSERT(ac->fd); + fd = ac->fd; + ac->fd = nullptr; + gpr_mu_unlock(&ac->mu); + + grpc_timer_cancel(&ac->alarm); + + gpr_mu_lock(&ac->mu); + if (error != GRPC_ERROR_NONE) { + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string("Timeout occurred")); + goto finish; + } + + do { + so_error_size = sizeof(so_error); + err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error, + &so_error_size); + } while (err < 0 && errno == EINTR); + if (err < 0) { + error = GRPC_OS_ERROR(errno, "getsockopt"); + goto finish; + } + + switch (so_error) { + case 0: + grpc_pollset_set_del_fd(ac->interested_parties, fd); + *ep = grpc_tcp_client_create_from_fd(fd, ac->channel_args, ac->addr_str); + fd = nullptr; + break; + case 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; + case ECONNREFUSED: + /* This error shouldn't happen for anything other than connect(). */ + error = GRPC_OS_ERROR(so_error, "connect"); + break; + default: + /* We don't really know which syscall triggered the problem here, + so punt by reporting getsockopt(). */ + error = GRPC_OS_ERROR(so_error, "getsockopt(SO_ERROR)"); + break; + } + +finish: + if (fd != nullptr) { + grpc_pollset_set_del_fd(ac->interested_parties, fd); + grpc_fd_orphan(fd, nullptr, nullptr, "tcp_client_orphan"); + fd = nullptr; + } + done = (--ac->refs == 0); + // Create a copy of the data from "ac" to be accessed after the unlock, as + // "ac" and its contents may be deallocated by the time they are read. + const grpc_slice addr_str_slice = grpc_slice_from_copied_string(ac->addr_str); + gpr_mu_unlock(&ac->mu); + if (error != GRPC_ERROR_NONE) { + char* error_descr; + grpc_slice str; + bool ret = grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, &str); + GPR_ASSERT(ret); + char* desc = grpc_slice_to_c_string(str); + gpr_asprintf(&error_descr, "Failed to connect to remote host: %s", desc); + error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_copied_string(error_descr)); + gpr_free(error_descr); + gpr_free(desc); + error = grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, + addr_str_slice /* takes ownership */); + } else { + grpc_slice_unref_internal(addr_str_slice); + } + if (done) { + // This is safe even outside the lock, because "done", the sentinel, is + // populated *inside* the lock. + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_str); + grpc_channel_args_destroy(ac->channel_args); + gpr_free(ac); + } + GRPC_CLOSURE_SCHED(closure, error); +} + +grpc_error* grpc_tcp_client_prepare_fd(const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_resolved_address* mapped_addr, + grpc_fd** fdobj) { + grpc_dualstack_mode dsmode; + int fd; + grpc_error* error; + char* name; + char* addr_str; + *fdobj = nullptr; + /* Use dualstack sockets where available. Set mapped to v6 or v4 mapped to + v6. */ + if (!grpc_sockaddr_to_v4mapped(addr, mapped_addr)) { + /* addr is v4 mapped to v6 or v6. */ + memcpy(mapped_addr, addr, sizeof(*mapped_addr)); + } + error = + grpc_create_dualstack_socket(mapped_addr, SOCK_STREAM, 0, &dsmode, &fd); + if (error != GRPC_ERROR_NONE) { + return error; + } + if (dsmode == GRPC_DSMODE_IPV4) { + /* Original addr is either v4 or v4 mapped to v6. Set mapped_addr to v4. */ + if (!grpc_sockaddr_is_v4mapped(addr, mapped_addr)) { + memcpy(mapped_addr, addr, sizeof(*mapped_addr)); + } + } + if ((error = prepare_socket(mapped_addr, fd, channel_args)) != + GRPC_ERROR_NONE) { + return error; + } + addr_str = grpc_sockaddr_to_uri(mapped_addr); + gpr_asprintf(&name, "tcp-client:%s", addr_str); + *fdobj = grpc_fd_create(fd, name, true); + gpr_free(name); + gpr_free(addr_str); + return GRPC_ERROR_NONE; +} + +void grpc_tcp_client_create_from_prepared_fd( + grpc_pollset_set* interested_parties, grpc_closure* closure, grpc_fd* fdobj, + const grpc_channel_args* channel_args, const grpc_resolved_address* addr, + grpc_millis deadline, grpc_endpoint** ep) { + const int fd = grpc_fd_wrapped_fd(fdobj); + int err; + async_connect* ac; + do { + err = connect(fd, reinterpret_cast(addr->addr), + addr->len); + } while (err < 0 && errno == EINTR); + if (err >= 0) { + char* addr_str = grpc_sockaddr_to_uri(addr); + *ep = grpc_tcp_client_create_from_fd(fdobj, channel_args, addr_str); + gpr_free(addr_str); + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + return; + } + if (errno != EWOULDBLOCK && errno != EINPROGRESS) { + grpc_fd_orphan(fdobj, nullptr, nullptr, "tcp_client_connect_error"); + GRPC_CLOSURE_SCHED(closure, GRPC_OS_ERROR(errno, "connect")); + return; + } + + grpc_pollset_set_add_fd(interested_parties, fdobj); + + ac = static_cast(gpr_malloc(sizeof(async_connect))); + ac->closure = closure; + ac->ep = ep; + ac->fd = fdobj; + ac->interested_parties = interested_parties; + ac->addr_str = grpc_sockaddr_to_uri(addr); + gpr_mu_init(&ac->mu); + ac->refs = 2; + GRPC_CLOSURE_INIT(&ac->write_closure, on_writable, ac, + grpc_schedule_on_exec_ctx); + ac->channel_args = grpc_channel_args_copy(channel_args); + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "CLIENT_CONNECT: %s: asynchronously connecting fd %p", + ac->addr_str, fdobj); + } + + gpr_mu_lock(&ac->mu); + GRPC_CLOSURE_INIT(&ac->on_alarm, tc_on_alarm, ac, grpc_schedule_on_exec_ctx); + grpc_timer_init(&ac->alarm, deadline, &ac->on_alarm); + grpc_fd_notify_on_write(ac->fd, &ac->write_closure); + gpr_mu_unlock(&ac->mu); +} + +static void tcp_connect(grpc_closure* closure, grpc_endpoint** ep, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { + grpc_resolved_address mapped_addr; + grpc_fd* fdobj = nullptr; + grpc_error* error; + *ep = nullptr; + if ((error = grpc_tcp_client_prepare_fd(channel_args, addr, &mapped_addr, + &fdobj)) != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(closure, error); + return; + } + grpc_tcp_client_create_from_prepared_fd(interested_parties, closure, fdobj, + channel_args, &mapped_addr, deadline, + ep); +} + +grpc_tcp_client_vtable grpc_posix_tcp_client_vtable = {tcp_connect}; +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.h b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.h new file mode 100644 index 00000000000000..d0168ef1331d1c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_posix.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H +#define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H + +#include + +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/tcp_client.h" + +/* Create an endpoint from a connected grpc_fd. + + fd: a connected FD. Ownership is taken. + channel_args: may contain custom settings for the endpoint + addr_str: destination address in printable format + Returns: a new endpoint +*/ +grpc_endpoint* grpc_tcp_client_create_from_fd( + grpc_fd* fd, const grpc_channel_args* channel_args, const char* addr_str); + +/* Return a configured, unbound, unconnected TCP client grpc_fd. + + channel_args: may contain custom settings for the fd + addr: the destination address + mapped_addr: out parameter. addr mapped to an address appropriate to the + type of socket FD created. For example, if addr is IPv4 and dual stack + sockets are available, mapped_addr will be an IPv4-mapped IPv6 address + fdobj: out parameter. The new FD + Returns: error, if any. Out parameters are not set on error +*/ +grpc_error* grpc_tcp_client_prepare_fd(const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_resolved_address* mapped_addr, + grpc_fd** fdobj); + +/* Connect a configured TCP client grpc_fd. + + interested_parties: a set of pollsets that would be interested in this + connection being established (in order to continue their work + closure: called when complete. On success, *ep will be set. + fdobj: an FD returned from grpc_tcp_client_prepare_fd(). Ownership is taken + channel_args: may contain custom settings for the endpoint + deadline: connection deadline + ep: out parameter. Set before closure is called if successful +*/ +void grpc_tcp_client_create_from_prepared_fd( + grpc_pollset_set* interested_parties, grpc_closure* closure, grpc_fd* fdobj, + const grpc_channel_args* channel_args, const grpc_resolved_address* addr, + grpc_millis deadline, grpc_endpoint** ep); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_client_windows.cc b/third_party/grpc/src/core/lib/iomgr/tcp_client_windows.cc new file mode 100644 index 00000000000000..e5b5502597e4ee --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_client_windows.cc @@ -0,0 +1,231 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr_windows.h" + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_windows.h" +#include "src/core/lib/iomgr/timer.h" + +typedef struct { + grpc_closure* on_done; + gpr_mu mu; + grpc_winsocket* socket; + grpc_timer alarm; + grpc_closure on_alarm; + char* addr_name; + int refs; + grpc_closure on_connect; + grpc_endpoint** endpoint; + grpc_channel_args* channel_args; +} async_connect; + +static void async_connect_unlock_and_cleanup(async_connect* ac, + grpc_winsocket* socket) { + int done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (done) { + grpc_channel_args_destroy(ac->channel_args); + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_name); + gpr_free(ac); + } + if (socket != NULL) grpc_winsocket_destroy(socket); +} + +static void on_alarm(void* acp, grpc_error* error) { + async_connect* ac = (async_connect*)acp; + gpr_mu_lock(&ac->mu); + grpc_winsocket* socket = ac->socket; + ac->socket = NULL; + if (socket != NULL) { + grpc_winsocket_shutdown(socket); + } + async_connect_unlock_and_cleanup(ac, socket); +} + +static void on_connect(void* acp, grpc_error* error) { + async_connect* ac = (async_connect*)acp; + grpc_endpoint** ep = ac->endpoint; + GPR_ASSERT(*ep == NULL); + grpc_closure* on_done = ac->on_done; + + GRPC_ERROR_REF(error); + + gpr_mu_lock(&ac->mu); + grpc_winsocket* socket = ac->socket; + ac->socket = NULL; + gpr_mu_unlock(&ac->mu); + + grpc_timer_cancel(&ac->alarm); + + gpr_mu_lock(&ac->mu); + + if (error == GRPC_ERROR_NONE) { + if (socket != NULL) { + DWORD transfered_bytes = 0; + DWORD flags; + BOOL wsa_success = + WSAGetOverlappedResult(socket->socket, &socket->write_info.overlapped, + &transfered_bytes, FALSE, &flags); + GPR_ASSERT(transfered_bytes == 0); + if (!wsa_success) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx"); + closesocket(socket->socket); + } else { + *ep = grpc_tcp_create(socket, ac->channel_args, ac->addr_name); + socket = NULL; + } + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket is null"); + } + } + + async_connect_unlock_and_cleanup(ac, socket); + /* If the connection was aborted, the callback was already called when + the deadline was met. */ + GRPC_CLOSURE_SCHED(on_done, error); +} + +/* Tries to issue one async connection, then schedules both an IOCP + notification request for the connection, and one timeout alert. */ +static void tcp_connect(grpc_closure* on_done, grpc_endpoint** endpoint, + grpc_pollset_set* interested_parties, + const grpc_channel_args* channel_args, + const grpc_resolved_address* addr, + grpc_millis deadline) { + SOCKET sock = INVALID_SOCKET; + BOOL success; + int status; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address local_address; + async_connect* ac; + grpc_winsocket* socket = NULL; + LPFN_CONNECTEX ConnectEx; + GUID guid = WSAID_CONNECTEX; + DWORD ioctl_num_bytes; + grpc_winsocket_callback_info* info; + grpc_error* error = GRPC_ERROR_NONE; + + *endpoint = NULL; + + /* Use dualstack sockets where available. */ + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); + goto failure; + } + + error = grpc_tcp_prepare_socket(sock); + if (error != GRPC_ERROR_NONE) { + 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) { + error = GRPC_WSA_ERROR(WSAGetLastError(), + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); + goto failure; + } + + grpc_sockaddr_make_wildcard6(0, &local_address); + + status = + bind(sock, (grpc_sockaddr*)&local_address.addr, (int)local_address.len); + if (status != 0) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); + goto failure; + } + + socket = grpc_winsocket_create(sock, "client"); + info = &socket->write_info; + success = ConnectEx(sock, (grpc_sockaddr*)&addr->addr, (int)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 last_error = WSAGetLastError(); + if (last_error != ERROR_IO_PENDING) { + error = GRPC_WSA_ERROR(last_error, "ConnectEx"); + goto failure; + } + } + + ac = (async_connect*)gpr_malloc(sizeof(async_connect)); + ac->on_done = on_done; + ac->socket = socket; + gpr_mu_init(&ac->mu); + ac->refs = 2; + ac->addr_name = grpc_sockaddr_to_uri(addr); + ac->endpoint = endpoint; + ac->channel_args = grpc_channel_args_copy(channel_args); + GRPC_CLOSURE_INIT(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx); + + GRPC_CLOSURE_INIT(&ac->on_alarm, on_alarm, ac, grpc_schedule_on_exec_ctx); + grpc_timer_init(&ac->alarm, deadline, &ac->on_alarm); + grpc_socket_notify_on_write(socket, &ac->on_connect); + return; + +failure: + GPR_ASSERT(error != GRPC_ERROR_NONE); + char* target_uri = grpc_sockaddr_to_uri(addr); + grpc_error* final_error = grpc_error_set_str( + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Failed to connect", + &error, 1), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(target_uri)); + GRPC_ERROR_UNREF(error); + if (socket != NULL) { + grpc_winsocket_destroy(socket); + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + } + GRPC_CLOSURE_SCHED(on_done, final_error); +} + +grpc_tcp_client_vtable grpc_windows_tcp_client_vtable = {tcp_connect}; + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_custom.cc b/third_party/grpc/src/core/lib/iomgr/tcp_custom.cc new file mode 100644 index 00000000000000..f7a5f36cdcd608 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_custom.cc @@ -0,0 +1,368 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include +#include + +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +#define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 + +extern grpc_core::TraceFlag grpc_tcp_trace; + +grpc_socket_vtable* grpc_custom_socket_vtable = nullptr; +extern grpc_tcp_server_vtable custom_tcp_server_vtable; +extern grpc_tcp_client_vtable custom_tcp_client_vtable; + +void grpc_custom_endpoint_init(grpc_socket_vtable* impl) { + grpc_custom_socket_vtable = impl; + grpc_set_tcp_client_impl(&custom_tcp_client_vtable); + grpc_set_tcp_server_impl(&custom_tcp_server_vtable); +} + +typedef struct { + grpc_endpoint base; + gpr_refcount refcount; + grpc_custom_socket* socket; + + grpc_closure* read_cb; + grpc_closure* write_cb; + + grpc_slice_buffer* read_slices; + grpc_slice_buffer* write_slices; + + grpc_resource_user* resource_user; + grpc_resource_user_slice_allocator slice_allocator; + + bool shutting_down; + + char* peer_string; +} custom_tcp_endpoint; + +static void tcp_free(grpc_custom_socket* s) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)s->endpoint; + grpc_resource_user_unref(tcp->resource_user); + gpr_free(tcp->peer_string); + gpr_free(tcp); + s->refs--; + if (s->refs == 0) { + grpc_custom_socket_vtable->destroy(s); + gpr_free(s); + } +} + +#ifndef NDEBUG +#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(custom_tcp_endpoint* tcp, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason, + val, val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp->socket); + } +} + +static void tcp_ref(custom_tcp_endpoint* tcp, const char* reason, + const char* file, int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp->socket, reason, + val, val + 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(custom_tcp_endpoint* tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp->socket); + } +} + +static void tcp_ref(custom_tcp_endpoint* tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void call_read_cb(custom_tcp_endpoint* tcp, grpc_error* error) { + grpc_closure* cb = tcp->read_cb; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p call_cb %p %p:%p", tcp->socket, cb, cb->cb, + cb->cb_arg); + size_t i; + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "read: error=%s", str); + + for (i = 0; i < tcp->read_slices->count; i++) { + char* dump = grpc_dump_slice(tcp->read_slices->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); + gpr_free(dump); + } + } + TCP_UNREF(tcp, "read"); + tcp->read_slices = nullptr; + tcp->read_cb = nullptr; + GRPC_CLOSURE_SCHED(cb, error); +} + +static void custom_read_callback(grpc_custom_socket* socket, size_t nread, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_buffer garbage; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + if (error == GRPC_ERROR_NONE && nread == 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); + } + if (error == GRPC_ERROR_NONE) { + // Successful read + if ((size_t)nread < tcp->read_slices->length) { + /* TODO(murgatroid99): Instead of discarding the unused part of the read + * buffer, reuse it as the next read buffer. */ + grpc_slice_buffer_init(&garbage); + grpc_slice_buffer_trim_end( + tcp->read_slices, tcp->read_slices->length - (size_t)nread, &garbage); + grpc_slice_buffer_reset_and_unref_internal(&garbage); + } + } else { + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + } + call_read_cb(tcp, error); +} + +static void tcp_read_allocation_done(void* tcpp, grpc_error* error) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)tcpp; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p read_allocation_done: %s", tcp->socket, + grpc_error_string(error)); + } + if (error == GRPC_ERROR_NONE) { + /* Before calling read, we allocate a buffer with exactly one slice + * to tcp->read_slices and wait for the callback indicating that the + * allocation was successful. So slices[0] should always exist here */ + char* buffer = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[0]); + size_t len = GRPC_SLICE_LENGTH(tcp->read_slices->slices[0]); + grpc_custom_socket_vtable->read(tcp->socket, buffer, len, + custom_read_callback); + } else { + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + call_read_cb(tcp, GRPC_ERROR_REF(error)); + } + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "Initiating read on %p: error=%s", tcp->socket, str); + } +} + +static void endpoint_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices, + grpc_closure* cb) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + GPR_ASSERT(tcp->read_cb == nullptr); + tcp->read_cb = cb; + tcp->read_slices = read_slices; + grpc_slice_buffer_reset_and_unref_internal(read_slices); + TCP_REF(tcp, "read"); + grpc_resource_user_alloc_slices(&tcp->slice_allocator, + GRPC_TCP_DEFAULT_READ_SLICE_SIZE, 1, + tcp->read_slices); +} + +static void custom_write_callback(grpc_custom_socket* socket, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + grpc_closure* cb = tcp->write_cb; + tcp->write_cb = nullptr; + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "write complete on %p: error=%s", tcp->socket, str); + } + TCP_UNREF(tcp, "write"); + GRPC_CLOSURE_SCHED(cb, error); +} + +static void endpoint_write(grpc_endpoint* ep, grpc_slice_buffer* write_slices, + grpc_closure* cb, void* arg) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + if (grpc_tcp_trace.enabled()) { + size_t j; + + for (j = 0; j < write_slices->count; j++) { + char* data = grpc_dump_slice(write_slices->slices[j], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp->socket, tcp->peer_string, + data); + gpr_free(data); + } + } + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "TCP socket is shutting down")); + return; + } + + GPR_ASSERT(tcp->write_cb == nullptr); + tcp->write_slices = write_slices; + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); + if (tcp->write_slices->count == 0) { + // No slices means we don't have to do anything, + // and libuv doesn't like empty writes + GRPC_CLOSURE_SCHED(cb, GRPC_ERROR_NONE); + return; + } + tcp->write_cb = cb; + TCP_REF(tcp, "write"); + grpc_custom_socket_vtable->write(tcp->socket, tcp->write_slices, + custom_write_callback); +} + +static void endpoint_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_add_to_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_delete_from_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset) { + // No-op. We're ignoring pollsets currently + (void)ep; + (void)pollset; +} + +static void endpoint_shutdown(grpc_endpoint* ep, grpc_error* why) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + if (!tcp->shutting_down) { + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(why); + gpr_log(GPR_INFO, "TCP %p shutdown why=%s", tcp->socket, str); + } + tcp->shutting_down = true; + // GRPC_CLOSURE_SCHED(tcp->read_cb, GRPC_ERROR_REF(why)); + // GRPC_CLOSURE_SCHED(tcp->write_cb, GRPC_ERROR_REF(why)); + // tcp->read_cb = nullptr; + // tcp->write_cb = nullptr; + grpc_resource_user_shutdown(tcp->resource_user); + grpc_custom_socket_vtable->shutdown(tcp->socket); + } + GRPC_ERROR_UNREF(why); +} + +static void custom_close_callback(grpc_custom_socket* socket) { + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } else if (socket->endpoint) { + grpc_core::ExecCtx exec_ctx; + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)socket->endpoint; + TCP_UNREF(tcp, "destroy"); + } +} + +static void endpoint_destroy(grpc_endpoint* ep) { + grpc_network_status_unregister_endpoint(ep); + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + grpc_custom_socket_vtable->close(tcp->socket, custom_close_callback); +} + +static char* endpoint_get_peer(grpc_endpoint* ep) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + return gpr_strdup(tcp->peer_string); +} + +static grpc_resource_user* endpoint_get_resource_user(grpc_endpoint* ep) { + custom_tcp_endpoint* tcp = (custom_tcp_endpoint*)ep; + return tcp->resource_user; +} + +static int endpoint_get_fd(grpc_endpoint* ep) { return -1; } + +static bool endpoint_can_track_err(grpc_endpoint* ep) { return false; } + +static grpc_endpoint_vtable vtable = {endpoint_read, + endpoint_write, + endpoint_add_to_pollset, + endpoint_add_to_pollset_set, + endpoint_delete_from_pollset_set, + endpoint_shutdown, + endpoint_destroy, + endpoint_get_resource_user, + endpoint_get_peer, + endpoint_get_fd, + endpoint_can_track_err}; + +grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket, + grpc_resource_quota* resource_quota, + char* peer_string) { + custom_tcp_endpoint* tcp = + (custom_tcp_endpoint*)gpr_malloc(sizeof(custom_tcp_endpoint)); + grpc_core::ExecCtx exec_ctx; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "Creating TCP endpoint %p", socket); + } + memset(tcp, 0, sizeof(custom_tcp_endpoint)); + socket->refs++; + socket->endpoint = (grpc_endpoint*)tcp; + tcp->socket = socket; + tcp->base.vtable = &vtable; + gpr_ref_init(&tcp->refcount, 1); + tcp->peer_string = gpr_strdup(peer_string); + tcp->shutting_down = false; + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + grpc_resource_user_slice_allocator_init( + &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); + /* Tell network status tracking code about the new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + + return &tcp->base; +} diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_custom.h b/third_party/grpc/src/core/lib/iomgr/tcp_custom.h new file mode 100644 index 00000000000000..784ef84222148c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_custom.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H + +#include + +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/sockaddr.h" + +typedef struct grpc_tcp_listener grpc_tcp_listener; +typedef struct grpc_custom_tcp_connect grpc_custom_tcp_connect; + +typedef struct grpc_custom_socket { + // Implementation defined + void* impl; + grpc_endpoint* endpoint; + grpc_tcp_listener* listener; + grpc_custom_tcp_connect* connector; + int refs; +} grpc_custom_socket; + +typedef void (*grpc_custom_connect_callback)(grpc_custom_socket* socket, + grpc_error* error); +typedef void (*grpc_custom_write_callback)(grpc_custom_socket* socket, + grpc_error* error); +typedef void (*grpc_custom_read_callback)(grpc_custom_socket* socket, + size_t nread, grpc_error* error); +typedef void (*grpc_custom_accept_callback)(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error); +typedef void (*grpc_custom_close_callback)(grpc_custom_socket* socket); + +typedef struct grpc_socket_vtable { + grpc_error* (*init)(grpc_custom_socket* socket, int domain); + void (*connect)(grpc_custom_socket* socket, const grpc_sockaddr* addr, + size_t len, grpc_custom_connect_callback cb); + void (*destroy)(grpc_custom_socket* socket); + void (*shutdown)(grpc_custom_socket* socket); + void (*close)(grpc_custom_socket* socket, grpc_custom_close_callback cb); + void (*write)(grpc_custom_socket* socket, grpc_slice_buffer* slices, + grpc_custom_write_callback cb); + void (*read)(grpc_custom_socket* socket, char* buffer, size_t length, + grpc_custom_read_callback cb); + grpc_error* (*getpeername)(grpc_custom_socket* socket, + const grpc_sockaddr* addr, int* len); + grpc_error* (*getsockname)(grpc_custom_socket* socket, + const grpc_sockaddr* addr, int* len); + grpc_error* (*bind)(grpc_custom_socket* socket, const grpc_sockaddr* addr, + size_t len, int flags); + grpc_error* (*listen)(grpc_custom_socket* socket); + void (*accept)(grpc_custom_socket* socket, grpc_custom_socket* client, + grpc_custom_accept_callback cb); +} grpc_socket_vtable; + +/* Internal APIs */ +void grpc_custom_endpoint_init(grpc_socket_vtable* impl); + +void grpc_custom_close_server_callback(grpc_tcp_listener* listener); + +grpc_endpoint* custom_tcp_endpoint_create(grpc_custom_socket* socket, + grpc_resource_quota* resource_quota, + char* peer_string); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_CUSTOM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_posix.cc b/third_party/grpc/src/core/lib/iomgr/tcp_posix.cc new file mode 100644 index 00000000000000..c268c18664a849 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_posix.cc @@ -0,0 +1,1182 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_TCP + +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/tcp_posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/buffer_list.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +#ifdef GRPC_HAVE_MSG_NOSIGNAL +#define SENDMSG_FLAGS MSG_NOSIGNAL +#else +#define SENDMSG_FLAGS 0 +#endif + +#ifdef GRPC_MSG_IOVLEN_TYPE +typedef GRPC_MSG_IOVLEN_TYPE msg_iovlen_type; +#else +typedef size_t msg_iovlen_type; +#endif + +extern grpc_core::TraceFlag grpc_tcp_trace; + +namespace { +struct grpc_tcp { + grpc_endpoint base; + grpc_fd* em_fd; + int fd; + /* Used by the endpoint read function to distinguish the very first read call + * from the rest */ + bool is_first_read; + double target_length; + double bytes_read_this_round; + gpr_refcount refcount; + gpr_atm shutdown_count; + + int min_read_chunk_size; + int max_read_chunk_size; + + /* garbage after the last read */ + grpc_slice_buffer last_read_buffer; + + grpc_slice_buffer* incoming_buffer; + grpc_slice_buffer* outgoing_buffer; + /** byte within outgoing_buffer->slices[0] to write next */ + size_t outgoing_byte_idx; + + grpc_closure* read_cb; + grpc_closure* write_cb; + grpc_closure* release_fd_cb; + int* release_fd; + + grpc_closure read_done_closure; + grpc_closure write_done_closure; + grpc_closure error_closure; + + char* peer_string; + + grpc_resource_user* resource_user; + grpc_resource_user_slice_allocator slice_allocator; + + grpc_core::TracedBuffer* tb_head; /* List of traced buffers */ + gpr_mu tb_mu; /* Lock for access to list of traced buffers */ + + /* grpc_endpoint_write takes an argument which if non-null means that the + * transport layer wants the TCP layer to collect timestamps for this write. + * This arg is forwarded to the timestamps callback function when the ACK + * timestamp is received from the kernel. This arg is a (void *) which allows + * users of this API to pass in a pointer to any kind of structure. This + * structure could actually be a tag or any book-keeping object that the user + * can use to distinguish between different traced writes. The only + * requirement from the TCP endpoint layer is that this arg should be non-null + * if the user wants timestamps for the write. */ + void* outgoing_buffer_arg; + /* A counter which starts at 0. It is initialized the first time the socket + * options for collecting timestamps are set, and is incremented with each + * byte sent. */ + int bytes_counter; + bool socket_ts_enabled; /* True if timestamping options are set on the socket + */ + bool ts_capable; /* Cache whether we can set timestamping options */ + gpr_atm + stop_error_notification; /* Set to 1 if we do not want to be notified on + errors anymore */ +}; + +struct backup_poller { + gpr_mu* pollset_mu; + grpc_closure run_poller; +}; + +} // namespace + +#define BACKUP_POLLER_POLLSET(b) ((grpc_pollset*)((b) + 1)) + +static gpr_atm g_uncovered_notifications_pending; +static gpr_atm g_backup_poller; /* backup_poller* */ + +static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error); +static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error); +static void tcp_drop_uncovered_then_handle_write(void* arg /* grpc_tcp */, + grpc_error* error); + +static void done_poller(void* bp, grpc_error* error_ignored) { + backup_poller* p = static_cast(bp); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p destroy", p); + } + grpc_pollset_destroy(BACKUP_POLLER_POLLSET(p)); + gpr_free(p); +} + +static void run_poller(void* bp, grpc_error* error_ignored) { + backup_poller* p = static_cast(bp); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p run", p); + } + gpr_mu_lock(p->pollset_mu); + grpc_millis deadline = grpc_core::ExecCtx::Get()->Now() + 10 * GPR_MS_PER_SEC; + GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS(); + GRPC_LOG_IF_ERROR( + "backup_poller:pollset_work", + grpc_pollset_work(BACKUP_POLLER_POLLSET(p), nullptr, deadline)); + gpr_mu_unlock(p->pollset_mu); + /* last "uncovered" notification is the ref that keeps us polling, if we get + * there try a cas to release it */ + if (gpr_atm_no_barrier_load(&g_uncovered_notifications_pending) == 1 && + gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) { + gpr_mu_lock(p->pollset_mu); + bool cas_ok = gpr_atm_full_cas(&g_backup_poller, (gpr_atm)p, 0); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok); + } + gpr_mu_unlock(p->pollset_mu); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p shutdown", p); + } + grpc_pollset_shutdown(BACKUP_POLLER_POLLSET(p), + GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p, + grpc_schedule_on_exec_ctx)); + } else { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p reschedule", p); + } + GRPC_CLOSURE_SCHED(&p->run_poller, GRPC_ERROR_NONE); + } +} + +static void drop_uncovered(grpc_tcp* tcp) { + backup_poller* p = (backup_poller*)gpr_atm_acq_load(&g_backup_poller); + gpr_atm old_count = + gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, -1); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p uncover cnt %d->%d", p, + static_cast(old_count), static_cast(old_count) - 1); + } + GPR_ASSERT(old_count != 1); +} + +// gRPC API considers a Write operation to be done the moment it clears ‘flow +// control’ i.e., not necessarily sent on the wire. This means that the +// application MIGHT not call `grpc_completion_queue_next/pluck` in a timely +// manner when its `Write()` API is acked. +// +// We need to ensure that the fd is 'covered' (i.e being monitored by some +// polling thread and progress is made) and hence add it to a backup poller here +static void cover_self(grpc_tcp* tcp) { + backup_poller* p; + gpr_atm old_count = + gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER: cover cnt %d->%d", + static_cast(old_count), 2 + static_cast(old_count)); + } + if (old_count == 0) { + GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED(); + p = static_cast( + gpr_zalloc(sizeof(*p) + grpc_pollset_size())); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p create", p); + } + grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu); + gpr_atm_rel_store(&g_backup_poller, (gpr_atm)p); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&p->run_poller, run_poller, p, + grpc_executor_scheduler(GRPC_EXECUTOR_LONG)), + GRPC_ERROR_NONE); + } else { + while ((p = (backup_poller*)gpr_atm_acq_load(&g_backup_poller)) == + nullptr) { + // spin waiting for backup poller + } + } + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "BACKUP_POLLER:%p add %p", p, tcp); + } + grpc_pollset_add_fd(BACKUP_POLLER_POLLSET(p), tcp->em_fd); + if (old_count != 0) { + drop_uncovered(tcp); + } +} + +static void notify_on_read(grpc_tcp* tcp) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p notify_on_read", tcp); + } + GRPC_CLOSURE_INIT(&tcp->read_done_closure, tcp_handle_read, tcp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(tcp->em_fd, &tcp->read_done_closure); +} + +static void notify_on_write(grpc_tcp* tcp) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p notify_on_write", tcp); + } + if (grpc_event_engine_run_in_background()) { + // If there is a polling engine always running in the background, there is + // no need to run the backup poller. + GRPC_CLOSURE_INIT(&tcp->write_done_closure, tcp_handle_write, tcp, + grpc_schedule_on_exec_ctx); + } else { + cover_self(tcp); + GRPC_CLOSURE_INIT(&tcp->write_done_closure, + tcp_drop_uncovered_then_handle_write, tcp, + grpc_schedule_on_exec_ctx); + } + grpc_fd_notify_on_write(tcp->em_fd, &tcp->write_done_closure); +} + +static void tcp_drop_uncovered_then_handle_write(void* arg, grpc_error* error) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p got_write: %s", arg, grpc_error_string(error)); + } + drop_uncovered(static_cast(arg)); + tcp_handle_write(arg, error); +} + +static void add_to_estimate(grpc_tcp* tcp, size_t bytes) { + tcp->bytes_read_this_round += static_cast(bytes); +} + +static void finish_estimate(grpc_tcp* tcp) { + /* If we read >80% of the target buffer in one read loop, increase the size + of the target buffer to either the amount read, or twice its previous + value */ + if (tcp->bytes_read_this_round > tcp->target_length * 0.8) { + tcp->target_length = + GPR_MAX(2 * tcp->target_length, tcp->bytes_read_this_round); + } else { + tcp->target_length = + 0.99 * tcp->target_length + 0.01 * tcp->bytes_read_this_round; + } + tcp->bytes_read_this_round = 0; +} + +static size_t get_target_read_size(grpc_tcp* tcp) { + grpc_resource_quota* rq = grpc_resource_user_quota(tcp->resource_user); + double pressure = grpc_resource_quota_get_memory_pressure(rq); + double target = + tcp->target_length * (pressure > 0.8 ? (1.0 - pressure) / 0.2 : 1.0); + size_t sz = ((static_cast GPR_CLAMP(target, tcp->min_read_chunk_size, + tcp->max_read_chunk_size)) + + 255) & + ~static_cast(255); + /* don't use more than 1/16th of the overall resource quota for a single read + * alloc */ + size_t rqmax = grpc_resource_quota_peek_size(rq); + if (sz > rqmax / 16 && rqmax > 1024) { + sz = rqmax / 16; + } + return sz; +} + +static grpc_error* tcp_annotate_error(grpc_error* src_error, grpc_tcp* tcp) { + return grpc_error_set_str( + grpc_error_set_int( + grpc_error_set_int(src_error, GRPC_ERROR_INT_FD, tcp->fd), + /* All tcp errors are marked with UNAVAILABLE so that application may + * choose to retry. */ + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), + GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(tcp->peer_string)); +} + +static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error); +static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error); + +static void tcp_shutdown(grpc_endpoint* ep, grpc_error* why) { + grpc_tcp* tcp = reinterpret_cast(ep); + grpc_fd_shutdown(tcp->em_fd, why); + grpc_resource_user_shutdown(tcp->resource_user); +} + +static void tcp_free(grpc_tcp* tcp) { + grpc_fd_orphan(tcp->em_fd, tcp->release_fd_cb, tcp->release_fd, + "tcp_unref_orphan"); + grpc_slice_buffer_destroy_internal(&tcp->last_read_buffer); + grpc_resource_user_unref(tcp->resource_user); + gpr_free(tcp->peer_string); + gpr_mu_destroy(&tcp->tb_mu); + gpr_free(tcp); +} + +#ifndef NDEBUG +#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) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file, + int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val + 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_network_status_unregister_endpoint(ep); + grpc_tcp* tcp = reinterpret_cast(ep); + grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer); + if (grpc_event_engine_can_track_errors()) { + gpr_mu_lock(&tcp->tb_mu); + grpc_core::TracedBuffer::Shutdown( + &tcp->tb_head, tcp->outgoing_buffer_arg, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("endpoint destroyed")); + gpr_mu_unlock(&tcp->tb_mu); + tcp->outgoing_buffer_arg = nullptr; + gpr_atm_no_barrier_store(&tcp->stop_error_notification, true); + grpc_fd_set_error(tcp->em_fd); + } + TCP_UNREF(tcp, "destroy"); +} + +static void call_read_cb(grpc_tcp* tcp, grpc_error* error) { + grpc_closure* cb = tcp->read_cb; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg); + size_t i; + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "read: error=%s", str); + + for (i = 0; i < tcp->incoming_buffer->count; i++) { + char* dump = grpc_dump_slice(tcp->incoming_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); + gpr_free(dump); + } + } + + tcp->read_cb = nullptr; + tcp->incoming_buffer = nullptr; + GRPC_CLOSURE_SCHED(cb, error); +} + +#define MAX_READ_IOVEC 4 +static void tcp_do_read(grpc_tcp* tcp) { + GPR_TIMER_SCOPE("tcp_do_read", 0); + struct msghdr msg; + struct iovec iov[MAX_READ_IOVEC]; + ssize_t read_bytes; + size_t i; + + GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC); + + for (i = 0; i < tcp->incoming_buffer->count; i++) { + iov[i].iov_base = GRPC_SLICE_START_PTR(tcp->incoming_buffer->slices[i]); + iov[i].iov_len = GRPC_SLICE_LENGTH(tcp->incoming_buffer->slices[i]); + } + + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = static_cast(tcp->incoming_buffer->count); + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + GRPC_STATS_INC_TCP_READ_OFFER(tcp->incoming_buffer->length); + GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(tcp->incoming_buffer->count); + + do { + GPR_TIMER_SCOPE("recvmsg", 0); + GRPC_STATS_INC_SYSCALL_READ(); + read_bytes = recvmsg(tcp->fd, &msg, 0); + } while (read_bytes < 0 && errno == EINTR); + + if (read_bytes < 0) { + /* NB: After calling call_read_cb a parallel call of the read handler may + * be running. */ + if (errno == EAGAIN) { + finish_estimate(tcp); + /* We've consumed the edge, request a new one */ + notify_on_read(tcp); + } else { + grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer); + call_read_cb(tcp, + tcp_annotate_error(GRPC_OS_ERROR(errno, "recvmsg"), tcp)); + TCP_UNREF(tcp, "read"); + } + } else if (read_bytes == 0) { + /* 0 read size ==> end of stream */ + grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer); + call_read_cb( + tcp, tcp_annotate_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), tcp)); + TCP_UNREF(tcp, "read"); + } else { + GRPC_STATS_INC_TCP_READ_SIZE(read_bytes); + add_to_estimate(tcp, static_cast(read_bytes)); + GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length); + if (static_cast(read_bytes) == tcp->incoming_buffer->length) { + finish_estimate(tcp); + } else if (static_cast(read_bytes) < tcp->incoming_buffer->length) { + grpc_slice_buffer_trim_end( + tcp->incoming_buffer, + tcp->incoming_buffer->length - static_cast(read_bytes), + &tcp->last_read_buffer); + } + GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length); + call_read_cb(tcp, GRPC_ERROR_NONE); + TCP_UNREF(tcp, "read"); + } +} + +static void tcp_read_allocation_done(void* tcpp, grpc_error* error) { + grpc_tcp* tcp = static_cast(tcpp); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p read_allocation_done: %s", tcp, + grpc_error_string(error)); + } + if (error != GRPC_ERROR_NONE) { + grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer); + grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer); + call_read_cb(tcp, GRPC_ERROR_REF(error)); + TCP_UNREF(tcp, "read"); + } else { + tcp_do_read(tcp); + } +} + +static void tcp_continue_read(grpc_tcp* tcp) { + size_t target_read_size = get_target_read_size(tcp); + if (tcp->incoming_buffer->length < target_read_size / 2 && + tcp->incoming_buffer->count < MAX_READ_IOVEC) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p alloc_slices", tcp); + } + grpc_resource_user_alloc_slices(&tcp->slice_allocator, target_read_size, 1, + tcp->incoming_buffer); + } else { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p do_read", tcp); + } + tcp_do_read(tcp); + } +} + +static void tcp_handle_read(void* arg /* grpc_tcp */, grpc_error* error) { + grpc_tcp* tcp = static_cast(arg); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p got_read: %s", tcp, grpc_error_string(error)); + } + + if (error != GRPC_ERROR_NONE) { + grpc_slice_buffer_reset_and_unref_internal(tcp->incoming_buffer); + grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer); + call_read_cb(tcp, GRPC_ERROR_REF(error)); + TCP_UNREF(tcp, "read"); + } else { + tcp_continue_read(tcp); + } +} + +static void tcp_read(grpc_endpoint* ep, grpc_slice_buffer* incoming_buffer, + grpc_closure* cb) { + grpc_tcp* tcp = reinterpret_cast(ep); + GPR_ASSERT(tcp->read_cb == nullptr); + tcp->read_cb = cb; + tcp->incoming_buffer = incoming_buffer; + grpc_slice_buffer_reset_and_unref_internal(incoming_buffer); + grpc_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer); + TCP_REF(tcp, "read"); + if (tcp->is_first_read) { + /* Endpoint read called for the very first time. Register read callback with + * the polling engine */ + tcp->is_first_read = false; + notify_on_read(tcp); + } else { + /* Not the first time. We may or may not have more bytes available. In any + * case call tcp->read_done_closure (i.e tcp_handle_read()) which does the + * right thing (i.e calls tcp_do_read() which either reads the available + * bytes or calls notify_on_read() to be notified when new bytes become + * available */ + GRPC_CLOSURE_SCHED(&tcp->read_done_closure, GRPC_ERROR_NONE); + } +} + +/* A wrapper around sendmsg. It sends \a msg over \a fd and returns the number + * of bytes sent. */ +ssize_t tcp_send(int fd, const struct msghdr* msg) { + GPR_TIMER_SCOPE("sendmsg", 1); + ssize_t sent_length; + do { + /* TODO(klempner): Cork if this is a partial write */ + GRPC_STATS_INC_SYSCALL_WRITE(); + sent_length = sendmsg(fd, msg, SENDMSG_FLAGS); + } while (sent_length < 0 && errno == EINTR); + return sent_length; +} + +/** This is to be called if outgoing_buffer_arg is not null. On linux platforms, + * this will call sendmsg with socket options set to collect timestamps inside + * the kernel. On return, sent_length is set to the return value of the sendmsg + * call. Returns false if setting the socket options failed. This is not + * implemented for non-linux platforms currently, and crashes out. + */ +static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg, + size_t sending_length, + ssize_t* sent_length); + +/** The callback function to be invoked when we get an error on the socket. */ +static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error); + +#ifdef GRPC_LINUX_ERRQUEUE +static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg, + size_t sending_length, + ssize_t* sent_length) { + if (!tcp->socket_ts_enabled) { + uint32_t opt = grpc_core::kTimestampingSocketOptions; + if (setsockopt(tcp->fd, SOL_SOCKET, SO_TIMESTAMPING, + static_cast(&opt), sizeof(opt)) != 0) { + grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_ERROR, "Failed to set timestamping options on the socket."); + } + return false; + } + tcp->bytes_counter = -1; + tcp->socket_ts_enabled = true; + } + /* Set control message to indicate that you want timestamps. */ + union { + char cmsg_buf[CMSG_SPACE(sizeof(uint32_t))]; + struct cmsghdr align; + } u; + cmsghdr* cmsg = reinterpret_cast(u.cmsg_buf); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SO_TIMESTAMPING; + cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t)); + *reinterpret_cast(CMSG_DATA(cmsg)) = + grpc_core::kTimestampingRecordingOptions; + msg->msg_control = u.cmsg_buf; + msg->msg_controllen = CMSG_SPACE(sizeof(uint32_t)); + + /* If there was an error on sendmsg the logic in tcp_flush will handle it. */ + ssize_t length = tcp_send(tcp->fd, msg); + *sent_length = length; + /* Only save timestamps if all the bytes were taken by sendmsg. */ + if (sending_length == static_cast(length)) { + gpr_mu_lock(&tcp->tb_mu); + grpc_core::TracedBuffer::AddNewEntry( + &tcp->tb_head, static_cast(tcp->bytes_counter + length), + tcp->outgoing_buffer_arg); + gpr_mu_unlock(&tcp->tb_mu); + tcp->outgoing_buffer_arg = nullptr; + } + return true; +} + +/** Reads \a cmsg to derive timestamps from the control messages. If a valid + * timestamp is found, the traced buffer list is updated with this timestamp. + * The caller of this function should be looping on the control messages found + * in \a msg. \a cmsg should point to the control message that the caller wants + * processed. + * On return, a pointer to a control message is returned. On the next iteration, + * CMSG_NXTHDR(msg, ret_val) should be passed as \a cmsg. */ +struct cmsghdr* process_timestamp(grpc_tcp* tcp, msghdr* msg, + struct cmsghdr* cmsg) { + auto next_cmsg = CMSG_NXTHDR(msg, cmsg); + if (next_cmsg == nullptr) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_ERROR, "Received timestamp without extended error"); + } + return cmsg; + } + + if (!(next_cmsg->cmsg_level == SOL_IP || next_cmsg->cmsg_level == SOL_IPV6) || + !(next_cmsg->cmsg_type == IP_RECVERR || + next_cmsg->cmsg_type == IPV6_RECVERR)) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_ERROR, "Unexpected control message"); + } + return cmsg; + } + + auto tss = + reinterpret_cast(CMSG_DATA(cmsg)); + auto serr = reinterpret_cast(CMSG_DATA(next_cmsg)); + if (serr->ee_errno != ENOMSG || + serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { + gpr_log(GPR_ERROR, "Unexpected control message"); + return cmsg; + } + /* The error handling can potentially be done on another thread so we need + * to protect the traced buffer list. A lock free list might be better. Using + * a simple mutex for now. */ + gpr_mu_lock(&tcp->tb_mu); + grpc_core::TracedBuffer::ProcessTimestamp(&tcp->tb_head, serr, tss); + gpr_mu_unlock(&tcp->tb_mu); + return next_cmsg; +} + +/** For linux platforms, reads the socket's error queue and processes error + * messages from the queue. + */ +static void process_errors(grpc_tcp* tcp) { + while (true) { + struct iovec iov; + iov.iov_base = nullptr; + iov.iov_len = 0; + struct msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 0; + msg.msg_flags = 0; + + union { + char rbuf[1024 /*CMSG_SPACE(sizeof(scm_timestamping)) + + CMSG_SPACE(sizeof(sock_extended_err) + sizeof(sockaddr_in))*/]; + struct cmsghdr align; + } aligned_buf; + memset(&aligned_buf, 0, sizeof(aligned_buf)); + + msg.msg_control = aligned_buf.rbuf; + msg.msg_controllen = sizeof(aligned_buf.rbuf); + + int r, saved_errno; + do { + r = recvmsg(tcp->fd, &msg, MSG_ERRQUEUE); + saved_errno = errno; + } while (r < 0 && saved_errno == EINTR); + + if (r == -1 && saved_errno == EAGAIN) { + return; /* No more errors to process */ + } + if (r == -1) { + return; + } + if (grpc_tcp_trace.enabled()) { + if ((msg.msg_flags & MSG_CTRUNC) == 1) { + gpr_log(GPR_INFO, "Error message was truncated."); + } + } + + if (msg.msg_controllen == 0) { + /* There was no control message found. It was probably spurious. */ + return; + } + bool seen = false; + for (auto cmsg = CMSG_FIRSTHDR(&msg); cmsg && cmsg->cmsg_len; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_TIMESTAMPING) { + /* Got a control message that is not a timestamp. Don't know how to + * handle this. */ + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, + "unknown control message cmsg_level:%d cmsg_type:%d", + cmsg->cmsg_level, cmsg->cmsg_type); + } + return; + } + cmsg = process_timestamp(tcp, &msg, cmsg); + seen = true; + } + if (!seen) { + return; + } + } +} + +static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error) { + grpc_tcp* tcp = static_cast(arg); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p got_error: %s", tcp, grpc_error_string(error)); + } + + if (error != GRPC_ERROR_NONE || + static_cast(gpr_atm_acq_load(&tcp->stop_error_notification))) { + /* We aren't going to register to hear on error anymore, so it is safe to + * unref. */ + TCP_UNREF(tcp, "error-tracking"); + return; + } + + /* We are still interested in collecting timestamps, so let's try reading + * them. */ + process_errors(tcp); + /* This might not a timestamps error. Set the read and write closures to be + * ready. */ + grpc_fd_set_readable(tcp->em_fd); + grpc_fd_set_writable(tcp->em_fd); + GRPC_CLOSURE_INIT(&tcp->error_closure, tcp_handle_error, tcp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_error(tcp->em_fd, &tcp->error_closure); +} + +#else /* GRPC_LINUX_ERRQUEUE */ +static bool tcp_write_with_timestamps(grpc_tcp* tcp, struct msghdr* msg, + size_t sending_length, + ssize_t* sent_length) { + gpr_log(GPR_ERROR, "Write with timestamps not supported for this platform"); + GPR_ASSERT(0); + return false; +} + +static void tcp_handle_error(void* arg /* grpc_tcp */, grpc_error* error) { + gpr_log(GPR_ERROR, "Error handling is not supported for this platform"); + GPR_ASSERT(0); +} +#endif /* GRPC_LINUX_ERRQUEUE */ + +/* If outgoing_buffer_arg is filled, shuts down the list early, so that any + * release operations needed can be performed on the arg */ +void tcp_shutdown_buffer_list(grpc_tcp* tcp) { + if (tcp->outgoing_buffer_arg) { + gpr_mu_lock(&tcp->tb_mu); + grpc_core::TracedBuffer::Shutdown( + &tcp->tb_head, tcp->outgoing_buffer_arg, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("TracedBuffer list shutdown")); + gpr_mu_unlock(&tcp->tb_mu); + tcp->outgoing_buffer_arg = nullptr; + } +} + +/* returns true if done, false if pending; if returning true, *error is set */ +#if defined(IOV_MAX) && IOV_MAX < 1000 +#define MAX_WRITE_IOVEC IOV_MAX +#else +#define MAX_WRITE_IOVEC 1000 +#endif +static bool tcp_flush(grpc_tcp* tcp, grpc_error** error) { + struct msghdr msg; + struct iovec iov[MAX_WRITE_IOVEC]; + msg_iovlen_type iov_size; + ssize_t sent_length = 0; + size_t sending_length; + size_t trailing; + size_t unwind_slice_idx; + size_t unwind_byte_idx; + + // We always start at zero, because we eagerly unref and trim the slice + // buffer as we write + size_t outgoing_slice_idx = 0; + + for (;;) { + sending_length = 0; + unwind_slice_idx = outgoing_slice_idx; + unwind_byte_idx = tcp->outgoing_byte_idx; + for (iov_size = 0; outgoing_slice_idx != tcp->outgoing_buffer->count && + iov_size != MAX_WRITE_IOVEC; + iov_size++) { + iov[iov_size].iov_base = + GRPC_SLICE_START_PTR( + tcp->outgoing_buffer->slices[outgoing_slice_idx]) + + tcp->outgoing_byte_idx; + iov[iov_size].iov_len = + GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]) - + tcp->outgoing_byte_idx; + sending_length += iov[iov_size].iov_len; + outgoing_slice_idx++; + tcp->outgoing_byte_idx = 0; + } + GPR_ASSERT(iov_size > 0); + + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = iov_size; + msg.msg_flags = 0; + bool tried_sending_message = false; + if (tcp->outgoing_buffer_arg != nullptr) { + if (!tcp->ts_capable || + !tcp_write_with_timestamps(tcp, &msg, sending_length, &sent_length)) { + /* We could not set socket options to collect Fathom timestamps. + * Fallback on writing without timestamps. */ + tcp->ts_capable = false; + tcp_shutdown_buffer_list(tcp); + } else { + tried_sending_message = true; + } + } + if (!tried_sending_message) { + msg.msg_control = nullptr; + msg.msg_controllen = 0; + + GRPC_STATS_INC_TCP_WRITE_SIZE(sending_length); + GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(iov_size); + + sent_length = tcp_send(tcp->fd, &msg); + } + + if (sent_length < 0) { + if (errno == EAGAIN) { + tcp->outgoing_byte_idx = unwind_byte_idx; + // unref all and forget about all slices that have been written to this + // point + for (size_t idx = 0; idx < unwind_slice_idx; ++idx) { + grpc_slice_unref_internal( + grpc_slice_buffer_take_first(tcp->outgoing_buffer)); + } + return false; + } else if (errno == EPIPE) { + *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp); + grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer); + tcp_shutdown_buffer_list(tcp); + return true; + } else { + *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp); + grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer); + tcp_shutdown_buffer_list(tcp); + return true; + } + } + + GPR_ASSERT(tcp->outgoing_byte_idx == 0); + tcp->bytes_counter += sent_length; + trailing = sending_length - static_cast(sent_length); + while (trailing > 0) { + size_t slice_length; + + outgoing_slice_idx--; + slice_length = + GRPC_SLICE_LENGTH(tcp->outgoing_buffer->slices[outgoing_slice_idx]); + if (slice_length > trailing) { + tcp->outgoing_byte_idx = slice_length - trailing; + break; + } else { + trailing -= slice_length; + } + } + if (outgoing_slice_idx == tcp->outgoing_buffer->count) { + *error = GRPC_ERROR_NONE; + grpc_slice_buffer_reset_and_unref_internal(tcp->outgoing_buffer); + return true; + } + } +} + +static void tcp_handle_write(void* arg /* grpc_tcp */, grpc_error* error) { + grpc_tcp* tcp = static_cast(arg); + grpc_closure* cb; + + if (error != GRPC_ERROR_NONE) { + cb = tcp->write_cb; + tcp->write_cb = nullptr; + cb->cb(cb->cb_arg, error); + TCP_UNREF(tcp, "write"); + return; + } + + if (!tcp_flush(tcp, &error)) { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "write: delayed"); + } + notify_on_write(tcp); + } else { + cb = tcp->write_cb; + tcp->write_cb = nullptr; + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "write: %s", str); + } + GRPC_CLOSURE_SCHED(cb, error); + TCP_UNREF(tcp, "write"); + } +} + +static void tcp_write(grpc_endpoint* ep, grpc_slice_buffer* buf, + grpc_closure* cb, void* arg) { + GPR_TIMER_SCOPE("tcp_write", 0); + grpc_tcp* tcp = reinterpret_cast(ep); + grpc_error* error = GRPC_ERROR_NONE; + + if (grpc_tcp_trace.enabled()) { + size_t i; + + for (i = 0; i < buf->count; i++) { + char* data = + grpc_dump_slice(buf->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); + gpr_free(data); + } + } + + GPR_ASSERT(tcp->write_cb == nullptr); + + tcp->outgoing_buffer_arg = arg; + if (buf->length == 0) { + GRPC_CLOSURE_SCHED( + cb, grpc_fd_is_shutdown(tcp->em_fd) + ? tcp_annotate_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"), tcp) + : GRPC_ERROR_NONE); + tcp_shutdown_buffer_list(tcp); + return; + } + tcp->outgoing_buffer = buf; + tcp->outgoing_byte_idx = 0; + if (arg) { + GPR_ASSERT(grpc_event_engine_can_track_errors()); + } + + if (!tcp_flush(tcp, &error)) { + TCP_REF(tcp, "write"); + tcp->write_cb = cb; + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "write: delayed"); + } + notify_on_write(tcp); + } else { + if (grpc_tcp_trace.enabled()) { + const char* str = grpc_error_string(error); + gpr_log(GPR_INFO, "write: %s", str); + } + GRPC_CLOSURE_SCHED(cb, error); + } +} + +static void tcp_add_to_pollset(grpc_endpoint* ep, grpc_pollset* pollset) { + grpc_tcp* tcp = reinterpret_cast(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 = reinterpret_cast(ep); + grpc_pollset_set_add_fd(pollset_set, tcp->em_fd); +} + +static void tcp_delete_from_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pollset_set) { + grpc_tcp* tcp = reinterpret_cast(ep); + grpc_pollset_set_del_fd(pollset_set, tcp->em_fd); +} + +static char* tcp_get_peer(grpc_endpoint* ep) { + grpc_tcp* tcp = reinterpret_cast(ep); + return gpr_strdup(tcp->peer_string); +} + +static int tcp_get_fd(grpc_endpoint* ep) { + grpc_tcp* tcp = reinterpret_cast(ep); + return tcp->fd; +} + +static grpc_resource_user* tcp_get_resource_user(grpc_endpoint* ep) { + grpc_tcp* tcp = reinterpret_cast(ep); + return tcp->resource_user; +} + +static bool tcp_can_track_err(grpc_endpoint* ep) { + grpc_tcp* tcp = reinterpret_cast(ep); + if (!grpc_event_engine_can_track_errors()) { + return false; + } + struct sockaddr addr; + socklen_t len = sizeof(addr); + if (getsockname(tcp->fd, &addr, &len) < 0) { + return false; + } + if (addr.sa_family == AF_INET || addr.sa_family == AF_INET6) { + return true; + } + return false; +} + +static const grpc_endpoint_vtable vtable = {tcp_read, + tcp_write, + tcp_add_to_pollset, + tcp_add_to_pollset_set, + tcp_delete_from_pollset_set, + tcp_shutdown, + tcp_destroy, + tcp_get_resource_user, + tcp_get_peer, + tcp_get_fd, + tcp_can_track_err}; + +#define MAX_CHUNK_SIZE 32 * 1024 * 1024 + +grpc_endpoint* grpc_tcp_create(grpc_fd* em_fd, + const grpc_channel_args* channel_args, + const char* peer_string) { + int tcp_read_chunk_size = GRPC_TCP_DEFAULT_READ_SLICE_SIZE; + int tcp_max_read_chunk_size = 4 * 1024 * 1024; + int tcp_min_read_chunk_size = 256; + grpc_resource_quota* resource_quota = grpc_resource_quota_create(nullptr); + if (channel_args != nullptr) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_TCP_READ_CHUNK_SIZE)) { + grpc_integer_options options = {tcp_read_chunk_size, 1, MAX_CHUNK_SIZE}; + tcp_read_chunk_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE)) { + grpc_integer_options options = {tcp_read_chunk_size, 1, MAX_CHUNK_SIZE}; + tcp_min_read_chunk_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE)) { + grpc_integer_options options = {tcp_read_chunk_size, 1, MAX_CHUNK_SIZE}; + tcp_max_read_chunk_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } else if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(resource_quota); + resource_quota = + grpc_resource_quota_ref_internal(static_cast( + channel_args->args[i].value.pointer.p)); + } + } + } + + if (tcp_min_read_chunk_size > tcp_max_read_chunk_size) { + tcp_min_read_chunk_size = tcp_max_read_chunk_size; + } + tcp_read_chunk_size = GPR_CLAMP(tcp_read_chunk_size, tcp_min_read_chunk_size, + tcp_max_read_chunk_size); + + grpc_tcp* tcp = static_cast(gpr_malloc(sizeof(grpc_tcp))); + tcp->base.vtable = &vtable; + tcp->peer_string = gpr_strdup(peer_string); + tcp->fd = grpc_fd_wrapped_fd(em_fd); + tcp->read_cb = nullptr; + tcp->write_cb = nullptr; + tcp->release_fd_cb = nullptr; + tcp->release_fd = nullptr; + tcp->incoming_buffer = nullptr; + tcp->target_length = static_cast(tcp_read_chunk_size); + tcp->min_read_chunk_size = tcp_min_read_chunk_size; + tcp->max_read_chunk_size = tcp_max_read_chunk_size; + tcp->bytes_read_this_round = 0; + /* Will be set to false by the very first endpoint read function */ + tcp->is_first_read = true; + tcp->bytes_counter = -1; + tcp->socket_ts_enabled = false; + tcp->ts_capable = true; + tcp->outgoing_buffer_arg = nullptr; + /* paired with unref in grpc_tcp_destroy */ + gpr_ref_init(&tcp->refcount, 1); + gpr_atm_no_barrier_store(&tcp->shutdown_count, 0); + tcp->em_fd = em_fd; + grpc_slice_buffer_init(&tcp->last_read_buffer); + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + grpc_resource_user_slice_allocator_init( + &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); + /* Tell network status tracker about new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + grpc_resource_quota_unref_internal(resource_quota); + gpr_mu_init(&tcp->tb_mu); + tcp->tb_head = nullptr; + /* Start being notified on errors if event engine can track errors. */ + if (grpc_event_engine_can_track_errors()) { + /* Grab a ref to tcp so that we can safely access the tcp struct when + * processing errors. We unref when we no longer want to track errors + * separately. */ + TCP_REF(tcp, "error-tracking"); + gpr_atm_rel_store(&tcp->stop_error_notification, 0); + GRPC_CLOSURE_INIT(&tcp->error_closure, tcp_handle_error, tcp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_error(tcp->em_fd, &tcp->error_closure); + } + + return &tcp->base; +} + +int grpc_tcp_fd(grpc_endpoint* ep) { + grpc_tcp* tcp = reinterpret_cast(ep); + GPR_ASSERT(ep->vtable == &vtable); + return grpc_fd_wrapped_fd(tcp->em_fd); +} + +void grpc_tcp_destroy_and_release_fd(grpc_endpoint* ep, int* fd, + grpc_closure* done) { + grpc_network_status_unregister_endpoint(ep); + grpc_tcp* tcp = reinterpret_cast(ep); + GPR_ASSERT(ep->vtable == &vtable); + tcp->release_fd = fd; + tcp->release_fd_cb = done; + grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer); + if (grpc_event_engine_can_track_errors()) { + /* Stop errors notification. */ + gpr_mu_lock(&tcp->tb_mu); + grpc_core::TracedBuffer::Shutdown( + &tcp->tb_head, tcp->outgoing_buffer_arg, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("endpoint destroyed")); + gpr_mu_unlock(&tcp->tb_mu); + tcp->outgoing_buffer_arg = nullptr; + gpr_atm_no_barrier_store(&tcp->stop_error_notification, true); + grpc_fd_set_error(tcp->em_fd); + } + TCP_UNREF(tcp, "destroy"); +} + +#endif /* GRPC_POSIX_SOCKET_TCP */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_posix.h b/third_party/grpc/src/core/lib/iomgr/tcp_posix.h new file mode 100644 index 00000000000000..eff825cb925dc1 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_posix.h @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_POSIX_H +#define GRPC_CORE_LIB_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 + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/buffer_list.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/ev_posix.h" + +extern grpc_core::TraceFlag 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, const grpc_channel_args* args, + const char* peer_string); + +/* Return the tcp endpoint's fd, or -1 if this is not available. Does not + release the fd. + Requires: ep must be a tcp endpoint. + */ +int grpc_tcp_fd(grpc_endpoint* ep); + +/* Destroy the tcp endpoint without closing its fd. *fd will be set and done + * will be called when the endpoint is destroyed. + * Requires: ep must be a tcp endpoint and fd must not be NULL. */ +void grpc_tcp_destroy_and_release_fd(grpc_endpoint* ep, int* fd, + grpc_closure* done); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server.cc new file mode 100644 index 00000000000000..ea745f266b9c75 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server.cc @@ -0,0 +1,73 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/tcp_server.h" + +grpc_tcp_server_vtable* grpc_tcp_server_impl; + +grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + return grpc_tcp_server_impl->create(shutdown_complete, args, server); +} + +void grpc_tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void* cb_arg) { + grpc_tcp_server_impl->start(server, pollsets, pollset_count, on_accept_cb, + cb_arg); +} + +grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* out_port) { + return grpc_tcp_server_impl->add_port(s, addr, out_port); +} + +unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return grpc_tcp_server_impl->port_fd_count(s, port_index); +} + +int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return grpc_tcp_server_impl->port_fd(s, port_index, fd_index); +} + +grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s) { + return grpc_tcp_server_impl->ref(s); +} + +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + grpc_tcp_server_impl->shutdown_starting_add(s, shutdown_starting); +} + +void grpc_tcp_server_unref(grpc_tcp_server* s) { + grpc_tcp_server_impl->unref(s); +} + +void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s) { + grpc_tcp_server_impl->shutdown_listeners(s); +} + +void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl) { + grpc_tcp_server_impl = impl; +} diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server.h b/third_party/grpc/src/core/lib/iomgr/tcp_server.h new file mode 100644 index 00000000000000..8fcbb2f6807d00 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server.h @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_H +#define GRPC_CORE_LIB_IOMGR_TCP_SERVER_H + +#include + +#include + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/resolve_address.h" + +/* Forward decl of grpc_tcp_server */ +typedef struct grpc_tcp_server grpc_tcp_server; + +typedef struct grpc_tcp_server_acceptor { + /* grpc_tcp_server_cb functions share a ref on from_server that is valid + until the function returns. */ + grpc_tcp_server* from_server; + /* Indices that may be passed to grpc_tcp_server_port_fd(). */ + unsigned port_index; + unsigned fd_index; +} grpc_tcp_server_acceptor; + +/* Called for newly connected TCP connections. + Takes ownership of acceptor. */ +typedef void (*grpc_tcp_server_cb)(void* arg, grpc_endpoint* ep, + grpc_pollset* accepting_pollset, + grpc_tcp_server_acceptor* acceptor); + +typedef struct grpc_tcp_server_vtable { + grpc_error* (*create)(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server); + void (*start)(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, grpc_tcp_server_cb on_accept_cb, + void* cb_arg); + grpc_error* (*add_port)(grpc_tcp_server* s, const grpc_resolved_address* addr, + int* out_port); + unsigned (*port_fd_count)(grpc_tcp_server* s, unsigned port_index); + int (*port_fd)(grpc_tcp_server* s, unsigned port_index, unsigned fd_index); + grpc_tcp_server* (*ref)(grpc_tcp_server* s); + void (*shutdown_starting_add)(grpc_tcp_server* s, + grpc_closure* shutdown_starting); + void (*unref)(grpc_tcp_server* s); + void (*shutdown_listeners)(grpc_tcp_server* s); +} grpc_tcp_server_vtable; + +/* Create a server, initially not bound to any ports. The caller owns one ref. + If shutdown_complete is not NULL, it will be used by + grpc_tcp_server_unref() when the ref count reaches zero. */ +grpc_error* grpc_tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server); + +/* 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 on_accept_cb, void* cb_arg); + +/* Add a port to the server, returning the newly allocated port on success, or + -1 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_tcp_server_add_ports to handle + all of the multiple socket port matching logic in one place */ +grpc_error* grpc_tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* out_port); + +/* Number of fds at the given port_index, or 0 if port_index is out of + bounds. */ +unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server* s, unsigned port_index); + +/* Returns the file descriptor of the Mth (fd_index) listening socket of the Nth + (port_index) call to add_port() on this server, or -1 if the indices are out + of bounds. The file descriptor remains owned by the server, and will be + cleaned up when the ref count reaches zero. */ +int grpc_tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index); + +/* Ref s and return s. */ +grpc_tcp_server* grpc_tcp_server_ref(grpc_tcp_server* s); + +/* shutdown_starting is called when ref count has reached zero and the server is + about to be destroyed. The server will be deleted after it returns. Calling + grpc_tcp_server_ref() from it has no effect. */ +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting); + +/* If the refcount drops to zero, enqueue calls on exec_ctx to + shutdown_listeners and delete s. */ +void grpc_tcp_server_unref(grpc_tcp_server* s); + +/* Shutdown the fds of listeners. */ +void grpc_tcp_server_shutdown_listeners(grpc_tcp_server* s); + +void grpc_tcp_server_global_init(); + +void grpc_set_tcp_server_impl(grpc_tcp_server_vtable* impl); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_custom.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_custom.cc new file mode 100644 index 00000000000000..019b354473b9ae --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_custom.cc @@ -0,0 +1,472 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include +#include + +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/iomgr/tcp_server.h" + +extern grpc_core::TraceFlag grpc_tcp_trace; + +extern grpc_socket_vtable* grpc_custom_socket_vtable; + +/* one listening port */ +struct grpc_tcp_listener { + grpc_tcp_server* server; + unsigned port_index; + int port; + + grpc_custom_socket* socket; + + /* linked list */ + struct grpc_tcp_listener* next; + + bool closed; +}; + +struct grpc_tcp_server { + gpr_refcount refs; + + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void* on_accept_cb_arg; + + int open_ports; + + /* linked list of server ports */ + grpc_tcp_listener* head; + grpc_tcp_listener* tail; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure* shutdown_complete; + + bool shutdown; + + grpc_resource_quota* resource_quota; +}; + +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); + s->resource_quota = grpc_resource_quota_create(nullptr); + for (size_t i = 0; i < (args == nullptr ? 0 : args->num_args); i++) { + if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_POINTER) { + grpc_resource_quota_unref_internal(s->resource_quota); + s->resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)args->args[i].value.pointer.p); + } else { + grpc_resource_quota_unref_internal(s->resource_quota); + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool"); + } + } + } + gpr_ref_init(&s->refs, 1); + s->on_accept_cb = nullptr; + s->on_accept_cb_arg = nullptr; + s->open_ports = 0; + s->head = nullptr; + s->tail = nullptr; + s->shutdown_starting.head = nullptr; + s->shutdown_starting.tail = nullptr; + s->shutdown_complete = shutdown_complete; + s->shutdown = false; + *server = s; + return GRPC_ERROR_NONE; +} + +static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + gpr_ref(&s->refs); + return s; +} + +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); +} + +static void finish_shutdown(grpc_tcp_server* s) { + GPR_ASSERT(s->shutdown); + if (s->shutdown_complete != nullptr) { + GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); + } + + while (s->head) { + grpc_tcp_listener* sp = s->head; + s->head = sp->next; + sp->next = nullptr; + gpr_free(sp); + } + grpc_resource_quota_unref_internal(s->resource_quota); + gpr_free(s); +} + +static void custom_close_callback(grpc_custom_socket* socket) { + grpc_tcp_listener* sp = socket->listener; + if (sp) { + grpc_core::ExecCtx exec_ctx; + sp->server->open_ports--; + if (sp->server->open_ports == 0 && sp->server->shutdown) { + finish_shutdown(sp->server); + } + } + socket->refs--; + if (socket->refs == 0) { + grpc_custom_socket_vtable->destroy(socket); + gpr_free(socket); + } +} + +void grpc_custom_close_server_callback(grpc_tcp_listener* sp) { + if (sp) { + grpc_core::ExecCtx exec_ctx; + sp->server->open_ports--; + if (sp->server->open_ports == 0 && sp->server->shutdown) { + finish_shutdown(sp->server); + } + } +} + +static void close_listener(grpc_tcp_listener* sp) { + grpc_custom_socket* socket = sp->socket; + if (!sp->closed) { + sp->closed = true; + grpc_custom_socket_vtable->close(socket, custom_close_callback); + } +} + +static void tcp_server_destroy(grpc_tcp_server* s) { + int immediately_done = 0; + grpc_tcp_listener* sp; + + GPR_ASSERT(!s->shutdown); + s->shutdown = true; + + if (s->open_ports == 0) { + immediately_done = 1; + } + for (sp = s->head; sp; sp = sp->next) { + close_listener(sp); + } + + if (immediately_done) { + finish_shutdown(s); + } +} + +static void tcp_server_unref(grpc_tcp_server* s) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + if (gpr_unref(&s->refs)) { + /* Complete shutdown_starting work before destroying. */ + grpc_core::ExecCtx exec_ctx; + GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting); + grpc_core::ExecCtx::Get()->Flush(); + tcp_server_destroy(s); + } +} + +static void finish_accept(grpc_tcp_listener* sp, grpc_custom_socket* socket) { + grpc_tcp_server_acceptor* acceptor = + (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor)); + grpc_endpoint* ep = nullptr; + grpc_resolved_address peer_name; + char* peer_name_string; + grpc_error* err; + + peer_name_string = nullptr; + memset(&peer_name, 0, sizeof(grpc_resolved_address)); + peer_name.len = GRPC_MAX_SOCKADDR_SIZE; + err = grpc_custom_socket_vtable->getpeername( + socket, (grpc_sockaddr*)&peer_name.addr, (int*)&peer_name.len); + if (err == GRPC_ERROR_NONE) { + peer_name_string = grpc_sockaddr_to_uri(&peer_name); + } else { + GRPC_LOG_IF_ERROR("getpeername error", err); + GRPC_ERROR_UNREF(err); + } + if (grpc_tcp_trace.enabled()) { + if (peer_name_string) { + gpr_log(GPR_INFO, "SERVER_CONNECT: %p accepted connection: %s", + sp->server, peer_name_string); + } else { + gpr_log(GPR_INFO, "SERVER_CONNECT: %p accepted connection", sp->server); + } + } + ep = custom_tcp_endpoint_create(socket, sp->server->resource_quota, + peer_name_string); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = 0; + sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, nullptr, acceptor); + gpr_free(peer_name_string); +} + +static void custom_accept_callback(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error); + +static void custom_accept_callback(grpc_custom_socket* socket, + grpc_custom_socket* client, + grpc_error* error) { + grpc_core::ExecCtx exec_ctx; + grpc_tcp_listener* sp = socket->listener; + if (error != GRPC_ERROR_NONE) { + if (!sp->closed) { + gpr_log(GPR_ERROR, "Accept failed: %s", grpc_error_string(error)); + } + gpr_free(client); + GRPC_ERROR_UNREF(error); + return; + } + finish_accept(sp, client); + if (!sp->closed) { + grpc_custom_socket* new_socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + new_socket->endpoint = nullptr; + new_socket->listener = nullptr; + new_socket->connector = nullptr; + new_socket->refs = 1; + grpc_custom_socket_vtable->accept(sp->socket, new_socket, + custom_accept_callback); + } +} + +static grpc_error* add_socket_to_server(grpc_tcp_server* s, + grpc_custom_socket* socket, + const grpc_resolved_address* addr, + unsigned port_index, + grpc_tcp_listener** listener) { + grpc_tcp_listener* sp = nullptr; + int port = -1; + grpc_error* error; + grpc_resolved_address sockname_temp; + + // The last argument to uv_tcp_bind is flags + error = grpc_custom_socket_vtable->bind(socket, (grpc_sockaddr*)addr->addr, + addr->len, 0); + if (error != GRPC_ERROR_NONE) { + return error; + } + + error = grpc_custom_socket_vtable->listen(socket); + if (error != GRPC_ERROR_NONE) { + return error; + } + + sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE; + error = grpc_custom_socket_vtable->getsockname( + socket, (grpc_sockaddr*)&sockname_temp.addr, (int*)&sockname_temp.len); + if (error != GRPC_ERROR_NONE) { + return error; + } + + port = grpc_sockaddr_get_port(&sockname_temp); + + GPR_ASSERT(port >= 0); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = (grpc_tcp_listener*)gpr_zalloc(sizeof(grpc_tcp_listener)); + sp->next = nullptr; + if (s->head == nullptr) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->socket = socket; + sp->port = port; + sp->port_index = port_index; + sp->closed = false; + s->open_ports++; + *listener = sp; + + return GRPC_ERROR_NONE; +} + +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* port) { + // This function is mostly copied from tcp_server_windows.c + grpc_tcp_listener* sp = nullptr; + grpc_custom_socket* socket; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address* allocated_addr = nullptr; + grpc_resolved_address sockname_temp; + unsigned port_index = 0; + grpc_error* error = GRPC_ERROR_NONE; + int family; + + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + + if (s->tail != nullptr) { + port_index = s->tail->port_index + 1; + } + + /* 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 (sp = s->head; sp; sp = sp->next) { + socket = sp->socket; + sockname_temp.len = GRPC_MAX_SOCKADDR_SIZE; + if (nullptr == grpc_custom_socket_vtable->getsockname( + socket, (grpc_sockaddr*)&sockname_temp.addr, + (int*)&sockname_temp.len)) { + *port = grpc_sockaddr_get_port(&sockname_temp); + if (*port > 0) { + allocated_addr = + (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, *port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &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 = &wildcard; + } + + if (grpc_tcp_trace.enabled()) { + char* port_string; + grpc_sockaddr_to_string(&port_string, addr, 0); + const char* str = grpc_error_string(error); + if (port_string) { + gpr_log(GPR_INFO, "SERVER %p add_port %s error=%s", s, port_string, str); + gpr_free(port_string); + } else { + gpr_log(GPR_INFO, "SERVER %p add_port error=%s", s, str); + } + } + + family = grpc_sockaddr_get_family(addr); + socket = (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + socket->refs = 1; + socket->endpoint = nullptr; + socket->listener = nullptr; + socket->connector = nullptr; + grpc_custom_socket_vtable->init(socket, family); + + if (error == GRPC_ERROR_NONE) { + error = add_socket_to_server(s, socket, addr, port_index, &sp); + } + gpr_free(allocated_addr); + + if (error != GRPC_ERROR_NONE) { + grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to add port to server", &error, 1); + GRPC_ERROR_UNREF(error); + error = error_out; + *port = -1; + } else { + GPR_ASSERT(sp != nullptr); + *port = sp->port; + } + socket->listener = sp; + return error; +} + +static void tcp_server_start(grpc_tcp_server* server, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void* cb_arg) { + grpc_tcp_listener* sp; + (void)pollsets; + (void)pollset_count; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "SERVER_START %p", server); + } + GPR_ASSERT(on_accept_cb); + GPR_ASSERT(!server->on_accept_cb); + server->on_accept_cb = on_accept_cb; + server->on_accept_cb_arg = cb_arg; + for (sp = server->head; sp; sp = sp->next) { + grpc_custom_socket* new_socket = + (grpc_custom_socket*)gpr_malloc(sizeof(grpc_custom_socket)); + new_socket->endpoint = nullptr; + new_socket->listener = nullptr; + new_socket->connector = nullptr; + new_socket->refs = 1; + grpc_custom_socket_vtable->accept(sp->socket, new_socket, + custom_accept_callback); + } +} + +static unsigned tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return 0; +} + +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return -1; +} + +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) { + for (grpc_tcp_listener* sp = s->head; sp; sp = sp->next) { + if (!sp->closed) { + sp->closed = true; + grpc_custom_socket_vtable->close(sp->socket, custom_close_callback); + } + } +} + +grpc_tcp_server_vtable custom_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; + +#ifdef GRPC_UV_TEST +grpc_tcp_server_vtable* default_tcp_server_vtable = &custom_tcp_server_vtable; +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_posix.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_posix.cc new file mode 100644 index 00000000000000..824db07fbfdd00 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_posix.cc @@ -0,0 +1,562 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_TCP_SERVER + +#include "src/core/lib/iomgr/tcp_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + grpc_tcp_server* s = + static_cast(gpr_zalloc(sizeof(grpc_tcp_server))); + s->so_reuseport = grpc_is_socket_reuse_port_supported(); + s->expand_wildcard_addrs = false; + for (size_t i = 0; i < (args == nullptr ? 0 : args->num_args); i++) { + if (0 == strcmp(GRPC_ARG_ALLOW_REUSEPORT, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_INTEGER) { + s->so_reuseport = grpc_is_socket_reuse_port_supported() && + (args->args[i].value.integer != 0); + } else { + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING(GRPC_ARG_ALLOW_REUSEPORT + " must be an integer"); + } + } else if (0 == strcmp(GRPC_ARG_EXPAND_WILDCARD_ADDRS, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_INTEGER) { + s->expand_wildcard_addrs = (args->args[i].value.integer != 0); + } else { + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + GRPC_ARG_EXPAND_WILDCARD_ADDRS " must be an integer"); + } + } + } + gpr_ref_init(&s->refs, 1); + gpr_mu_init(&s->mu); + s->active_ports = 0; + s->destroyed_ports = 0; + s->shutdown = false; + s->shutdown_starting.head = nullptr; + s->shutdown_starting.tail = nullptr; + s->shutdown_complete = shutdown_complete; + s->on_accept_cb = nullptr; + s->on_accept_cb_arg = nullptr; + s->head = nullptr; + s->tail = nullptr; + s->nports = 0; + s->channel_args = grpc_channel_args_copy(args); + gpr_atm_no_barrier_store(&s->next_pollset_to_assign, 0); + *server = s; + return GRPC_ERROR_NONE; +} + +static void finish_shutdown(grpc_tcp_server* s) { + gpr_mu_lock(&s->mu); + GPR_ASSERT(s->shutdown); + gpr_mu_unlock(&s->mu); + if (s->shutdown_complete != nullptr) { + GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); + } + + gpr_mu_destroy(&s->mu); + + while (s->head) { + grpc_tcp_listener* sp = s->head; + s->head = sp->next; + gpr_free(sp); + } + grpc_channel_args_destroy(s->channel_args); + + gpr_free(s); +} + +static void destroyed_port(void* server, grpc_error* error) { + grpc_tcp_server* s = static_cast(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); + } +} + +/* 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) { + /* delete ALL the things */ + gpr_mu_lock(&s->mu); + + GPR_ASSERT(s->shutdown); + + if (s->head) { + grpc_tcp_listener* sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_unlink_if_unix_domain_socket(&sp->addr); + GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s, + grpc_schedule_on_exec_ctx); + grpc_fd_orphan(sp->emfd, &sp->destroyed_closure, nullptr, + "tcp_listener_shutdown"); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + finish_shutdown(s); + } +} + +static void tcp_server_destroy(grpc_tcp_server* s) { + gpr_mu_lock(&s->mu); + + GPR_ASSERT(!s->shutdown); + s->shutdown = true; + + /* shutdown all fd's */ + if (s->active_ports) { + grpc_tcp_listener* sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_fd_shutdown( + sp->emfd, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server destroyed")); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + deactivated_all_ports(s); + } +} + +/* event manager callback when reads are ready */ +static void on_read(void* arg, grpc_error* err) { + grpc_tcp_listener* sp = static_cast(arg); + grpc_pollset* read_notifier_pollset; + if (err != GRPC_ERROR_NONE) { + goto error; + } + + /* loop until accept4 returns EAGAIN, and then re-arm notification */ + for (;;) { + grpc_resolved_address addr; + char* addr_str; + char* name; + memset(&addr, 0, sizeof(addr)); + addr.len = static_cast(sizeof(struct sockaddr_storage)); + /* 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, &addr, 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_mu_lock(&sp->server->mu); + if (!sp->server->shutdown_listeners) { + gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno)); + } else { + /* if we have shutdown listeners, accept4 could fail, and we + needn't notify users */ + } + gpr_mu_unlock(&sp->server->mu); + goto error; + } + } + + grpc_set_socket_no_sigpipe_if_possible(fd); + + addr_str = grpc_sockaddr_to_uri(&addr); + gpr_asprintf(&name, "tcp-server-connection:%s", addr_str); + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "SERVER_CONNECT: incoming connection: %s", addr_str); + } + + grpc_fd* fdobj = grpc_fd_create(fd, name, true); + + read_notifier_pollset = + sp->server->pollsets[static_cast(gpr_atm_no_barrier_fetch_add( + &sp->server->next_pollset_to_assign, 1)) % + sp->server->pollset_count]; + + grpc_pollset_add_fd(read_notifier_pollset, fdobj); + + // Create acceptor. + grpc_tcp_server_acceptor* acceptor = + static_cast(gpr_malloc(sizeof(*acceptor))); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = sp->fd_index; + + sp->server->on_accept_cb( + sp->server->on_accept_cb_arg, + grpc_tcp_create(fdobj, sp->server->channel_args, addr_str), + read_notifier_pollset, acceptor); + + gpr_free(name); + gpr_free(addr_str); + } + + GPR_UNREACHABLE_CODE(return ); + +error: + gpr_mu_lock(&sp->server->mu); + if (0 == --sp->server->active_ports && sp->server->shutdown) { + gpr_mu_unlock(&sp->server->mu); + deactivated_all_ports(sp->server); + } else { + gpr_mu_unlock(&sp->server->mu); + } +} + +/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ +static grpc_error* add_wildcard_addrs_to_server(grpc_tcp_server* s, + unsigned port_index, + int requested_port, + int* out_port) { + grpc_resolved_address wild4; + grpc_resolved_address wild6; + unsigned fd_index = 0; + grpc_dualstack_mode dsmode; + grpc_tcp_listener* sp = nullptr; + grpc_tcp_listener* sp2 = nullptr; + grpc_error* v6_err = GRPC_ERROR_NONE; + grpc_error* v4_err = GRPC_ERROR_NONE; + *out_port = -1; + + if (grpc_tcp_server_have_ifaddrs() && s->expand_wildcard_addrs) { + return grpc_tcp_server_add_all_local_addrs(s, port_index, requested_port, + out_port); + } + + grpc_sockaddr_make_wildcards(requested_port, &wild4, &wild6); + /* Try listening on IPv6 first. */ + if ((v6_err = grpc_tcp_server_add_addr(s, &wild6, port_index, fd_index, + &dsmode, &sp)) == GRPC_ERROR_NONE) { + ++fd_index; + requested_port = *out_port = sp->port; + if (dsmode == GRPC_DSMODE_DUALSTACK || dsmode == GRPC_DSMODE_IPV4) { + return GRPC_ERROR_NONE; + } + } + /* If we got a v6-only socket or nothing, try adding 0.0.0.0. */ + grpc_sockaddr_set_port(&wild4, requested_port); + if ((v4_err = grpc_tcp_server_add_addr(s, &wild4, port_index, fd_index, + &dsmode, &sp2)) == GRPC_ERROR_NONE) { + *out_port = sp2->port; + if (sp != nullptr) { + sp2->is_sibling = 1; + sp->sibling = sp2; + } + } + if (*out_port > 0) { + if (v6_err != GRPC_ERROR_NONE) { + gpr_log(GPR_INFO, + "Failed to add :: listener, " + "the environment may not support IPv6: %s", + grpc_error_string(v6_err)); + GRPC_ERROR_UNREF(v6_err); + } + if (v4_err != GRPC_ERROR_NONE) { + gpr_log(GPR_INFO, + "Failed to add 0.0.0.0 listener, " + "the environment may not support IPv4: %s", + grpc_error_string(v4_err)); + GRPC_ERROR_UNREF(v4_err); + } + return GRPC_ERROR_NONE; + } else { + grpc_error* root_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to add any wildcard listeners"); + GPR_ASSERT(v6_err != GRPC_ERROR_NONE && v4_err != GRPC_ERROR_NONE); + root_err = grpc_error_add_child(root_err, v6_err); + root_err = grpc_error_add_child(root_err, v4_err); + return root_err; + } +} + +static grpc_error* clone_port(grpc_tcp_listener* listener, unsigned count) { + grpc_tcp_listener* sp = nullptr; + char* addr_str; + char* name; + grpc_error* err; + + for (grpc_tcp_listener* l = listener->next; l && l->is_sibling; l = l->next) { + l->fd_index += count; + } + + for (unsigned i = 0; i < count; i++) { + int fd = -1; + int port = -1; + grpc_dualstack_mode dsmode; + err = grpc_create_dualstack_socket(&listener->addr, SOCK_STREAM, 0, &dsmode, + &fd); + if (err != GRPC_ERROR_NONE) return err; + err = grpc_tcp_server_prepare_socket(listener->server, fd, &listener->addr, + true, &port); + if (err != GRPC_ERROR_NONE) return err; + listener->server->nports++; + grpc_sockaddr_to_string(&addr_str, &listener->addr, 1); + gpr_asprintf(&name, "tcp-server-listener:%s/clone-%d", addr_str, i); + sp = static_cast(gpr_malloc(sizeof(grpc_tcp_listener))); + sp->next = listener->next; + listener->next = sp; + /* sp (the new listener) is a sibling of 'listener' (the original + listener). */ + sp->is_sibling = 1; + sp->sibling = listener->sibling; + listener->sibling = sp; + sp->server = listener->server; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd, name, true); + memcpy(&sp->addr, &listener->addr, sizeof(grpc_resolved_address)); + sp->port = port; + sp->port_index = listener->port_index; + sp->fd_index = listener->fd_index + count - i; + GPR_ASSERT(sp->emfd); + while (listener->server->tail->next != nullptr) { + listener->server->tail = listener->server->tail->next; + } + gpr_free(addr_str); + gpr_free(name); + } + + return GRPC_ERROR_NONE; +} + +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* out_port) { + grpc_tcp_listener* sp; + grpc_resolved_address sockname_temp; + grpc_resolved_address addr6_v4mapped; + int requested_port = grpc_sockaddr_get_port(addr); + unsigned port_index = 0; + grpc_dualstack_mode dsmode; + grpc_error* err; + *out_port = -1; + if (s->tail != nullptr) { + port_index = s->tail->port_index + 1; + } + grpc_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 (requested_port == 0) { + for (sp = s->head; sp; sp = sp->next) { + sockname_temp.len = + static_cast(sizeof(struct sockaddr_storage)); + if (0 == + getsockname(sp->fd, + reinterpret_cast(&sockname_temp.addr), + &sockname_temp.len)) { + int used_port = grpc_sockaddr_get_port(&sockname_temp); + if (used_port > 0) { + memcpy(&sockname_temp, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(&sockname_temp, used_port); + requested_port = used_port; + addr = &sockname_temp; + break; + } + } + } + } + if (grpc_sockaddr_is_wildcard(addr, &requested_port)) { + return add_wildcard_addrs_to_server(s, port_index, requested_port, + out_port); + } + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + if ((err = grpc_tcp_server_add_addr(s, addr, port_index, 0, &dsmode, &sp)) == + GRPC_ERROR_NONE) { + *out_port = sp->port; + } + return err; +} + +/* Return listener at port_index or NULL. Should only be called with s->mu + locked. */ +static grpc_tcp_listener* get_port_index(grpc_tcp_server* s, + unsigned port_index) { + unsigned num_ports = 0; + grpc_tcp_listener* sp; + for (sp = s->head; sp; sp = sp->next) { + if (!sp->is_sibling) { + if (++num_ports > port_index) { + return sp; + } + } + } + return nullptr; +} + +unsigned tcp_server_port_fd_count(grpc_tcp_server* s, unsigned port_index) { + unsigned num_fds = 0; + gpr_mu_lock(&s->mu); + grpc_tcp_listener* sp = get_port_index(s, port_index); + for (; sp; sp = sp->sibling) { + ++num_fds; + } + gpr_mu_unlock(&s->mu); + return num_fds; +} + +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + gpr_mu_lock(&s->mu); + grpc_tcp_listener* sp = get_port_index(s, port_index); + for (; sp; sp = sp->sibling, --fd_index) { + if (fd_index == 0) { + gpr_mu_unlock(&s->mu); + return sp->fd; + } + } + gpr_mu_unlock(&s->mu); + return -1; +} + +static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollsets, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void* on_accept_cb_arg) { + size_t i; + grpc_tcp_listener* sp; + GPR_ASSERT(on_accept_cb); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->on_accept_cb); + GPR_ASSERT(s->active_ports == 0); + s->on_accept_cb = on_accept_cb; + s->on_accept_cb_arg = on_accept_cb_arg; + s->pollsets = pollsets; + s->pollset_count = pollset_count; + sp = s->head; + while (sp != nullptr) { + if (s->so_reuseport && !grpc_is_unix_socket(&sp->addr) && + pollset_count > 1) { + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "clone_port", clone_port(sp, (unsigned)(pollset_count - 1)))); + for (i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(pollsets[i], sp->emfd); + GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + s->active_ports++; + sp = sp->next; + } + } else { + for (i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(pollsets[i], sp->emfd); + } + GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(sp->emfd, &sp->read_closure); + s->active_ports++; + sp = sp->next; + } + } + gpr_mu_unlock(&s->mu); +} + +grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { + gpr_ref_non_zero(&s->refs); + return s; +} + +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + gpr_mu_lock(&s->mu); + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); + gpr_mu_unlock(&s->mu); +} + +static void tcp_server_unref(grpc_tcp_server* s) { + if (gpr_unref(&s->refs)) { + grpc_tcp_server_shutdown_listeners(s); + gpr_mu_lock(&s->mu); + GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting); + gpr_mu_unlock(&s->mu); + tcp_server_destroy(s); + } +} + +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) { + gpr_mu_lock(&s->mu); + s->shutdown_listeners = true; + /* shutdown all fd's */ + if (s->active_ports) { + grpc_tcp_listener* sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_fd_shutdown(sp->emfd, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown")); + } + } + gpr_mu_unlock(&s->mu); +} + +grpc_tcp_server_vtable grpc_posix_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; +#endif /* GRPC_POSIX_SOCKET_TCP_SERVER */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix.h b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix.h new file mode 100644 index 00000000000000..dd199097b2cdf3 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix.h @@ -0,0 +1,122 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H +#define GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H + +#include + +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/tcp_server.h" + +/* one listening port */ +typedef struct grpc_tcp_listener { + int fd; + grpc_fd* emfd; + grpc_tcp_server* server; + grpc_resolved_address addr; + int port; + unsigned port_index; + unsigned fd_index; + grpc_closure read_closure; + grpc_closure destroyed_closure; + struct grpc_tcp_listener* next; + /* sibling is a linked list of all listeners for a given port. add_port and + clone_port place all new listeners in the same sibling list. A member of + the 'sibling' list is also a member of the 'next' list. The head of each + sibling list has is_sibling==0, and subsequent members of sibling lists + have is_sibling==1. is_sibling allows separate sibling lists to be + identified while iterating through 'next'. */ + struct grpc_tcp_listener* sibling; + int is_sibling; +} grpc_tcp_listener; + +/* the overall server */ +struct grpc_tcp_server { + gpr_refcount refs; + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void* on_accept_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? */ + bool shutdown; + /* have listeners been shutdown? */ + bool shutdown_listeners; + /* use SO_REUSEPORT */ + bool so_reuseport; + /* expand wildcard addresses to a list of all local addresses */ + bool expand_wildcard_addrs; + + /* linked list of server ports */ + grpc_tcp_listener* head; + grpc_tcp_listener* tail; + unsigned nports; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure* shutdown_complete; + + /* all pollsets interested in new connections */ + grpc_pollset** pollsets; + /* number of pollsets in the pollsets array */ + size_t pollset_count; + + /* next pollset to assign a channel to */ + gpr_atm next_pollset_to_assign; + + /* channel args for this server */ + grpc_channel_args* channel_args; +}; + +/* If successful, add a listener to \a s for \a addr, set \a dsmode for the + socket, and return the \a listener. */ +grpc_error* grpc_tcp_server_add_addr(grpc_tcp_server* s, + const grpc_resolved_address* addr, + unsigned port_index, unsigned fd_index, + grpc_dualstack_mode* dsmode, + grpc_tcp_listener** listener); + +/* Get all addresses assigned to network interfaces on the machine and create a + listener for each. requested_port is the port to use for every listener, or 0 + to select one random port that will be used for every listener. Set *out_port + to the port selected. Return GRPC_ERROR_NONE only if all listeners were + added. */ +grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s, + unsigned port_index, + int requested_port, + int* out_port); + +/* Prepare a recently-created socket for listening. */ +grpc_error* grpc_tcp_server_prepare_socket(grpc_tcp_server*, int fd, + const grpc_resolved_address* addr, + bool so_reuseport, int* port); +/* Ruturn true if the platform supports ifaddrs */ +bool grpc_tcp_server_have_ifaddrs(void); + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_common.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_common.cc new file mode 100644 index 00000000000000..8d8d3f4273efc0 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_common.cc @@ -0,0 +1,223 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON + +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 + +static gpr_once s_init_max_accept_queue_size = GPR_ONCE_INIT; +static int s_max_accept_queue_size; + +/* 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 == nullptr) { + /* 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 == '\n') { + n = static_cast(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; +} + +static grpc_error* add_socket_to_server(grpc_tcp_server* s, int fd, + const grpc_resolved_address* addr, + unsigned port_index, unsigned fd_index, + grpc_tcp_listener** listener) { + grpc_tcp_listener* sp = nullptr; + int port = -1; + char* addr_str; + char* name; + + grpc_error* err = + grpc_tcp_server_prepare_socket(s, fd, addr, s->so_reuseport, &port); + if (err == GRPC_ERROR_NONE) { + GPR_ASSERT(port > 0); + grpc_sockaddr_to_string(&addr_str, addr, 1); + gpr_asprintf(&name, "tcp-server-listener:%s", addr_str); + gpr_mu_lock(&s->mu); + s->nports++; + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = static_cast(gpr_malloc(sizeof(grpc_tcp_listener))); + sp->next = nullptr; + if (s->head == nullptr) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd, name, true); + memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); + sp->port = port; + sp->port_index = port_index; + sp->fd_index = fd_index; + sp->is_sibling = 0; + sp->sibling = nullptr; + GPR_ASSERT(sp->emfd); + gpr_mu_unlock(&s->mu); + gpr_free(addr_str); + gpr_free(name); + } + + *listener = sp; + return err; +} + +/* If successful, add a listener to s for addr, set *dsmode for the socket, and + return the *listener. */ +grpc_error* grpc_tcp_server_add_addr(grpc_tcp_server* s, + const grpc_resolved_address* addr, + unsigned port_index, unsigned fd_index, + grpc_dualstack_mode* dsmode, + grpc_tcp_listener** listener) { + grpc_resolved_address addr4_copy; + int fd; + grpc_error* err = + grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, dsmode, &fd); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (*dsmode == GRPC_DSMODE_IPV4 && + grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { + addr = &addr4_copy; + } + return add_socket_to_server(s, fd, addr, port_index, fd_index, listener); +} + +/* Prepare a recently-created socket for listening. */ +grpc_error* grpc_tcp_server_prepare_socket(grpc_tcp_server* s, int fd, + const grpc_resolved_address* addr, + bool so_reuseport, int* port) { + grpc_resolved_address sockname_temp; + grpc_error* err = GRPC_ERROR_NONE; + + GPR_ASSERT(fd >= 0); + + if (so_reuseport && !grpc_is_unix_socket(addr)) { + err = grpc_set_socket_reuse_port(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + } + + err = grpc_set_socket_nonblocking(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_cloexec(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + if (!grpc_is_unix_socket(addr)) { + err = grpc_set_socket_low_latency(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_reuse_addr(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_tcp_user_timeout(fd, s->channel_args, + false /* is_client */); + if (err != GRPC_ERROR_NONE) goto error; + } + err = grpc_set_socket_no_sigpipe_if_possible(fd); + if (err != GRPC_ERROR_NONE) goto error; + + if (s->channel_args) { + for (size_t i = 0; i < s->channel_args->num_args; i++) { + if (0 == strcmp(s->channel_args->args[i].key, GRPC_ARG_SOCKET_MUTATOR)) { + GPR_ASSERT(s->channel_args->args[i].type == GRPC_ARG_POINTER); + grpc_socket_mutator* mutator = static_cast( + s->channel_args->args[i].value.pointer.p); + err = grpc_set_socket_with_mutator(fd, mutator); + if (err != GRPC_ERROR_NONE) goto error; + } + } + } + + if (bind(fd, reinterpret_cast(const_cast(addr->addr)), + addr->len) < 0) { + err = GRPC_OS_ERROR(errno, "bind"); + goto error; + } + + if (listen(fd, get_max_accept_queue_size()) < 0) { + err = GRPC_OS_ERROR(errno, "listen"); + goto error; + } + + sockname_temp.len = static_cast(sizeof(struct sockaddr_storage)); + + if (getsockname(fd, reinterpret_cast(sockname_temp.addr), + &sockname_temp.len) < 0) { + err = GRPC_OS_ERROR(errno, "getsockname"); + goto error; + } + + *port = grpc_sockaddr_get_port(&sockname_temp); + return GRPC_ERROR_NONE; + +error: + GPR_ASSERT(err != GRPC_ERROR_NONE); + if (fd >= 0) { + close(fd); + } + grpc_error* ret = + grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Unable to configure socket", &err, 1), + GRPC_ERROR_INT_FD, fd); + GRPC_ERROR_UNREF(err); + return ret; +} + +#endif /* GRPC_POSIX_SOCKET_TCP_SERVER_UTILS_COMMON */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc new file mode 100644 index 00000000000000..7fd86c57ebf6a5 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc @@ -0,0 +1,184 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_HAVE_IFADDRS + +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +/* Return the listener in s with address addr or NULL. */ +static grpc_tcp_listener* find_listener_with_addr(grpc_tcp_server* s, + grpc_resolved_address* addr) { + grpc_tcp_listener* l; + gpr_mu_lock(&s->mu); + for (l = s->head; l != nullptr; l = l->next) { + if (l->addr.len != addr->len) { + continue; + } + if (memcmp(l->addr.addr, addr->addr, addr->len) == 0) { + break; + } + } + gpr_mu_unlock(&s->mu); + return l; +} + +/* Bind to "::" to get a port number not used by any address. */ +static grpc_error* get_unused_port(int* port) { + grpc_resolved_address wild; + grpc_sockaddr_make_wildcard6(0, &wild); + grpc_dualstack_mode dsmode; + int fd; + grpc_error* err = + grpc_create_dualstack_socket(&wild, SOCK_STREAM, 0, &dsmode, &fd); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (dsmode == GRPC_DSMODE_IPV4) { + grpc_sockaddr_make_wildcard4(0, &wild); + } + if (bind(fd, reinterpret_cast(wild.addr), wild.len) != + 0) { + err = GRPC_OS_ERROR(errno, "bind"); + close(fd); + return err; + } + if (getsockname(fd, reinterpret_cast(wild.addr), &wild.len) != + 0) { + err = GRPC_OS_ERROR(errno, "getsockname"); + close(fd); + return err; + } + close(fd); + *port = grpc_sockaddr_get_port(&wild); + return *port <= 0 ? GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad port") + : GRPC_ERROR_NONE; +} + +grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s, + unsigned port_index, + int requested_port, + int* out_port) { + struct ifaddrs* ifa = nullptr; + struct ifaddrs* ifa_it; + unsigned fd_index = 0; + grpc_tcp_listener* sp = nullptr; + grpc_error* err = GRPC_ERROR_NONE; + if (requested_port == 0) { + /* Note: There could be a race where some local addrs can listen on the + selected port and some can't. The sane way to handle this would be to + retry by recreating the whole grpc_tcp_server. Backing out individual + listeners and orphaning the FDs looks like too much trouble. */ + if ((err = get_unused_port(&requested_port)) != GRPC_ERROR_NONE) { + return err; + } else if (requested_port <= 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad get_unused_port()"); + } + gpr_log(GPR_DEBUG, "Picked unused port %d", requested_port); + } + if (getifaddrs(&ifa) != 0 || ifa == nullptr) { + return GRPC_OS_ERROR(errno, "getifaddrs"); + } + for (ifa_it = ifa; ifa_it != nullptr; ifa_it = ifa_it->ifa_next) { + grpc_resolved_address addr; + char* addr_str = nullptr; + grpc_dualstack_mode dsmode; + grpc_tcp_listener* new_sp = nullptr; + const char* ifa_name = (ifa_it->ifa_name ? ifa_it->ifa_name : ""); + if (ifa_it->ifa_addr == nullptr) { + continue; + } else if (ifa_it->ifa_addr->sa_family == AF_INET) { + addr.len = static_cast(sizeof(grpc_sockaddr_in)); + } else if (ifa_it->ifa_addr->sa_family == AF_INET6) { + addr.len = static_cast(sizeof(grpc_sockaddr_in6)); + } else { + continue; + } + memcpy(addr.addr, ifa_it->ifa_addr, addr.len); + if (!grpc_sockaddr_set_port(&addr, requested_port)) { + /* Should never happen, because we check sa_family above. */ + err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set port"); + break; + } + if (grpc_sockaddr_to_string(&addr_str, &addr, 0) < 0) { + addr_str = gpr_strdup(""); + } + gpr_log(GPR_DEBUG, + "Adding local addr from interface %s flags 0x%x to server: %s", + ifa_name, ifa_it->ifa_flags, addr_str); + /* We could have multiple interfaces with the same address (e.g., bonding), + so look for duplicates. */ + if (find_listener_with_addr(s, &addr) != nullptr) { + gpr_log(GPR_DEBUG, "Skipping duplicate addr %s on interface %s", addr_str, + ifa_name); + gpr_free(addr_str); + continue; + } + if ((err = grpc_tcp_server_add_addr(s, &addr, port_index, fd_index, &dsmode, + &new_sp)) != GRPC_ERROR_NONE) { + char* err_str = nullptr; + grpc_error* root_err; + if (gpr_asprintf(&err_str, "Failed to add listener: %s", addr_str) < 0) { + err_str = gpr_strdup("Failed to add listener"); + } + root_err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_str); + gpr_free(err_str); + gpr_free(addr_str); + err = grpc_error_add_child(root_err, err); + break; + } else { + GPR_ASSERT(requested_port == new_sp->port); + ++fd_index; + if (sp != nullptr) { + new_sp->is_sibling = 1; + sp->sibling = new_sp; + } + sp = new_sp; + } + gpr_free(addr_str); + } + freeifaddrs(ifa); + if (err != GRPC_ERROR_NONE) { + return err; + } else if (sp == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No local addresses"); + } else { + *out_port = sp->port; + return GRPC_ERROR_NONE; + } +} + +bool grpc_tcp_server_have_ifaddrs(void) { return true; } + +#endif /* GRPC_HAVE_IFADDRS */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc new file mode 100644 index 00000000000000..86ee14f2856685 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#if defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) + +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" + +grpc_error* grpc_tcp_server_add_all_local_addrs(grpc_tcp_server* s, + unsigned port_index, + int requested_port, + int* out_port) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("no ifaddrs available"); +} + +bool grpc_tcp_server_have_ifaddrs(void) { return false; } + +#endif /* defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_server_windows.cc b/third_party/grpc/src/core/lib/iomgr/tcp_server_windows.cc new file mode 100644 index 00000000000000..b01afdcc9dbf75 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_server_windows.cc @@ -0,0 +1,560 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/tcp_windows.h" + +#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 + +/* one listening port */ +typedef struct grpc_tcp_listener grpc_tcp_listener; +struct grpc_tcp_listener { + /* This seemingly magic number comes from AcceptEx's documentation. each + address buffer needs to have at least 16 more bytes at their end. */ + uint8_t addresses[(sizeof(grpc_sockaddr_in6) + 16) * 2]; + /* This will hold the socket for the next accept. */ + SOCKET new_socket; + /* The listener winsocket. */ + grpc_winsocket* socket; + /* The actual TCP port number. */ + int port; + unsigned port_index; + grpc_tcp_server* server; + /* The cached AcceptEx for that port. */ + LPFN_ACCEPTEX AcceptEx; + int shutting_down; + int outstanding_calls; + /* closure for socket notification of accept being ready */ + grpc_closure on_accept; + /* linked list */ + struct grpc_tcp_listener* next; +}; + +/* the overall server */ +struct grpc_tcp_server { + gpr_refcount refs; + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void* on_accept_cb_arg; + + gpr_mu mu; + + /* active port count: how many ports are actually still listening */ + int active_ports; + + /* linked list of server ports */ + grpc_tcp_listener* head; + grpc_tcp_listener* tail; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure* shutdown_complete; + + grpc_channel_args* channel_args; +}; + +/* Public function. Allocates the proper data structures to hold a + grpc_tcp_server. */ +static grpc_error* tcp_server_create(grpc_closure* shutdown_complete, + const grpc_channel_args* args, + grpc_tcp_server** server) { + grpc_tcp_server* s = (grpc_tcp_server*)gpr_malloc(sizeof(grpc_tcp_server)); + s->channel_args = grpc_channel_args_copy(args); + gpr_ref_init(&s->refs, 1); + gpr_mu_init(&s->mu); + s->active_ports = 0; + s->on_accept_cb = NULL; + s->on_accept_cb_arg = NULL; + s->head = NULL; + s->tail = NULL; + s->shutdown_starting.head = NULL; + s->shutdown_starting.tail = NULL; + s->shutdown_complete = shutdown_complete; + *server = s; + return GRPC_ERROR_NONE; +} + +static void destroy_server(void* arg, grpc_error* error) { + grpc_tcp_server* s = (grpc_tcp_server*)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. */ + while (s->head) { + grpc_tcp_listener* sp = s->head; + s->head = sp->next; + sp->next = NULL; + grpc_winsocket_destroy(sp->socket); + gpr_free(sp); + } + grpc_channel_args_destroy(s->channel_args); + gpr_mu_destroy(&s->mu); + gpr_free(s); +} + +static void finish_shutdown_locked(grpc_tcp_server* s) { + if (s->shutdown_complete != NULL) { + GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); + } + + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(destroy_server, s, grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); +} + +static grpc_tcp_server* tcp_server_ref(grpc_tcp_server* s) { + gpr_ref_non_zero(&s->refs); + return s; +} + +static void tcp_server_shutdown_starting_add(grpc_tcp_server* s, + grpc_closure* shutdown_starting) { + gpr_mu_lock(&s->mu); + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); + gpr_mu_unlock(&s->mu); +} + +static void tcp_server_destroy(grpc_tcp_server* s) { + grpc_tcp_listener* sp; + gpr_mu_lock(&s->mu); + + /* 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) { + finish_shutdown_locked(s); + } else { + for (sp = s->head; sp; sp = sp->next) { + sp->shutting_down = 1; + grpc_winsocket_shutdown(sp->socket); + } + } + gpr_mu_unlock(&s->mu); +} + +static void tcp_server_unref(grpc_tcp_server* s) { + if (gpr_unref(&s->refs)) { + grpc_tcp_server_shutdown_listeners(s); + gpr_mu_lock(&s->mu); + GRPC_CLOSURE_LIST_SCHED(&s->shutdown_starting); + gpr_mu_unlock(&s->mu); + tcp_server_destroy(s); + } +} + +/* Prepare (bind) a recently-created socket for listening. */ +static grpc_error* prepare_socket(SOCKET sock, + const grpc_resolved_address* addr, + int* port) { + grpc_resolved_address sockname_temp; + grpc_error* error = GRPC_ERROR_NONE; + int sockname_temp_len; + + error = grpc_tcp_prepare_socket(sock); + if (error != GRPC_ERROR_NONE) { + goto failure; + } + + if (bind(sock, (const grpc_sockaddr*)addr->addr, (int)addr->len) == + SOCKET_ERROR) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); + goto failure; + } + + if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "listen"); + goto failure; + } + + sockname_temp_len = sizeof(struct sockaddr_storage); + if (getsockname(sock, (grpc_sockaddr*)sockname_temp.addr, + &sockname_temp_len) == SOCKET_ERROR) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname"); + goto failure; + } + sockname_temp.len = (size_t)sockname_temp_len; + + *port = grpc_sockaddr_get_port(&sockname_temp); + return GRPC_ERROR_NONE; + +failure: + GPR_ASSERT(error != GRPC_ERROR_NONE); + char* tgtaddr = grpc_sockaddr_to_uri(addr); + grpc_error_set_int( + grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to prepare server socket", &error, 1), + GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(tgtaddr)), + GRPC_ERROR_INT_FD, (intptr_t)sock); + gpr_free(tgtaddr); + GRPC_ERROR_UNREF(error); + if (sock != INVALID_SOCKET) closesocket(sock); + return error; +} + +static void decrement_active_ports_and_notify_locked(grpc_tcp_listener* sp) { + sp->shutting_down = 0; + GPR_ASSERT(sp->server->active_ports > 0); + if (0 == --sp->server->active_ports) { + finish_shutdown_locked(sp->server); + } +} + +/* 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 grpc_error* start_accept_locked(grpc_tcp_listener* port) { + SOCKET sock = INVALID_SOCKET; + BOOL success; + DWORD addrlen = sizeof(grpc_sockaddr_in6) + 16; + DWORD bytes_received = 0; + grpc_error* error = GRPC_ERROR_NONE; + + if (port->shutting_down) { + return GRPC_ERROR_NONE; + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); + goto failure; + } + + error = grpc_tcp_prepare_socket(sock); + if (error != GRPC_ERROR_NONE) 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 last_error = WSAGetLastError(); + if (last_error != ERROR_IO_PENDING) { + error = GRPC_WSA_ERROR(last_error, "AcceptEx"); + 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, &port->on_accept); + port->outstanding_calls++; + return error; + +failure: + GPR_ASSERT(error != GRPC_ERROR_NONE); + if (sock != INVALID_SOCKET) closesocket(sock); + return error; +} + +/* Event manager callback when reads are ready. */ +static void on_accept(void* arg, grpc_error* error) { + grpc_tcp_listener* sp = (grpc_tcp_listener*)arg; + SOCKET sock = sp->new_socket; + grpc_winsocket_callback_info* info = &sp->socket->read_info; + grpc_endpoint* ep = NULL; + grpc_resolved_address peer_name; + char* peer_name_string; + char* fd_name; + DWORD transfered_bytes; + DWORD flags; + BOOL wsa_success; + int err; + + gpr_mu_lock(&sp->server->mu); + + peer_name.len = sizeof(struct sockaddr_storage); + + /* 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 (error != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(error); + gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", msg); + + gpr_mu_unlock(&sp->server->mu); + 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) { + 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); + } + int peer_name_len = (int)peer_name.len; + err = getpeername(sock, (grpc_sockaddr*)peer_name.addr, &peer_name_len); + peer_name.len = (size_t)peer_name_len; + if (!err) { + peer_name_string = grpc_sockaddr_to_uri(&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), + sp->server->channel_args, peer_name_string); + gpr_free(fd_name); + gpr_free(peer_name_string); + } else { + closesocket(sock); + } + } + + /* The only time we should call our callback, is where we successfully + managed to accept a connection, and created an endpoint. */ + if (ep) { + // Create acceptor. + grpc_tcp_server_acceptor* acceptor = + (grpc_tcp_server_acceptor*)gpr_malloc(sizeof(*acceptor)); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = 0; + sp->server->on_accept_cb(sp->server->on_accept_cb_arg, ep, NULL, acceptor); + } + /* 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. */ + GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(sp))); + if (0 == --sp->outstanding_calls) { + decrement_active_ports_and_notify_locked(sp); + } + gpr_mu_unlock(&sp->server->mu); +} + +static grpc_error* add_socket_to_server(grpc_tcp_server* s, SOCKET sock, + const grpc_resolved_address* addr, + unsigned port_index, + grpc_tcp_listener** listener) { + grpc_tcp_listener* sp = NULL; + int port = -1; + int status; + GUID guid = WSAID_ACCEPTEX; + DWORD ioctl_num_bytes; + LPFN_ACCEPTEX AcceptEx; + grpc_error* error = GRPC_ERROR_NONE; + + /* 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 NULL; + } + + error = prepare_socket(sock, addr, &port); + if (error != GRPC_ERROR_NONE) { + return error; + } + + GPR_ASSERT(port >= 0); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = (grpc_tcp_listener*)gpr_malloc(sizeof(grpc_tcp_listener)); + sp->next = NULL; + if (s->head == NULL) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->socket = grpc_winsocket_create(sock, "listener"); + sp->shutting_down = 0; + sp->outstanding_calls = 0; + sp->AcceptEx = AcceptEx; + sp->new_socket = INVALID_SOCKET; + sp->port = port; + sp->port_index = port_index; + GRPC_CLOSURE_INIT(&sp->on_accept, on_accept, sp, grpc_schedule_on_exec_ctx); + GPR_ASSERT(sp->socket); + gpr_mu_unlock(&s->mu); + *listener = sp; + + return GRPC_ERROR_NONE; +} + +static grpc_error* tcp_server_add_port(grpc_tcp_server* s, + const grpc_resolved_address* addr, + int* port) { + grpc_tcp_listener* sp = NULL; + SOCKET sock; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address* allocated_addr = NULL; + grpc_resolved_address sockname_temp; + unsigned port_index = 0; + grpc_error* error = GRPC_ERROR_NONE; + + if (s->tail != NULL) { + port_index = s->tail->port_index + 1; + } + + /* 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 (sp = s->head; sp; sp = sp->next) { + int sockname_temp_len = sizeof(struct sockaddr_storage); + if (0 == getsockname(sp->socket->socket, + (grpc_sockaddr*)sockname_temp.addr, + &sockname_temp_len)) { + sockname_temp.len = (size_t)sockname_temp_len; + *port = grpc_sockaddr_get_port(&sockname_temp); + if (*port > 0) { + allocated_addr = + (grpc_resolved_address*)gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, *port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &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 = &wildcard; + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); + goto done; + } + + error = add_socket_to_server(s, sock, addr, port_index, &sp); + +done: + gpr_free(allocated_addr); + + if (error != GRPC_ERROR_NONE) { + grpc_error* error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to add port to server", &error, 1); + GRPC_ERROR_UNREF(error); + error = error_out; + *port = -1; + } else { + GPR_ASSERT(sp != NULL); + *port = sp->port; + } + return error; +} + +static void tcp_server_start(grpc_tcp_server* s, grpc_pollset** pollset, + size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void* on_accept_cb_arg) { + grpc_tcp_listener* sp; + GPR_ASSERT(on_accept_cb); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->on_accept_cb); + GPR_ASSERT(s->active_ports == 0); + s->on_accept_cb = on_accept_cb; + s->on_accept_cb_arg = on_accept_cb_arg; + for (sp = s->head; sp; sp = sp->next) { + GPR_ASSERT(GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(sp))); + s->active_ports++; + } + gpr_mu_unlock(&s->mu); +} + +static unsigned tcp_server_port_fd_count(grpc_tcp_server* s, + unsigned port_index) { + return 0; +} + +static int tcp_server_port_fd(grpc_tcp_server* s, unsigned port_index, + unsigned fd_index) { + return -1; +} + +static void tcp_server_shutdown_listeners(grpc_tcp_server* s) {} + +grpc_tcp_server_vtable grpc_windows_tcp_server_vtable = { + tcp_server_create, + tcp_server_start, + tcp_server_add_port, + tcp_server_port_fd_count, + tcp_server_port_fd, + tcp_server_ref, + tcp_server_shutdown_starting_add, + tcp_server_unref, + tcp_server_shutdown_listeners}; +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_uv.cc b/third_party/grpc/src/core/lib/iomgr/tcp_uv.cc new file mode 100644 index 00000000000000..8d0e4a5e79edcb --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_uv.cc @@ -0,0 +1,420 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV +#include +#include + +#include + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/resolve_address_custom.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/tcp_custom.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +#include + +#define IGNORE_CONST(addr) ((grpc_sockaddr*)(uintptr_t)(addr)) + +typedef struct uv_socket_t { + uv_connect_t connect_req; + uv_write_t write_req; + uv_shutdown_t shutdown_req; + uv_tcp_t* handle; + uv_buf_t* write_buffers; + + char* read_buf; + size_t read_len; + + bool pending_connection; + grpc_custom_socket* accept_socket; + grpc_error* accept_error; + + grpc_custom_connect_callback connect_cb; + grpc_custom_write_callback write_cb; + grpc_custom_read_callback read_cb; + grpc_custom_accept_callback accept_cb; + grpc_custom_close_callback close_cb; + +} uv_socket_t; + +static grpc_error* tcp_error_create(const char* desc, int status) { + if (status == 0) { + return GRPC_ERROR_NONE; + } + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(desc); + /* All tcp errors are marked with UNAVAILABLE so that application may + * choose to retry. */ + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + return grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); +} + +static void uv_socket_destroy(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + gpr_free(uv_socket->handle); + gpr_free(uv_socket); +} + +static void alloc_uv_buf(uv_handle_t* handle, size_t suggested_size, + uv_buf_t* buf) { + uv_socket_t* uv_socket = + (uv_socket_t*)((grpc_custom_socket*)handle->data)->impl; + (void)suggested_size; + buf->base = uv_socket->read_buf; + buf->len = uv_socket->read_len; +} + +static void uv_read_callback(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + grpc_error* error = GRPC_ERROR_NONE; + if (nread == 0) { + // Nothing happened. Wait for the next callback + return; + } + // TODO(murgatroid99): figure out what the return value here means + uv_read_stop(stream); + if (nread == UV_EOF) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); + } else if (nread < 0) { + error = tcp_error_create("TCP Read failed", nread); + } + grpc_custom_socket* socket = (grpc_custom_socket*)stream->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->read_cb(socket, (size_t)nread, error); +} + +static void uv_close_callback(uv_handle_t* handle) { + grpc_custom_socket* socket = (grpc_custom_socket*)handle->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + if (uv_socket->accept_socket) { + uv_socket->accept_cb(socket, uv_socket->accept_socket, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket closed")); + } + uv_socket->close_cb(socket); +} + +static void uv_socket_read(grpc_custom_socket* socket, char* buffer, + size_t length, grpc_custom_read_callback read_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status; + grpc_error* error; + uv_socket->read_cb = read_cb; + uv_socket->read_buf = buffer; + uv_socket->read_len = length; + // TODO(murgatroid99): figure out what the return value here means + status = + uv_read_start((uv_stream_t*)uv_socket->handle, (uv_alloc_cb)alloc_uv_buf, + (uv_read_cb)uv_read_callback); + if (status != 0) { + error = tcp_error_create("TCP Read failed at start", status); + uv_socket->read_cb(socket, 0, error); + } +} + +static void uv_write_callback(uv_write_t* req, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)req->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + gpr_free(uv_socket->write_buffers); + uv_socket->write_cb(socket, tcp_error_create("TCP Write failed", status)); +} + +void uv_socket_write(grpc_custom_socket* socket, + grpc_slice_buffer* write_slices, + grpc_custom_write_callback write_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->write_cb = write_cb; + uv_buf_t* uv_buffers; + uv_write_t* write_req; + + uv_buffers = (uv_buf_t*)gpr_malloc(sizeof(uv_buf_t) * write_slices->count); + for (size_t i = 0; i < write_slices->count; i++) { + uv_buffers[i].base = (char*)GRPC_SLICE_START_PTR(write_slices->slices[i]); + uv_buffers[i].len = GRPC_SLICE_LENGTH(write_slices->slices[i]); + } + + uv_socket->write_buffers = uv_buffers; + write_req = &uv_socket->write_req; + write_req->data = socket; + // TODO(murgatroid99): figure out what the return value here means + uv_write(write_req, (uv_stream_t*)uv_socket->handle, uv_buffers, + write_slices->count, uv_write_callback); +} + +static void shutdown_callback(uv_shutdown_t* req, int status) {} + +static void uv_socket_shutdown(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_shutdown_t* req = &uv_socket->shutdown_req; + uv_shutdown(req, (uv_stream_t*)uv_socket->handle, shutdown_callback); +} + +static void uv_socket_close(grpc_custom_socket* socket, + grpc_custom_close_callback close_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->close_cb = close_cb; + uv_close((uv_handle_t*)uv_socket->handle, uv_close_callback); +} + +static grpc_error* uv_socket_init_helper(uv_socket_t* uv_socket, int domain) { + uv_tcp_t* tcp = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); + uv_socket->handle = tcp; + int status = uv_tcp_init_ex(uv_default_loop(), tcp, (unsigned int)domain); + if (status != 0) { + return tcp_error_create("Failed to initialize UV tcp handle", status); + } +#if defined(GPR_LINUX) && defined(SO_REUSEPORT) + if (domain == AF_INET || domain == AF_INET6) { + int enable = 1; + int fd; + uv_fileno((uv_handle_t*)tcp, &fd); + // TODO Handle error here. + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)); + } +#endif + uv_socket->write_buffers = nullptr; + uv_socket->read_len = 0; + uv_tcp_nodelay(uv_socket->handle, 1); + // Node uses a garbage collector to call destructors, so we don't + // want to hold the uv loop open with active gRPC objects. + uv_unref((uv_handle_t*)uv_socket->handle); + uv_socket->pending_connection = false; + uv_socket->accept_socket = nullptr; + uv_socket->accept_error = GRPC_ERROR_NONE; + return GRPC_ERROR_NONE; +} + +static grpc_error* uv_socket_init(grpc_custom_socket* socket, int domain) { + uv_socket_t* uv_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t)); + grpc_error* error = uv_socket_init_helper(uv_socket, domain); + if (error != GRPC_ERROR_NONE) { + return error; + } + uv_socket->handle->data = socket; + socket->impl = uv_socket; + return GRPC_ERROR_NONE; +} + +static grpc_error* uv_socket_getpeername(grpc_custom_socket* socket, + const grpc_sockaddr* addr, + int* addr_len) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int err = uv_tcp_getpeername(uv_socket->handle, + (struct sockaddr*)IGNORE_CONST(addr), addr_len); + return tcp_error_create("getpeername failed", err); +} + +static grpc_error* uv_socket_getsockname(grpc_custom_socket* socket, + const grpc_sockaddr* addr, + int* addr_len) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int err = uv_tcp_getsockname(uv_socket->handle, + (struct sockaddr*)IGNORE_CONST(addr), addr_len); + return tcp_error_create("getsockname failed", err); +} + +static void accept_new_connection(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + if (!uv_socket->pending_connection || !uv_socket->accept_socket) { + return; + } + grpc_custom_socket* new_socket = uv_socket->accept_socket; + grpc_error* error = uv_socket->accept_error; + uv_socket->accept_socket = nullptr; + uv_socket->accept_error = GRPC_ERROR_NONE; + uv_socket->pending_connection = false; + if (uv_socket->accept_error != GRPC_ERROR_NONE) { + uv_stream_t dummy_handle; + uv_accept((uv_stream_t*)uv_socket->handle, &dummy_handle); + uv_socket->accept_cb(socket, new_socket, error); + } else { + uv_socket_t* uv_new_socket = (uv_socket_t*)gpr_malloc(sizeof(uv_socket_t)); + uv_socket_init_helper(uv_new_socket, AF_UNSPEC); + // UV documentation says this is guaranteed to succeed + GPR_ASSERT(uv_accept((uv_stream_t*)uv_socket->handle, + (uv_stream_t*)uv_new_socket->handle) == 0); + new_socket->impl = uv_new_socket; + uv_new_socket->handle->data = new_socket; + uv_socket->accept_cb(socket, new_socket, error); + } +} + +static void uv_on_connect(uv_stream_t* server, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)server->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + GPR_ASSERT(!uv_socket->pending_connection); + uv_socket->pending_connection = true; + if (status < 0) { + switch (status) { + case UV_EINTR: + case UV_EAGAIN: + return; + default: + uv_socket->accept_error = tcp_error_create("accept failed", status); + } + } + accept_new_connection(socket); +} + +void uv_socket_accept(grpc_custom_socket* socket, + grpc_custom_socket* new_socket, + grpc_custom_accept_callback accept_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->accept_cb = accept_cb; + GPR_ASSERT(uv_socket->accept_socket == nullptr); + uv_socket->accept_socket = new_socket; + accept_new_connection(socket); +} + +static grpc_error* uv_socket_bind(grpc_custom_socket* socket, + const grpc_sockaddr* addr, size_t len, + int flags) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status = + uv_tcp_bind((uv_tcp_t*)uv_socket->handle, (struct sockaddr*)addr, 0); + return tcp_error_create("Failed to bind to port", status); +} + +static grpc_error* uv_socket_listen(grpc_custom_socket* socket) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + int status = + uv_listen((uv_stream_t*)uv_socket->handle, SOMAXCONN, uv_on_connect); + return tcp_error_create("Failed to listen to port", status); +} + +static void uv_tc_on_connect(uv_connect_t* req, int status) { + grpc_custom_socket* socket = (grpc_custom_socket*)req->data; + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + grpc_error* error; + if (status == UV_ECANCELED) { + // This should only happen if the handle is already closed + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timeout occurred"); + } else { + error = tcp_error_create("Failed to connect to remote host", status); + } + uv_socket->connect_cb(socket, error); +} + +static void uv_socket_connect(grpc_custom_socket* socket, + const grpc_sockaddr* addr, size_t len, + grpc_custom_connect_callback connect_cb) { + uv_socket_t* uv_socket = (uv_socket_t*)socket->impl; + uv_socket->connect_cb = connect_cb; + uv_socket->connect_req.data = socket; + int status = uv_tcp_connect(&uv_socket->connect_req, uv_socket->handle, + (struct sockaddr*)addr, uv_tc_on_connect); + if (status != 0) { + // The callback will not be called + uv_socket->connect_cb(socket, tcp_error_create("connect failed", status)); + } +} + +static grpc_resolved_addresses* handle_addrinfo_result( + struct addrinfo* result) { + struct addrinfo* resp; + size_t i; + grpc_resolved_addresses* addresses = + (grpc_resolved_addresses*)gpr_malloc(sizeof(grpc_resolved_addresses)); + addresses->naddrs = 0; + for (resp = result; resp != nullptr; resp = resp->ai_next) { + addresses->naddrs++; + } + addresses->addrs = (grpc_resolved_address*)gpr_malloc( + sizeof(grpc_resolved_address) * addresses->naddrs); + for (resp = result, i = 0; resp != nullptr; resp = resp->ai_next, i++) { + memcpy(&addresses->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + addresses->addrs[i].len = resp->ai_addrlen; + } + // addrinfo objects are allocated by libuv (e.g. in uv_getaddrinfo) + // and not by gpr_malloc + uv_freeaddrinfo(result); + return addresses; +} + +static void uv_resolve_callback(uv_getaddrinfo_t* req, int status, + struct addrinfo* res) { + grpc_custom_resolver* r = (grpc_custom_resolver*)req->data; + gpr_free(req); + grpc_resolved_addresses* result = nullptr; + if (status == 0) { + result = handle_addrinfo_result(res); + } + grpc_custom_resolve_callback(r, result, + tcp_error_create("getaddrinfo failed", status)); +} + +static grpc_error* uv_resolve(char* host, char* port, + grpc_resolved_addresses** result) { + int status; + uv_getaddrinfo_t req; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + status = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); + if (status != 0) { + *result = nullptr; + } else { + *result = handle_addrinfo_result(req.addrinfo); + } + return tcp_error_create("getaddrinfo failed", status); +} + +static void uv_resolve_async(grpc_custom_resolver* r, char* host, char* port) { + int status; + uv_getaddrinfo_t* req = + (uv_getaddrinfo_t*)gpr_malloc(sizeof(uv_getaddrinfo_t)); + req->data = r; + struct addrinfo hints; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = GRPC_AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = GRPC_SOCK_STREAM; /* stream socket */ + hints.ai_flags = GRPC_AI_PASSIVE; /* for wildcard IP address */ + status = uv_getaddrinfo(uv_default_loop(), req, uv_resolve_callback, host, + port, &hints); + if (status != 0) { + gpr_free(req); + grpc_error* error = tcp_error_create("getaddrinfo failed", status); + grpc_custom_resolve_callback(r, NULL, error); + } +} + +grpc_custom_resolver_vtable uv_resolver_vtable = {uv_resolve, uv_resolve_async}; + +grpc_socket_vtable grpc_uv_socket_vtable = { + uv_socket_init, uv_socket_connect, uv_socket_destroy, + uv_socket_shutdown, uv_socket_close, uv_socket_write, + uv_socket_read, uv_socket_getpeername, uv_socket_getsockname, + uv_socket_bind, uv_socket_listen, uv_socket_accept}; + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_windows.cc b/third_party/grpc/src/core/lib/iomgr/tcp_windows.cc new file mode 100644 index 00000000000000..86ee1010cf7638 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_windows.cc @@ -0,0 +1,536 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include + +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/sockaddr_windows.h" + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_windows.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +#if defined(__MSYS__) && defined(GPR_ARCH_64) +/* Nasty workaround for nasty bug when using the 64 bits msys compiler + in conjunction with Microsoft Windows headers. */ +#define GRPC_FIONBIO _IOW('f', 126, uint32_t) +#else +#define GRPC_FIONBIO FIONBIO +#endif + +extern grpc_core::TraceFlag grpc_tcp_trace; + +grpc_error* grpc_tcp_set_non_block(SOCKET sock) { + int status; + uint32_t param = 1; + DWORD ret; + status = WSAIoctl(sock, GRPC_FIONBIO, ¶m, sizeof(param), NULL, 0, &ret, + NULL, NULL); + return status == 0 + ? GRPC_ERROR_NONE + : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)"); +} + +static grpc_error* 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 + ? GRPC_ERROR_NONE + : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)"); +} + +static grpc_error* enable_loopback_fast_path(SOCKET sock) { + int status; + uint32_t param = 1; + DWORD ret; + status = WSAIoctl(sock, /*SIO_LOOPBACK_FAST_PATH==*/_WSAIOW(IOC_VENDOR, 16), + ¶m, sizeof(param), NULL, 0, &ret, 0, 0); + if (status == SOCKET_ERROR) { + status = WSAGetLastError(); + } + return status == 0 || status == WSAEOPNOTSUPP + ? GRPC_ERROR_NONE + : GRPC_WSA_ERROR(status, "WSAIoctl(SIO_LOOPBACK_FAST_PATH)"); +} + +grpc_error* grpc_tcp_prepare_socket(SOCKET sock) { + grpc_error* err; + err = grpc_tcp_set_non_block(sock); + if (err != GRPC_ERROR_NONE) return err; + err = set_dualstack(sock); + if (err != GRPC_ERROR_NONE) return err; + err = enable_loopback_fast_path(sock); + if (err != GRPC_ERROR_NONE) return err; + return GRPC_ERROR_NONE; +} + +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_closure on_read; + grpc_closure on_write; + + grpc_closure* read_cb; + grpc_closure* write_cb; + + /* garbage after the last read */ + grpc_slice_buffer last_read_buffer; + + grpc_slice_buffer* write_slices; + grpc_slice_buffer* read_slices; + + grpc_resource_user* resource_user; + + /* 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; + grpc_error* shutdown_error; + + 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); + grpc_slice_buffer_destroy_internal(&tcp->last_read_buffer); + grpc_resource_user_unref(tcp->resource_user); + if (tcp->shutting_down) GRPC_ERROR_UNREF(tcp->shutdown_error); + gpr_free(tcp); +} + +#ifndef NDEBUG +#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) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(tcp); + } +} + +static void tcp_ref(grpc_tcp* tcp, const char* reason, const char* file, + int line) { + if (grpc_tcp_trace.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val + 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 void on_read(void* tcpp, grpc_error* error) { + grpc_tcp* tcp = (grpc_tcp*)tcpp; + grpc_closure* cb = tcp->read_cb; + grpc_winsocket* socket = tcp->socket; + grpc_winsocket_callback_info* info = &socket->read_info; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p on_read", tcp); + } + + GRPC_ERROR_REF(error); + + if (error == GRPC_ERROR_NONE) { + if (info->wsa_error != 0 && !tcp->shutting_down) { + char* utf8_message = gpr_format_message(info->wsa_error); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(utf8_message); + gpr_free(utf8_message); + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + } else { + if (info->bytes_transfered != 0 && !tcp->shutting_down) { + GPR_ASSERT((size_t)info->bytes_transfered <= tcp->read_slices->length); + if (static_cast(info->bytes_transfered) != + tcp->read_slices->length) { + grpc_slice_buffer_trim_end( + tcp->read_slices, + tcp->read_slices->length - + static_cast(info->bytes_transfered), + &tcp->last_read_buffer); + } + GPR_ASSERT((size_t)info->bytes_transfered == tcp->read_slices->length); + + if (grpc_tcp_trace.enabled()) { + size_t i; + for (i = 0; i < tcp->read_slices->count; i++) { + char* dump = grpc_dump_slice(tcp->read_slices->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "READ %p (peer=%s): %s", tcp, tcp->peer_string, + dump); + gpr_free(dump); + } + } + } else { + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p unref read_slice", tcp); + } + grpc_slice_buffer_reset_and_unref_internal(tcp->read_slices); + error = tcp->shutting_down + ? GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "TCP stream shutting down", &tcp->shutdown_error, 1) + : GRPC_ERROR_CREATE_FROM_STATIC_STRING("End of TCP stream"); + } + } + } + + tcp->read_cb = NULL; + TCP_UNREF(tcp, "read"); + GRPC_CLOSURE_SCHED(cb, error); +} + +#define DEFAULT_TARGET_READ_SIZE 8192 +#define MAX_WSABUF_COUNT 16 +static void win_read(grpc_endpoint* ep, grpc_slice_buffer* read_slices, + grpc_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 buffers[MAX_WSABUF_COUNT]; + size_t i; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p win_read", tcp); + } + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED( + cb, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "TCP socket is shutting down", &tcp->shutdown_error, 1)); + return; + } + + tcp->read_cb = cb; + tcp->read_slices = read_slices; + grpc_slice_buffer_reset_and_unref_internal(read_slices); + grpc_slice_buffer_swap(read_slices, &tcp->last_read_buffer); + + if (tcp->read_slices->length < DEFAULT_TARGET_READ_SIZE / 2 && + tcp->read_slices->count < MAX_WSABUF_COUNT) { + // TODO(jtattermusch): slice should be allocated using resource quota + grpc_slice_buffer_add(tcp->read_slices, + GRPC_SLICE_MALLOC(DEFAULT_TARGET_READ_SIZE)); + } + + GPR_ASSERT(tcp->read_slices->count <= MAX_WSABUF_COUNT); + for (i = 0; i < tcp->read_slices->count; i++) { + buffers[i].len = (ULONG)GRPC_SLICE_LENGTH( + tcp->read_slices->slices[i]); // we know slice size fits in 32bit. + buffers[i].buf = (char*)GRPC_SLICE_START_PTR(tcp->read_slices->slices[i]); + } + + TCP_REF(tcp, "read"); + + /* First let's try a synchronous, non-blocking read. */ + status = WSARecv(tcp->socket->socket, buffers, (DWORD)tcp->read_slices->count, + &bytes_read, &flags, NULL, NULL); + info->wsa_error = status == 0 ? 0 : WSAGetLastError(); + + /* Did we get data immediately ? Yay. */ + if (info->wsa_error != WSAEWOULDBLOCK) { + info->bytes_transfered = bytes_read; + GRPC_CLOSURE_SCHED(&tcp->on_read, GRPC_ERROR_NONE); + return; + } + + /* Otherwise, let's retry, by queuing a read. */ + memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); + status = WSARecv(tcp->socket->socket, buffers, (DWORD)tcp->read_slices->count, + &bytes_read, &flags, &info->overlapped, NULL); + + if (status != 0) { + int wsa_error = WSAGetLastError(); + if (wsa_error != WSA_IO_PENDING) { + info->wsa_error = wsa_error; + GRPC_CLOSURE_SCHED(&tcp->on_read, + GRPC_WSA_ERROR(info->wsa_error, "WSARecv")); + return; + } + } + + grpc_socket_notify_on_read(tcp->socket, &tcp->on_read); +} + +/* Asynchronous callback from the IOCP, or the background thread. */ +static void on_write(void* tcpp, grpc_error* error) { + grpc_tcp* tcp = (grpc_tcp*)tcpp; + grpc_winsocket* handle = tcp->socket; + grpc_winsocket_callback_info* info = &handle->write_info; + grpc_closure* cb; + + if (grpc_tcp_trace.enabled()) { + gpr_log(GPR_INFO, "TCP:%p on_write", tcp); + } + + GRPC_ERROR_REF(error); + + gpr_mu_lock(&tcp->mu); + cb = tcp->write_cb; + tcp->write_cb = NULL; + gpr_mu_unlock(&tcp->mu); + + if (error == GRPC_ERROR_NONE) { + if (info->wsa_error != 0) { + error = GRPC_WSA_ERROR(info->wsa_error, "WSASend"); + } else { + GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length); + } + } + + TCP_UNREF(tcp, "write"); + GRPC_CLOSURE_SCHED(cb, error); +} + +/* Initiates a write. */ +static void win_write(grpc_endpoint* ep, grpc_slice_buffer* slices, + grpc_closure* cb, void* arg) { + 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[MAX_WSABUF_COUNT]; + WSABUF* allocated = NULL; + WSABUF* buffers = local_buffers; + size_t len; + + if (grpc_tcp_trace.enabled()) { + size_t i; + for (i = 0; i < slices->count; i++) { + char* data = + grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); + gpr_free(data); + } + } + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED( + cb, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "TCP socket is shutting down", &tcp->shutdown_error, 1)); + return; + } + + tcp->write_cb = cb; + tcp->write_slices = slices; + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); + 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++) { + len = GRPC_SLICE_LENGTH(tcp->write_slices->slices[i]); + GPR_ASSERT(len <= ULONG_MAX); + buffers[i].len = (ULONG)len; + buffers[i].buf = (char*)GRPC_SLICE_START_PTR(tcp->write_slices->slices[i]); + } + + /* First, let's try a synchronous, non-blocking write. */ + status = WSASend(socket->socket, buffers, (DWORD)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_error* error = status == 0 + ? GRPC_ERROR_NONE + : GRPC_WSA_ERROR(info->wsa_error, "WSASend"); + GRPC_CLOSURE_SCHED(cb, error); + if (allocated) gpr_free(allocated); + return; + } + + 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, (DWORD)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"); + GRPC_CLOSURE_SCHED(cb, GRPC_WSA_ERROR(wsa_error, "WSASend")); + return; + } + } + + /* 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, &tcp->on_write); +} + +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); +} + +static void win_delete_from_pollset_set(grpc_endpoint* ep, + grpc_pollset_set* pss) {} + +/* 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_error* why) { + 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. */ + if (!tcp->shutting_down) { + tcp->shutting_down = 1; + tcp->shutdown_error = why; + } else { + GRPC_ERROR_UNREF(why); + } + grpc_winsocket_shutdown(tcp->socket); + gpr_mu_unlock(&tcp->mu); + grpc_resource_user_shutdown(tcp->resource_user); +} + +static void win_destroy(grpc_endpoint* ep) { + grpc_network_status_unregister_endpoint(ep); + grpc_tcp* tcp = (grpc_tcp*)ep; + grpc_slice_buffer_reset_and_unref_internal(&tcp->last_read_buffer); + 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_resource_user* win_get_resource_user(grpc_endpoint* ep) { + grpc_tcp* tcp = (grpc_tcp*)ep; + return tcp->resource_user; +} + +static int win_get_fd(grpc_endpoint* ep) { return -1; } + +static bool win_can_track_err(grpc_endpoint* ep) { return false; } + +static grpc_endpoint_vtable vtable = {win_read, + win_write, + win_add_to_pollset, + win_add_to_pollset_set, + win_delete_from_pollset_set, + win_shutdown, + win_destroy, + win_get_resource_user, + win_get_peer, + win_get_fd, + win_can_track_err}; + +grpc_endpoint* grpc_tcp_create(grpc_winsocket* socket, + grpc_channel_args* channel_args, + const char* peer_string) { + grpc_resource_quota* resource_quota = grpc_resource_quota_create(NULL); + if (channel_args != NULL) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota*)channel_args->args[i].value.pointer.p); + } + } + } + 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); + GRPC_CLOSURE_INIT(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx); + tcp->peer_string = gpr_strdup(peer_string); + grpc_slice_buffer_init(&tcp->last_read_buffer); + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + /* Tell network status tracking code about the new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + grpc_resource_quota_unref_internal(resource_quota); + + return &tcp->base; +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/third_party/grpc/src/core/lib/iomgr/tcp_windows.h b/third_party/grpc/src/core/lib/iomgr/tcp_windows.h new file mode 100644 index 00000000000000..04ef8102b65ce4 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/tcp_windows.h @@ -0,0 +1,53 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H +#define GRPC_CORE_LIB_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 + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/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, + grpc_channel_args* channel_args, + const char* peer_string); + +grpc_error* grpc_tcp_prepare_socket(SOCKET sock); + +grpc_error* grpc_tcp_set_non_block(SOCKET sock); + +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H */ diff --git a/third_party/grpc/src/core/iomgr/time_averaged_stats.c b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.cc similarity index 51% rename from third_party/grpc/src/core/iomgr/time_averaged_stats.c rename to third_party/grpc/src/core/lib/iomgr/time_averaged_stats.cc index e075db4373515f..6369e48db0e572 100644 --- a/third_party/grpc/src/core/iomgr/time_averaged_stats.c +++ b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.cc @@ -1,37 +1,24 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include "src/core/iomgr/time_averaged_stats.h" +#include + +#include "src/core/lib/iomgr/time_averaged_stats.h" void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats, double init_avg, double regress_weight, diff --git a/third_party/grpc/src/core/iomgr/time_averaged_stats.h b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.h similarity index 61% rename from third_party/grpc/src/core/iomgr/time_averaged_stats.h rename to third_party/grpc/src/core/lib/iomgr/time_averaged_stats.h index 4e9e3956c28cda..8745f7fa13f9bb 100644 --- a/third_party/grpc/src/core/iomgr/time_averaged_stats.h +++ b/third_party/grpc/src/core/lib/iomgr/time_averaged_stats.h @@ -1,38 +1,23 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_IOMGR_TIME_AVERAGED_STATS_H -#define GRPC_INTERNAL_CORE_IOMGR_TIME_AVERAGED_STATS_H +#ifndef GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H +#define GRPC_CORE_LIB_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 @@ -85,4 +70,4 @@ void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats, value. */ double grpc_time_averaged_stats_update_average(grpc_time_averaged_stats* stats); -#endif /* GRPC_INTERNAL_CORE_IOMGR_TIME_AVERAGED_STATS_H */ +#endif /* GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/timer.cc b/third_party/grpc/src/core/lib/iomgr/timer.cc new file mode 100644 index 00000000000000..e647cdefa04c8e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +grpc_timer_vtable* grpc_timer_impl; + +void grpc_set_timer_impl(grpc_timer_vtable* vtable) { + grpc_timer_impl = vtable; +} + +void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { + grpc_timer_impl->init(timer, deadline, closure); +} + +void grpc_timer_cancel(grpc_timer* timer) { grpc_timer_impl->cancel(timer); } + +grpc_timer_check_result grpc_timer_check(grpc_millis* next) { + return grpc_timer_impl->check(next); +} + +void grpc_timer_list_init() { grpc_timer_impl->list_init(); } + +void grpc_timer_list_shutdown() { grpc_timer_impl->list_shutdown(); } + +void grpc_timer_consume_kick() { grpc_timer_impl->consume_kick(); } diff --git a/third_party/grpc/src/core/lib/iomgr/timer.h b/third_party/grpc/src/core/lib/iomgr/timer.h new file mode 100644 index 00000000000000..17e933b865895c --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer.h @@ -0,0 +1,126 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TIMER_H +#define GRPC_CORE_LIB_IOMGR_TIMER_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#include +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/iomgr.h" + +typedef struct grpc_timer { + grpc_millis deadline; + uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ + bool pending; + struct grpc_timer* next; + struct grpc_timer* prev; + grpc_closure* closure; +#ifndef NDEBUG + struct grpc_timer* hash_table_next; +#endif + + // Optional field used by custom timers + void* custom_timer; +} grpc_timer; + +typedef enum { + GRPC_TIMERS_NOT_CHECKED, + GRPC_TIMERS_CHECKED_AND_EMPTY, + GRPC_TIMERS_FIRED, +} grpc_timer_check_result; + +typedef struct grpc_timer_vtable { + void (*init)(grpc_timer* timer, grpc_millis, grpc_closure* closure); + void (*cancel)(grpc_timer* timer); + + /* Internal API */ + grpc_timer_check_result (*check)(grpc_millis* next); + void (*list_init)(); + void (*list_shutdown)(void); + void (*consume_kick)(void); +} grpc_timer_vtable; + +/* Initialize *timer. When expired or canceled, closure will be called with + error set to indicate if it expired (GRPC_ERROR_NONE) or was canceled + (GRPC_ERROR_CANCELLED). *closure is guaranteed to be called exactly once, and + application code should check the error to determine how it was invoked. The + application callback is also responsible for maintaining information about + when to free up any user-level state. Behavior is undefined for a deadline of + GRPC_MILLIS_INF_FUTURE. */ +void grpc_timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure); + +/* Initialize *timer without setting it. This can later be passed through + the regular init or cancel */ +void grpc_timer_init_unset(grpc_timer* timer); + +/* Note that there is no timer destroy function. This is because the + timer 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 timer event management state is destroyed just before + that callback is invoked. If the user has additional state associated with + the timer, the user is responsible for determining when it is safe to + destroy that state. */ + +/* Cancel an *timer. + There are three cases: + 1. We normally cancel the timer + 2. The timer has already run + 3. We can't cancel the timer because it is "in flight". + + In all of these cases, the cancellation is still considered successful. + They are essentially distinguished in that the timer_cb will be run + exactly once from either the cancellation (with error GRPC_ERROR_CANCELLED) + or from the activation (with error GRPC_ERROR_NONE). + + Note carefully that the callback function MAY occur in the same callstack + as grpc_timer_cancel. It's expected that most timers 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 init() on a given timer */ +void grpc_timer_cancel(grpc_timer* timer); + +/* iomgr internal api for dealing with timers */ + +/* Check for timers to be run, and run them. + Return true if timer callbacks were executed. + If next is non-null, TRY to update *next with the next running timer + IF that timer 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. */ +grpc_timer_check_result grpc_timer_check(grpc_millis* next); +void grpc_timer_list_init(); +void grpc_timer_list_shutdown(); + +/* Consume a kick issued by grpc_kick_poller */ +void grpc_timer_consume_kick(void); + +/* the following must be implemented by each iomgr implementation */ +void grpc_kick_poller(void); + +/* Sets the timer implementation */ +void grpc_set_timer_impl(grpc_timer_vtable* vtable); + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/timer_custom.cc b/third_party/grpc/src/core/lib/iomgr/timer_custom.cc new file mode 100644 index 00000000000000..71d825ff9f5548 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_custom.cc @@ -0,0 +1,93 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_custom.h" + +static grpc_custom_timer_vtable* custom_timer_impl; + +void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_core::ExecCtx exec_ctx; + grpc_timer* timer = t->original; + GPR_ASSERT(timer->pending); + timer->pending = 0; + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); + custom_timer_impl->stop(t); + gpr_free(t); +} + +static void timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { + uint64_t timeout; + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + if (deadline <= grpc_core::ExecCtx::Get()->Now()) { + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + timer->pending = false; + return; + } else { + timeout = deadline - now; + } + timer->pending = true; + timer->closure = closure; + grpc_custom_timer* timer_wrapper = + (grpc_custom_timer*)gpr_malloc(sizeof(grpc_custom_timer)); + timer_wrapper->timeout_ms = timeout; + timer->custom_timer = (void*)timer_wrapper; + timer_wrapper->original = timer; + custom_timer_impl->start(timer_wrapper); +} + +static void timer_cancel(grpc_timer* timer) { + GRPC_CUSTOM_IOMGR_ASSERT_SAME_THREAD(); + grpc_custom_timer* tw = (grpc_custom_timer*)timer->custom_timer; + if (timer->pending) { + timer->pending = 0; + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED); + custom_timer_impl->stop(tw); + gpr_free(tw); + } +} + +static grpc_timer_check_result timer_check(grpc_millis* next) { + return GRPC_TIMERS_NOT_CHECKED; +} + +static void timer_list_init() {} +static void timer_list_shutdown() {} + +static void timer_consume_kick(void) {} + +static grpc_timer_vtable custom_timer_vtable = { + timer_init, timer_cancel, timer_check, + timer_list_init, timer_list_shutdown, timer_consume_kick}; + +void grpc_custom_timer_init(grpc_custom_timer_vtable* impl) { + custom_timer_impl = impl; + grpc_set_timer_impl(&custom_timer_vtable); +} diff --git a/third_party/grpc/src/core/lib/iomgr/timer_custom.h b/third_party/grpc/src/core/lib/iomgr/timer_custom.h new file mode 100644 index 00000000000000..bfea8bafa69f85 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_custom.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H +#define GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H + +#include + +#include "src/core/lib/iomgr/timer.h" + +typedef struct grpc_custom_timer { + // Implementation defined + void* timer; + uint64_t timeout_ms; + + grpc_timer* original; +} grpc_custom_timer; + +typedef struct grpc_custom_timer_vtable { + void (*start)(grpc_custom_timer* t); + void (*stop)(grpc_custom_timer* t); +} grpc_custom_timer_vtable; + +void grpc_custom_timer_init(grpc_custom_timer_vtable* impl); + +void grpc_custom_timer_callback(grpc_custom_timer* t, grpc_error* error); + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_CUSTOM_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/timer_generic.cc b/third_party/grpc/src/core/lib/iomgr/timer_generic.cc new file mode 100644 index 00000000000000..6a925add80c3ef --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_generic.cc @@ -0,0 +1,746 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +#include "src/core/lib/iomgr/timer.h" + +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/spinlock.h" +#include "src/core/lib/gpr/tls.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/time_averaged_stats.h" +#include "src/core/lib/iomgr/timer_heap.h" + +#define INVALID_HEAP_INDEX 0xffffffffu + +#define ADD_DEADLINE_SCALE 0.33 +#define MIN_QUEUE_WINDOW_DURATION 0.01 +#define MAX_QUEUE_WINDOW_DURATION 1 + +grpc_core::TraceFlag grpc_timer_trace(false, "timer"); +grpc_core::TraceFlag grpc_timer_check_trace(false, "timer_check"); + +/* A "timer shard". Contains a 'heap' and a 'list' of timers. All timers with + * deadlines earlier than 'queue_deadline_cap' are maintained in the heap and + * others are maintained in the list (unordered). This helps to keep the number + * of elements in the heap low. + * + * The 'queue_deadline_cap' gets recomputed periodically based on the timer + * stats maintained in 'stats' and the relevant timers are then moved from the + * 'list' to 'heap'. + */ +typedef struct { + gpr_mu mu; + grpc_time_averaged_stats stats; + /* All and only timers with deadlines < this will be in the heap. */ + grpc_millis queue_deadline_cap; + /* The deadline of the next timer due in this shard. */ + grpc_millis min_deadline; + /* Index of this timer_shard in the g_shard_queue. */ + uint32_t shard_queue_index; + /* This holds all timers with deadlines < queue_deadline_cap. Timers in this + list have the top bit of their deadline set to 0. */ + grpc_timer_heap heap; + /* This holds timers whose deadline is >= queue_deadline_cap. */ + grpc_timer list; +} timer_shard; + +static size_t g_num_shards; + +/* Array of timer shards. Whenever a timer (grpc_timer *) is added, its address + * is hashed to select the timer shard to add the timer to */ +static timer_shard* g_shards; + +/* Maintains a sorted list of timer shards (sorted by their min_deadline, i.e + * the deadline of the next timer in each shard). + * Access to this is protected by g_shared_mutables.mu */ +static timer_shard** g_shard_queue; + +#ifndef NDEBUG + +/* == DEBUG ONLY: hash table for duplicate timer detection == */ + +#define NUM_HASH_BUCKETS 1009 /* Prime number close to 1000 */ + +static gpr_mu g_hash_mu[NUM_HASH_BUCKETS]; /* One mutex per bucket */ +static grpc_timer* g_timer_ht[NUM_HASH_BUCKETS] = {nullptr}; + +static void init_timer_ht() { + for (int i = 0; i < NUM_HASH_BUCKETS; i++) { + gpr_mu_init(&g_hash_mu[i]); + } +} + +static void destroy_timer_ht() { + for (int i = 0; i < NUM_HASH_BUCKETS; i++) { + gpr_mu_destroy(&g_hash_mu[i]); + } +} + +static bool is_in_ht(grpc_timer* t) { + size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); + + gpr_mu_lock(&g_hash_mu[i]); + grpc_timer* p = g_timer_ht[i]; + while (p != nullptr && p != t) { + p = p->hash_table_next; + } + gpr_mu_unlock(&g_hash_mu[i]); + + return (p == t); +} + +static void add_to_ht(grpc_timer* t) { + GPR_ASSERT(!t->hash_table_next); + size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); + + gpr_mu_lock(&g_hash_mu[i]); + grpc_timer* p = g_timer_ht[i]; + while (p != nullptr && p != t) { + p = p->hash_table_next; + } + + if (p == t) { + grpc_closure* c = t->closure; + gpr_log(GPR_ERROR, + "** Duplicate timer (%p) being added. Closure: (%p), created at: " + "(%s:%d), scheduled at: (%s:%d) **", + t, c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated); + abort(); + } + + /* Timer not present in the bucket. Insert at head of the list */ + t->hash_table_next = g_timer_ht[i]; + g_timer_ht[i] = t; + gpr_mu_unlock(&g_hash_mu[i]); +} + +static void remove_from_ht(grpc_timer* t) { + size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); + bool removed = false; + + gpr_mu_lock(&g_hash_mu[i]); + if (g_timer_ht[i] == t) { + g_timer_ht[i] = g_timer_ht[i]->hash_table_next; + removed = true; + } else if (g_timer_ht[i] != nullptr) { + grpc_timer* p = g_timer_ht[i]; + while (p->hash_table_next != nullptr && p->hash_table_next != t) { + p = p->hash_table_next; + } + + if (p->hash_table_next == t) { + p->hash_table_next = t->hash_table_next; + removed = true; + } + } + gpr_mu_unlock(&g_hash_mu[i]); + + if (!removed) { + grpc_closure* c = t->closure; + gpr_log(GPR_ERROR, + "** Removing timer (%p) that is not added to hash table. Closure " + "(%p), created at: (%s:%d), scheduled at: (%s:%d) **", + t, c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated); + abort(); + } + + t->hash_table_next = nullptr; +} + +/* If a timer is added to a timer shard (either heap or a list), it must + * be pending. A timer is added to hash table only-if it is added to the + * timer shard. + * Therefore, if timer->pending is false, it cannot be in hash table */ +static void validate_non_pending_timer(grpc_timer* t) { + if (!t->pending && is_in_ht(t)) { + grpc_closure* c = t->closure; + gpr_log(GPR_ERROR, + "** gpr_timer_cancel() called on a non-pending timer (%p) which " + "is in the hash table. Closure: (%p), created at: (%s:%d), " + "scheduled at: (%s:%d) **", + t, c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated); + abort(); + } +} + +#define INIT_TIMER_HASH_TABLE() init_timer_ht() +#define DESTROY_TIMER_HASH_TABLE() destroy_timer_ht() +#define ADD_TO_HASH_TABLE(t) add_to_ht((t)) +#define REMOVE_FROM_HASH_TABLE(t) remove_from_ht((t)) +#define VALIDATE_NON_PENDING_TIMER(t) validate_non_pending_timer((t)) + +#else + +#define INIT_TIMER_HASH_TABLE() +#define DESTROY_TIMER_HASH_TABLE() +#define ADD_TO_HASH_TABLE(t) +#define REMOVE_FROM_HASH_TABLE(t) +#define VALIDATE_NON_PENDING_TIMER(t) + +#endif + +#if GPR_ARCH_64 +/* NOTE: TODO(sreek) - Currently the thread local storage support in grpc is + for intptr_t which means on 32-bit machines it is not wide enough to hold + grpc_millis which is 64-bit. Adding thread local support for 64 bit values + is a lot of work for very little gain. So we are currently restricting this + optimization to only 64 bit machines */ + +/* Thread local variable that stores the deadline of the next timer the thread + * has last-seen. This is an optimization to prevent the thread from checking + * shared_mutables.min_timer (which requires acquiring shared_mutables.mu lock, + * an expensive operation) */ +GPR_TLS_DECL(g_last_seen_min_timer); +#endif + +struct shared_mutables { + /* The deadline of the next timer due across all timer shards */ + grpc_millis min_timer; + /* Allow only one run_some_expired_timers at once */ + gpr_spinlock checker_mu; + bool initialized; + /* Protects g_shard_queue (and the shared_mutables struct itself) */ + gpr_mu mu; +} GPR_ALIGN_STRUCT(GPR_CACHELINE_SIZE); + +static struct shared_mutables g_shared_mutables; + +static grpc_millis saturating_add(grpc_millis a, grpc_millis b) { + if (a > GRPC_MILLIS_INF_FUTURE - b) { + return GRPC_MILLIS_INF_FUTURE; + } + return a + b; +} + +static grpc_timer_check_result run_some_expired_timers(grpc_millis now, + grpc_millis* next, + grpc_error* error); + +static grpc_millis compute_min_deadline(timer_shard* shard) { + return grpc_timer_heap_is_empty(&shard->heap) + ? saturating_add(shard->queue_deadline_cap, 1) + : grpc_timer_heap_top(&shard->heap)->deadline; +} + +static void timer_list_init() { + uint32_t i; + + g_num_shards = GPR_CLAMP(2 * gpr_cpu_num_cores(), 1, 32); + g_shards = + static_cast(gpr_zalloc(g_num_shards * sizeof(*g_shards))); + g_shard_queue = static_cast( + gpr_zalloc(g_num_shards * sizeof(*g_shard_queue))); + + g_shared_mutables.initialized = true; + g_shared_mutables.checker_mu = GPR_SPINLOCK_INITIALIZER; + gpr_mu_init(&g_shared_mutables.mu); + g_shared_mutables.min_timer = grpc_core::ExecCtx::Get()->Now(); + +#if GPR_ARCH_64 + gpr_tls_init(&g_last_seen_min_timer); + gpr_tls_set(&g_last_seen_min_timer, 0); +#endif + + for (i = 0; i < g_num_shards; i++) { + timer_shard* 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 = g_shared_mutables.min_timer; + shard->shard_queue_index = i; + grpc_timer_heap_init(&shard->heap); + shard->list.next = shard->list.prev = &shard->list; + shard->min_deadline = compute_min_deadline(shard); + g_shard_queue[i] = shard; + } + + INIT_TIMER_HASH_TABLE(); +} + +static void timer_list_shutdown() { + size_t i; + run_some_expired_timers( + GRPC_MILLIS_INF_FUTURE, nullptr, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timer list shutdown")); + for (i = 0; i < g_num_shards; i++) { + timer_shard* shard = &g_shards[i]; + gpr_mu_destroy(&shard->mu); + grpc_timer_heap_destroy(&shard->heap); + } + gpr_mu_destroy(&g_shared_mutables.mu); + +#if GPR_ARCH_64 + gpr_tls_destroy(&g_last_seen_min_timer); +#endif + + gpr_free(g_shards); + gpr_free(g_shard_queue); + g_shared_mutables.initialized = false; + + DESTROY_TIMER_HASH_TABLE(); +} + +/* returns true if the first element in the list */ +static void list_join(grpc_timer* head, grpc_timer* timer) { + timer->next = head; + timer->prev = head->prev; + timer->next->prev = timer->prev->next = timer; +} + +static void list_remove(grpc_timer* timer) { + timer->next->prev = timer->prev; + timer->prev->next = timer->next; +} + +static void swap_adjacent_shards_in_queue(uint32_t first_shard_queue_index) { + timer_shard* 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(timer_shard* shard) { + while (shard->shard_queue_index > 0 && + shard->min_deadline < + g_shard_queue[shard->shard_queue_index - 1]->min_deadline) { + swap_adjacent_shards_in_queue(shard->shard_queue_index - 1); + } + while (shard->shard_queue_index < g_num_shards - 1 && + shard->min_deadline > + g_shard_queue[shard->shard_queue_index + 1]->min_deadline) { + swap_adjacent_shards_in_queue(shard->shard_queue_index); + } +} + +void grpc_timer_init_unset(grpc_timer* timer) { timer->pending = false; } + +static void timer_init(grpc_timer* timer, grpc_millis deadline, + grpc_closure* closure) { + int is_first_timer = 0; + timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)]; + timer->closure = closure; + timer->deadline = deadline; + +#ifndef NDEBUG + timer->hash_table_next = nullptr; +#endif + + if (grpc_timer_trace.enabled()) { + gpr_log(GPR_INFO, "TIMER %p: SET %" PRId64 " now %" PRId64 " call %p[%p]", + timer, deadline, grpc_core::ExecCtx::Get()->Now(), closure, + closure->cb); + } + + if (!g_shared_mutables.initialized) { + timer->pending = false; + GRPC_CLOSURE_SCHED(timer->closure, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Attempt to create timer before initialization")); + return; + } + + gpr_mu_lock(&shard->mu); + timer->pending = true; + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + if (deadline <= now) { + timer->pending = false; + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_NONE); + gpr_mu_unlock(&shard->mu); + /* early out */ + return; + } + + grpc_time_averaged_stats_add_sample( + &shard->stats, static_cast(deadline - now) / 1000.0); + + ADD_TO_HASH_TABLE(timer); + + if (deadline < shard->queue_deadline_cap) { + is_first_timer = grpc_timer_heap_add(&shard->heap, timer); + } else { + timer->heap_index = INVALID_HEAP_INDEX; + list_join(&shard->list, timer); + } + if (grpc_timer_trace.enabled()) { + gpr_log(GPR_INFO, + " .. add to shard %d with queue_deadline_cap=%" PRId64 + " => is_first_timer=%s", + static_cast(shard - g_shards), shard->queue_deadline_cap, + is_first_timer ? "true" : "false"); + } + 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_timer_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_timer_check, which might beat us to the lock. In + that case, it is possible that the timer 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_timer_check that intervened failed to + trigger the new timer because the min_deadline hadn't yet been reduced. + In that case, the timer will simply have to wait for the next + grpc_timer_check. */ + if (is_first_timer) { + gpr_mu_lock(&g_shared_mutables.mu); + if (grpc_timer_trace.enabled()) { + gpr_log(GPR_INFO, " .. old shard min_deadline=%" PRId64, + shard->min_deadline); + } + if (deadline < shard->min_deadline) { + grpc_millis old_min_deadline = g_shard_queue[0]->min_deadline; + shard->min_deadline = deadline; + note_deadline_change(shard); + if (shard->shard_queue_index == 0 && deadline < old_min_deadline) { +#if GPR_ARCH_64 + // TODO: sreek - Using c-style cast here. static_cast<> gives an error + // (on mac platforms complaining that gpr_atm* is (long *) while + // (&g_shared_mutables.min_timer) is a (long long *). The cast should be + // safe since we know that both are pointer types and 64-bit wide. + gpr_atm_no_barrier_store((gpr_atm*)(&g_shared_mutables.min_timer), + deadline); +#else + // On 32-bit systems, gpr_atm_no_barrier_store does not work on 64-bit + // types (like grpc_millis). So all reads and writes to + // g_shared_mutables.min_timer varialbe under g_shared_mutables.mu + g_shared_mutables.min_timer = deadline; +#endif + grpc_kick_poller(); + } + } + gpr_mu_unlock(&g_shared_mutables.mu); + } +} + +static void timer_consume_kick(void) { +#if GPR_ARCH_64 + /* Force re-evaluation of last seen min */ + gpr_tls_set(&g_last_seen_min_timer, 0); +#endif +} + +static void timer_cancel(grpc_timer* timer) { + if (!g_shared_mutables.initialized) { + /* must have already been cancelled, also the shard mutex is invalid */ + return; + } + + timer_shard* shard = &g_shards[GPR_HASH_POINTER(timer, g_num_shards)]; + gpr_mu_lock(&shard->mu); + if (grpc_timer_trace.enabled()) { + gpr_log(GPR_INFO, "TIMER %p: CANCEL pending=%s", timer, + timer->pending ? "true" : "false"); + } + + if (timer->pending) { + REMOVE_FROM_HASH_TABLE(timer); + + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_CANCELLED); + timer->pending = false; + if (timer->heap_index == INVALID_HEAP_INDEX) { + list_remove(timer); + } else { + grpc_timer_heap_remove(&shard->heap, timer); + } + } else { + VALIDATE_NON_PENDING_TIMER(timer); + } + gpr_mu_unlock(&shard->mu); +} + +/* Rebalances the timer shard by computing a new 'queue_deadline_cap' and moving + all relevant timers in shard->list (i.e timers with deadlines earlier than + 'queue_deadline_cap') into into shard->heap. + Returns 'true' if shard->heap has atleast ONE element + REQUIRES: shard->mu locked */ +static bool refill_heap(timer_shard* shard, grpc_millis 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_timer *timer, *next; + + /* Compute the new cap and put all timers under it into the queue: */ + shard->queue_deadline_cap = + saturating_add(GPR_MAX(now, shard->queue_deadline_cap), + static_cast(deadline_delta * 1000.0)); + + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, " .. shard[%d]->queue_deadline_cap --> %" PRId64, + static_cast(shard - g_shards), shard->queue_deadline_cap); + } + for (timer = shard->list.next; timer != &shard->list; timer = next) { + next = timer->next; + + if (timer->deadline < shard->queue_deadline_cap) { + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, " .. add timer with deadline %" PRId64 " to heap", + timer->deadline); + } + list_remove(timer); + grpc_timer_heap_add(&shard->heap, timer); + } + } + return !grpc_timer_heap_is_empty(&shard->heap); +} + +/* This pops the next non-cancelled timer with deadline <= now from the + queue, or returns NULL if there isn't one. + REQUIRES: shard->mu locked */ +static grpc_timer* pop_one(timer_shard* shard, grpc_millis now) { + grpc_timer* timer; + for (;;) { + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, " .. shard[%d]: heap_empty=%s", + static_cast(shard - g_shards), + grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false"); + } + if (grpc_timer_heap_is_empty(&shard->heap)) { + if (now < shard->queue_deadline_cap) return nullptr; + if (!refill_heap(shard, now)) return nullptr; + } + timer = grpc_timer_heap_top(&shard->heap); + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, + " .. check top timer deadline=%" PRId64 " now=%" PRId64, + timer->deadline, now); + } + if (timer->deadline > now) return nullptr; + if (grpc_timer_trace.enabled()) { + gpr_log(GPR_INFO, "TIMER %p: FIRE %" PRId64 "ms late via %s scheduler", + timer, now - timer->deadline, + timer->closure->scheduler->vtable->name); + } + timer->pending = false; + grpc_timer_heap_pop(&shard->heap); + return timer; + } +} + +/* REQUIRES: shard->mu unlocked */ +static size_t pop_timers(timer_shard* shard, grpc_millis now, + grpc_millis* new_min_deadline, grpc_error* error) { + size_t n = 0; + grpc_timer* timer; + gpr_mu_lock(&shard->mu); + while ((timer = pop_one(shard, now))) { + REMOVE_FROM_HASH_TABLE(timer); + GRPC_CLOSURE_SCHED(timer->closure, GRPC_ERROR_REF(error)); + n++; + } + *new_min_deadline = compute_min_deadline(shard); + gpr_mu_unlock(&shard->mu); + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, " .. shard[%d] popped %" PRIdPTR, + static_cast(shard - g_shards), n); + } + return n; +} + +static grpc_timer_check_result run_some_expired_timers(grpc_millis now, + grpc_millis* next, + grpc_error* error) { + grpc_timer_check_result result = GRPC_TIMERS_NOT_CHECKED; + +#if GPR_ARCH_64 + // TODO: sreek - Using c-style cast here. static_cast<> gives an error (on + // mac platforms complaining that gpr_atm* is (long *) while + // (&g_shared_mutables.min_timer) is a (long long *). The cast should be + // safe since we know that both are pointer types and 64-bit wide + grpc_millis min_timer = static_cast( + gpr_atm_no_barrier_load((gpr_atm*)(&g_shared_mutables.min_timer))); + gpr_tls_set(&g_last_seen_min_timer, min_timer); +#else + // On 32-bit systems, gpr_atm_no_barrier_load does not work on 64-bit types + // (like grpc_millis). So all reads and writes to g_shared_mutables.min_timer + // are done under g_shared_mutables.mu + gpr_mu_lock(&g_shared_mutables.mu); + grpc_millis min_timer = g_shared_mutables.min_timer; + gpr_mu_unlock(&g_shared_mutables.mu); +#endif + if (now < min_timer) { + if (next != nullptr) *next = GPR_MIN(*next, min_timer); + return GRPC_TIMERS_CHECKED_AND_EMPTY; + } + + if (gpr_spinlock_trylock(&g_shared_mutables.checker_mu)) { + gpr_mu_lock(&g_shared_mutables.mu); + result = GRPC_TIMERS_CHECKED_AND_EMPTY; + + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, " .. shard[%d]->min_deadline = %" PRId64, + static_cast(g_shard_queue[0] - g_shards), + g_shard_queue[0]->min_deadline); + } + + while (g_shard_queue[0]->min_deadline < now || + (now != GRPC_MILLIS_INF_FUTURE && + g_shard_queue[0]->min_deadline == now)) { + grpc_millis new_min_deadline; + + /* For efficiency, we pop as many available timers as we can from the + shard. This may violate perfect timer deadline ordering, but that + shouldn't be a big deal because we don't make ordering guarantees. */ + if (pop_timers(g_shard_queue[0], now, &new_min_deadline, error) > 0) { + result = GRPC_TIMERS_FIRED; + } + + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, + " .. result --> %d" + ", shard[%d]->min_deadline %" PRId64 " --> %" PRId64 + ", now=%" PRId64, + result, static_cast(g_shard_queue[0] - g_shards), + g_shard_queue[0]->min_deadline, new_min_deadline, now); + } + + /* An grpc_timer_init() on the shard could intervene here, adding a new + timer that is earlier than new_min_deadline. However, + grpc_timer_init() will block on the master_lock before it can call + set_min_deadline, so this one will complete first and then the Addtimer + 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_MIN(*next, g_shard_queue[0]->min_deadline); + } + +#if GPR_ARCH_64 + // TODO: sreek - Using c-style cast here. static_cast<> gives an error (on + // mac platforms complaining that gpr_atm* is (long *) while + // (&g_shared_mutables.min_timer) is a (long long *). The cast should be + // safe since we know that both are pointer types and 64-bit wide + gpr_atm_no_barrier_store((gpr_atm*)(&g_shared_mutables.min_timer), + g_shard_queue[0]->min_deadline); +#else + // On 32-bit systems, gpr_atm_no_barrier_store does not work on 64-bit + // types (like grpc_millis). So all reads and writes to + // g_shared_mutables.min_timer are done under g_shared_mutables.mu + g_shared_mutables.min_timer = g_shard_queue[0]->min_deadline; +#endif + gpr_mu_unlock(&g_shared_mutables.mu); + gpr_spinlock_unlock(&g_shared_mutables.checker_mu); + } + + GRPC_ERROR_UNREF(error); + + return result; +} + +static grpc_timer_check_result timer_check(grpc_millis* next) { + // prelude + grpc_millis now = grpc_core::ExecCtx::Get()->Now(); + +#if GPR_ARCH_64 + /* fetch from a thread-local first: this avoids contention on a globally + mutable cacheline in the common case */ + grpc_millis min_timer = gpr_tls_get(&g_last_seen_min_timer); +#else + // On 32-bit systems, we currently do not have thread local support for 64-bit + // types. In this case, directly read from g_shared_mutables.min_timer. + // Also, note that on 32-bit systems, gpr_atm_no_barrier_store does not work + // on 64-bit types (like grpc_millis). So all reads and writes to + // g_shared_mutables.min_timer are done under g_shared_mutables.mu + gpr_mu_lock(&g_shared_mutables.mu); + grpc_millis min_timer = g_shared_mutables.min_timer; + gpr_mu_unlock(&g_shared_mutables.mu); +#endif + + if (now < min_timer) { + if (next != nullptr) { + *next = GPR_MIN(*next, min_timer); + } + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "TIMER CHECK SKIP: now=%" PRId64 " min_timer=%" PRId64, + now, min_timer); + } + return GRPC_TIMERS_CHECKED_AND_EMPTY; + } + + grpc_error* shutdown_error = + now != GRPC_MILLIS_INF_FUTURE + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system"); + + // tracing + if (grpc_timer_check_trace.enabled()) { + char* next_str; + if (next == nullptr) { + next_str = gpr_strdup("NULL"); + } else { + gpr_asprintf(&next_str, "%" PRId64, *next); + } +#if GPR_ARCH_64 + gpr_log(GPR_INFO, + "TIMER CHECK BEGIN: now=%" PRId64 " next=%s tls_min=%" PRId64 + " glob_min=%" PRId64, + now, next_str, min_timer, + static_cast(gpr_atm_no_barrier_load( + (gpr_atm*)(&g_shared_mutables.min_timer)))); +#else + gpr_log(GPR_INFO, "TIMER CHECK BEGIN: now=%" PRId64 " next=%s min=%" PRId64, + now, next_str, min_timer); +#endif + gpr_free(next_str); + } + // actual code + grpc_timer_check_result r = + run_some_expired_timers(now, next, shutdown_error); + // tracing + if (grpc_timer_check_trace.enabled()) { + char* next_str; + if (next == nullptr) { + next_str = gpr_strdup("NULL"); + } else { + gpr_asprintf(&next_str, "%" PRId64, *next); + } + gpr_log(GPR_INFO, "TIMER CHECK END: r=%d; next=%s", r, next_str); + gpr_free(next_str); + } + return r; +} + +grpc_timer_vtable grpc_generic_timer_vtable = { + timer_init, timer_cancel, timer_check, + timer_list_init, timer_list_shutdown, timer_consume_kick}; diff --git a/third_party/grpc/src/core/lib/iomgr/timer_generic.h b/third_party/grpc/src/core/lib/iomgr/timer_generic.h new file mode 100644 index 00000000000000..97a45133553865 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_generic.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H +#define GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H + +#include + +#include +#include "src/core/lib/iomgr/exec_ctx.h" + +struct grpc_timer { + gpr_atm deadline; + uint32_t heap_index; /* INVALID_HEAP_INDEX if not in heap */ + bool pending; + struct grpc_timer* next; + struct grpc_timer* prev; + grpc_closure* closure; +#ifndef NDEBUG + struct grpc_timer* hash_table_next; +#endif +}; + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_GENERIC_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/timer_heap.cc b/third_party/grpc/src/core/lib/iomgr/timer_heap.cc new file mode 100644 index 00000000000000..2c6a599149b788 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_heap.cc @@ -0,0 +1,135 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/timer_heap.h" + +#include + +#include + +#include "src/core/lib/gpr/useful.h" + +/* 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_timer** first, uint32_t i, grpc_timer* t) { + while (i > 0) { + uint32_t parent = static_cast((static_cast(i) - 1) / 2); + if (first[parent]->deadline <= t->deadline) 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_timer** first, uint32_t i, uint32_t length, + grpc_timer* t) { + for (;;) { + uint32_t left_child = 1u + 2u * i; + if (left_child >= length) break; + uint32_t right_child = left_child + 1; + uint32_t next_i = right_child < length && first[left_child]->deadline > + first[right_child]->deadline + ? right_child + : left_child; + if (t->deadline <= first[next_i]->deadline) 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_timer_heap* heap) { + if (heap->timer_count >= 8 && + heap->timer_count <= heap->timer_capacity / SHRINK_FULLNESS_FACTOR / 2) { + heap->timer_capacity = heap->timer_count * SHRINK_FULLNESS_FACTOR; + heap->timers = static_cast( + gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer*))); + } +} + +static void note_changed_priority(grpc_timer_heap* heap, grpc_timer* timer) { + uint32_t i = timer->heap_index; + uint32_t parent = static_cast((static_cast(i) - 1) / 2); + if (heap->timers[parent]->deadline > timer->deadline) { + adjust_upwards(heap->timers, i, timer); + } else { + adjust_downwards(heap->timers, i, heap->timer_count, timer); + } +} + +void grpc_timer_heap_init(grpc_timer_heap* heap) { + memset(heap, 0, sizeof(*heap)); +} + +void grpc_timer_heap_destroy(grpc_timer_heap* heap) { gpr_free(heap->timers); } + +bool grpc_timer_heap_add(grpc_timer_heap* heap, grpc_timer* timer) { + if (heap->timer_count == heap->timer_capacity) { + heap->timer_capacity = + GPR_MAX(heap->timer_capacity + 1, heap->timer_capacity * 3 / 2); + heap->timers = static_cast( + gpr_realloc(heap->timers, heap->timer_capacity * sizeof(grpc_timer*))); + } + timer->heap_index = heap->timer_count; + adjust_upwards(heap->timers, heap->timer_count, timer); + heap->timer_count++; + return timer->heap_index == 0; +} + +void grpc_timer_heap_remove(grpc_timer_heap* heap, grpc_timer* timer) { + uint32_t i = timer->heap_index; + if (i == heap->timer_count - 1) { + heap->timer_count--; + maybe_shrink(heap); + return; + } + heap->timers[i] = heap->timers[heap->timer_count - 1]; + heap->timers[i]->heap_index = i; + heap->timer_count--; + maybe_shrink(heap); + note_changed_priority(heap, heap->timers[i]); +} + +bool grpc_timer_heap_is_empty(grpc_timer_heap* heap) { + return heap->timer_count == 0; +} + +grpc_timer* grpc_timer_heap_top(grpc_timer_heap* heap) { + return heap->timers[0]; +} + +void grpc_timer_heap_pop(grpc_timer_heap* heap) { + grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); +} diff --git a/third_party/grpc/src/core/lib/iomgr/timer_heap.h b/third_party/grpc/src/core/lib/iomgr/timer_heap.h new file mode 100644 index 00000000000000..7b983e7133a38b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_heap.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H +#define GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H + +#include + +#include "src/core/lib/iomgr/timer.h" + +typedef struct { + grpc_timer** timers; + uint32_t timer_count; + uint32_t timer_capacity; +} grpc_timer_heap; + +/* return true if the new timer is the first timer in the heap */ +bool grpc_timer_heap_add(grpc_timer_heap* heap, grpc_timer* timer); + +void grpc_timer_heap_init(grpc_timer_heap* heap); +void grpc_timer_heap_destroy(grpc_timer_heap* heap); + +void grpc_timer_heap_remove(grpc_timer_heap* heap, grpc_timer* timer); +grpc_timer* grpc_timer_heap_top(grpc_timer_heap* heap); +void grpc_timer_heap_pop(grpc_timer_heap* heap); + +bool grpc_timer_heap_is_empty(grpc_timer_heap* heap); + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/timer_manager.cc b/third_party/grpc/src/core/lib/iomgr/timer_manager.cc new file mode 100644 index 00000000000000..cb123298cf5b8b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_manager.cc @@ -0,0 +1,376 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" + +struct completed_thread { + grpc_core::Thread thd; + completed_thread* next; +}; + +extern grpc_core::TraceFlag grpc_timer_check_trace; + +// global mutex +static gpr_mu g_mu; +// are we multi-threaded +static bool g_threaded; +// cv to wait until a thread is needed +static gpr_cv g_cv_wait; +// cv for notification when threading ends +static gpr_cv g_cv_shutdown; +// number of threads in the system +static int g_thread_count; +// number of threads sitting around waiting +static int g_waiter_count; +// linked list of threads that have completed (and need joining) +static completed_thread* g_completed_threads; +// was the manager kicked by the timer system +static bool g_kicked; +// is there a thread waiting until the next timer should fire? +static bool g_has_timed_waiter; +// the deadline of the current timed waiter thread (only relevant if +// g_has_timed_waiter is true) +static grpc_millis g_timed_waiter_deadline; +// generation counter to track which thread is waiting for the next timer +static uint64_t g_timed_waiter_generation; + +static void timer_thread(void* completed_thread_ptr); + +// For debug of the timer manager crash only. +// TODO (mxyan): remove after bug is fixed. +#ifdef GRPC_DEBUG_TIMER_MANAGER +extern int64_t g_timer_manager_init_count; +extern int64_t g_timer_manager_shutdown_count; +extern int64_t g_fork_count; +extern int64_t g_next_value; +#endif // GRPC_DEBUG_TIMER_MANAGER + +static void gc_completed_threads(void) { + if (g_completed_threads != nullptr) { + completed_thread* to_gc = g_completed_threads; + g_completed_threads = nullptr; + gpr_mu_unlock(&g_mu); + while (to_gc != nullptr) { + to_gc->thd.Join(); + completed_thread* next = to_gc->next; + gpr_free(to_gc); + to_gc = next; + } + gpr_mu_lock(&g_mu); + } +} + +static void start_timer_thread_and_unlock(void) { + GPR_ASSERT(g_threaded); + ++g_waiter_count; + ++g_thread_count; + gpr_mu_unlock(&g_mu); + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "Spawn timer thread"); + } + completed_thread* ct = + static_cast(gpr_malloc(sizeof(*ct))); + ct->thd = grpc_core::Thread("grpc_global_timer", timer_thread, ct); + ct->thd.Start(); +} + +void grpc_timer_manager_tick() { + grpc_core::ExecCtx exec_ctx; + grpc_timer_check(nullptr); +} + +static void run_some_timers() { + // if there's something to execute... + gpr_mu_lock(&g_mu); + // remove a waiter from the pool, and start another thread if necessary + --g_waiter_count; + if (g_waiter_count == 0 && g_threaded) { + // The number of timer threads is always increasing until all the threads + // are stopped. In rare cases, if a large number of timers fire + // simultaneously, we may end up using a large number of threads. + start_timer_thread_and_unlock(); + } else { + // if there's no thread waiting with a timeout, kick an existing untimed + // waiter so that the next deadline is not missed + if (!g_has_timed_waiter) { + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "kick untimed waiter"); + } + gpr_cv_signal(&g_cv_wait); + } + gpr_mu_unlock(&g_mu); + } + // without our lock, flush the exec_ctx + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "flush exec_ctx"); + } + grpc_core::ExecCtx::Get()->Flush(); + gpr_mu_lock(&g_mu); + // garbage collect any threads hanging out that are dead + gc_completed_threads(); + // get ready to wait again + ++g_waiter_count; + gpr_mu_unlock(&g_mu); +} + +// wait until 'next' (or forever if there is already a timed waiter in the pool) +// returns true if the thread should continue executing (false if it should +// shutdown) +static bool wait_until(grpc_millis next) { + gpr_mu_lock(&g_mu); + // if we're not threaded anymore, leave + if (!g_threaded) { + gpr_mu_unlock(&g_mu); + return false; + } + + // If g_kicked is true at this point, it means there was a kick from the timer + // system that the timer-manager threads here missed. We cannot trust 'next' + // here any longer (since there might be an earlier deadline). So if g_kicked + // is true at this point, we should quickly exit this and get the next + // deadline from the timer system + + if (!g_kicked) { + // if there's no timed waiter, we should become one: that waiter waits + // only until the next timer should expire. All other timers wait forever + // + // 'g_timed_waiter_generation' is a global generation counter. The idea here + // is that the thread becoming a timed-waiter increments and stores this + // global counter locally in 'my_timed_waiter_generation' before going to + // sleep. After waking up, if my_timed_waiter_generation == + // g_timed_waiter_generation, it can be sure that it was the timed_waiter + // thread (and that no other thread took over while this was asleep) + // + // Initialize my_timed_waiter_generation to some value that is NOT equal to + // g_timed_waiter_generation + uint64_t my_timed_waiter_generation = g_timed_waiter_generation - 1; + + /* If there's no timed waiter, we should become one: that waiter waits only + until the next timer should expire. All other timer threads wait forever + unless their 'next' is earlier than the current timed-waiter's deadline + (in which case the thread with earlier 'next' takes over as the new timed + waiter) */ + if (next != GRPC_MILLIS_INF_FUTURE) { + if (!g_has_timed_waiter || (next < g_timed_waiter_deadline)) { + my_timed_waiter_generation = ++g_timed_waiter_generation; + g_has_timed_waiter = true; + g_timed_waiter_deadline = next; + + if (grpc_timer_check_trace.enabled()) { + grpc_millis wait_time = next - grpc_core::ExecCtx::Get()->Now(); + gpr_log(GPR_INFO, "sleep for a %" PRId64 " milliseconds", wait_time); + } + } else { // g_timed_waiter == true && next >= g_timed_waiter_deadline + next = GRPC_MILLIS_INF_FUTURE; + } + } + + if (grpc_timer_check_trace.enabled() && next == GRPC_MILLIS_INF_FUTURE) { + gpr_log(GPR_INFO, "sleep until kicked"); + } + + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. +#ifdef GRPC_DEBUG_TIMER_MANAGER + g_next_value = next; +#endif + gpr_cv_wait(&g_cv_wait, &g_mu, + grpc_millis_to_timespec(next, GPR_CLOCK_MONOTONIC)); + + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "wait ended: was_timed:%d kicked:%d", + my_timed_waiter_generation == g_timed_waiter_generation, + g_kicked); + } + // if this was the timed waiter, then we need to check timers, and flag + // that there's now no timed waiter... we'll look for a replacement if + // there's work to do after checking timers (code above) + if (my_timed_waiter_generation == g_timed_waiter_generation) { + g_has_timed_waiter = false; + g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE; + } + } + + // if this was a kick from the timer system, consume it (and don't stop + // this thread yet) + if (g_kicked) { + grpc_timer_consume_kick(); + g_kicked = false; + } + + gpr_mu_unlock(&g_mu); + return true; +} + +static void timer_main_loop() { + for (;;) { + grpc_millis next = GRPC_MILLIS_INF_FUTURE; + grpc_core::ExecCtx::Get()->InvalidateNow(); + + // check timer state, updates next to the next time to run a check + switch (grpc_timer_check(&next)) { + case GRPC_TIMERS_FIRED: + run_some_timers(); + break; + case GRPC_TIMERS_NOT_CHECKED: + /* This case only happens under contention, meaning more than one timer + manager thread checked timers concurrently. + + If that happens, we're guaranteed that some other thread has just + checked timers, and this will avalanche into some other thread seeing + empty timers and doing a timed sleep. + + Consequently, we can just sleep forever here and be happy at some + saved wakeup cycles. */ + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "timers not checked: expect another thread to"); + } + next = GRPC_MILLIS_INF_FUTURE; + // fallthrough + case GRPC_TIMERS_CHECKED_AND_EMPTY: + if (!wait_until(next)) { + return; + } + break; + } + } +} + +static void timer_thread_cleanup(completed_thread* ct) { + gpr_mu_lock(&g_mu); + // terminate the thread: drop the waiter count, thread count, and let whomever + // stopped the threading stuff know that we're done + --g_waiter_count; + --g_thread_count; + if (0 == g_thread_count) { + gpr_cv_signal(&g_cv_shutdown); + } + ct->next = g_completed_threads; + g_completed_threads = ct; + gpr_mu_unlock(&g_mu); + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "End timer thread"); + } +} + +static void timer_thread(void* completed_thread_ptr) { + // this threads exec_ctx: we try to run things through to completion here + // since it's easy to spin up new threads + grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_INTERNAL_THREAD); + timer_main_loop(); + + timer_thread_cleanup(static_cast(completed_thread_ptr)); +} + +static void start_threads(void) { + gpr_mu_lock(&g_mu); + if (!g_threaded) { + g_threaded = true; + start_timer_thread_and_unlock(); + } else { + gpr_mu_unlock(&g_mu); + } +} + +void grpc_timer_manager_init(void) { + gpr_mu_init(&g_mu); + gpr_cv_init(&g_cv_wait); +#ifdef GRPC_DEBUG_TIMER_MANAGER + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. + g_timer_manager_init_count++; +#endif + gpr_cv_init(&g_cv_shutdown); + g_threaded = false; + g_thread_count = 0; + g_waiter_count = 0; + g_completed_threads = nullptr; + + g_has_timed_waiter = false; + g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE; + + start_threads(); +} + +static void stop_threads(void) { + gpr_mu_lock(&g_mu); + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "stop timer threads: threaded=%d", g_threaded); + } + if (g_threaded) { + g_threaded = false; + gpr_cv_broadcast(&g_cv_wait); + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "num timer threads: %d", g_thread_count); + } + while (g_thread_count > 0) { + gpr_cv_wait(&g_cv_shutdown, &g_mu, gpr_inf_future(GPR_CLOCK_MONOTONIC)); + if (grpc_timer_check_trace.enabled()) { + gpr_log(GPR_INFO, "num timer threads: %d", g_thread_count); + } + gc_completed_threads(); + } + } + gpr_mu_unlock(&g_mu); +} + +void grpc_timer_manager_shutdown(void) { +#ifdef GRPC_DEBUG_TIMER_MANAGER + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. + g_timer_manager_shutdown_count++; +#endif + stop_threads(); + + gpr_mu_destroy(&g_mu); + gpr_cv_destroy(&g_cv_wait); + gpr_cv_destroy(&g_cv_shutdown); +} + +void grpc_timer_manager_set_threading(bool threaded) { +#ifdef GRPC_DEBUG_TIMER_MANAGER + // For debug of the timer manager crash only. + // TODO (mxyan): remove after bug is fixed. + g_fork_count++; +#endif + if (threaded) { + start_threads(); + } else { + stop_threads(); + } +} + +void grpc_kick_poller(void) { + gpr_mu_lock(&g_mu); + g_kicked = true; + g_has_timed_waiter = false; + g_timed_waiter_deadline = GRPC_MILLIS_INF_FUTURE; + ++g_timed_waiter_generation; + gpr_cv_signal(&g_cv_wait); + gpr_mu_unlock(&g_mu); +} diff --git a/third_party/grpc/src/core/lib/iomgr/timer_manager.h b/third_party/grpc/src/core/lib/iomgr/timer_manager.h new file mode 100644 index 00000000000000..00dcdc461b5446 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_manager.h @@ -0,0 +1,39 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H +#define GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H + +#include + +#include + +/* Timer Manager tries to keep only one thread waiting for the next timeout at + all times, and thus effectively preventing the thundering herd problem. */ + +void grpc_timer_manager_init(void); +void grpc_timer_manager_shutdown(void); + +/* enable/disable threading - must be called after grpc_timer_manager_init and + * before grpc_timer_manager_shutdown */ +void grpc_timer_manager_set_threading(bool enabled); +/* explicitly perform one tick of the timer system - for when threading is + * disabled */ +void grpc_timer_manager_tick(void); + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/timer_uv.cc b/third_party/grpc/src/core/lib/iomgr/timer_uv.cc new file mode 100644 index 00000000000000..8b7c82eb7dea89 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/timer_uv.cc @@ -0,0 +1,66 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/iomgr_custom.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_custom.h" + +#include + +static void timer_close_callback(uv_handle_t* handle) { gpr_free(handle); } + +static void stop_uv_timer(uv_timer_t* handle) { + uv_timer_stop(handle); + uv_unref((uv_handle_t*)handle); + uv_close((uv_handle_t*)handle, timer_close_callback); +} + +void run_expired_timer(uv_timer_t* handle) { + grpc_custom_timer* timer_wrapper = (grpc_custom_timer*)handle->data; + grpc_custom_timer_callback(timer_wrapper, GRPC_ERROR_NONE); +} + +static void timer_start(grpc_custom_timer* t) { + uv_timer_t* uv_timer; + uv_timer = (uv_timer_t*)gpr_malloc(sizeof(uv_timer_t)); + uv_timer_init(uv_default_loop(), uv_timer); + uv_timer->data = t; + t->timer = (void*)uv_timer; + uv_timer_start(uv_timer, run_expired_timer, t->timeout_ms, 0); + // Node uses a garbage collector to call destructors, so we don't + // want to hold the uv loop open with active gRPC objects. + uv_unref((uv_handle_t*)uv_timer); +} + +static void timer_stop(grpc_custom_timer* t) { + stop_uv_timer((uv_timer_t*)t->timer); +} + +grpc_custom_timer_vtable uv_timer_vtable = {timer_start, timer_stop}; + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/udp_server.cc b/third_party/grpc/src/core/lib/iomgr/udp_server.cc new file mode 100644 index 00000000000000..3dd7cab855ced9 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/udp_server.cc @@ -0,0 +1,746 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifndef SO_RXQ_OVFL +#define SO_RXQ_OVFL 40 +#endif + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/udp_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_factory_posix.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +/* A listener which implements basic features of Listening on a port for + * I/O events*/ +class GrpcUdpListener { + public: + GrpcUdpListener(grpc_udp_server* server, int fd, + const grpc_resolved_address* addr); + ~GrpcUdpListener(); + + /* Called when grpc server starts to listening on the grpc_fd. */ + void StartListening(grpc_pollset** pollsets, size_t pollset_count, + GrpcUdpHandlerFactory* handler_factory); + + /* Called when data is available to read from the socket. + * Return true if there is more data to read from fd. */ + void OnRead(grpc_error* error, void* do_read_arg); + + /* Called when the socket is writeable. The given closure should be scheduled + * when the socket becomes blocked next time. */ + void OnCanWrite(grpc_error* error, void* do_write_arg); + + /* Called when the grpc_fd is about to be orphaned (and the FD closed). */ + void OnFdAboutToOrphan(); + + /* Called to orphan fd of this listener.*/ + void OrphanFd(); + + /* Called when this listener is going to be destroyed. */ + void OnDestroy(); + + int fd() const { return fd_; } + + protected: + grpc_fd* emfd() const { return emfd_; } + + gpr_mu* mutex() { return &mutex_; } + + private: + /* event manager callback when reads are ready */ + static void on_read(void* arg, grpc_error* error); + static void on_write(void* arg, grpc_error* error); + + static void do_read(void* arg, grpc_error* error); + static void do_write(void* arg, grpc_error* error); + // Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback + // interface. + static void fd_notify_on_write_wrapper(void* arg, grpc_error* error); + + static void shutdown_fd(void* args, grpc_error* error); + + int fd_; + grpc_fd* emfd_; + grpc_udp_server* server_; + grpc_resolved_address addr_; + grpc_closure read_closure_; + grpc_closure write_closure_; + // To be called when corresponding QuicGrpcServer closes all active + // connections. + grpc_closure orphan_fd_closure_; + grpc_closure destroyed_closure_; + // To be scheduled on another thread to actually read/write. + grpc_closure do_read_closure_; + grpc_closure do_write_closure_; + grpc_closure notify_on_write_closure_; + // True if orphan_cb is trigered. + bool orphan_notified_; + // True if grpc_fd_notify_on_write() is called after on_write() call. + bool notify_on_write_armed_; + // True if fd has been shutdown. + bool already_shutdown_; + // Object actually handles I/O events. Assigned in StartListening(). + GrpcUdpHandler* udp_handler_ = nullptr; + // To be notified on destruction. + GrpcUdpHandlerFactory* handler_factory_ = nullptr; + // Required to access above fields. + gpr_mu mutex_; +}; + +GrpcUdpListener::GrpcUdpListener(grpc_udp_server* server, int fd, + const grpc_resolved_address* addr) + : fd_(fd), + server_(server), + orphan_notified_(false), + already_shutdown_(false) { + char* addr_str; + char* name; + grpc_sockaddr_to_string(&addr_str, addr, 1); + gpr_asprintf(&name, "udp-server-listener:%s", addr_str); + gpr_free(addr_str); + emfd_ = grpc_fd_create(fd, name, true); + memcpy(&addr_, addr, sizeof(grpc_resolved_address)); + GPR_ASSERT(emfd_); + gpr_free(name); + gpr_mu_init(&mutex_); +} + +GrpcUdpListener::~GrpcUdpListener() { gpr_mu_destroy(&mutex_); } + +/* the overall server */ +struct grpc_udp_server { + gpr_mu mu; + + /* factory to use for creating and binding sockets, or NULL */ + grpc_socket_factory* socket_factory; + + /* 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; + + /* An array of listeners */ + grpc_core::InlinedVector listeners; + + /* factory for use to create udp listeners */ + GrpcUdpHandlerFactory* handler_factory; + + /* shutdown callback */ + grpc_closure* shutdown_complete; + + /* all pollsets interested in new connections */ + grpc_pollset** pollsets; + /* number of pollsets in the pollsets array */ + size_t pollset_count; + /* opaque object to pass to callbacks */ + void* user_data; + + /* latch has_so_reuseport during server creation */ + bool so_reuseport; +}; + +static grpc_socket_factory* get_socket_factory(const grpc_channel_args* args) { + if (args) { + const grpc_arg* arg = grpc_channel_args_find(args, GRPC_ARG_SOCKET_FACTORY); + if (arg) { + GPR_ASSERT(arg->type == GRPC_ARG_POINTER); + return static_cast(arg->value.pointer.p); + } + } + return nullptr; +} + +grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args) { + grpc_udp_server* s = grpc_core::New(); + gpr_mu_init(&s->mu); + s->socket_factory = get_socket_factory(args); + if (s->socket_factory) { + grpc_socket_factory_ref(s->socket_factory); + } + s->active_ports = 0; + s->destroyed_ports = 0; + s->shutdown = 0; + s->so_reuseport = grpc_is_socket_reuse_port_supported(); + return s; +} + +// static +void GrpcUdpListener::shutdown_fd(void* args, grpc_error* error) { + if (args == nullptr) { + // No-op if shutdown args are null. + return; + } + auto sp = static_cast(args); + gpr_mu_lock(sp->mutex()); + gpr_log(GPR_DEBUG, "shutdown fd %d", sp->fd_); + grpc_fd_shutdown(sp->emfd_, GRPC_ERROR_REF(error)); + sp->already_shutdown_ = true; + if (!sp->notify_on_write_armed_) { + // Re-arm write notification to notify listener with error. This is + // necessary to decrement active_ports. + sp->notify_on_write_armed_ = true; + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); + } + gpr_mu_unlock(sp->mutex()); +} + +static void finish_shutdown(grpc_udp_server* s) { + if (s->shutdown_complete != nullptr) { + GRPC_CLOSURE_SCHED(s->shutdown_complete, GRPC_ERROR_NONE); + } + + gpr_mu_destroy(&s->mu); + + gpr_log(GPR_DEBUG, "Destroy all listeners."); + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].OnDestroy(); + } + + if (s->socket_factory) { + grpc_socket_factory_unref(s->socket_factory); + } + + grpc_core::Delete(s); +} + +static void destroyed_port(void* server, grpc_error* error) { + grpc_udp_server* s = static_cast(server); + gpr_mu_lock(&s->mu); + s->destroyed_ports++; + if (s->destroyed_ports == s->listeners.size()) { + gpr_mu_unlock(&s->mu); + finish_shutdown(s); + } else { + gpr_mu_unlock(&s->mu); + } +} + +/* 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) { + /* delete ALL the things */ + gpr_mu_lock(&s->mu); + + GPR_ASSERT(s->shutdown); + + if (s->listeners.size() == 0) { + gpr_mu_unlock(&s->mu); + finish_shutdown(s); + return; + } + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].OrphanFd(); + } + gpr_mu_unlock(&s->mu); +} + +void GrpcUdpListener::OrphanFd() { + gpr_log(GPR_DEBUG, "Orphan fd %d, emfd %p", fd_, emfd_); + grpc_unlink_if_unix_domain_socket(&addr_); + + GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_, + grpc_schedule_on_exec_ctx); + /* Because at this point, all listening sockets have been shutdown already, no + * need to call OnFdAboutToOrphan() to notify the handler again. */ + grpc_fd_orphan(emfd_, &destroyed_closure_, nullptr, "udp_listener_shutdown"); +} + +void grpc_udp_server_destroy(grpc_udp_server* s, grpc_closure* on_done) { + gpr_mu_lock(&s->mu); + + GPR_ASSERT(!s->shutdown); + s->shutdown = 1; + + s->shutdown_complete = on_done; + + gpr_log(GPR_DEBUG, "start to destroy udp_server"); + /* shutdown all fd's */ + if (s->active_ports) { + for (size_t i = 0; i < s->listeners.size(); ++i) { + GrpcUdpListener* sp = &s->listeners[i]; + sp->OnFdAboutToOrphan(); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + deactivated_all_ports(s); + } +} + +void GrpcUdpListener::OnFdAboutToOrphan() { + gpr_mu_lock(&mutex_); + grpc_unlink_if_unix_domain_socket(&addr_); + + GRPC_CLOSURE_INIT(&destroyed_closure_, destroyed_port, server_, + grpc_schedule_on_exec_ctx); + if (!orphan_notified_ && udp_handler_ != nullptr) { + /* Singals udp_handler that the FD is about to be closed and + * should no longer be used. */ + GRPC_CLOSURE_INIT(&orphan_fd_closure_, shutdown_fd, this, + grpc_schedule_on_exec_ctx); + gpr_log(GPR_DEBUG, "fd %d about to be orphaned", fd_); + udp_handler_->OnFdAboutToOrphan(&orphan_fd_closure_, server_->user_data); + orphan_notified_ = true; + } + gpr_mu_unlock(&mutex_); +} + +static int bind_socket(grpc_socket_factory* socket_factory, int sockfd, + const grpc_resolved_address* addr) { + return (socket_factory != nullptr) + ? grpc_socket_factory_bind(socket_factory, sockfd, addr) + : bind(sockfd, + reinterpret_cast( + const_cast(addr->addr)), + addr->len); +} + +/* Prepare a recently-created socket for listening. */ +static int prepare_socket(grpc_socket_factory* socket_factory, int fd, + const grpc_resolved_address* addr, int rcv_buf_size, + int snd_buf_size, bool so_reuseport) { + grpc_resolved_address sockname_temp; + grpc_sockaddr* addr_ptr = + reinterpret_cast(const_cast(addr->addr)); + + if (fd < 0) { + goto error; + } + + if (grpc_set_socket_nonblocking(fd, 1) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set nonblocking %d: %s", fd, strerror(errno)); + goto error; + } + if (grpc_set_socket_cloexec(fd, 1) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set cloexec %d: %s", fd, strerror(errno)); + goto error; + } + + if (grpc_set_socket_ip_pktinfo_if_possible(fd) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set ip_pktinfo."); + goto error; + } else if (addr_ptr->sa_family == AF_INET6) { + if (grpc_set_socket_ipv6_recvpktinfo_if_possible(fd) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set ipv6_recvpktinfo."); + goto error; + } + } + + if (grpc_set_socket_sndbuf(fd, snd_buf_size) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Failed to set send buffer size to %d bytes", + snd_buf_size); + goto error; + } + + if (grpc_set_socket_rcvbuf(fd, rcv_buf_size) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Failed to set receive buffer size to %d bytes", + rcv_buf_size); + goto error; + } + + { + int get_overflow = 1; + if (0 != setsockopt(fd, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, + sizeof(get_overflow))) { + gpr_log(GPR_INFO, "Failed to set socket overflow support"); + } + } + + if (so_reuseport && !grpc_is_unix_socket(addr) && + grpc_set_socket_reuse_port(fd, 1) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Failed to set SO_REUSEPORT for fd %d", fd); + goto error; + } + + if (bind_socket(socket_factory, fd, addr) < 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_temp.len = static_cast(sizeof(struct sockaddr_storage)); + + if (getsockname(fd, reinterpret_cast(sockname_temp.addr), + &sockname_temp.len) < 0) { + gpr_log(GPR_ERROR, "Unable to get the address socket %d is bound to: %s", + fd, strerror(errno)); + goto error; + } + + return grpc_sockaddr_get_port(&sockname_temp); + +error: + if (fd >= 0) { + close(fd); + } + return -1; +} + +// static +void GrpcUdpListener::do_read(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast(arg); + GPR_ASSERT(error == GRPC_ERROR_NONE); + /* TODO: the reason we hold server->mu here is merely to prevent fd + * shutdown while we are reading. However, it blocks do_write(). Switch to + * read lock if available. */ + gpr_mu_lock(sp->mutex()); + /* Tell the registered callback that data is available to read. */ + if (!sp->already_shutdown_ && sp->udp_handler_->Read()) { + /* There maybe more packets to read. Schedule read_more_cb_ closure to run + * after finishing this event loop. */ + GRPC_CLOSURE_SCHED(&sp->do_read_closure_, GRPC_ERROR_NONE); + } else { + /* Finish reading all the packets, re-arm the notification event so we can + * get another chance to read. Or fd already shutdown, re-arm to get a + * notification with shutdown error. */ + grpc_fd_notify_on_read(sp->emfd_, &sp->read_closure_); + } + gpr_mu_unlock(sp->mutex()); +} + +// static +void GrpcUdpListener::on_read(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast(arg); + sp->OnRead(error, arg); +} + +void GrpcUdpListener::OnRead(grpc_error* error, void* do_read_arg) { + if (error != GRPC_ERROR_NONE) { + gpr_mu_lock(&server_->mu); + if (0 == --server_->active_ports && server_->shutdown) { + gpr_mu_unlock(&server_->mu); + deactivated_all_ports(server_); + } else { + gpr_mu_unlock(&server_->mu); + } + return; + } + + /* Read once. If there is more data to read, off load the work to another + * thread to finish. */ + if (udp_handler_->Read()) { + /* There maybe more packets to read. Schedule read_more_cb_ closure to run + * after finishing this event loop. */ + GRPC_CLOSURE_INIT(&do_read_closure_, do_read, do_read_arg, + grpc_executor_scheduler(GRPC_EXECUTOR_LONG)); + GRPC_CLOSURE_SCHED(&do_read_closure_, GRPC_ERROR_NONE); + } else { + /* Finish reading all the packets, re-arm the notification event so we can + * get another chance to read. Or fd already shutdown, re-arm to get a + * notification with shutdown error. */ + grpc_fd_notify_on_read(emfd_, &read_closure_); + } +} + +// static +// Wrapper of grpc_fd_notify_on_write() with a grpc_closure callback interface. +void GrpcUdpListener::fd_notify_on_write_wrapper(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast(arg); + gpr_mu_lock(sp->mutex()); + if (!sp->notify_on_write_armed_) { + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); + sp->notify_on_write_armed_ = true; + } + gpr_mu_unlock(sp->mutex()); +} + +// static +void GrpcUdpListener::do_write(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast(arg); + gpr_mu_lock(sp->mutex()); + if (sp->already_shutdown_) { + // If fd has been shutdown, don't write any more and re-arm notification. + grpc_fd_notify_on_write(sp->emfd_, &sp->write_closure_); + } else { + sp->notify_on_write_armed_ = false; + /* Tell the registered callback that the socket is writeable. */ + GPR_ASSERT(error == GRPC_ERROR_NONE); + GRPC_CLOSURE_INIT(&sp->notify_on_write_closure_, fd_notify_on_write_wrapper, + arg, grpc_schedule_on_exec_ctx); + sp->udp_handler_->OnCanWrite(sp->server_->user_data, + &sp->notify_on_write_closure_); + } + gpr_mu_unlock(sp->mutex()); +} + +// static +void GrpcUdpListener::on_write(void* arg, grpc_error* error) { + GrpcUdpListener* sp = static_cast(arg); + sp->OnCanWrite(error, arg); +} + +void GrpcUdpListener::OnCanWrite(grpc_error* error, void* do_write_arg) { + if (error != GRPC_ERROR_NONE) { + gpr_mu_lock(&server_->mu); + if (0 == --server_->active_ports && server_->shutdown) { + gpr_mu_unlock(&server_->mu); + deactivated_all_ports(server_); + } else { + gpr_mu_unlock(&server_->mu); + } + return; + } + + /* Schedule actual write in another thread. */ + GRPC_CLOSURE_INIT(&do_write_closure_, do_write, do_write_arg, + grpc_executor_scheduler(GRPC_EXECUTOR_LONG)); + + GRPC_CLOSURE_SCHED(&do_write_closure_, GRPC_ERROR_NONE); +} + +static int add_socket_to_server(grpc_udp_server* s, int fd, + const grpc_resolved_address* addr, + int rcv_buf_size, int snd_buf_size) { + gpr_log(GPR_DEBUG, "add socket %d to server", fd); + + int port = prepare_socket(s->socket_factory, fd, addr, rcv_buf_size, + snd_buf_size, s->so_reuseport); + if (port >= 0) { + gpr_mu_lock(&s->mu); + s->listeners.emplace_back(s, fd, addr); + gpr_log(GPR_DEBUG, + "add socket %d to server for port %d, %zu listener(s) in total", fd, + port, s->listeners.size()); + gpr_mu_unlock(&s->mu); + } + return port; +} + +int grpc_udp_server_add_port(grpc_udp_server* s, + const grpc_resolved_address* addr, + int rcv_buf_size, int snd_buf_size, + GrpcUdpHandlerFactory* handler_factory, + size_t num_listeners) { + if (num_listeners > 1 && !s->so_reuseport) { + gpr_log(GPR_ERROR, + "Try to have multiple listeners on same port, but SO_REUSEPORT is " + "not supported. Only create 1 listener."); + } + char* addr_str; + grpc_sockaddr_to_string(&addr_str, addr, 1); + gpr_log(GPR_DEBUG, "add address: %s to server", addr_str); + gpr_free(addr_str); + + int allocated_port1 = -1; + int allocated_port2 = -1; + int fd; + grpc_dualstack_mode dsmode; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wild4; + grpc_resolved_address wild6; + grpc_resolved_address addr4_copy; + grpc_resolved_address* allocated_addr = nullptr; + grpc_resolved_address sockname_temp; + int port = 0; + + /* 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) { + /* Loop through existing listeners to find the port in use. */ + for (size_t i = 0; i < s->listeners.size(); ++i) { + sockname_temp.len = + static_cast(sizeof(struct sockaddr_storage)); + if (0 == getsockname(s->listeners[i].fd(), + reinterpret_cast(sockname_temp.addr), + &sockname_temp.len)) { + port = grpc_sockaddr_get_port(&sockname_temp); + if (port > 0) { + /* Found such a port, update |addr| to reflects this port. */ + allocated_addr = static_cast( + gpr_malloc(sizeof(grpc_resolved_address))); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + s->handler_factory = handler_factory; + for (size_t i = 0; i < num_listeners; ++i) { + /* 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 = &wild6; + // TODO(rjshade): Test and propagate the returned grpc_error*: + GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( + s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); + allocated_port1 = + add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size); + if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { + if (port == 0) { + /* This is the first time to bind to |addr|. If its port is still + * wildcard port, update |addr| with the ephermeral port returned by + * kernel. Thus |addr| can have a specific port in following + * iterations. */ + grpc_sockaddr_set_port(addr, allocated_port1); + port = allocated_port1; + } else if (allocated_port1 >= 0) { + /* The following sucessfully created socket should have same port as + * the first one. */ + GPR_ASSERT(port == allocated_port1); + } + /* A dualstack socket is created, no need to create corresponding IPV4 + * socket. */ + continue; + } + + /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ + if (port == 0 && allocated_port1 > 0) { + /* |port| hasn't been assigned to an emphemeral port yet, |wild4| must + * have a wildcard port. Update it with the emphemeral port created + * during binding.*/ + grpc_sockaddr_set_port(&wild4, allocated_port1); + port = allocated_port1; + } + /* |wild4| should have been updated with an emphemeral port by now. Use + * this IPV4 address to create a IPV4 socket. */ + addr = &wild4; + } + + // TODO(rjshade): Test and propagate the returned grpc_error*: + GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( + s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); + 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 = &addr4_copy; + } + allocated_port2 = + add_socket_to_server(s, fd, addr, rcv_buf_size, snd_buf_size); + if (port == 0) { + /* Update |addr| with the ephermeral port returned by kernel. So |addr| + * can have a specific port in following iterations. */ + grpc_sockaddr_set_port(addr, allocated_port2); + port = allocated_port2; + } else if (allocated_port2 >= 0) { + GPR_ASSERT(port == allocated_port2); + } + } + + gpr_free(allocated_addr); + return port; +} + +int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index) { + if (port_index >= s->listeners.size()) { + return -1; + } + + return s->listeners[port_index].fd(); +} + +void grpc_udp_server_start(grpc_udp_server* s, grpc_pollset** pollsets, + size_t pollset_count, void* user_data) { + gpr_log(GPR_DEBUG, "grpc_udp_server_start"); + gpr_mu_lock(&s->mu); + GPR_ASSERT(s->active_ports == 0); + s->pollsets = pollsets; + s->user_data = user_data; + + for (size_t i = 0; i < s->listeners.size(); ++i) { + s->listeners[i].StartListening(pollsets, pollset_count, s->handler_factory); + } + + gpr_mu_unlock(&s->mu); +} + +void GrpcUdpListener::StartListening(grpc_pollset** pollsets, + size_t pollset_count, + GrpcUdpHandlerFactory* handler_factory) { + gpr_mu_lock(&mutex_); + handler_factory_ = handler_factory; + udp_handler_ = handler_factory->CreateUdpHandler(emfd_, server_->user_data); + for (size_t i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(pollsets[i], emfd_); + } + GRPC_CLOSURE_INIT(&read_closure_, on_read, this, grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(emfd_, &read_closure_); + + GRPC_CLOSURE_INIT(&write_closure_, on_write, this, grpc_schedule_on_exec_ctx); + notify_on_write_armed_ = true; + grpc_fd_notify_on_write(emfd_, &write_closure_); + + /* Registered for both read and write callbacks: increment active_ports + * twice to account for this, and delay free-ing of memory until both + * on_read and on_write have fired. */ + server_->active_ports += 2; + gpr_mu_unlock(&mutex_); +} + +void GrpcUdpListener::OnDestroy() { + if (udp_handler_ != nullptr) { + handler_factory_->DestroyUdpHandler(udp_handler_); + } +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/udp_server.h b/third_party/grpc/src/core/lib/iomgr/udp_server.h new file mode 100644 index 00000000000000..3656791c1f8c96 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/udp_server.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_UDP_SERVER_H +#define GRPC_CORE_LIB_IOMGR_UDP_SERVER_H + +#include + +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" + +/* Forward decl of struct grpc_server */ +/* This is not typedef'ed to avoid a typedef-redefinition error */ +struct grpc_server; + +/* Forward decl of grpc_udp_server */ +typedef struct grpc_udp_server grpc_udp_server; + +/* An interface associated with a socket. udp server delivers I/O event on that + * socket to the subclass of this interface which is created through + * GrpcUdpHandlerFactory. + * Its implementation should do the real IO work, e.g. read packet and write. */ +class GrpcUdpHandler { + public: + GrpcUdpHandler(grpc_fd* emfd, void* user_data) {} + virtual ~GrpcUdpHandler() {} + + // Interfaces to be implemented by subclasses to do the actual setup/tear down + // or I/O. + + // Called when data is available to read from the socket. Returns true if + // there is more data to read after this call. + virtual bool Read() GRPC_ABSTRACT; + // Called when socket becomes write unblocked. The given closure should be + // scheduled when the socket becomes blocked next time. + virtual void OnCanWrite(void* user_data, + grpc_closure* notify_on_write_closure) GRPC_ABSTRACT; + // Called before the gRPC FD is orphaned. Notify udp server to continue + // orphaning fd by scheduling the given closure, afterwards the associated fd + // will be closed. + virtual void OnFdAboutToOrphan(grpc_closure* orphan_fd_closure, + void* user_data) GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; + +class GrpcUdpHandlerFactory { + public: + virtual ~GrpcUdpHandlerFactory() {} + /* Called when start to listen on a socket. + * Return an instance of the implementation of GrpcUdpHandler interface which + * will process I/O events for this socket from now on. */ + virtual GrpcUdpHandler* CreateUdpHandler(grpc_fd* emfd, + void* user_data) GRPC_ABSTRACT; + virtual void DestroyUdpHandler(GrpcUdpHandler* handler) GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS +}; + +/* Create a server, initially not bound to any ports */ +grpc_udp_server* grpc_udp_server_create(const grpc_channel_args* args); + +/* Start listening to bound ports. user_data is passed to callbacks. */ +void grpc_udp_server_start(grpc_udp_server* udp_server, grpc_pollset** pollsets, + size_t pollset_count, void* user_data); + +int grpc_udp_server_get_fd(grpc_udp_server* s, unsigned port_index); + +/* Add a port to the server, returning port number on success, or negative + on failure. + + Create |num_listeners| sockets for given address to listen on using + SO_REUSEPORT if supported. + + 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 |num_listeners| sockets, but possibly 2 * |num_listeners| 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 grpc_resolved_address* addr, + int rcv_buf_size, int snd_buf_size, + GrpcUdpHandlerFactory* handler_factory, + size_t num_listeners); + +void grpc_udp_server_destroy(grpc_udp_server* server, grpc_closure* on_done); + +#endif /* GRPC_CORE_LIB_IOMGR_UDP_SERVER_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.cc b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.cc new file mode 100644 index 00000000000000..22fcaf57fc05af --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.cc @@ -0,0 +1,104 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_HAVE_UNIX_SOCKET + +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#include +#include + +#include "src/core/lib/gpr/useful.h" + +void grpc_create_socketpair_if_unix(int sv[2]) { + GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); +} + +grpc_error* grpc_resolve_unix_domain_address(const char* name, + grpc_resolved_addresses** addrs) { + struct sockaddr_un* un; + if (strlen(name) > + GPR_ARRAY_SIZE(((struct sockaddr_un*)nullptr)->sun_path) - 1) { + char* err_msg; + grpc_error* err; + gpr_asprintf(&err_msg, + "Path name should not have more than %" PRIuPTR " characters.", + GPR_ARRAY_SIZE(un->sun_path) - 1); + err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); + gpr_free(err_msg); + return err; + } + *addrs = static_cast( + gpr_malloc(sizeof(grpc_resolved_addresses))); + (*addrs)->naddrs = 1; + (*addrs)->addrs = static_cast( + gpr_malloc(sizeof(grpc_resolved_address))); + un = reinterpret_cast((*addrs)->addrs->addr); + un->sun_family = AF_UNIX; + strncpy(un->sun_path, name, sizeof(un->sun_path)); + (*addrs)->addrs->len = + static_cast(strlen(un->sun_path) + sizeof(un->sun_family) + 1); + return GRPC_ERROR_NONE; +} + +int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + return addr->sa_family == AF_UNIX; +} + +void grpc_unlink_if_unix_domain_socket( + const grpc_resolved_address* resolved_addr) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + if (addr->sa_family != AF_UNIX) { + return; + } + struct sockaddr_un* un = reinterpret_cast( + const_cast(resolved_addr->addr)); + struct stat st; + + if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) { + unlink(un->sun_path); + } +} + +char* grpc_sockaddr_to_uri_unix_if_possible( + const grpc_resolved_address* resolved_addr) { + const grpc_sockaddr* addr = + reinterpret_cast(resolved_addr->addr); + if (addr->sa_family != AF_UNIX) { + return nullptr; + } + + char* result; + gpr_asprintf(&result, "unix:%s", ((struct sockaddr_un*)addr)->sun_path); + return result; +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.h b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.h new file mode 100644 index 00000000000000..917d0327a96ec2 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H +#define GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H + +#include + +#include "src/core/lib/iomgr/port.h" + +#include + +#include "src/core/lib/iomgr/resolve_address.h" + +void grpc_create_socketpair_if_unix(int sv[2]); + +grpc_error* grpc_resolve_unix_domain_address( + const char* name, grpc_resolved_addresses** addresses); + +int grpc_is_unix_socket(const grpc_resolved_address* resolved_addr); + +void grpc_unlink_if_unix_domain_socket( + const grpc_resolved_address* resolved_addr); + +char* grpc_sockaddr_to_uri_unix_if_possible( + const grpc_resolved_address* resolved_addr); + +#endif /* GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix_noop.cc b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix_noop.cc new file mode 100644 index 00000000000000..dfab3e0acbc56a --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/unix_sockets_posix_noop.cc @@ -0,0 +1,49 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#ifndef GRPC_HAVE_UNIX_SOCKET + +#include + +void grpc_create_socketpair_if_unix(int sv[2]) { + // TODO: Either implement this for the non-Unix socket case or make + // sure that it is never called in any such case. Until then, leave an + // assertion to notify if this gets called inadvertently + GPR_ASSERT(0); +} + +grpc_error* grpc_resolve_unix_domain_address( + const char* name, grpc_resolved_addresses** addresses) { + *addresses = NULL; + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Unix domain sockets are not supported on Windows"); +} + +int grpc_is_unix_socket(const grpc_resolved_address* addr) { return false; } + +void grpc_unlink_if_unix_domain_socket(const grpc_resolved_address* addr) {} + +char* grpc_sockaddr_to_uri_unix_if_possible(const grpc_resolved_address* addr) { + return NULL; +} + +#endif diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.cc new file mode 100644 index 00000000000000..74faa6379efd77 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.cc @@ -0,0 +1,107 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_WAKEUP_FD + +#include "src/core/lib/iomgr/wakeup_fd_cv.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/thd.h" + +#define MAX_TABLE_RESIZE 256 + +extern grpc_cv_fd_table g_cvfds; + +static grpc_error* cv_fd_init(grpc_wakeup_fd* fd_info) { + unsigned int i, newsize; + int idx; + gpr_mu_lock(&g_cvfds.mu); + if (!g_cvfds.free_fds) { + newsize = GPR_MIN(g_cvfds.size * 2, g_cvfds.size + MAX_TABLE_RESIZE); + g_cvfds.cvfds = static_cast( + gpr_realloc(g_cvfds.cvfds, sizeof(grpc_fd_node) * newsize)); + for (i = g_cvfds.size; i < newsize; i++) { + g_cvfds.cvfds[i].is_set = 0; + g_cvfds.cvfds[i].cvs = nullptr; + g_cvfds.cvfds[i].next_free = g_cvfds.free_fds; + g_cvfds.free_fds = &g_cvfds.cvfds[i]; + } + g_cvfds.size = newsize; + } + + idx = static_cast(g_cvfds.free_fds - g_cvfds.cvfds); + g_cvfds.free_fds = g_cvfds.free_fds->next_free; + g_cvfds.cvfds[idx].cvs = nullptr; + g_cvfds.cvfds[idx].is_set = 0; + fd_info->read_fd = GRPC_IDX_TO_FD(idx); + fd_info->write_fd = -1; + gpr_mu_unlock(&g_cvfds.mu); + return GRPC_ERROR_NONE; +} + +static grpc_error* cv_fd_wakeup(grpc_wakeup_fd* fd_info) { + grpc_cv_node* cvn; + gpr_mu_lock(&g_cvfds.mu); + g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 1; + cvn = g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs; + while (cvn) { + gpr_cv_signal(cvn->cv); + cvn = cvn->next; + } + gpr_mu_unlock(&g_cvfds.mu); + return GRPC_ERROR_NONE; +} + +static grpc_error* cv_fd_consume(grpc_wakeup_fd* fd_info) { + gpr_mu_lock(&g_cvfds.mu); + g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 0; + gpr_mu_unlock(&g_cvfds.mu); + return GRPC_ERROR_NONE; +} + +static void cv_fd_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd == 0) { + return; + } + gpr_mu_lock(&g_cvfds.mu); + // Assert that there are no active pollers + GPR_ASSERT(!g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs); + g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].next_free = g_cvfds.free_fds; + g_cvfds.free_fds = &g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)]; + gpr_mu_unlock(&g_cvfds.mu); +} + +static int cv_check_availability(void) { return 1; } + +const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable = { + cv_fd_init, cv_fd_consume, cv_fd_wakeup, cv_fd_destroy, + cv_check_availability}; + +#endif /* GRPC_POSIX_WAKUP_FD */ diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.h b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.h new file mode 100644 index 00000000000000..86365f07e17299 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_cv.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * wakeup_fd_cv uses condition variables to implement wakeup fds. + * + * It is intended for use only in cases when eventfd() and pipe() are not + * available. It can only be used with the "poll" engine. + * + * Implementation: + * A global table of cv wakeup fds is mantained. A cv wakeup fd is a negative + * file descriptor. poll() is then run in a background thread with only the + * real socket fds while we wait on a condition variable trigged by either the + * poll() completion or a wakeup_fd() call. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H +#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H + +#include + +#include + +#include "src/core/lib/iomgr/ev_posix.h" + +#define GRPC_FD_TO_IDX(fd) (-(fd)-1) +#define GRPC_IDX_TO_FD(idx) (-(idx)-1) + +typedef struct grpc_cv_node { + gpr_cv* cv; + struct grpc_cv_node* next; + struct grpc_cv_node* prev; +} grpc_cv_node; + +typedef struct grpc_fd_node { + int is_set; + grpc_cv_node* cvs; + struct grpc_fd_node* next_free; +} grpc_fd_node; + +typedef struct grpc_cv_fd_table { + gpr_mu mu; + gpr_refcount pollcount; + gpr_cv shutdown_cv; + grpc_fd_node* cvfds; + grpc_fd_node* free_fds; + unsigned int size; + grpc_poll_function_type poll; +} grpc_cv_fd_table; + +extern const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable; + +#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_eventfd.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_eventfd.cc new file mode 100644 index 00000000000000..d68c9ada1f2fac --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_eventfd.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_LINUX_EVENTFD + +#include +#include +#include + +#include + +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" + +static grpc_error* eventfd_create(grpc_wakeup_fd* fd_info) { + fd_info->read_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + fd_info->write_fd = -1; + if (fd_info->read_fd < 0) { + return GRPC_OS_ERROR(errno, "eventfd"); + } + return GRPC_ERROR_NONE; +} + +static grpc_error* 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); + if (err < 0 && errno != EAGAIN) { + return GRPC_OS_ERROR(errno, "eventfd_read"); + } + return GRPC_ERROR_NONE; +} + +static grpc_error* eventfd_wakeup(grpc_wakeup_fd* fd_info) { + GPR_TIMER_SCOPE("eventfd_wakeup", 0); + int err; + do { + err = eventfd_write(fd_info->read_fd, 1); + } while (err < 0 && errno == EINTR); + if (err < 0) { + return GRPC_OS_ERROR(errno, "eventfd_write"); + } + return GRPC_ERROR_NONE; +} + +static void eventfd_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd != 0) close(fd_info->read_fd); +} + +static int eventfd_check_availability(void) { + const int efd = eventfd(0, 0); + const int is_available = efd >= 0; + if (is_available) close(efd); + return is_available; +} + +const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { + eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy, + eventfd_check_availability}; + +#endif /* GRPC_LINUX_EVENTFD */ diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_nospecial.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_nospecial.cc new file mode 100644 index 00000000000000..64778929f006e8 --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_nospecial.cc @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * This is a dummy file to provide an invalid specialized_wakeup_fd_vtable on + * systems without anything better than pipe. + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_NO_SPECIAL_WAKEUP_FD + +#include +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +static int check_availability_invalid(void) { return 0; } + +const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { + nullptr, nullptr, nullptr, nullptr, check_availability_invalid}; + +#endif /* GRPC_POSIX_NO_SPECIAL_WAKEUP_FD */ diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc new file mode 100644 index 00000000000000..cb173903a9314e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.cc @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_WAKEUP_FD + +#include "src/core/lib/iomgr/wakeup_fd_pipe.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +#include +#include +#include + +#include + +#include "src/core/lib/iomgr/socket_utils_posix.h" + +static grpc_error* pipe_init(grpc_wakeup_fd* fd_info) { + int pipefd[2]; + int r = pipe(pipefd); + if (0 != r) { + gpr_log(GPR_ERROR, "pipe creation failed (%d): %s", errno, strerror(errno)); + return GRPC_OS_ERROR(errno, "pipe"); + } + grpc_error* err; + err = grpc_set_socket_nonblocking(pipefd[0], 1); + if (err != GRPC_ERROR_NONE) return err; + err = grpc_set_socket_nonblocking(pipefd[1], 1); + if (err != GRPC_ERROR_NONE) return err; + fd_info->read_fd = pipefd[0]; + fd_info->write_fd = pipefd[1]; + return GRPC_ERROR_NONE; +} + +static grpc_error* pipe_consume(grpc_wakeup_fd* fd_info) { + char buf[128]; + ssize_t r; + + for (;;) { + r = read(fd_info->read_fd, buf, sizeof(buf)); + if (r > 0) continue; + if (r == 0) return GRPC_ERROR_NONE; + switch (errno) { + case EAGAIN: + return GRPC_ERROR_NONE; + case EINTR: + continue; + default: + return GRPC_OS_ERROR(errno, "read"); + } + } +} + +static grpc_error* pipe_wakeup(grpc_wakeup_fd* fd_info) { + char c = 0; + while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR) + ; + return GRPC_ERROR_NONE; +} + +static void pipe_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd != 0) close(fd_info->read_fd); + if (fd_info->write_fd != 0) close(fd_info->write_fd); +} + +static int pipe_check_availability(void) { + grpc_wakeup_fd fd; + fd.read_fd = fd.write_fd = -1; + + if (pipe_init(&fd) == GRPC_ERROR_NONE) { + pipe_destroy(&fd); + return 1; + } else { + return 0; + } +} + +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/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.h b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.h new file mode 100644 index 00000000000000..17569763059b3b --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_pipe.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H +#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H + +#include + +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +extern const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable; + +#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H */ diff --git a/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.cc b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.cc new file mode 100644 index 00000000000000..b5b8b37a9af49e --- /dev/null +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.cc @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_WAKEUP_FD + +#include +#include "src/core/lib/iomgr/wakeup_fd_cv.h" +#include "src/core/lib/iomgr/wakeup_fd_pipe.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +static const grpc_wakeup_fd_vtable* wakeup_fd_vtable = nullptr; + +int grpc_allow_specialized_wakeup_fd = 1; +int grpc_allow_pipe_wakeup_fd = 1; + +int has_real_wakeup_fd = 1; +int cv_wakeup_fds_enabled = 0; + +void grpc_wakeup_fd_global_init(void) { + if (grpc_allow_specialized_wakeup_fd && + grpc_specialized_wakeup_fd_vtable.check_availability()) { + wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable; + } else if (grpc_allow_pipe_wakeup_fd && + grpc_pipe_wakeup_fd_vtable.check_availability()) { + wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; + } else { + has_real_wakeup_fd = 0; + } +} + +void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = nullptr; } + +int grpc_has_wakeup_fd(void) { return has_real_wakeup_fd; } + +int grpc_cv_wakeup_fds_enabled(void) { return cv_wakeup_fds_enabled; } + +void grpc_enable_cv_wakeup_fds(int enable) { cv_wakeup_fds_enabled = enable; } + +grpc_error* grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info) { + if (cv_wakeup_fds_enabled) { + return grpc_cv_wakeup_fd_vtable.init(fd_info); + } + return wakeup_fd_vtable->init(fd_info); +} + +grpc_error* grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info) { + if (cv_wakeup_fds_enabled) { + return grpc_cv_wakeup_fd_vtable.consume(fd_info); + } + return wakeup_fd_vtable->consume(fd_info); +} + +grpc_error* grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info) { + if (cv_wakeup_fds_enabled) { + return grpc_cv_wakeup_fd_vtable.wakeup(fd_info); + } + return wakeup_fd_vtable->wakeup(fd_info); +} + +void grpc_wakeup_fd_destroy(grpc_wakeup_fd* fd_info) { + if (cv_wakeup_fds_enabled) { + grpc_cv_wakeup_fd_vtable.destroy(fd_info); + } else { + wakeup_fd_vtable->destroy(fd_info); + } +} + +#endif /* GRPC_POSIX_WAKEUP_FD */ diff --git a/third_party/grpc/src/core/iomgr/wakeup_fd_posix.h b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.h similarity index 51% rename from third_party/grpc/src/core/iomgr/wakeup_fd_posix.h rename to third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.h index ffd60d1d4e68dc..670c319593baf8 100644 --- a/third_party/grpc/src/core/iomgr/wakeup_fd_posix.h +++ b/third_party/grpc/src/core/lib/iomgr/wakeup_fd_posix.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -46,7 +31,7 @@ * * Setup: * 1. Before calling anything, call global_init() at least once. - * 1. Call grpc_wakeup_fd_create() to get a wakeup_fd. + * 1. Call grpc_wakeup_fd_init() to set up 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. @@ -59,8 +44,12 @@ * 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 +#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H +#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H + +#include + +#include "src/core/lib/iomgr/error.h" void grpc_wakeup_fd_global_init(void); void grpc_wakeup_fd_global_destroy(void); @@ -69,12 +58,16 @@ void grpc_wakeup_fd_global_destroy(void); * purposes only.*/ void grpc_wakeup_fd_global_init_force_fallback(void); +int grpc_has_wakeup_fd(void); +int grpc_cv_wakeup_fds_enabled(void); +void grpc_enable_cv_wakeup_fds(int enable); + 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); + grpc_error* (*init)(grpc_wakeup_fd* fd_info); + grpc_error* (*consume)(grpc_wakeup_fd* fd_info); + grpc_error* (*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); @@ -86,16 +79,18 @@ struct grpc_wakeup_fd { }; extern int grpc_allow_specialized_wakeup_fd; +extern int grpc_allow_pipe_wakeup_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); +grpc_error* grpc_wakeup_fd_init(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT; +grpc_error* grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd* fd_info) + GRPC_MUST_USE_RESULT; +grpc_error* grpc_wakeup_fd_wakeup(grpc_wakeup_fd* fd_info) GRPC_MUST_USE_RESULT; 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 */ +#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H */ diff --git a/third_party/grpc/src/core/lib/json/json.cc b/third_party/grpc/src/core/lib/json/json.cc new file mode 100644 index 00000000000000..e78b73cefd35b1 --- /dev/null +++ b/third_party/grpc/src/core/lib/json/json.cc @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include + +#include "src/core/lib/json/json.h" + +grpc_json* grpc_json_create(grpc_json_type type) { + grpc_json* json = static_cast(gpr_zalloc(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; + } + + if (json->owns_value) { + gpr_free((void*)json->value); + } + + gpr_free(json); +} + +grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child, + grpc_json* sibling) { + // link child up to parent + child->parent = parent; + // first child case. + if (parent->child == nullptr) { + GPR_ASSERT(sibling == nullptr); + parent->child = child; + return child; + } + if (sibling == nullptr) { + sibling = parent->child; + } + // always find the right most sibling. + while (sibling->next != nullptr) { + sibling = sibling->next; + } + sibling->next = child; + return child; +} + +grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent, + const char* key, const char* value, + grpc_json_type type, bool owns_value) { + grpc_json* child = grpc_json_create(type); + grpc_json_link_child(parent, child, sibling); + child->owns_value = owns_value; + child->value = value; + child->key = key; + return child; +} + +grpc_json* grpc_json_add_number_string_child(grpc_json* parent, grpc_json* it, + const char* name, int64_t num) { + char* num_str; + gpr_asprintf(&num_str, "%" PRId64, num); + return grpc_json_create_child(it, parent, name, num_str, GRPC_JSON_STRING, + true); +} diff --git a/third_party/grpc/src/core/lib/json/json.h b/third_party/grpc/src/core/lib/json/json.h new file mode 100644 index 00000000000000..8173845c723b3c --- /dev/null +++ b/third_party/grpc/src/core/lib/json/json.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_JSON_JSON_H +#define GRPC_CORE_LIB_JSON_JSON_H + +#include + +#include +#include + +#include "src/core/lib/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; + + /* if set, destructor will free value */ + bool owns_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, unless the boolean, owns_value, + * is true. + */ +grpc_json* grpc_json_create(grpc_json_type type); +void grpc_json_destroy(grpc_json* json); + +/* Links the child json object into the parent's json tree. If the parent + * already has children, then passing in the most recently added child as the + * sibling parameter is an optimization. For if sibling is NULL, this function + * will manually traverse the tree in order to find the right most sibling. + */ +grpc_json* grpc_json_link_child(grpc_json* parent, grpc_json* child, + grpc_json* sibling); + +/* Creates a child json object into the parent's json tree then links it in + * as described above. */ +grpc_json* grpc_json_create_child(grpc_json* sibling, grpc_json* parent, + const char* key, const char* value, + grpc_json_type type, bool owns_value); + +/* Creates a child json string object from the integer num, then links the + json object into the parent's json tree */ +grpc_json* grpc_json_add_number_string_child(grpc_json* parent, grpc_json* it, + const char* name, int64_t num); + +#endif /* GRPC_CORE_LIB_JSON_JSON_H */ diff --git a/third_party/grpc/src/core/lib/json/json_common.h b/third_party/grpc/src/core/lib/json/json_common.h new file mode 100644 index 00000000000000..bfa2ba32bea180 --- /dev/null +++ b/third_party/grpc/src/core/lib/json/json_common.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_JSON_JSON_COMMON_H +#define GRPC_CORE_LIB_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_CORE_LIB_JSON_JSON_COMMON_H */ diff --git a/third_party/grpc/src/core/json/json_reader.c b/third_party/grpc/src/core/lib/json/json_reader.cc similarity index 85% rename from third_party/grpc/src/core/json/json_reader.c rename to third_party/grpc/src/core/lib/json/json_reader.cc index 30da6f28f39632..819572e4dea73a 100644 --- a/third_party/grpc/src/core/json/json_reader.c +++ b/third_party/grpc/src/core/lib/json/json_reader.cc @@ -1,98 +1,83 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015-2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include - #include +#include + #include -#include "src/core/json/json_reader.h" +#include "src/core/lib/json/json_reader.h" -static void json_reader_string_clear(grpc_json_reader *reader) { +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, uint32_t c) { +static void json_reader_string_add_char(grpc_json_reader* reader, uint32_t c) { reader->vtable->string_add_char(reader->userdata, c); } -static void json_reader_string_add_utf32(grpc_json_reader *reader, +static void json_reader_string_add_utf32(grpc_json_reader* reader, uint32_t utf32) { reader->vtable->string_add_utf32(reader->userdata, utf32); } -static uint32_t grpc_json_reader_read_char(grpc_json_reader *reader) { +static uint32_t 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, +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) { + grpc_json_reader* reader) { return reader->vtable->container_ends(reader->userdata); } -static void json_reader_set_key(grpc_json_reader *reader) { +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) { +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) { +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) { +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) { +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) { +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) { +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; @@ -100,13 +85,13 @@ void grpc_json_reader_init(grpc_json_reader *reader, reader->state = GRPC_JSON_STATE_VALUE_BEGIN; } -int grpc_json_reader_is_complete(grpc_json_reader *reader) { +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) { +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader* reader) { uint32_t c, success; /* This state-machine is a strict implementation of ECMA-404 */ @@ -153,7 +138,7 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: case GRPC_JSON_STATE_VALUE_NUMBER_EPM: - success = (uint32_t)json_reader_set_number(reader); + success = static_cast(json_reader_set_number(reader)); if (!success) return GRPC_JSON_PARSE_ERROR; json_reader_string_clear(reader); reader->state = GRPC_JSON_STATE_VALUE_END; @@ -171,8 +156,9 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { switch (reader->state) { case GRPC_JSON_STATE_OBJECT_KEY_STRING: case GRPC_JSON_STATE_VALUE_STRING: - if (reader->unicode_high_surrogate != 0) + if (reader->unicode_high_surrogate != 0) { return GRPC_JSON_PARSE_ERROR; + } json_reader_string_add_char(reader, c); break; @@ -180,11 +166,19 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: case GRPC_JSON_STATE_VALUE_NUMBER_EPM: - success = (uint32_t)json_reader_set_number(reader); + if (reader->depth == 0) { + return GRPC_JSON_PARSE_ERROR; + } else if ((c == '}') && !reader->in_object) { + return GRPC_JSON_PARSE_ERROR; + } else if ((c == ']') && !reader->in_array) { + return GRPC_JSON_PARSE_ERROR; + } + success = static_cast(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. */ + /* The missing break here is intentional. */ + /* fallthrough */ case GRPC_JSON_STATE_VALUE_END: case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: @@ -195,8 +189,10 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { } if (reader->in_object) { reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; - } else { + } else if (reader->in_array) { reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + } else { + return GRPC_JSON_PARSE_ERROR; } } else { if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR; @@ -280,20 +276,23 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { break; case GRPC_JSON_STATE_OBJECT_KEY_STRING: - GPR_ASSERT(reader->unicode_high_surrogate == 0); + 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; + if (c < 32) 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) + 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); @@ -362,6 +361,8 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { reader->in_object = 0; reader->in_array = 1; break; + default: + return GRPC_JSON_PARSE_ERROR; } break; @@ -371,8 +372,9 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { } else { reader->state = GRPC_JSON_STATE_VALUE_STRING; } - if (reader->unicode_high_surrogate && c != 'u') + if (reader->unicode_high_surrogate && c != 'u') { return GRPC_JSON_PARSE_ERROR; + } switch (c) { case '"': case '/': @@ -415,8 +417,10 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { } else { return GRPC_JSON_PARSE_ERROR; } - reader->unicode_char = (uint16_t)(reader->unicode_char << 4); - reader->unicode_char = (uint16_t)(reader->unicode_char | c); + reader->unicode_char = + static_cast(reader->unicode_char << 4); + reader->unicode_char = + static_cast(reader->unicode_char | c); switch (reader->state) { case GRPC_JSON_STATE_STRING_ESCAPE_U1: @@ -443,9 +447,9 @@ grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { if (reader->unicode_high_surrogate == 0) return GRPC_JSON_PARSE_ERROR; utf32 = 0x10000; - utf32 += (uint32_t)( + utf32 += static_cast( (reader->unicode_high_surrogate - 0xd800) * 0x400); - utf32 += (uint32_t)(reader->unicode_char - 0xdc00); + utf32 += static_cast(reader->unicode_char - 0xdc00); json_reader_string_add_utf32(reader, utf32); reader->unicode_high_surrogate = 0; } else { diff --git a/third_party/grpc/src/core/json/json_reader.h b/third_party/grpc/src/core/lib/json/json_reader.h similarity index 61% rename from third_party/grpc/src/core/json/json_reader.h rename to third_party/grpc/src/core/lib/json/json_reader.h index 90b9f1f9fead95..78f7ad9f3a81fd 100644 --- a/third_party/grpc/src/core/json/json_reader.h +++ b/third_party/grpc/src/core/lib/json/json_reader.h @@ -1,41 +1,27 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_JSON_JSON_READER_H -#define GRPC_INTERNAL_CORE_JSON_JSON_READER_H +#ifndef GRPC_CORE_LIB_JSON_JSON_READER_H +#define GRPC_CORE_LIB_JSON_JSON_READER_H #include -#include "src/core/json/json_common.h" + +#include "src/core/lib/json/json_common.h" typedef enum { GRPC_JSON_STATE_OBJECT_KEY_BEGIN, @@ -82,27 +68,27 @@ struct grpc_json_reader; typedef struct grpc_json_reader_vtable { /* Clears your internal string scratchpad. */ - void (*string_clear)(void *userdata); + void (*string_clear)(void* userdata); /* Adds a char to the string scratchpad. */ - void (*string_add_char)(void *userdata, uint32_t c); + void (*string_add_char)(void* userdata, uint32_t c); /* Adds a utf32 char to the string scratchpad. */ - void (*string_add_utf32)(void *userdata, uint32_t c); + void (*string_add_utf32)(void* userdata, uint32_t c); /* Reads a character from your input. May be utf-8, 16 or 32. */ - uint32_t (*read_char)(void *userdata); + uint32_t (*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); + 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); + grpc_json_type (*container_ends)(void* userdata); /* Your internal string scratchpad is an object's key. */ - void (*set_key)(void *userdata); + void (*set_key)(void* userdata); /* Your internal string scratchpad is a string value. */ - void (*set_string)(void *userdata); + void (*set_string)(void* userdata); /* Your internal string scratchpad is a numerical value. Return 1 if valid. */ - int (*set_number)(void *userdata); + 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); + void (*set_true)(void* userdata); + void (*set_false)(void* userdata); + void (*set_null)(void* userdata); } grpc_json_reader_vtable; typedef struct grpc_json_reader { @@ -110,8 +96,8 @@ typedef struct grpc_json_reader { * The definition is public so you can put it on your stack. */ - void *userdata; - grpc_json_reader_vtable *vtable; + void* userdata; + grpc_json_reader_vtable* vtable; int depth; int in_object; int in_array; @@ -144,17 +130,17 @@ typedef enum { * . 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); +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); +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); +int grpc_json_reader_is_complete(grpc_json_reader* reader); -#endif /* GRPC_INTERNAL_CORE_JSON_JSON_READER_H */ +#endif /* GRPC_CORE_LIB_JSON_JSON_READER_H */ diff --git a/third_party/grpc/src/core/json/json_string.c b/third_party/grpc/src/core/lib/json/json_string.cc similarity index 65% rename from third_party/grpc/src/core/json/json_string.c rename to third_party/grpc/src/core/lib/json/json_string.cc index 2bc0b513d5ce3d..4f9175b9e7a52a 100644 --- a/third_party/grpc/src/core/json/json_string.c +++ b/third_party/grpc/src/core/lib/json/json_string.cc @@ -1,45 +1,32 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include + #include +#include #include #include -#include "src/core/json/json.h" -#include "src/core/json/json_reader.h" -#include "src/core/json/json_writer.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_reader.h" +#include "src/core/lib/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 @@ -53,13 +40,13 @@ * input size, and never expands it. */ typedef struct { - grpc_json *top; - grpc_json *current_container; - grpc_json *current_value; - uint8_t *input; - uint8_t *key; - uint8_t *string; - uint8_t *string_ptr; + grpc_json* top; + grpc_json* current_container; + grpc_json* current_value; + uint8_t* input; + uint8_t* key; + uint8_t* string; + uint8_t* string_ptr; size_t remaining_input; } json_reader_userdata; @@ -67,7 +54,7 @@ typedef struct { * The point is that we allocate that string in chunks of 256 bytes. */ typedef struct { - char *output; + char* output; size_t free_space; size_t string_len; size_t allocated; @@ -77,35 +64,36 @@ typedef struct { * 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; +static void json_writer_output_check(void* userdata, size_t needed) { + json_writer_userdata* state = static_cast(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->output = + static_cast(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; +static void json_writer_output_char(void* userdata, char c) { + json_writer_userdata* state = static_cast(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, +static void json_writer_output_string_with_len(void* userdata, const char* str, size_t len) { - json_writer_userdata *state = userdata; + json_writer_userdata* state = static_cast(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) { +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); } @@ -113,8 +101,8 @@ static void json_writer_output_string(void *userdata, const char *str) { /* 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; +static void json_reader_string_clear(void* userdata) { + json_reader_userdata* state = static_cast(userdata); if (state->string) { GPR_ASSERT(state->string_ptr < state->input); *state->string_ptr++ = 0; @@ -122,17 +110,17 @@ static void json_reader_string_clear(void *userdata) { state->string = state->string_ptr; } -static void json_reader_string_add_char(void *userdata, uint32_t c) { - json_reader_userdata *state = userdata; +static void json_reader_string_add_char(void* userdata, uint32_t c) { + json_reader_userdata* state = static_cast(userdata); GPR_ASSERT(state->string_ptr < state->input); GPR_ASSERT(c <= 0xff); - *state->string_ptr++ = (uint8_t)c; + *state->string_ptr++ = static_cast(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, uint32_t c) { +static void json_reader_string_add_utf32(void* userdata, uint32_t c) { if (c <= 0x7f) { json_reader_string_add_char(userdata, c); } else if (c <= 0x7ff) { @@ -162,9 +150,9 @@ static void json_reader_string_add_utf32(void *userdata, uint32_t c) { /* 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 uint32_t json_reader_read_char(void *userdata) { +static uint32_t json_reader_read_char(void* userdata) { uint32_t r; - json_reader_userdata *state = userdata; + json_reader_userdata* state = static_cast(userdata); if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; @@ -182,9 +170,9 @@ static uint32_t json_reader_read_char(void *userdata) { /* 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); +static grpc_json* json_create_and_link(void* userdata, grpc_json_type type) { + json_reader_userdata* state = static_cast(userdata); + grpc_json* json = grpc_json_create(type); json->parent = state->current_container; json->prev = state->current_value; @@ -198,7 +186,7 @@ static grpc_json *json_create_and_link(void *userdata, grpc_json_type type) { json->parent->child = json; } if (json->parent->type == GRPC_JSON_OBJECT) { - json->key = (char *)state->key; + json->key = reinterpret_cast(state->key); } } if (!state->top) { @@ -208,15 +196,15 @@ static grpc_json *json_create_and_link(void *userdata, grpc_json_type type) { return json; } -static void json_reader_container_begins(void *userdata, grpc_json_type type) { - json_reader_userdata *state = userdata; - grpc_json *container; +static void json_reader_container_begins(void* userdata, grpc_json_type type) { + json_reader_userdata* state = static_cast(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; + state->current_value = nullptr; } /* It's important to remember that the reader is mostly stateless, so it @@ -228,9 +216,9 @@ static void json_reader_container_begins(void *userdata, grpc_json_type type) { * 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) { +static grpc_json_type json_reader_container_ends(void* userdata) { grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; - json_reader_userdata *state = userdata; + json_reader_userdata* state = static_cast(userdata); GPR_ASSERT(state->current_container); @@ -250,36 +238,36 @@ static grpc_json_type json_reader_container_ends(void *userdata) { * 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; +static void json_reader_set_key(void* userdata) { + json_reader_userdata* state = static_cast(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 void json_reader_set_string(void* userdata) { + json_reader_userdata* state = static_cast(userdata); + grpc_json* json = json_create_and_link(userdata, GRPC_JSON_STRING); + json->value = reinterpret_cast(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; +static int json_reader_set_number(void* userdata) { + json_reader_userdata* state = static_cast(userdata); + grpc_json* json = json_create_and_link(userdata, GRPC_JSON_NUMBER); + json->value = reinterpret_cast(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) { +static void json_reader_set_true(void* userdata) { json_create_and_link(userdata, GRPC_JSON_TRUE); } -static void json_reader_set_false(void *userdata) { +static void json_reader_set_false(void* userdata) { json_create_and_link(userdata, GRPC_JSON_FALSE); } -static void json_reader_set_null(void *userdata) { +static void json_reader_set_null(void* userdata) { json_create_and_link(userdata, GRPC_JSON_NULL); } @@ -292,17 +280,17 @@ static grpc_json_reader_vtable reader_vtable = { 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* 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* json = nullptr; grpc_json_reader_status status; - if (!input) return NULL; + if (!input) return nullptr; - state.top = state.current_container = state.current_value = NULL; - state.string = state.key = NULL; - state.string_ptr = state.input = (uint8_t *)input; + state.top = state.current_container = state.current_value = nullptr; + state.string = state.key = nullptr; + state.string_ptr = state.input = reinterpret_cast(input); state.remaining_input = size; grpc_json_reader_init(&reader, &reader_vtable, &state); @@ -311,7 +299,7 @@ grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { if ((status != GRPC_JSON_DONE) && json) { grpc_json_destroy(json); - json = NULL; + json = nullptr; } return json; @@ -319,11 +307,11 @@ grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { #define UNBOUND_JSON_STRING_LENGTH 0x7fffffff -grpc_json *grpc_json_parse_string(char *input) { +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, +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); @@ -363,11 +351,11 @@ 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) { +char* grpc_json_dump_to_string(grpc_json* json, int indent) { grpc_json_writer writer; json_writer_userdata state; - state.output = NULL; + state.output = nullptr; state.free_space = state.string_len = state.allocated = 0; grpc_json_writer_init(&writer, indent, &writer_vtable, &state); diff --git a/third_party/grpc/src/core/json/json_writer.c b/third_party/grpc/src/core/lib/json/json_writer.cc similarity index 64% rename from third_party/grpc/src/core/json/json_writer.c rename to third_party/grpc/src/core/lib/json/json_writer.cc index 326ec2d431f061..7bbdccc7a3742a 100644 --- a/third_party/grpc/src/core/json/json_writer.c +++ b/third_party/grpc/src/core/lib/json/json_writer.cc @@ -1,58 +1,43 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include - #include -#include "src/core/json/json_writer.h" +#include + +#include "src/core/lib/json/json_writer.h" -static void json_writer_output_char(grpc_json_writer *writer, char c) { +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) { +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) { +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) { +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; @@ -60,14 +45,14 @@ void grpc_json_writer_init(grpc_json_writer *writer, int indent, writer->userdata = userdata; } -static void json_writer_output_indent(grpc_json_writer *writer) { +static void json_writer_output_indent(grpc_json_writer* writer) { static const char spacesstr[] = " " " " " " " "; - unsigned spaces = (unsigned)(writer->depth * writer->indent); + unsigned spaces = static_cast(writer->depth * writer->indent); if (writer->indent == 0) return; @@ -79,7 +64,7 @@ static void json_writer_output_indent(grpc_json_writer *writer) { while (spaces >= (sizeof(spacesstr) - 1)) { json_writer_output_string_with_len(writer, spacesstr, sizeof(spacesstr) - 1); - spaces -= (unsigned)(sizeof(spacesstr) - 1); + spaces -= static_cast(sizeof(spacesstr) - 1); } if (spaces == 0) return; @@ -88,7 +73,7 @@ static void json_writer_output_indent(grpc_json_writer *writer) { writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); } -static void json_writer_value_end(grpc_json_writer *writer) { +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; @@ -100,7 +85,7 @@ static void json_writer_value_end(grpc_json_writer *writer) { } } -static void json_writer_escape_utf16(grpc_json_writer *writer, uint16_t utf16) { +static void json_writer_escape_utf16(grpc_json_writer* writer, uint16_t utf16) { static const char hex[] = "0123456789abcdef"; json_writer_output_string_with_len(writer, "\\u", 2); @@ -110,17 +95,17 @@ static void json_writer_escape_utf16(grpc_json_writer *writer, uint16_t utf16) { json_writer_output_char(writer, hex[(utf16)&0x0f]); } -static void json_writer_escape_string(grpc_json_writer *writer, - const char *string) { +static void json_writer_escape_string(grpc_json_writer* writer, + const char* string) { json_writer_output_char(writer, '"'); for (;;) { - uint8_t c = (uint8_t)*string++; + uint8_t c = static_cast(*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); + json_writer_output_char(writer, static_cast(c)); } else if ((c < 32) || (c == 127)) { switch (c) { case '\b': @@ -161,7 +146,7 @@ static void json_writer_escape_string(grpc_json_writer *writer, } for (i = 0; i < extra; i++) { utf32 <<= 6; - c = (uint8_t)(*string++); + c = static_cast(*string++); /* Breaks out and bail on any invalid UTF-8 sequence, including \0. */ if ((c & 0xc0) != 0x80) { valid = 0; @@ -194,10 +179,12 @@ static void json_writer_escape_string(grpc_json_writer *writer, * That range is exactly 20 bits. */ utf32 -= 0x10000; - json_writer_escape_utf16(writer, (uint16_t)(0xd800 | (utf32 >> 10))); - json_writer_escape_utf16(writer, (uint16_t)(0xdc00 | (utf32 & 0x3ff))); + json_writer_escape_utf16(writer, + static_cast(0xd800 | (utf32 >> 10))); + json_writer_escape_utf16( + writer, static_cast(0xdc00 | (utf32 & 0x3ff))); } else { - json_writer_escape_utf16(writer, (uint16_t)utf32); + json_writer_escape_utf16(writer, static_cast(utf32)); } } } @@ -205,7 +192,7 @@ static void json_writer_escape_string(grpc_json_writer *writer, json_writer_output_char(writer, '"'); } -void grpc_json_writer_container_begins(grpc_json_writer *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); @@ -215,7 +202,7 @@ void grpc_json_writer_container_begins(grpc_json_writer *writer, writer->depth++; } -void grpc_json_writer_container_ends(grpc_json_writer *writer, +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'); @@ -226,7 +213,7 @@ void grpc_json_writer_container_ends(grpc_json_writer *writer, writer->got_key = 0; } -void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string) { +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); @@ -234,23 +221,23 @@ void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string) { writer->got_key = 1; } -void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string) { +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) { +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) { +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); diff --git a/third_party/grpc/src/core/lib/json/json_writer.h b/third_party/grpc/src/core/lib/json/json_writer.h new file mode 100644 index 00000000000000..ba0bedde7fab74 --- /dev/null +++ b/third_party/grpc/src/core/lib/json/json_writer.h @@ -0,0 +1,84 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* 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_CORE_LIB_JSON_JSON_WRITER_H +#define GRPC_CORE_LIB_JSON_JSON_WRITER_H + +#include + +#include + +#include "src/core/lib/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_CORE_LIB_JSON_JSON_WRITER_H */ diff --git a/third_party/grpc/src/core/profiling/basic_timers.c b/third_party/grpc/src/core/lib/profiling/basic_timers.cc similarity index 54% rename from third_party/grpc/src/core/profiling/basic_timers.c rename to third_party/grpc/src/core/lib/profiling/basic_timers.cc index df32472d1c7257..b19ad9fc23d0f0 100644 --- a/third_party/grpc/src/core/profiling/basic_timers.c +++ b/third_party/grpc/src/core/lib/profiling/basic_timers.cc @@ -1,55 +1,44 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include -#ifdef GRPC_BASIC_PROFILER +#include "src/core/lib/profiling/timers.h" -#include "src/core/profiling/timers.h" +#ifdef GRPC_BASIC_PROFILER #include #include -#include #include -#include +#include +#include +#include #include +#include + +#include "src/core/lib/gpr/env.h" typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type; typedef struct gpr_timer_entry { gpr_timespec tm; - const char *tagstr; - const char *file; + const char* tagstr; + const char* file; short line; char type; uint8_t important; @@ -60,31 +49,43 @@ typedef struct gpr_timer_entry { typedef struct gpr_timer_log { size_t num_entries; - struct gpr_timer_log *next; - struct gpr_timer_log *prev; + struct gpr_timer_log* next; + struct gpr_timer_log* prev; gpr_timer_entry log[MAX_COUNT]; } gpr_timer_log; typedef struct gpr_timer_log_list { - gpr_timer_log *head; + gpr_timer_log* head; /* valid iff head!=NULL */ - gpr_timer_log *tail; + gpr_timer_log* tail; } gpr_timer_log_list; -static __thread gpr_timer_log *g_thread_log; +static __thread gpr_timer_log* g_thread_log; static gpr_once g_once_init = GPR_ONCE_INIT; -static FILE *output_file; -static const char *output_filename = "latency_trace.txt"; +static FILE* output_file; +static const char* output_filename_or_null = NULL; static pthread_mutex_t g_mu; static pthread_cond_t g_cv; static gpr_timer_log_list g_in_progress_logs; static gpr_timer_log_list g_done_logs; static int g_shutdown; -static gpr_thd_id g_writing_thread; +static pthread_t g_writing_thread; static __thread int g_thread_id; static int g_next_thread_id; +static int g_writing_enabled = 1; + +static const char* output_filename() { + if (output_filename_or_null == NULL) { + output_filename_or_null = gpr_getenv("LATENCY_TRACE"); + if (output_filename_or_null == NULL || + strlen(output_filename_or_null) == 0) { + output_filename_or_null = "latency_trace.txt"; + } + } + return output_filename_or_null; +} -static int timer_log_push_back(gpr_timer_log_list *list, gpr_timer_log *log) { +static int timer_log_push_back(gpr_timer_log_list* list, gpr_timer_log* log) { if (list->head == NULL) { list->head = list->tail = log; log->next = log->prev = NULL; @@ -98,8 +99,8 @@ static int timer_log_push_back(gpr_timer_log_list *list, gpr_timer_log *log) { } } -static gpr_timer_log *timer_log_pop_front(gpr_timer_log_list *list) { - gpr_timer_log *out = list->head; +static gpr_timer_log* timer_log_pop_front(gpr_timer_log_list* list) { + gpr_timer_log* out = list->head; if (out != NULL) { list->head = out->next; if (list->head != NULL) { @@ -111,7 +112,7 @@ static gpr_timer_log *timer_log_pop_front(gpr_timer_log_list *list) { return out; } -static void timer_log_remove(gpr_timer_log_list *list, gpr_timer_log *log) { +static void timer_log_remove(gpr_timer_log_list* list, gpr_timer_log* log) { if (log->prev == NULL) { list->head = log->next; if (list->head != NULL) { @@ -130,27 +131,27 @@ static void timer_log_remove(gpr_timer_log_list *list, gpr_timer_log *log) { } } -static void write_log(gpr_timer_log *log) { +static void write_log(gpr_timer_log* log) { size_t i; if (output_file == NULL) { - output_file = fopen(output_filename, "w"); + output_file = fopen(output_filename(), "w"); } for (i = 0; i < log->num_entries; i++) { - gpr_timer_entry *entry = &(log->log[i]); + gpr_timer_entry* entry = &(log->log[i]); if (gpr_time_cmp(entry->tm, gpr_time_0(entry->tm.clock_type)) < 0) { entry->tm = gpr_time_0(entry->tm.clock_type); } fprintf(output_file, - "{\"t\": %lld.%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": " + "{\"t\": %" PRId64 + ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": " "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n", - (long long)entry->tm.tv_sec, (int)entry->tm.tv_nsec, entry->thd, - entry->type, entry->tagstr, entry->file, entry->line, - entry->important); + entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type, + entry->tagstr, entry->file, entry->line, entry->important); } } -static void writing_thread(void *unused) { - gpr_timer_log *log; +static void* writing_thread(void* unused) { + gpr_timer_log* log; pthread_mutex_lock(&g_mu); for (;;) { while ((log = timer_log_pop_front(&g_done_logs)) == NULL && !g_shutdown) { @@ -164,25 +165,25 @@ static void writing_thread(void *unused) { } if (g_shutdown) { pthread_mutex_unlock(&g_mu); - return; + return NULL; } } } -static void flush_logs(gpr_timer_log_list *list) { - gpr_timer_log *log; +static void flush_logs(gpr_timer_log_list* list) { + gpr_timer_log* log; while ((log = timer_log_pop_front(list)) != NULL) { write_log(log); free(log); } } -static void finish_writing() { +static void finish_writing(void) { pthread_mutex_lock(&g_mu); g_shutdown = 1; pthread_cond_signal(&g_cv); pthread_mutex_unlock(&g_mu); - gpr_thd_join(g_writing_thread); + pthread_join(g_writing_thread, NULL); gpr_log(GPR_INFO, "flushing logs"); @@ -196,21 +197,25 @@ static void finish_writing() { } } -void gpr_timers_set_log_filename(const char *filename) { - output_filename = filename; +void gpr_timers_set_log_filename(const char* filename) { + output_filename_or_null = filename; } static void init_output() { - gpr_thd_options options = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&options); - gpr_thd_new(&g_writing_thread, writing_thread, NULL, &options); + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + pthread_create(&g_writing_thread, &attr, &writing_thread, NULL); + pthread_attr_destroy(&attr); + atexit(finish_writing); } static void rotate_log() { - gpr_timer_log *new = malloc(sizeof(*new)); + /* Using malloc here, as this code could end up being called by gpr_malloc */ + gpr_timer_log* log = static_cast(malloc(sizeof(*log))); gpr_once_init(&g_once_init, init_output); - new->num_entries = 0; + log->num_entries = 0; pthread_mutex_lock(&g_mu); if (g_thread_log != NULL) { timer_log_remove(&g_in_progress_logs, g_thread_log); @@ -220,14 +225,18 @@ static void rotate_log() { } else { g_thread_id = g_next_thread_id++; } - timer_log_push_back(&g_in_progress_logs, new); + timer_log_push_back(&g_in_progress_logs, log); pthread_mutex_unlock(&g_mu); - g_thread_log = new; + g_thread_log = log; } -static void gpr_timers_log_add(const char *tagstr, marker_type type, - int important, const char *file, int line) { - gpr_timer_entry *entry; +static void gpr_timers_log_add(const char* tagstr, marker_type type, + int important, const char* file, int line) { + gpr_timer_entry* entry; + + if (!g_writing_enabled) { + return; + } if (g_thread_log == NULL || g_thread_log->num_entries == MAX_COUNT) { rotate_log(); @@ -245,21 +254,23 @@ static void gpr_timers_log_add(const char *tagstr, marker_type type, } /* Latency profiler API implementation. */ -void gpr_timer_add_mark(const char *tagstr, int important, const char *file, +void gpr_timer_add_mark(const char* tagstr, int important, const char* file, int line) { gpr_timers_log_add(tagstr, MARK, important, file, line); } -void gpr_timer_begin(const char *tagstr, int important, const char *file, +void gpr_timer_begin(const char* tagstr, int important, const char* file, int line) { gpr_timers_log_add(tagstr, BEGIN, important, file, line); } -void gpr_timer_end(const char *tagstr, int important, const char *file, +void gpr_timer_end(const char* tagstr, int important, const char* file, int line) { gpr_timers_log_add(tagstr, END, important, file, line); } +void gpr_timer_set_enabled(int enabled) { g_writing_enabled = enabled; } + /* Basic profiler specific API functions. */ void gpr_timers_global_init(void) {} @@ -270,5 +281,7 @@ void gpr_timers_global_init(void) {} void gpr_timers_global_destroy(void) {} -void gpr_timers_set_log_filename(const char *filename) {} +void gpr_timers_set_log_filename(const char* filename) {} + +void gpr_timer_set_enabled(int enabled) {} #endif /* GRPC_BASIC_PROFILER */ diff --git a/third_party/grpc/src/core/profiling/stap_probes.d b/third_party/grpc/src/core/lib/profiling/stap_probes.d similarity index 100% rename from third_party/grpc/src/core/profiling/stap_probes.d rename to third_party/grpc/src/core/lib/profiling/stap_probes.d diff --git a/third_party/grpc/src/core/lib/profiling/stap_timers.cc b/third_party/grpc/src/core/lib/profiling/stap_timers.cc new file mode 100644 index 00000000000000..5ee1c4317c714e --- /dev/null +++ b/third_party/grpc/src/core/lib/profiling/stap_timers.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GRPC_STAP_PROFILER + +#include "src/core/lib/profiling/timers.h" + +#include +/* Generated from src/core/profiling/stap_probes.d */ +#include "src/core/lib/profiling/stap_probes.h" + +/* Latency profiler API implementation. */ +void gpr_timer_add_mark(int tag, const char* tagstr, void* id, const char* file, + int line) { + _STAP_ADD_MARK(tag); +} + +void gpr_timer_add_important_mark(int tag, const char* tagstr, void* id, + const char* file, int line) { + _STAP_ADD_IMPORTANT_MARK(tag); +} + +void gpr_timer_begin(int tag, const char* tagstr, void* id, const char* file, + int line) { + _STAP_TIMING_NS_BEGIN(tag); +} + +void gpr_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/third_party/grpc/src/core/lib/profiling/timers.h b/third_party/grpc/src/core/lib/profiling/timers.h new file mode 100644 index 00000000000000..7ff72783ec91cf --- /dev/null +++ b/third_party/grpc/src/core/lib/profiling/timers.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_PROFILING_TIMERS_H +#define GRPC_CORE_LIB_PROFILING_TIMERS_H + +void gpr_timers_global_init(void); +void gpr_timers_global_destroy(void); + +void gpr_timer_add_mark(const char* tagstr, int important, const char* file, + int line); +void gpr_timer_begin(const char* tagstr, int important, const char* file, + int line); +void gpr_timer_end(const char* tagstr, int important, const char* file, + int line); + +void gpr_timers_set_log_filename(const char* filename); + +void gpr_timer_set_enabled(int enabled); + +#if !(defined(GRPC_STAP_PROFILER) + defined(GRPC_BASIC_PROFILER) + \ + defined(GRPC_CUSTOM_PROFILER)) +/* No profiling. No-op all the things. */ +#define GPR_TIMER_MARK(tag, important) \ + do { \ + } while (0) + +#define GPR_TIMER_SCOPE(tag, important) \ + 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 +#if defined(GRPC_STAP_PROFILER) && defined(GRPC_CUSTOM_PROFILER) +#error "GRPC_STAP_PROFILER and GRPC_CUSTOM_PROFILER are mutually exclusive." +#endif +#if defined(GRPC_CUSTOM_PROFILER) && defined(GRPC_BASIC_PROFILER) +#error "GRPC_CUSTOM_PROFILER and GRPC_BASIC_PROFILER are mutually exclusive." +#endif + +/* Generic profiling interface. */ +#define GPR_TIMER_MARK(tag, important) \ + gpr_timer_add_mark(tag, important, __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 */ + +namespace grpc { +class ProfileScope { + public: + ProfileScope(const char* desc, bool important, const char* file, int line) + : desc_(desc) { + gpr_timer_begin(desc_, important ? 1 : 0, file, line); + } + ~ProfileScope() { gpr_timer_end(desc_, 0, "n/a", 0); } + + private: + const char* const desc_; +}; +} // namespace grpc + +#define GPR_TIMER_SCOPE_NAME_INTERNAL(prefix, line) prefix##line +#define GPR_TIMER_SCOPE_NAME(prefix, line) \ + GPR_TIMER_SCOPE_NAME_INTERNAL(prefix, line) +#define GPR_TIMER_SCOPE(tag, important) \ + ::grpc::ProfileScope GPR_TIMER_SCOPE_NAME(_profile_scope_, __LINE__)( \ + (tag), (important), __FILE__, __LINE__) + +#endif /* at least one profiler requested. */ + +#endif /* GRPC_CORE_LIB_PROFILING_TIMERS_H */ diff --git a/third_party/grpc/src/core/lib/security/context/security_context.cc b/third_party/grpc/src/core/lib/security/context/security_context.cc new file mode 100644 index 00000000000000..8443ee0695a30e --- /dev/null +++ b/third_party/grpc/src/core/lib/security/context/security_context.cc @@ -0,0 +1,322 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/arena.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" + +#include +#include +#include +#include + +grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount( + false, "auth_context_refcount"); + +/* --- grpc_call --- */ + +grpc_call_error grpc_call_set_credentials(grpc_call* call, + grpc_call_credentials* creds) { + grpc_core::ExecCtx exec_ctx; + grpc_client_security_context* ctx = nullptr; + GRPC_API_TRACE("grpc_call_set_credentials(call=%p, creds=%p)", 2, + (call, creds)); + if (!grpc_call_is_client(call)) { + gpr_log(GPR_ERROR, "Method is client-side only."); + return GRPC_CALL_ERROR_NOT_ON_SERVER; + } + ctx = static_cast( + grpc_call_context_get(call, GRPC_CONTEXT_SECURITY)); + if (ctx == nullptr) { + ctx = grpc_client_security_context_create(grpc_call_get_arena(call), creds); + grpc_call_context_set(call, GRPC_CONTEXT_SECURITY, ctx, + grpc_client_security_context_destroy); + } else { + ctx->creds = creds != nullptr ? creds->Ref() : nullptr; + } + + 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); + GRPC_API_TRACE("grpc_call_auth_context(call=%p)", 1, (call)); + if (sec_ctx == nullptr) return nullptr; + if (grpc_call_is_client(call)) { + auto* sc = static_cast(sec_ctx); + if (sc->auth_context == nullptr) { + return nullptr; + } else { + return sc->auth_context + ->Ref(DEBUG_LOCATION, "grpc_call_auth_context client") + .release(); + } + } else { + auto* sc = static_cast(sec_ctx); + if (sc->auth_context == nullptr) { + return nullptr; + } else { + return sc->auth_context + ->Ref(DEBUG_LOCATION, "grpc_call_auth_context server") + .release(); + } + } +} + +void grpc_auth_context_release(grpc_auth_context* context) { + GRPC_API_TRACE("grpc_auth_context_release(context=%p)", 1, (context)); + if (context == nullptr) return; + context->Unref(DEBUG_LOCATION, "grpc_auth_context_unref"); +} + +/* --- grpc_client_security_context --- */ +grpc_client_security_context::~grpc_client_security_context() { + auth_context.reset(DEBUG_LOCATION, "client_security_context"); + if (extension.instance != nullptr && extension.destroy != nullptr) { + extension.destroy(extension.instance); + } +} + +grpc_client_security_context* grpc_client_security_context_create( + gpr_arena* arena, grpc_call_credentials* creds) { + return new (gpr_arena_alloc(arena, sizeof(grpc_client_security_context))) + grpc_client_security_context(creds != nullptr ? creds->Ref() : nullptr); +} + +void grpc_client_security_context_destroy(void* ctx) { + grpc_core::ExecCtx exec_ctx; + grpc_client_security_context* c = + static_cast(ctx); + c->~grpc_client_security_context(); +} + +/* --- grpc_server_security_context --- */ +grpc_server_security_context::~grpc_server_security_context() { + auth_context.reset(DEBUG_LOCATION, "server_security_context"); + if (extension.instance != nullptr && extension.destroy != nullptr) { + extension.destroy(extension.instance); + } +} + +grpc_server_security_context* grpc_server_security_context_create( + gpr_arena* arena) { + return new (gpr_arena_alloc(arena, sizeof(grpc_server_security_context))) + grpc_server_security_context(); +} + +void grpc_server_security_context_destroy(void* ctx) { + grpc_server_security_context* c = + static_cast(ctx); + c->~grpc_server_security_context(); +} + +/* --- grpc_auth_context --- */ + +static grpc_auth_property_iterator empty_iterator = {nullptr, 0, nullptr}; + +const char* grpc_auth_context_peer_identity_property_name( + const grpc_auth_context* ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_identity_property_name(ctx=%p)", 1, + (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); + GRPC_API_TRACE( + "grpc_auth_context_set_peer_identity_property_name(ctx=%p, name=%s)", 2, + (ctx, name)); + if (prop == nullptr) { + gpr_log(GPR_ERROR, "Property name %s not found in auth context.", + name != nullptr ? name : "NULL"); + return 0; + } + ctx->set_peer_identity_property_name(prop->name); + return 1; +} + +int grpc_auth_context_peer_is_authenticated(const grpc_auth_context* ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_is_authenticated(ctx=%p)", 1, (ctx)); + return ctx->is_authenticated(); +} + +grpc_auth_property_iterator grpc_auth_context_property_iterator( + const grpc_auth_context* ctx) { + grpc_auth_property_iterator it = empty_iterator; + GRPC_API_TRACE("grpc_auth_context_property_iterator(ctx=%p)", 1, (ctx)); + if (ctx == nullptr) return it; + it.ctx = ctx; + return it; +} + +const grpc_auth_property* grpc_auth_property_iterator_next( + grpc_auth_property_iterator* it) { + GRPC_API_TRACE("grpc_auth_property_iterator_next(it=%p)", 1, (it)); + if (it == nullptr || it->ctx == nullptr) return nullptr; + while (it->index == it->ctx->properties().count) { + if (it->ctx->chained() == nullptr) return nullptr; + it->ctx = it->ctx->chained(); + it->index = 0; + } + if (it->name == nullptr) { + 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 != nullptr); + 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; + GRPC_API_TRACE("grpc_auth_context_find_properties_by_name(ctx=%p, name=%s)", + 2, (ctx, name)); + if (ctx == nullptr || name == nullptr) 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) { + GRPC_API_TRACE("grpc_auth_context_peer_identity(ctx=%p)", 1, (ctx)); + if (ctx == nullptr) return empty_iterator; + return grpc_auth_context_find_properties_by_name( + ctx, ctx->peer_identity_property_name()); +} + +void grpc_auth_context::ensure_capacity() { + if (properties_.count == properties_.capacity) { + properties_.capacity = + GPR_MAX(properties_.capacity + 8, properties_.capacity * 2); + properties_.array = static_cast(gpr_realloc( + properties_.array, properties_.capacity * sizeof(grpc_auth_property))); + } +} + +void grpc_auth_context::add_property(const char* name, const char* value, + size_t value_length) { + ensure_capacity(); + grpc_auth_property* prop = &properties_.array[properties_.count++]; + prop->name = gpr_strdup(name); + prop->value = static_cast(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_property(grpc_auth_context* ctx, const char* name, + const char* value, size_t value_length) { + GRPC_API_TRACE( + "grpc_auth_context_add_property(ctx=%p, name=%s, value=%*.*s, " + "value_length=%lu)", + 6, + (ctx, name, (int)value_length, (int)value_length, value, + (unsigned long)value_length)); + ctx->add_property(name, value, value_length); +} + +void grpc_auth_context::add_cstring_property(const char* name, + const char* value) { + ensure_capacity(); + grpc_auth_property* prop = &properties_.array[properties_.count++]; + prop->name = gpr_strdup(name); + prop->value = gpr_strdup(value); + prop->value_length = strlen(value); +} + +void grpc_auth_context_add_cstring_property(grpc_auth_context* ctx, + const char* name, + const char* value) { + GRPC_API_TRACE( + "grpc_auth_context_add_cstring_property(ctx=%p, name=%s, value=%s)", 3, + (ctx, name, value)); + ctx->add_cstring_property(name, 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)); +} + +static void auth_context_pointer_arg_destroy(void* p) { + if (p != nullptr) { + static_cast(p)->Unref(DEBUG_LOCATION, + "auth_context_pointer_arg"); + } +} + +static void* auth_context_pointer_arg_copy(void* p) { + auto* ctx = static_cast(p); + return ctx == nullptr + ? nullptr + : ctx->Ref(DEBUG_LOCATION, "auth_context_pointer_arg").release(); +} + +static int auth_context_pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); } + +static const grpc_arg_pointer_vtable auth_context_pointer_vtable = { + auth_context_pointer_arg_copy, auth_context_pointer_arg_destroy, + auth_context_pointer_cmp}; + +grpc_arg grpc_auth_context_to_arg(grpc_auth_context* p) { + return grpc_channel_arg_pointer_create((char*)GRPC_AUTH_CONTEXT_ARG, p, + &auth_context_pointer_vtable); +} + +grpc_auth_context* grpc_auth_context_from_arg(const grpc_arg* arg) { + if (strcmp(arg->key, GRPC_AUTH_CONTEXT_ARG) != 0) return nullptr; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_AUTH_CONTEXT_ARG); + return nullptr; + } + return static_cast(arg->value.pointer.p); +} + +grpc_auth_context* grpc_find_auth_context_in_args( + const grpc_channel_args* args) { + size_t i; + if (args == nullptr) return nullptr; + for (i = 0; i < args->num_args; i++) { + grpc_auth_context* p = grpc_auth_context_from_arg(&args->args[i]); + if (p != nullptr) return p; + } + return nullptr; +} diff --git a/third_party/grpc/src/core/lib/security/context/security_context.h b/third_party/grpc/src/core/lib/security/context/security_context.h new file mode 100644 index 00000000000000..b43ee5e62d5d29 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/context/security_context.h @@ -0,0 +1,151 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H +#define GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H + +#include + +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/security/credentials/credentials.h" + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_auth_context_refcount; + +struct gpr_arena; + +/* --- grpc_auth_context --- + + High level authentication context object. Can optionally be chained. */ + +/* Property names are always NULL terminated. */ + +struct grpc_auth_property_array { + grpc_auth_property* array = nullptr; + size_t count = 0; + size_t capacity = 0; +}; + +void grpc_auth_property_reset(grpc_auth_property* property); + +// This type is forward declared as a C struct and we cannot define it as a +// class. Otherwise, compiler will complain about type mismatch due to +// -Wmismatched-tags. +struct grpc_auth_context + : public grpc_core::RefCounted { + public: + explicit grpc_auth_context( + grpc_core::RefCountedPtr chained) + : grpc_core::RefCounted( + &grpc_trace_auth_context_refcount), + chained_(std::move(chained)) { + if (chained_ != nullptr) { + peer_identity_property_name_ = chained_->peer_identity_property_name_; + } + } + + ~grpc_auth_context() { + chained_.reset(DEBUG_LOCATION, "chained"); + if (properties_.array != nullptr) { + for (size_t i = 0; i < properties_.count; i++) { + grpc_auth_property_reset(&properties_.array[i]); + } + gpr_free(properties_.array); + } + } + + const grpc_auth_context* chained() const { return chained_.get(); } + const grpc_auth_property_array& properties() const { return properties_; } + + bool is_authenticated() const { + return peer_identity_property_name_ != nullptr; + } + const char* peer_identity_property_name() const { + return peer_identity_property_name_; + } + void set_peer_identity_property_name(const char* name) { + peer_identity_property_name_ = name; + } + + void ensure_capacity(); + void add_property(const char* name, const char* value, size_t value_length); + void add_cstring_property(const char* name, const char* value); + + private: + grpc_core::RefCountedPtr chained_; + grpc_auth_property_array properties_; + const char* peer_identity_property_name_ = nullptr; +}; + +/* --- grpc_security_context_extension --- + + Extension to the security context that may be set in a filter and accessed + later by a higher level method on a grpc_call object. */ + +struct grpc_security_context_extension { + void* instance = nullptr; + void (*destroy)(void*) = nullptr; +}; + +/* --- grpc_client_security_context --- + + Internal client-side security context. */ + +struct grpc_client_security_context { + explicit grpc_client_security_context( + grpc_core::RefCountedPtr creds) + : creds(std::move(creds)) {} + ~grpc_client_security_context(); + + grpc_core::RefCountedPtr creds; + grpc_core::RefCountedPtr auth_context; + grpc_security_context_extension extension; +}; + +grpc_client_security_context* grpc_client_security_context_create( + gpr_arena* arena, grpc_call_credentials* creds); +void grpc_client_security_context_destroy(void* ctx); + +/* --- grpc_server_security_context --- + + Internal server-side security context. */ + +struct grpc_server_security_context { + grpc_server_security_context() = default; + ~grpc_server_security_context(); + + grpc_core::RefCountedPtr auth_context; + grpc_security_context_extension extension; +}; + +grpc_server_security_context* grpc_server_security_context_create( + gpr_arena* arena); +void grpc_server_security_context_destroy(void* ctx); + +/* --- Channel args for auth context --- */ +#define GRPC_AUTH_CONTEXT_ARG "grpc.auth_context" + +grpc_arg grpc_auth_context_to_arg(grpc_auth_context* c); +grpc_auth_context* grpc_auth_context_from_arg(const grpc_arg* arg); +grpc_auth_context* grpc_find_auth_context_in_args( + const grpc_channel_args* args); + +#endif /* GRPC_CORE_LIB_SECURITY_CONTEXT_SECURITY_CONTEXT_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.cc new file mode 100644 index 00000000000000..06546492bc747f --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.cc @@ -0,0 +1,107 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/alts/alts_credentials.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" +#include "src/core/lib/security/security_connector/alts/alts_security_connector.h" + +#define GRPC_CREDENTIALS_TYPE_ALTS "Alts" +#define GRPC_ALTS_HANDSHAKER_SERVICE_URL "metadata.google.internal:8080" + +grpc_alts_credentials::grpc_alts_credentials( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url) + : grpc_channel_credentials(GRPC_CREDENTIALS_TYPE_ALTS), + options_(grpc_alts_credentials_options_copy(options)), + handshaker_service_url_(handshaker_service_url == nullptr + ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL) + : gpr_strdup(handshaker_service_url)) {} + +grpc_alts_credentials::~grpc_alts_credentials() { + grpc_alts_credentials_options_destroy(options_); + gpr_free(handshaker_service_url_); +} + +grpc_core::RefCountedPtr +grpc_alts_credentials::create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target_name, const grpc_channel_args* args, + grpc_channel_args** new_args) { + return grpc_alts_channel_security_connector_create( + this->Ref(), std::move(call_creds), target_name); +} + +grpc_alts_server_credentials::grpc_alts_server_credentials( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url) + : grpc_server_credentials(GRPC_CREDENTIALS_TYPE_ALTS), + options_(grpc_alts_credentials_options_copy(options)), + handshaker_service_url_(handshaker_service_url == nullptr + ? gpr_strdup(GRPC_ALTS_HANDSHAKER_SERVICE_URL) + : gpr_strdup(handshaker_service_url)) {} + +grpc_core::RefCountedPtr +grpc_alts_server_credentials::create_security_connector() { + return grpc_alts_server_security_connector_create(this->Ref()); +} + +grpc_alts_server_credentials::~grpc_alts_server_credentials() { + grpc_alts_credentials_options_destroy(options_); + gpr_free(handshaker_service_url_); +} + +grpc_channel_credentials* grpc_alts_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts) { + if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) { + return nullptr; + } + return grpc_core::New(options, handshaker_service_url); +} + +grpc_server_credentials* grpc_alts_server_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts) { + if (!enable_untrusted_alts && !grpc_alts_is_running_on_gcp()) { + return nullptr; + } + return grpc_core::New(options, + handshaker_service_url); +} + +grpc_channel_credentials* grpc_alts_credentials_create( + const grpc_alts_credentials_options* options) { + return grpc_alts_credentials_create_customized( + options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false); +} + +grpc_server_credentials* grpc_alts_server_credentials_create( + const grpc_alts_credentials_options* options) { + return grpc_alts_server_credentials_create_customized( + options, GRPC_ALTS_HANDSHAKER_SERVICE_URL, false); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.h b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.h new file mode 100644 index 00000000000000..cc6d5222b16bcf --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/alts_credentials.h @@ -0,0 +1,109 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H + +#include + +#include + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/lib/security/credentials/credentials.h" + +/* Main struct for grpc ALTS channel credential. */ +class grpc_alts_credentials final : public grpc_channel_credentials { + public: + grpc_alts_credentials(const grpc_alts_credentials_options* options, + const char* handshaker_service_url); + ~grpc_alts_credentials() override; + + grpc_core::RefCountedPtr + create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target_name, const grpc_channel_args* args, + grpc_channel_args** new_args) override; + + const grpc_alts_credentials_options* options() const { return options_; } + grpc_alts_credentials_options* mutable_options() { return options_; } + const char* handshaker_service_url() const { return handshaker_service_url_; } + + private: + grpc_alts_credentials_options* options_; + char* handshaker_service_url_; +}; + +/* Main struct for grpc ALTS server credential. */ +class grpc_alts_server_credentials final : public grpc_server_credentials { + public: + grpc_alts_server_credentials(const grpc_alts_credentials_options* options, + const char* handshaker_service_url); + ~grpc_alts_server_credentials() override; + + grpc_core::RefCountedPtr + create_security_connector() override; + + const grpc_alts_credentials_options* options() const { return options_; } + grpc_alts_credentials_options* mutable_options() { return options_; } + const char* handshaker_service_url() const { return handshaker_service_url_; } + + private: + grpc_alts_credentials_options* options_; + char* handshaker_service_url_; +}; + +/** + * This method creates an ALTS channel credential object with customized + * information provided by caller. + * + * - options: grpc ALTS credentials options instance for client. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". If it's nullptr, the address of default metadata server will + * be used. + * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted + * mode. This mode can be enabled when we are sure ALTS is running on GCP or + * for testing purpose. + * + * It returns nullptr if the flag is disabled AND ALTS is not running on GCP. + * Otherwise, it returns the created credential object. + */ + +grpc_channel_credentials* grpc_alts_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts); + +/** + * This method creates an ALTS server credential object with customized + * information provided by caller. + * + * - options: grpc ALTS credentials options instance for server. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". If it's nullptr, the address of default metadata server will + * be used. + * - enable_untrusted_alts: a boolean flag used to enable ALTS in untrusted + * mode. This mode can be enabled when we are sure ALTS is running on GCP or + * for testing purpose. + * + * It returns nullptr if the flag is disabled and ALTS is not running on GCP. + * Otherwise, it returns the created credential object. + */ +grpc_server_credentials* grpc_alts_server_credentials_create_customized( + const grpc_alts_credentials_options* options, + const char* handshaker_service_url, bool enable_untrusted_alts); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_ALTS_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.cc new file mode 100644 index 00000000000000..96807876cf70f4 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include +#include +#include + +#include +#include + +const size_t kBiosDataBufferSize = 256; + +static char* trim(const char* src) { + if (src == nullptr) { + return nullptr; + } + char* des = nullptr; + size_t start = 0, end = strlen(src) - 1; + /* find the last character that is not a whitespace. */ + while (end != 0 && isspace(src[end])) { + end--; + } + /* find the first character that is not a whitespace. */ + while (start < strlen(src) && isspace(src[start])) { + start++; + } + if (start <= end) { + des = static_cast( + gpr_zalloc(sizeof(char) * (end - start + 2 /* '\0' */))); + memcpy(des, src + start, end - start + 1); + } + return des; +} + +namespace grpc_core { +namespace internal { + +char* read_bios_file(const char* bios_file) { + FILE* fp = fopen(bios_file, "r"); + if (!fp) { + gpr_log(GPR_ERROR, "BIOS data file cannot be opened."); + return nullptr; + } + char buf[kBiosDataBufferSize + 1]; + size_t ret = fread(buf, sizeof(char), kBiosDataBufferSize, fp); + buf[ret] = '\0'; + char* trimmed_buf = trim(buf); + fclose(fp); + return trimmed_buf; +} + +} // namespace internal +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.h b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.h new file mode 100644 index 00000000000000..aea4cea64322fc --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H + +namespace grpc_core { +namespace internal { + +/** + * This method is a helper function that reads a file containing system bios + * data. Exposed for testing only. + * + * - bios_file: a file containing BIOS data used to determine GCE tenancy + * information. + * + * It returns a buffer containing the data read from the file. + */ +char* read_bios_file(const char* bios_file); + +/** + * This method checks if system BIOS data contains Google-specific phrases. + * Exposed for testing only. + * + * - bios_data: a buffer containing system BIOS data. + * + * It returns true if the BIOS data contains Google-specific phrases, and false + * otherwise. + */ +bool check_bios_data(const char* bios_data); + +} // namespace internal +} // namespace grpc_core + +/** + * This method checks if a VM (Windows or Linux) is running within Google + * compute Engine (GCE) or not. It returns true if the VM is running in GCE and + * false otherwise. + */ +bool grpc_alts_is_running_on_gcp(); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_CHECK_GCP_ENVIRONMENT_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc new file mode 100644 index 00000000000000..8454fd7558cbd3 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_linux.cc @@ -0,0 +1,68 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_LINUX + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include +#include + +#include + +#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google" +#define GRPC_ALTS_EXPECT_NAME_GCE "Google Compute Engine" +#define GRPC_ALTS_PRODUCT_NAME_FILE "/sys/class/dmi/id/product_name" + +static bool g_compute_engine_detection_done = false; +static bool g_is_on_compute_engine = false; +static gpr_mu g_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +namespace grpc_core { +namespace internal { + +bool check_bios_data(const char* bios_data_file) { + char* bios_data = read_bios_file(bios_data_file); + bool result = + bios_data && ((!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE)) || + (!strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GCE))); + gpr_free(bios_data); + return result; +} + +} // namespace internal +} // namespace grpc_core + +static void init_mu(void) { gpr_mu_init(&g_mu); } + +bool grpc_alts_is_running_on_gcp() { + gpr_once_init(&g_once, init_mu); + gpr_mu_lock(&g_mu); + if (!g_compute_engine_detection_done) { + g_is_on_compute_engine = + grpc_core::internal::check_bios_data(GRPC_ALTS_PRODUCT_NAME_FILE); + g_compute_engine_detection_done = true; + } + gpr_mu_unlock(&g_mu); + return g_is_on_compute_engine; +} + +#endif // GPR_LINUX diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc new file mode 100644 index 00000000000000..d97681b86d29d8 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_no_op.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS) + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include + +bool grpc_alts_is_running_on_gcp() { + gpr_log(GPR_ERROR, + "Platforms other than Linux and Windows are not supported"); + return false; +} + +#endif // !defined(LINUX) && !defined(GPR_WINDOWS) diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc new file mode 100644 index 00000000000000..55efe0e9dd646c --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/check_gcp_environment_windows.cc @@ -0,0 +1,114 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS + +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" + +#include +#include +#include +#include + +#include +#include +#include + +#define GRPC_ALTS_EXPECT_NAME_GOOGLE "Google" +#define GRPC_ALTS_WINDOWS_CHECK_COMMAND "powershell.exe" +#define GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS \ + "(Get-WmiObject -Class Win32_BIOS).Manufacturer" +#define GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE "windows_bios.data" + +const size_t kBiosDataBufferSize = 256; + +static bool g_compute_engine_detection_done = false; +static bool g_is_on_compute_engine = false; +static gpr_mu g_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +namespace grpc_core { +namespace internal { + +bool check_bios_data(const char* bios_data_file) { + char* bios_data = read_bios_file(bios_data_file); + bool result = !strcmp(bios_data, GRPC_ALTS_EXPECT_NAME_GOOGLE); + remove(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE); + gpr_free(bios_data); + return result; +} + +} // namespace internal +} // namespace grpc_core + +static void init_mu(void) { gpr_mu_init(&g_mu); } + +static bool run_powershell() { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + HANDLE h = CreateFile(_T(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE), GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, &sa, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) { + gpr_log(GPR_ERROR, "CreateFile failed (%d).", GetLastError()); + return false; + } + PROCESS_INFORMATION pi; + STARTUPINFO si; + DWORD flags = CREATE_NO_WINDOW; + ZeroMemory(&pi, sizeof(pi)); + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput = NULL; + si.hStdError = h; + si.hStdOutput = h; + TCHAR cmd[kBiosDataBufferSize]; + _sntprintf(cmd, kBiosDataBufferSize, _T("%s %s"), + _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND), + _T(GRPC_ALTS_WINDOWS_CHECK_COMMAND_ARGS)); + if (!CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, NULL, NULL, &si, + &pi)) { + gpr_log(GPR_ERROR, "CreateProcess failed (%d).\n", GetLastError()); + return false; + } + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + CloseHandle(h); + return true; +} + +bool grpc_alts_is_running_on_gcp() { + gpr_once_init(&g_once, init_mu); + gpr_mu_lock(&g_mu); + if (!g_compute_engine_detection_done) { + g_is_on_compute_engine = + run_powershell() && + grpc_core::internal::check_bios_data(GRPC_ALTS_WINDOWS_CHECK_BIOS_FILE); + g_compute_engine_detection_done = true; + } + gpr_mu_unlock(&g_mu); + return g_is_on_compute_engine; +} + +#endif // GPR_WINDOWS diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc new file mode 100644 index 00000000000000..118d18d1191e7d --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_client_options.cc @@ -0,0 +1,127 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +static grpc_alts_credentials_options* alts_client_options_copy( + const grpc_alts_credentials_options* options); + +static void alts_client_options_destroy(grpc_alts_credentials_options* options); + +static target_service_account* target_service_account_create( + const char* service_account) { + if (service_account == nullptr) { + return nullptr; + } + auto* sa = static_cast( + gpr_zalloc(sizeof(target_service_account))); + sa->data = gpr_strdup(service_account); + return sa; +} + +void grpc_alts_credentials_client_options_add_target_service_account( + grpc_alts_credentials_options* options, const char* service_account) { + if (options == nullptr || service_account == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_alts_credentials_client_options_add_target_service_account()"); + return; + } + auto client_options = + reinterpret_cast(options); + target_service_account* node = target_service_account_create(service_account); + node->next = client_options->target_account_list_head; + client_options->target_account_list_head = node; +} + +static void target_service_account_destroy( + target_service_account* service_account) { + if (service_account == nullptr) { + return; + } + gpr_free(service_account->data); + gpr_free(service_account); +} + +static const grpc_alts_credentials_options_vtable vtable = { + alts_client_options_copy, alts_client_options_destroy}; + +grpc_alts_credentials_options* grpc_alts_credentials_client_options_create( + void) { + auto client_options = static_cast( + gpr_zalloc(sizeof(grpc_alts_credentials_client_options))); + client_options->base.vtable = &vtable; + return &client_options->base; +} + +static grpc_alts_credentials_options* alts_client_options_copy( + const grpc_alts_credentials_options* options) { + if (options == nullptr) { + return nullptr; + } + grpc_alts_credentials_options* new_options = + grpc_alts_credentials_client_options_create(); + auto new_client_options = + reinterpret_cast(new_options); + /* Copy target service accounts. */ + target_service_account* prev = nullptr; + auto node = + (reinterpret_cast(options)) + ->target_account_list_head; + while (node != nullptr) { + target_service_account* new_node = + target_service_account_create(node->data); + if (prev == nullptr) { + new_client_options->target_account_list_head = new_node; + } else { + prev->next = new_node; + } + prev = new_node; + node = node->next; + } + /* Copy rpc protocol versions. */ + grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions, + &new_options->rpc_versions); + return new_options; +} + +static void alts_client_options_destroy( + grpc_alts_credentials_options* options) { + if (options == nullptr) { + return; + } + auto* client_options = + reinterpret_cast(options); + target_service_account* node = client_options->target_account_list_head; + while (node != nullptr) { + target_service_account* next_node = node->next; + target_service_account_destroy(node); + node = next_node; + } +} diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc new file mode 100644 index 00000000000000..d4281715409794 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.cc @@ -0,0 +1,46 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" + +#include +#include + +grpc_alts_credentials_options* grpc_alts_credentials_options_copy( + const grpc_alts_credentials_options* options) { + if (options != nullptr && options->vtable != nullptr && + options->vtable->copy != nullptr) { + return options->vtable->copy(options); + } + /* An error occurred. */ + gpr_log(GPR_ERROR, + "Invalid arguments to grpc_alts_credentials_options_copy()"); + return nullptr; +} + +void grpc_alts_credentials_options_destroy( + grpc_alts_credentials_options* options) { + if (options != nullptr) { + if (options->vtable != nullptr && options->vtable->destruct != nullptr) { + options->vtable->destruct(options); + } + gpr_free(options); + } +} diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h new file mode 100644 index 00000000000000..320af718bd3cc9 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h @@ -0,0 +1,75 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H + +#include + +#include + +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +/* V-table for grpc_alts_credentials_options */ +typedef struct grpc_alts_credentials_options_vtable { + grpc_alts_credentials_options* (*copy)( + const grpc_alts_credentials_options* options); + void (*destruct)(grpc_alts_credentials_options* options); +} grpc_alts_credentials_options_vtable; + +struct grpc_alts_credentials_options { + const struct grpc_alts_credentials_options_vtable* vtable; + grpc_gcp_rpc_protocol_versions rpc_versions; +}; + +typedef struct target_service_account { + struct target_service_account* next; + char* data; +} target_service_account; + +/** + * Main struct for ALTS client credentials options. The options contain a + * a list of target service accounts (if specified) used for secure naming + * check. + */ +typedef struct grpc_alts_credentials_client_options { + grpc_alts_credentials_options base; + target_service_account* target_account_list_head; +} grpc_alts_credentials_client_options; + +/** + * Main struct for ALTS server credentials options. The options currently + * do not contain any server-specific fields. + */ +typedef struct grpc_alts_credentials_server_options { + grpc_alts_credentials_options base; +} grpc_alts_credentials_server_options; + +/** + * This method performs a deep copy on grpc_alts_credentials_options instance. + * + * - options: a grpc_alts_credentials_options instance that needs to be copied. + * + * It returns a new grpc_alts_credentials_options instance on success and NULL + * on failure. + */ +grpc_alts_credentials_options* grpc_alts_credentials_options_copy( + const grpc_alts_credentials_options* options); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_ALTS_GRPC_ALTS_CREDENTIALS_OPTIONS_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc new file mode 100644 index 00000000000000..1a59c456758c1e --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/alts/grpc_alts_credentials_server_options.cc @@ -0,0 +1,59 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include + +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +static grpc_alts_credentials_options* alts_server_options_copy( + const grpc_alts_credentials_options* options); + +static void alts_server_options_destroy( + grpc_alts_credentials_options* options) {} + +static const grpc_alts_credentials_options_vtable vtable = { + alts_server_options_copy, alts_server_options_destroy}; + +grpc_alts_credentials_options* grpc_alts_credentials_server_options_create( + void) { + grpc_alts_credentials_server_options* server_options = + static_cast( + gpr_zalloc(sizeof(*server_options))); + server_options->base.vtable = &vtable; + return &server_options->base; +} + +static grpc_alts_credentials_options* alts_server_options_copy( + const grpc_alts_credentials_options* options) { + if (options == nullptr) { + return nullptr; + } + grpc_alts_credentials_options* new_options = + grpc_alts_credentials_server_options_create(); + /* Copy rpc protocol versions. */ + grpc_gcp_rpc_protocol_versions_copy(&options->rpc_versions, + &new_options->rpc_versions); + return new_options; +} diff --git a/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.cc new file mode 100644 index 00000000000000..586bbed778efb0 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.cc @@ -0,0 +1,209 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/composite/composite_credentials.h" + +#include +#include + +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include + +/* -- Composite call credentials. -- */ + +static void composite_call_metadata_cb(void* arg, grpc_error* error); + +namespace { +struct grpc_composite_call_credentials_metadata_context { + grpc_composite_call_credentials_metadata_context( + grpc_composite_call_credentials* composite_creds, + grpc_polling_entity* pollent, grpc_auth_metadata_context auth_md_context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata) + : composite_creds(composite_creds), + pollent(pollent), + auth_md_context(auth_md_context), + md_array(md_array), + on_request_metadata(on_request_metadata) { + GRPC_CLOSURE_INIT(&internal_on_request_metadata, composite_call_metadata_cb, + this, grpc_schedule_on_exec_ctx); + } + + grpc_composite_call_credentials* composite_creds; + size_t creds_index = 0; + grpc_polling_entity* pollent; + grpc_auth_metadata_context auth_md_context; + grpc_credentials_mdelem_array* md_array; + grpc_closure* on_request_metadata; + grpc_closure internal_on_request_metadata; +}; +} // namespace + +static void composite_call_metadata_cb(void* arg, grpc_error* error) { + grpc_composite_call_credentials_metadata_context* ctx = + static_cast(arg); + if (error == GRPC_ERROR_NONE) { + const grpc_composite_call_credentials::CallCredentialsList& inner = + ctx->composite_creds->inner(); + /* See if we need to get some more metadata. */ + if (ctx->creds_index < inner.size()) { + if (inner[ctx->creds_index++]->get_request_metadata( + ctx->pollent, ctx->auth_md_context, ctx->md_array, + &ctx->internal_on_request_metadata, &error)) { + // Synchronous response, so call ourselves recursively. + composite_call_metadata_cb(arg, error); + GRPC_ERROR_UNREF(error); + } + return; + } + // We're done! + } + GRPC_CLOSURE_SCHED(ctx->on_request_metadata, GRPC_ERROR_REF(error)); + gpr_free(ctx); +} + +bool grpc_composite_call_credentials::get_request_metadata( + grpc_polling_entity* pollent, grpc_auth_metadata_context auth_md_context, + grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, + grpc_error** error) { + grpc_composite_call_credentials_metadata_context* ctx; + ctx = grpc_core::New( + this, pollent, auth_md_context, md_array, on_request_metadata); + bool synchronous = true; + const CallCredentialsList& inner = ctx->composite_creds->inner(); + while (ctx->creds_index < inner.size()) { + if (inner[ctx->creds_index++]->get_request_metadata( + ctx->pollent, ctx->auth_md_context, ctx->md_array, + &ctx->internal_on_request_metadata, error)) { + if (*error != GRPC_ERROR_NONE) break; + } else { + synchronous = false; // Async return. + break; + } + } + if (synchronous) grpc_core::Delete(ctx); + return synchronous; +} + +void grpc_composite_call_credentials::cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) { + for (size_t i = 0; i < inner_.size(); ++i) { + inner_[i]->cancel_get_request_metadata(md_array, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +static size_t get_creds_array_size(const grpc_call_credentials* creds, + bool is_composite) { + return is_composite + ? static_cast(creds) + ->inner() + .size() + : 1; +} + +void grpc_composite_call_credentials::push_to_inner( + grpc_core::RefCountedPtr creds, bool is_composite) { + if (!is_composite) { + inner_.push_back(std::move(creds)); + return; + } + auto composite_creds = + static_cast(creds.get()); + for (size_t i = 0; i < composite_creds->inner().size(); ++i) { + inner_.push_back(std::move(composite_creds->inner_[i])); + } +} + +grpc_composite_call_credentials::grpc_composite_call_credentials( + grpc_core::RefCountedPtr creds1, + grpc_core::RefCountedPtr creds2) + : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) { + const bool creds1_is_composite = + strcmp(creds1->type(), GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0; + const bool creds2_is_composite = + strcmp(creds2->type(), GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0; + const size_t size = get_creds_array_size(creds1.get(), creds1_is_composite) + + get_creds_array_size(creds2.get(), creds2_is_composite); + inner_.reserve(size); + push_to_inner(std::move(creds1), creds1_is_composite); + push_to_inner(std::move(creds2), creds2_is_composite); +} + +static grpc_core::RefCountedPtr +composite_call_credentials_create( + grpc_core::RefCountedPtr creds1, + grpc_core::RefCountedPtr creds2) { + return grpc_core::MakeRefCounted( + std::move(creds1), std::move(creds2)); +} + +grpc_call_credentials* grpc_composite_call_credentials_create( + grpc_call_credentials* creds1, grpc_call_credentials* creds2, + void* reserved) { + GRPC_API_TRACE( + "grpc_composite_call_credentials_create(creds1=%p, creds2=%p, " + "reserved=%p)", + 3, (creds1, creds2, reserved)); + GPR_ASSERT(reserved == nullptr); + GPR_ASSERT(creds1 != nullptr); + GPR_ASSERT(creds2 != nullptr); + + return composite_call_credentials_create(creds1->Ref(), creds2->Ref()) + .release(); +} + +/* -- Composite channel credentials. -- */ + +grpc_core::RefCountedPtr +grpc_composite_channel_credentials::create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) { + GPR_ASSERT(inner_creds_ != nullptr && call_creds_ != nullptr); + /* If we are passed a call_creds, create a call composite to pass it + downstream. */ + if (call_creds != nullptr) { + return inner_creds_->create_security_connector( + composite_call_credentials_create(call_creds_, std::move(call_creds)), + target, args, new_args); + } else { + return inner_creds_->create_security_connector(call_creds_, target, args, + new_args); + } +} + +grpc_channel_credentials* grpc_composite_channel_credentials_create( + grpc_channel_credentials* channel_creds, grpc_call_credentials* call_creds, + void* reserved) { + GPR_ASSERT(channel_creds != nullptr && call_creds != nullptr && + reserved == nullptr); + GRPC_API_TRACE( + "grpc_composite_channel_credentials_create(channel_creds=%p, " + "call_creds=%p, reserved=%p)", + 3, (channel_creds, call_creds, reserved)); + return grpc_core::New( + channel_creds->Ref(), call_creds->Ref()); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.h b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.h new file mode 100644 index 00000000000000..7a1c7d5e42b918 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/composite/composite_credentials.h @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H + +#include + +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/credentials/credentials.h" + +/* -- Composite channel credentials. -- */ + +class grpc_composite_channel_credentials : public grpc_channel_credentials { + public: + grpc_composite_channel_credentials( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr call_creds) + : grpc_channel_credentials(channel_creds->type()), + inner_creds_(std::move(channel_creds)), + call_creds_(std::move(call_creds)) {} + + ~grpc_composite_channel_credentials() override = default; + + grpc_core::RefCountedPtr + duplicate_without_call_credentials() override { + return inner_creds_; + } + + grpc_core::RefCountedPtr + create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) override; + + const grpc_channel_credentials* inner_creds() const { + return inner_creds_.get(); + } + const grpc_call_credentials* call_creds() const { return call_creds_.get(); } + grpc_call_credentials* mutable_call_creds() { return call_creds_.get(); } + + private: + grpc_core::RefCountedPtr inner_creds_; + grpc_core::RefCountedPtr call_creds_; +}; + +/* -- Composite call credentials. -- */ + +class grpc_composite_call_credentials : public grpc_call_credentials { + public: + using CallCredentialsList = + grpc_core::InlinedVector, + 2>; + + grpc_composite_call_credentials( + grpc_core::RefCountedPtr creds1, + grpc_core::RefCountedPtr creds2); + ~grpc_composite_call_credentials() override = default; + + bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) override; + + void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, + grpc_error* error) override; + + const CallCredentialsList& inner() const { return inner_; } + + private: + void push_to_inner(grpc_core::RefCountedPtr creds, + bool is_composite); + + CallCredentialsList inner_; +}; + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/credentials/credentials.cc b/third_party/grpc/src/core/lib/security/credentials/credentials.cc new file mode 100644 index 00000000000000..90452d68d61645 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/credentials.cc @@ -0,0 +1,162 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include +#include +#include + +/* -- Common. -- */ + +void grpc_channel_credentials_release(grpc_channel_credentials* creds) { + GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds)); + grpc_core::ExecCtx exec_ctx; + if (creds) creds->Unref(); +} + +void grpc_call_credentials_release(grpc_call_credentials* creds) { + GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds)); + grpc_core::ExecCtx exec_ctx; + if (creds) creds->Unref(); +} + +static void credentials_pointer_arg_destroy(void* p) { + static_cast(p)->Unref(); +} + +static void* credentials_pointer_arg_copy(void* p) { + return static_cast(p)->Ref().release(); +} + +static int credentials_pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); } + +static const grpc_arg_pointer_vtable credentials_pointer_vtable = { + credentials_pointer_arg_copy, credentials_pointer_arg_destroy, + credentials_pointer_cmp}; + +grpc_arg grpc_channel_credentials_to_arg( + grpc_channel_credentials* credentials) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_CHANNEL_CREDENTIALS, + credentials, + &credentials_pointer_vtable); +} + +grpc_channel_credentials* grpc_channel_credentials_from_arg( + const grpc_arg* arg) { + if (strcmp(arg->key, GRPC_ARG_CHANNEL_CREDENTIALS)) return nullptr; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_ARG_CHANNEL_CREDENTIALS); + return nullptr; + } + return static_cast(arg->value.pointer.p); +} + +grpc_channel_credentials* grpc_channel_credentials_find_in_args( + const grpc_channel_args* args) { + size_t i; + if (args == nullptr) return nullptr; + for (i = 0; i < args->num_args; i++) { + grpc_channel_credentials* credentials = + grpc_channel_credentials_from_arg(&args->args[i]); + if (credentials != nullptr) return credentials; + } + return nullptr; +} + +void grpc_server_credentials_release(grpc_server_credentials* creds) { + GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds)); + grpc_core::ExecCtx exec_ctx; + if (creds) creds->Unref(); +} + +void grpc_server_credentials::set_auth_metadata_processor( + const grpc_auth_metadata_processor& processor) { + GRPC_API_TRACE( + "grpc_server_credentials_set_auth_metadata_processor(" + "creds=%p, " + "processor=grpc_auth_metadata_processor { process: %p, state: %p })", + 3, (this, (void*)(intptr_t)processor.process, processor.state)); + DestroyProcessor(); + processor_ = processor; +} + +void grpc_server_credentials_set_auth_metadata_processor( + grpc_server_credentials* creds, grpc_auth_metadata_processor processor) { + GPR_DEBUG_ASSERT(creds != nullptr); + creds->set_auth_metadata_processor(processor); +} + +static void server_credentials_pointer_arg_destroy(void* p) { + static_cast(p)->Unref(); +} + +static void* server_credentials_pointer_arg_copy(void* p) { + return static_cast(p)->Ref().release(); +} + +static int server_credentials_pointer_cmp(void* a, void* b) { + return GPR_ICMP(a, b); +} + +static const grpc_arg_pointer_vtable cred_ptr_vtable = { + server_credentials_pointer_arg_copy, server_credentials_pointer_arg_destroy, + server_credentials_pointer_cmp}; + +grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials* p) { + return grpc_channel_arg_pointer_create((char*)GRPC_SERVER_CREDENTIALS_ARG, p, + &cred_ptr_vtable); +} + +grpc_server_credentials* grpc_server_credentials_from_arg(const grpc_arg* arg) { + if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return nullptr; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_SERVER_CREDENTIALS_ARG); + return nullptr; + } + return static_cast(arg->value.pointer.p); +} + +grpc_server_credentials* grpc_find_server_credentials_in_args( + const grpc_channel_args* args) { + size_t i; + if (args == nullptr) return nullptr; + for (i = 0; i < args->num_args; i++) { + grpc_server_credentials* p = + grpc_server_credentials_from_arg(&args->args[i]); + if (p != nullptr) return p; + } + return nullptr; +} diff --git a/third_party/grpc/src/core/lib/security/credentials/credentials.h b/third_party/grpc/src/core/lib/security/credentials/credentials.h new file mode 100644 index 00000000000000..4091ef3dfb5ce5 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/credentials.h @@ -0,0 +1,271 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H + +#include + +#include +#include +#include +#include "src/core/lib/transport/metadata_batch.h" + +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/security/security_connector/security_connector.h" + +struct grpc_http_response; + +/* --- Constants. --- */ + +typedef enum { + GRPC_CREDENTIALS_OK = 0, + GRPC_CREDENTIALS_ERROR +} grpc_credentials_status; + +#define GRPC_FAKE_TRANSPORT_SECURITY_TYPE "fake" + +#define GRPC_CHANNEL_CREDENTIALS_TYPE_SSL "Ssl" +#define GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY \ + "FakeTransportSecurity" +#define GRPC_CHANNEL_CREDENTIALS_TYPE_GOOGLE_DEFAULT "GoogleDefault" + +#define GRPC_CALL_CREDENTIALS_TYPE_OAUTH2 "Oauth2" +#define GRPC_CALL_CREDENTIALS_TYPE_JWT "Jwt" +#define GRPC_CALL_CREDENTIALS_TYPE_IAM "Iam" +#define GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE "Composite" + +#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_SECURE_TOKEN_REFRESH_THRESHOLD_SECS 60 + +#define GRPC_COMPUTE_ENGINE_METADATA_HOST "metadata.google.internal" +#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" + +/* --- Google utils --- */ + +/* It is the caller's responsibility to gpr_free the result if not NULL. */ +char* grpc_get_well_known_google_credentials_file_path(void); + +/* Implementation function for the different platforms. */ +char* grpc_get_well_known_google_credentials_file_path_impl(void); + +/* Override for testing only. Not thread-safe */ +typedef char* (*grpc_well_known_credentials_path_getter)(void); +void grpc_override_well_known_credentials_path_getter( + grpc_well_known_credentials_path_getter getter); + +/* --- grpc_channel_credentials. --- */ + +#define GRPC_ARG_CHANNEL_CREDENTIALS "grpc.channel_credentials" + +// This type is forward declared as a C struct and we cannot define it as a +// class. Otherwise, compiler will complain about type mismatch due to +// -Wmismatched-tags. +struct grpc_channel_credentials + : grpc_core::RefCounted { + public: + explicit grpc_channel_credentials(const char* type) : type_(type) {} + virtual ~grpc_channel_credentials() = default; + + // 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. + virtual grpc_core::RefCountedPtr + create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) { + // Tell clang-tidy that call_creds cannot be passed as const-ref. + call_creds.reset(); + GRPC_ABSTRACT; + } + + // Creates a version of the channel credentials without any attached call + // credentials. This can be used in order to open a channel to a non-trusted + // gRPC load balancer. + virtual grpc_core::RefCountedPtr + duplicate_without_call_credentials() { + // By default we just increment the refcount. + return Ref(); + } + + const char* type() const { return type_; } + + GRPC_ABSTRACT_BASE_CLASS + + private: + const char* type_; +}; + +/* Util to encapsulate the channel credentials in a channel arg. */ +grpc_arg grpc_channel_credentials_to_arg(grpc_channel_credentials* credentials); + +/* Util to get the channel credentials from a channel arg. */ +grpc_channel_credentials* grpc_channel_credentials_from_arg( + const grpc_arg* arg); + +/* Util to find the channel credentials from channel args. */ +grpc_channel_credentials* grpc_channel_credentials_find_in_args( + const grpc_channel_args* args); + +/* --- grpc_credentials_mdelem_array. --- */ + +typedef struct { + grpc_mdelem* md = nullptr; + size_t size = 0; +} grpc_credentials_mdelem_array; + +/// Takes a new ref to \a md. +void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array* list, + grpc_mdelem md); + +/// Appends all elements from \a src to \a dst, taking a new ref to each one. +void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array* dst, + grpc_credentials_mdelem_array* src); + +void grpc_credentials_mdelem_array_destroy(grpc_credentials_mdelem_array* list); + +/* --- grpc_call_credentials. --- */ + +// This type is forward declared as a C struct and we cannot define it as a +// class. Otherwise, compiler will complain about type mismatch due to +// -Wmismatched-tags. +struct grpc_call_credentials + : public grpc_core::RefCounted { + public: + explicit grpc_call_credentials(const char* type) : type_(type) {} + virtual ~grpc_call_credentials() = default; + + // Returns true if completed synchronously, in which case \a error will + // be set to indicate the result. Otherwise, \a on_request_metadata will + // be invoked asynchronously when complete. \a md_array will be populated + // with the resulting metadata once complete. + virtual bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) GRPC_ABSTRACT; + + // Cancels a pending asynchronous operation started by + // grpc_call_credentials_get_request_metadata() with the corresponding + // value of \a md_array. + virtual void cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) GRPC_ABSTRACT; + + const char* type() const { return type_; } + + GRPC_ABSTRACT_BASE_CLASS + + private: + const char* type_; +}; + +/* Metadata-only credentials with the specified key and value where + asynchronicity can be simulated for testing. */ +grpc_call_credentials* grpc_md_only_test_credentials_create( + const char* md_key, const char* md_value, bool is_async); + +/* --- grpc_server_credentials. --- */ + +// This type is forward declared as a C struct and we cannot define it as a +// class. Otherwise, compiler will complain about type mismatch due to +// -Wmismatched-tags. +struct grpc_server_credentials + : public grpc_core::RefCounted { + public: + explicit grpc_server_credentials(const char* type) : type_(type) {} + + virtual ~grpc_server_credentials() { DestroyProcessor(); } + + virtual grpc_core::RefCountedPtr + create_security_connector() GRPC_ABSTRACT; + + const char* type() const { return type_; } + + const grpc_auth_metadata_processor& auth_metadata_processor() const { + return processor_; + } + void set_auth_metadata_processor( + const grpc_auth_metadata_processor& processor); + + GRPC_ABSTRACT_BASE_CLASS + + private: + void DestroyProcessor() { + if (processor_.destroy != nullptr && processor_.state != nullptr) { + processor_.destroy(processor_.state); + } + } + + const char* type_; + grpc_auth_metadata_processor processor_ = + grpc_auth_metadata_processor(); // Zero-initialize the C struct. +}; + +#define GRPC_SERVER_CREDENTIALS_ARG "grpc.server_credentials" + +grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials* c); +grpc_server_credentials* grpc_server_credentials_from_arg(const grpc_arg* arg); +grpc_server_credentials* grpc_find_server_credentials_in_args( + const grpc_channel_args* args); + +/* -- Credentials Metadata Request. -- */ + +struct grpc_credentials_metadata_request { + explicit grpc_credentials_metadata_request( + grpc_core::RefCountedPtr creds) + : creds(std::move(creds)) {} + ~grpc_credentials_metadata_request() { + grpc_http_response_destroy(&response); + } + + grpc_core::RefCountedPtr creds; + grpc_http_response response; +}; + +inline grpc_credentials_metadata_request* +grpc_credentials_metadata_request_create( + grpc_core::RefCountedPtr creds) { + return grpc_core::New(std::move(creds)); +} + +inline void grpc_credentials_metadata_request_destroy( + grpc_credentials_metadata_request* r) { + grpc_core::Delete(r); +} + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/credentials_metadata.cc b/third_party/grpc/src/core/lib/security/credentials/credentials_metadata.cc new file mode 100644 index 00000000000000..703de4aaafd11e --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/credentials_metadata.cc @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +#include + +#include + +#include "src/core/lib/slice/slice_internal.h" + +static void mdelem_list_ensure_capacity(grpc_credentials_mdelem_array* list, + size_t additional_space_needed) { + size_t target_size = list->size + additional_space_needed; + // Find the next power of two greater than the target size (i.e., + // whenever we add more space, we double what we already have). + size_t new_size = 2; + while (new_size < target_size) { + new_size *= 2; + } + list->md = static_cast( + gpr_realloc(list->md, sizeof(grpc_mdelem) * new_size)); +} + +void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array* list, + grpc_mdelem md) { + mdelem_list_ensure_capacity(list, 1); + list->md[list->size++] = GRPC_MDELEM_REF(md); +} + +void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array* dst, + grpc_credentials_mdelem_array* src) { + mdelem_list_ensure_capacity(dst, src->size); + for (size_t i = 0; i < src->size; ++i) { + dst->md[dst->size++] = GRPC_MDELEM_REF(src->md[i]); + } +} + +void grpc_credentials_mdelem_array_destroy( + grpc_credentials_mdelem_array* list) { + for (size_t i = 0; i < list->size; ++i) { + GRPC_MDELEM_UNREF(list->md[i]); + } + gpr_free(list->md); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.cc new file mode 100644 index 00000000000000..337dd7679fd603 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.cc @@ -0,0 +1,112 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/fake/fake_credentials.h" + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/security/security_connector/fake/fake_security_connector.h" + +/* -- Fake transport security credentials. -- */ + +namespace { +class grpc_fake_channel_credentials final : public grpc_channel_credentials { + public: + grpc_fake_channel_credentials() + : grpc_channel_credentials( + GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) {} + ~grpc_fake_channel_credentials() override = default; + + grpc_core::RefCountedPtr + create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) override { + return grpc_fake_channel_security_connector_create( + this->Ref(), std::move(call_creds), target, args); + } +}; + +class grpc_fake_server_credentials final : public grpc_server_credentials { + public: + grpc_fake_server_credentials() + : grpc_server_credentials( + GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY) {} + ~grpc_fake_server_credentials() override = default; + + grpc_core::RefCountedPtr + create_security_connector() override { + return grpc_fake_server_security_connector_create(this->Ref()); + } +}; +} // namespace + +grpc_channel_credentials* grpc_fake_transport_security_credentials_create() { + return grpc_core::New(); +} + +grpc_server_credentials* +grpc_fake_transport_security_server_credentials_create() { + return grpc_core::New(); +} + +grpc_arg grpc_fake_transport_expected_targets_arg(char* expected_targets) { + return grpc_channel_arg_string_create( + (char*)GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS, expected_targets); +} + +const char* grpc_fake_transport_get_expected_targets( + const grpc_channel_args* args) { + const grpc_arg* expected_target_arg = + grpc_channel_args_find(args, GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS); + return grpc_channel_arg_get_string(expected_target_arg); +} + +/* -- Metadata-only test credentials. -- */ + +bool grpc_md_only_test_credentials::get_request_metadata( + grpc_polling_entity* pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, + grpc_error** error) { + grpc_credentials_mdelem_array_add(md_array, md_); + if (is_async_) { + GRPC_CLOSURE_SCHED(on_request_metadata, GRPC_ERROR_NONE); + return false; + } + return true; +} + +void grpc_md_only_test_credentials::cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) { + GRPC_ERROR_UNREF(error); +} + +grpc_call_credentials* grpc_md_only_test_credentials_create( + const char* md_key, const char* md_value, bool is_async) { + return grpc_core::New(md_key, md_value, + is_async); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.h b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.h new file mode 100644 index 00000000000000..b7f6a1909fcbdd --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/fake/fake_credentials.h @@ -0,0 +1,82 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \ + "grpc.fake_security.expected_targets" + +/* -- Fake transport security credentials. -- */ + +/* Creates a fake transport security credentials object for testing. */ +grpc_channel_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); + +/* Used to verify the target names given to the fake transport security + * connector. + * + * The syntax of \a expected_targets by example: + * For LB channels: + * "backend_target_1,backend_target_2,...;lb_target_1,lb_target_2,..." + * For regular channels: + * "backend_taget_1,backend_target_2,..." + * + * That is to say, LB channels have a heading list of LB targets separated from + * the list of backend targets by a semicolon. For non-LB channels, only the + * latter is present. */ +grpc_arg grpc_fake_transport_expected_targets_arg(char* expected_targets); + +/* Return the value associated with the expected targets channel arg or NULL */ +const char* grpc_fake_transport_get_expected_targets( + const grpc_channel_args* args); + +/* -- Metadata-only Test credentials. -- */ + +class grpc_md_only_test_credentials : public grpc_call_credentials { + public: + grpc_md_only_test_credentials(const char* md_key, const char* md_value, + bool is_async) + : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2), + md_(grpc_mdelem_from_slices(grpc_slice_from_copied_string(md_key), + grpc_slice_from_copied_string(md_value))), + is_async_(is_async) {} + ~grpc_md_only_test_credentials() override { GRPC_MDELEM_UNREF(md_); } + + bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) override; + + void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, + grpc_error* error) override; + + private: + grpc_mdelem md_; + bool is_async_; +}; + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/google_default/credentials_generic.cc b/third_party/grpc/src/core/lib/security/credentials/google_default/credentials_generic.cc new file mode 100644 index 00000000000000..10ff0f620f9475 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/google_default/credentials_generic.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/google_default/google_default_credentials.h" + +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" + +char* grpc_get_well_known_google_credentials_file_path_impl(void) { + char* result = nullptr; + char* base = gpr_getenv(GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR); + if (base == nullptr) { + gpr_log(GPR_ERROR, "Could not get " GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR + " environment variable."); + return nullptr; + } + gpr_asprintf(&result, "%s/%s", base, GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX); + gpr_free(base); + return result; +} diff --git a/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.cc new file mode 100644 index 00000000000000..a86a17d5864abe --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.cc @@ -0,0 +1,374 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/security/credentials/alts/alts_credentials.h" +#include "src/core/lib/security/credentials/alts/check_gcp_environment.h" +#include "src/core/lib/security/credentials/google_default/google_default_credentials.h" +#include "src/core/lib/security/credentials/jwt/jwt_credentials.h" +#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/api_trace.h" + +/* -- Constants. -- */ + +#define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal" + +/* -- Default credentials. -- */ + +/* A sticky bit that will be set only if the result of metadata server detection + * is positive. We do not set the bit if the result is negative. Because it + * means the detection is done via network test that is unreliable and the + * unreliable result should not be referred by successive calls. */ +static int g_metadata_server_available = 0; +static int g_is_on_gce = 0; +static gpr_mu g_state_mu; +/* Protect a metadata_server_detector instance that can be modified by more than + * one gRPC threads */ +static gpr_mu* g_polling_mu; +static gpr_once g_once = GPR_ONCE_INIT; +static grpc_core::internal::grpc_gce_tenancy_checker g_gce_tenancy_checker = + grpc_alts_is_running_on_gcp; + +static void init_default_credentials(void) { gpr_mu_init(&g_state_mu); } + +typedef struct { + grpc_polling_entity pollent; + int is_done; + int success; + grpc_http_response response; +} metadata_server_detector; + +grpc_core::RefCountedPtr +grpc_google_default_channel_credentials::create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) { + bool is_grpclb_load_balancer = grpc_channel_arg_get_bool( + grpc_channel_args_find(args, GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER), + false); + bool is_backend_from_grpclb_load_balancer = grpc_channel_arg_get_bool( + grpc_channel_args_find( + args, GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER), + false); + bool use_alts = + is_grpclb_load_balancer || is_backend_from_grpclb_load_balancer; + /* Return failure if ALTS is selected but not running on GCE. */ + if (use_alts && !g_is_on_gce) { + gpr_log(GPR_ERROR, "ALTS is selected, but not running on GCE."); + return nullptr; + } + + grpc_core::RefCountedPtr sc = + use_alts ? alts_creds_->create_security_connector(call_creds, target, + args, new_args) + : ssl_creds_->create_security_connector(call_creds, target, args, + new_args); + /* grpclb-specific channel args are removed from the channel args set + * to ensure backends and fallback adresses will have the same set of channel + * args. By doing that, it guarantees the connections to backends will not be + * torn down and re-connected when switching in and out of fallback mode. + */ + if (use_alts) { + static const char* args_to_remove[] = { + GRPC_ARG_ADDRESS_IS_GRPCLB_LOAD_BALANCER, + GRPC_ARG_ADDRESS_IS_BACKEND_FROM_GRPCLB_LOAD_BALANCER, + }; + *new_args = grpc_channel_args_copy_and_add_and_remove( + args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), nullptr, 0); + } + return sc; +} + +static void on_metadata_server_detection_http_response(void* user_data, + grpc_error* error) { + metadata_server_detector* detector = + static_cast(user_data); + if (error == GRPC_ERROR_NONE && detector->response.status == 200 && + detector->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 < detector->response.hdr_count; i++) { + grpc_http_header* header = &detector->response.hdrs[i]; + if (strcmp(header->key, "Metadata-Flavor") == 0 && + strcmp(header->value, "Google") == 0) { + detector->success = 1; + break; + } + } + } + gpr_mu_lock(g_polling_mu); + detector->is_done = 1; + GRPC_LOG_IF_ERROR( + "Pollset kick", + grpc_pollset_kick(grpc_polling_entity_pollset(&detector->pollent), + nullptr)); + gpr_mu_unlock(g_polling_mu); +} + +static void destroy_pollset(void* p, grpc_error* e) { + grpc_pollset_destroy(static_cast(p)); +} + +static int is_metadata_server_reachable() { + metadata_server_detector detector; + grpc_httpcli_request request; + grpc_httpcli_context context; + grpc_closure destroy_closure; + /* The http call is local. If it takes more than one sec, it is for sure not + on compute engine. */ + grpc_millis max_detection_delay = GPR_MS_PER_SEC; + grpc_pollset* pollset = + static_cast(gpr_zalloc(grpc_pollset_size())); + grpc_pollset_init(pollset, &g_polling_mu); + detector.pollent = grpc_polling_entity_create_from_pollset(pollset); + detector.is_done = 0; + detector.success = 0; + memset(&detector.response, 0, sizeof(detector.response)); + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = (char*)GRPC_COMPUTE_ENGINE_DETECTION_HOST; + request.http.path = (char*)"/"; + grpc_httpcli_context_init(&context); + grpc_resource_quota* resource_quota = + grpc_resource_quota_create("google_default_credentials"); + grpc_httpcli_get( + &context, &detector.pollent, resource_quota, &request, + grpc_core::ExecCtx::Get()->Now() + max_detection_delay, + GRPC_CLOSURE_CREATE(on_metadata_server_detection_http_response, &detector, + grpc_schedule_on_exec_ctx), + &detector.response); + grpc_resource_quota_unref_internal(resource_quota); + grpc_core::ExecCtx::Get()->Flush(); + /* 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(g_polling_mu); + while (!detector.is_done) { + grpc_pollset_worker* worker = nullptr; + if (!GRPC_LOG_IF_ERROR( + "pollset_work", + grpc_pollset_work(grpc_polling_entity_pollset(&detector.pollent), + &worker, GRPC_MILLIS_INF_FUTURE))) { + detector.is_done = 1; + detector.success = 0; + } + } + gpr_mu_unlock(g_polling_mu); + grpc_httpcli_context_destroy(&context); + GRPC_CLOSURE_INIT(&destroy_closure, destroy_pollset, + grpc_polling_entity_pollset(&detector.pollent), + grpc_schedule_on_exec_ctx); + grpc_pollset_shutdown(grpc_polling_entity_pollset(&detector.pollent), + &destroy_closure); + g_polling_mu = nullptr; + grpc_core::ExecCtx::Get()->Flush(); + gpr_free(grpc_polling_entity_pollset(&detector.pollent)); + grpc_http_response_destroy(&detector.response); + return detector.success; +} + +/* Takes ownership of creds_path if not NULL. */ +static grpc_error* create_default_creds_from_path( + char* creds_path, grpc_core::RefCountedPtr* creds) { + grpc_json* json = nullptr; + grpc_auth_json_key key; + grpc_auth_refresh_token token; + grpc_core::RefCountedPtr result; + grpc_slice creds_data = grpc_empty_slice(); + grpc_error* error = GRPC_ERROR_NONE; + if (creds_path == nullptr) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("creds_path unset"); + goto end; + } + error = grpc_load_file(creds_path, 0, &creds_data); + if (error != GRPC_ERROR_NONE) { + goto end; + } + json = grpc_json_parse_string_with_len( + reinterpret_cast GRPC_SLICE_START_PTR(creds_data), + GRPC_SLICE_LENGTH(creds_data)); + if (json == nullptr) { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to parse JSON"), + GRPC_ERROR_STR_RAW_BYTES, grpc_slice_ref_internal(creds_data)); + 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()); + if (result == nullptr) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "grpc_service_account_jwt_access_credentials_create_from_auth_json_" + "key failed"); + } + 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); + if (result == nullptr) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "grpc_refresh_token_credentials_create_from_auth_refresh_token " + "failed"); + } + goto end; + } + +end: + GPR_ASSERT((result == nullptr) + (error == GRPC_ERROR_NONE) == 1); + if (creds_path != nullptr) gpr_free(creds_path); + grpc_slice_unref_internal(creds_data); + if (json != nullptr) grpc_json_destroy(json); + *creds = result; + return error; +} + +grpc_channel_credentials* grpc_google_default_credentials_create() { + grpc_channel_credentials* result = nullptr; + grpc_core::RefCountedPtr call_creds; + grpc_error* error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to create Google credentials"); + grpc_error* err; + grpc_core::ExecCtx exec_ctx; + + GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ()); + + gpr_once_init(&g_once, init_default_credentials); + + /* First, try the environment variable. */ + err = create_default_creds_from_path( + gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds); + if (err == GRPC_ERROR_NONE) goto end; + error = grpc_error_add_child(error, err); + + /* Then the well-known file. */ + err = create_default_creds_from_path( + grpc_get_well_known_google_credentials_file_path(), &call_creds); + if (err == GRPC_ERROR_NONE) goto end; + error = grpc_error_add_child(error, err); + + gpr_mu_lock(&g_state_mu); + + /* Try a platform-provided hint for GCE. */ + if (!g_metadata_server_available) { + g_is_on_gce = g_gce_tenancy_checker(); + g_metadata_server_available = g_is_on_gce; + } + /* TODO: Add a platform-provided hint for GAE. */ + + /* Do a network test for metadata server. */ + if (!g_metadata_server_available) { + g_metadata_server_available = is_metadata_server_reachable(); + } + gpr_mu_unlock(&g_state_mu); + + if (g_metadata_server_available) { + call_creds = grpc_core::RefCountedPtr( + grpc_google_compute_engine_credentials_create(nullptr)); + if (call_creds == nullptr) { + error = grpc_error_add_child( + error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to get credentials from network")); + } + } + +end: + if (call_creds != nullptr) { + /* Create google default credentials. */ + grpc_channel_credentials* ssl_creds = + grpc_ssl_credentials_create(nullptr, nullptr, nullptr, nullptr); + GPR_ASSERT(ssl_creds != nullptr); + grpc_alts_credentials_options* options = + grpc_alts_credentials_client_options_create(); + grpc_channel_credentials* alts_creds = + grpc_alts_credentials_create(options); + grpc_alts_credentials_options_destroy(options); + auto creds = + grpc_core::MakeRefCounted( + alts_creds != nullptr ? alts_creds->Ref() : nullptr, + ssl_creds != nullptr ? ssl_creds->Ref() : nullptr); + if (ssl_creds) ssl_creds->Unref(); + if (alts_creds) alts_creds->Unref(); + result = grpc_composite_channel_credentials_create( + creds.get(), call_creds.get(), nullptr); + GPR_ASSERT(result != nullptr); + } else { + gpr_log(GPR_ERROR, "Could not create google default credentials: %s", + grpc_error_string(error)); + } + GRPC_ERROR_UNREF(error); + return result; +} + +namespace grpc_core { +namespace internal { + +void set_gce_tenancy_checker_for_testing(grpc_gce_tenancy_checker checker) { + g_gce_tenancy_checker = checker; +} + +void grpc_flush_cached_google_default_credentials(void) { + grpc_core::ExecCtx exec_ctx; + gpr_once_init(&g_once, init_default_credentials); + gpr_mu_lock(&g_state_mu); + g_metadata_server_available = 0; + gpr_mu_unlock(&g_state_mu); +} + +} // namespace internal +} // namespace grpc_core + +/* -- Well known credentials path. -- */ + +static grpc_well_known_credentials_path_getter creds_path_getter = nullptr; + +char* grpc_get_well_known_google_credentials_file_path(void) { + if (creds_path_getter != nullptr) return creds_path_getter(); + return grpc_get_well_known_google_credentials_file_path_impl(); +} + +void grpc_override_well_known_credentials_path_getter( + grpc_well_known_credentials_path_getter getter) { + creds_path_getter = getter; +} diff --git a/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.h b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.h new file mode 100644 index 00000000000000..bf00f7285ad1f4 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/google_default/google_default_credentials.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H + +#include + +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/credentials/credentials.h" + +#define GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY "gcloud" +#define GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE \ + "application_default_credentials.json" + +#ifdef GPR_WINDOWS +#define GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR "APPDATA" +#define GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX \ + GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY \ + "/" GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE +#else +#define GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR "HOME" +#define GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX \ + ".config/" GRPC_GOOGLE_CLOUD_SDK_CONFIG_DIRECTORY \ + "/" GRPC_GOOGLE_WELL_KNOWN_CREDENTIALS_FILE +#endif + +class grpc_google_default_channel_credentials + : public grpc_channel_credentials { + public: + grpc_google_default_channel_credentials( + grpc_core::RefCountedPtr alts_creds, + grpc_core::RefCountedPtr ssl_creds) + : grpc_channel_credentials(GRPC_CHANNEL_CREDENTIALS_TYPE_GOOGLE_DEFAULT), + alts_creds_(std::move(alts_creds)), + ssl_creds_(std::move(ssl_creds)) {} + + ~grpc_google_default_channel_credentials() override = default; + + grpc_core::RefCountedPtr + create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) override; + + const grpc_channel_credentials* alts_creds() const { + return alts_creds_.get(); + } + const grpc_channel_credentials* ssl_creds() const { return ssl_creds_.get(); } + + private: + grpc_core::RefCountedPtr alts_creds_; + grpc_core::RefCountedPtr ssl_creds_; +}; + +namespace grpc_core { +namespace internal { + +typedef bool (*grpc_gce_tenancy_checker)(void); + +void set_gce_tenancy_checker_for_testing(grpc_gce_tenancy_checker checker); + +// TEST-ONLY. Reset the internal global state. +void grpc_flush_cached_google_default_credentials(void); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_GOOGLE_DEFAULT_GOOGLE_DEFAULT_CREDENTIALS_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.cc new file mode 100644 index 00000000000000..5cd561f676a7ac --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/iam/iam_credentials.h" + +#include + +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include +#include + +grpc_google_iam_credentials::~grpc_google_iam_credentials() { + grpc_credentials_mdelem_array_destroy(&md_array_); +} + +bool grpc_google_iam_credentials::get_request_metadata( + grpc_polling_entity* pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, + grpc_error** error) { + grpc_credentials_mdelem_array_append(md_array, &md_array_); + return true; +} + +void grpc_google_iam_credentials::cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) { + GRPC_ERROR_UNREF(error); +} + +grpc_google_iam_credentials::grpc_google_iam_credentials( + const char* token, const char* authority_selector) + : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_IAM) { + grpc_mdelem md = grpc_mdelem_from_slices( + grpc_slice_from_static_string(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY), + grpc_slice_from_copied_string(token)); + grpc_credentials_mdelem_array_add(&md_array_, md); + GRPC_MDELEM_UNREF(md); + md = grpc_mdelem_from_slices( + grpc_slice_from_static_string(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY), + grpc_slice_from_copied_string(authority_selector)); + grpc_credentials_mdelem_array_add(&md_array_, md); + GRPC_MDELEM_UNREF(md); +} + +grpc_call_credentials* grpc_google_iam_credentials_create( + const char* token, const char* authority_selector, void* reserved) { + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE( + "grpc_iam_credentials_create(token=%s, authority_selector=%s, " + "reserved=%p)", + 3, (token, authority_selector, reserved)); + GPR_ASSERT(reserved == nullptr); + GPR_ASSERT(token != nullptr); + GPR_ASSERT(authority_selector != nullptr); + return grpc_core::MakeRefCounted( + token, authority_selector) + .release(); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.h b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.h new file mode 100644 index 00000000000000..36f5ee89307e9f --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/iam/iam_credentials.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +class grpc_google_iam_credentials : public grpc_call_credentials { + public: + grpc_google_iam_credentials(const char* token, + const char* authority_selector); + ~grpc_google_iam_credentials() override; + + bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) override; + + void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, + grpc_error* error) override; + + private: + grpc_credentials_mdelem_array md_array_; +}; + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.cc b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.cc new file mode 100644 index 00000000000000..1c4827df0fcaf8 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.cc @@ -0,0 +1,314 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/jwt/json_token.h" + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/security/util/json_util.h" +#include "src/core/lib/slice/b64.h" + +extern "C" { +#include +#include +#include +} + +/* --- Constants. --- */ + +/* 1 hour max. */ +gpr_timespec grpc_max_auth_token_lifetime() { + gpr_timespec out; + out.tv_sec = 3600; + out.tv_nsec = 0; + out.clock_type = GPR_TIMESPAN; + return out; +} + +#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 = + nullptr; + +/* --- grpc_auth_json_key. --- */ + +int grpc_auth_json_key_is_valid(const grpc_auth_json_key* json_key) { + return (json_key != nullptr) && + 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 = nullptr; + const char* prop_value; + int success = 0; + + memset(&result, 0, sizeof(grpc_auth_json_key)); + result.type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json == nullptr) { + gpr_log(GPR_ERROR, "Invalid json."); + goto end; + } + + prop_value = grpc_json_get_string_property(json, "type"); + if (prop_value == nullptr || + strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) { + goto end; + } + result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT; + + if (!grpc_copy_json_string_property(json, "private_key_id", + &result.private_key_id) || + !grpc_copy_json_string_property(json, "client_id", &result.client_id) || + !grpc_copy_json_string_property(json, "client_email", + &result.client_email)) { + goto end; + } + + prop_value = grpc_json_get_string_property(json, "private_key"); + if (prop_value == nullptr) { + goto end; + } + bio = BIO_new(BIO_s_mem()); + success = BIO_puts(bio, prop_value); + if ((success < 0) || (static_cast(success) != strlen(prop_value))) { + gpr_log(GPR_ERROR, "Could not write into openssl BIO."); + goto end; + } + result.private_key = + PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, (void*)""); + if (result.private_key == nullptr) { + gpr_log(GPR_ERROR, "Could not deserialize private key."); + goto end; + } + success = 1; + +end: + if (bio != nullptr) 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 != nullptr) grpc_json_destroy(json); + gpr_free(scratchpad); + return result; +} + +void grpc_auth_json_key_destruct(grpc_auth_json_key* json_key) { + if (json_key == nullptr) return; + json_key->type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json_key->client_id != nullptr) { + gpr_free(json_key->client_id); + json_key->client_id = nullptr; + } + if (json_key->private_key_id != nullptr) { + gpr_free(json_key->private_key_id); + json_key->private_key_id = nullptr; + } + if (json_key->client_email != nullptr) { + gpr_free(json_key->client_email); + json_key->client_email = nullptr; + } + if (json_key->private_key != nullptr) { + RSA_free(json_key->private_key); + json_key->private_key = nullptr; + } +} + +/* --- 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 = nullptr; + char* json_str = nullptr; + char* result = nullptr; + + child = create_child(nullptr, 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 = nullptr; + char* json_str = nullptr; + char* result = nullptr; + 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()); + } + int64_ttoa(now.tv_sec, now_str); + int64_ttoa(expiration.tv_sec, expiration_str); + + child = create_child(nullptr, json, "iss", json_key->client_email, + GRPC_JSON_STRING); + if (scope != nullptr) { + 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 = + static_cast(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((uintptr_t)(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 nullptr; + } +} + +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 = nullptr; + EVP_PKEY* key = EVP_PKEY_new(); + size_t sig_len = 0; + unsigned char* sig = nullptr; + char* result = nullptr; + if (md == nullptr) return nullptr; + md_ctx = EVP_MD_CTX_create(); + if (md_ctx == nullptr) { + 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, nullptr, md, nullptr, 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, nullptr, &sig_len) != 1) { + gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed."); + goto end; + } + sig = static_cast(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 != nullptr) EVP_PKEY_free(key); + if (md_ctx != nullptr) EVP_MD_CTX_destroy(md_ctx); + if (sig != nullptr) 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 != nullptr) { + 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 == nullptr) { + gpr_free(to_sign); + return nullptr; + } + 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; +} diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.h b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.h new file mode 100644 index 00000000000000..3ed990140d3e95 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/jwt/json_token.h @@ -0,0 +1,77 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H + +#include + +#include "src/core/tsi/grpc_shadow_boringssl.h" + +#include +#include + +#include "src/core/lib/json/json.h" + +/* --- Constants. --- */ + +#define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token" + +/* --- 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); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.cc new file mode 100644 index 00000000000000..f2591a1ea5e2f7 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.cc @@ -0,0 +1,181 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/jwt/jwt_credentials.h" + +#include +#include + +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include +#include + +void grpc_service_account_jwt_access_credentials::reset_cache() { + GRPC_MDELEM_UNREF(cached_.jwt_md); + cached_.jwt_md = GRPC_MDNULL; + if (cached_.service_url != nullptr) { + gpr_free(cached_.service_url); + cached_.service_url = nullptr; + } + cached_.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); +} + +grpc_service_account_jwt_access_credentials:: + ~grpc_service_account_jwt_access_credentials() { + grpc_auth_json_key_destruct(&key_); + reset_cache(); + gpr_mu_destroy(&cache_mu_); +} + +bool grpc_service_account_jwt_access_credentials::get_request_metadata( + grpc_polling_entity* pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, + grpc_error** error) { + 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_mdelem jwt_md = GRPC_MDNULL; + { + gpr_mu_lock(&cache_mu_); + if (cached_.service_url != nullptr && + strcmp(cached_.service_url, context.service_url) == 0 && + !GRPC_MDISNULL(cached_.jwt_md) && + (gpr_time_cmp( + gpr_time_sub(cached_.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)), + refresh_threshold) > 0)) { + jwt_md = GRPC_MDELEM_REF(cached_.jwt_md); + } + gpr_mu_unlock(&cache_mu_); + } + + if (GRPC_MDISNULL(jwt_md)) { + char* jwt = nullptr; + /* Generate a new jwt. */ + gpr_mu_lock(&cache_mu_); + reset_cache(); + jwt = grpc_jwt_encode_and_sign(&key_, context.service_url, jwt_lifetime_, + nullptr); + if (jwt != nullptr) { + char* md_value; + gpr_asprintf(&md_value, "Bearer %s", jwt); + gpr_free(jwt); + cached_.jwt_expiration = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), jwt_lifetime_); + cached_.service_url = gpr_strdup(context.service_url); + cached_.jwt_md = grpc_mdelem_from_slices( + grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(md_value)); + gpr_free(md_value); + jwt_md = GRPC_MDELEM_REF(cached_.jwt_md); + } + gpr_mu_unlock(&cache_mu_); + } + + if (!GRPC_MDISNULL(jwt_md)) { + grpc_credentials_mdelem_array_add(md_array, jwt_md); + GRPC_MDELEM_UNREF(jwt_md); + } else { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT."); + } + return true; +} + +void grpc_service_account_jwt_access_credentials::cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) { + GRPC_ERROR_UNREF(error); +} + +grpc_service_account_jwt_access_credentials:: + grpc_service_account_jwt_access_credentials(grpc_auth_json_key key, + gpr_timespec token_lifetime) + : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_JWT), key_(key) { + gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime(); + if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) { + gpr_log(GPR_INFO, + "Cropping token lifetime to maximum allowed value (%d secs).", + static_cast(max_token_lifetime.tv_sec)); + token_lifetime = grpc_max_auth_token_lifetime(); + } + jwt_lifetime_ = token_lifetime; + gpr_mu_init(&cache_mu_); + reset_cache(); +} + +grpc_core::RefCountedPtr +grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + grpc_auth_json_key key, gpr_timespec token_lifetime) { + if (!grpc_auth_json_key_is_valid(&key)) { + gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation"); + return nullptr; + } + return grpc_core::MakeRefCounted( + key, token_lifetime); +} + +static char* redact_private_key(const char* json_key) { + char* json_copy = gpr_strdup(json_key); + grpc_json* json = grpc_json_parse_string(json_copy); + if (!json) { + gpr_free(json_copy); + return gpr_strdup(""); + } + const char* redacted = ""; + grpc_json* current = json->child; + while (current) { + if (current->type == GRPC_JSON_STRING && + strcmp(current->key, "private_key") == 0) { + current->value = const_cast(redacted); + break; + } + current = current->next; + } + char* clean_json = grpc_json_dump_to_string(json, 2); + gpr_free(json_copy); + grpc_json_destroy(json); + return clean_json; +} + +grpc_call_credentials* grpc_service_account_jwt_access_credentials_create( + const char* json_key, gpr_timespec token_lifetime, void* reserved) { + if (grpc_api_trace.enabled()) { + char* clean_json = redact_private_key(json_key); + gpr_log(GPR_INFO, + "grpc_service_account_jwt_access_credentials_create(" + "json_key=%s, " + "token_lifetime=" + "gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec, + static_cast(token_lifetime.clock_type), reserved); + gpr_free(clean_json); + } + GPR_ASSERT(reserved == nullptr); + grpc_core::ExecCtx exec_ctx; + return grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + grpc_auth_json_key_create_from_string(json_key), token_lifetime) + .release(); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.h b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.h new file mode 100644 index 00000000000000..5af909f44df829 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_credentials.h @@ -0,0 +1,68 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H + +#include + +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/jwt/json_token.h" + +class grpc_service_account_jwt_access_credentials + : public grpc_call_credentials { + public: + grpc_service_account_jwt_access_credentials(grpc_auth_json_key key, + gpr_timespec token_lifetime); + ~grpc_service_account_jwt_access_credentials() override; + + bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) override; + + void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, + grpc_error* error) override; + + const gpr_timespec& jwt_lifetime() const { return jwt_lifetime_; } + const grpc_auth_json_key& key() const { return key_; } + + private: + void reset_cache(); + + // 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_mdelem jwt_md = GRPC_MDNULL; + char* service_url = nullptr; + gpr_timespec jwt_expiration; + } cached_; + + grpc_auth_json_key key_; + gpr_timespec jwt_lifetime_; +}; + +// Private constructor for jwt credentials from an already parsed json key. +// Takes ownership of the key. +grpc_core::RefCountedPtr +grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + grpc_auth_json_key key, gpr_timespec token_lifetime); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.cc b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.cc new file mode 100644 index 00000000000000..cdef0f322a9a56 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.cc @@ -0,0 +1,938 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/grpc_shadow_boringssl.h" + +#include "src/core/lib/security/credentials/jwt/jwt_verifier.h" + +#include +#include + +#include +#include +#include +#include + +extern "C" { +#include +#include +#include +} + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/ssl_types.h" + +/* --- 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 nullptr; + } +} + +static grpc_json* parse_json_part_from_jwt(const char* str, size_t len, + grpc_slice* buffer) { + grpc_json* json; + + *buffer = grpc_base64_decode_with_len(str, len, 1); + if (GRPC_SLICE_IS_EMPTY(*buffer)) { + gpr_log(GPR_ERROR, "Invalid base64."); + return nullptr; + } + json = grpc_json_parse_string_with_len( + reinterpret_cast GRPC_SLICE_START_PTR(*buffer), + GRPC_SLICE_LENGTH(*buffer)); + if (json == nullptr) { + grpc_slice_unref_internal(*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 nullptr; + } + 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, nullptr, 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...). */ + grpc_slice buffer; +} jose_header; + +static void jose_header_destroy(jose_header* h) { + grpc_slice_unref_internal(h->buffer); + gpr_free(h); +} + +/* Takes ownership of json and buffer. */ +static jose_header* jose_header_from_json(grpc_json* json, grpc_slice buffer) { + grpc_json* cur; + jose_header* h = static_cast(gpr_zalloc(sizeof(jose_header))); + h->buffer = buffer; + for (cur = json->child; cur != nullptr; 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) == nullptr) { + 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 == nullptr) goto error; + } else if (strcmp(cur->key, "kid") == 0) { + h->kid = validate_string_field(cur, "kid"); + if (h->kid == nullptr) goto error; + } + } + if (h->alg == nullptr) { + 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 nullptr; +} + +/* --- 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; + grpc_slice buffer; +}; + +void grpc_jwt_claims_destroy(grpc_jwt_claims* claims) { + grpc_json_destroy(claims->json); + grpc_slice_unref_internal(claims->buffer); + gpr_free(claims); +} + +const grpc_json* grpc_jwt_claims_json(const grpc_jwt_claims* claims) { + if (claims == nullptr) return nullptr; + return claims->json; +} + +const char* grpc_jwt_claims_subject(const grpc_jwt_claims* claims) { + if (claims == nullptr) return nullptr; + return claims->sub; +} + +const char* grpc_jwt_claims_issuer(const grpc_jwt_claims* claims) { + if (claims == nullptr) return nullptr; + return claims->iss; +} + +const char* grpc_jwt_claims_id(const grpc_jwt_claims* claims) { + if (claims == nullptr) return nullptr; + return claims->jti; +} + +const char* grpc_jwt_claims_audience(const grpc_jwt_claims* claims) { + if (claims == nullptr) return nullptr; + return claims->aud; +} + +gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims* claims) { + if (claims == nullptr) return gpr_inf_past(GPR_CLOCK_REALTIME); + return claims->iat; +} + +gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims* claims) { + if (claims == nullptr) return gpr_inf_future(GPR_CLOCK_REALTIME); + return claims->exp; +} + +gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims* claims) { + if (claims == nullptr) 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, grpc_slice buffer) { + grpc_json* cur; + grpc_jwt_claims* claims = + static_cast(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 != nullptr; cur = cur->next) { + if (strcmp(cur->key, "sub") == 0) { + claims->sub = validate_string_field(cur, "sub"); + if (claims->sub == nullptr) goto error; + } else if (strcmp(cur->key, "iss") == 0) { + claims->iss = validate_string_field(cur, "iss"); + if (claims->iss == nullptr) goto error; + } else if (strcmp(cur->key, "aud") == 0) { + claims->aud = validate_string_field(cur, "aud"); + if (claims->aud == nullptr) goto error; + } else if (strcmp(cur->key, "jti") == 0) { + claims->jti = validate_string_field(cur, "jti"); + if (claims->jti == nullptr) 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 nullptr; +} + +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 != nullptr); + + 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; + } + + /* This should be probably up to the upper layer to decide but let's harcode + the 99% use case here for email issuers, where the JWT must be self + issued. */ + if (grpc_jwt_issuer_email_domain(claims->iss) != nullptr && + claims->sub != nullptr && strcmp(claims->iss, claims->sub) != 0) { + gpr_log(GPR_ERROR, + "Email issuer (%s) cannot assert another subject (%s) than itself.", + claims->iss, claims->sub); + return GRPC_JWT_VERIFIER_BAD_SUBJECT; + } + + if (audience == nullptr) { + audience_ok = claims->aud == nullptr; + } else { + audience_ok = claims->aud != nullptr && strcmp(audience, claims->aud) == 0; + } + if (!audience_ok) { + gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.", + audience == nullptr ? "NULL" : audience, + claims->aud == nullptr ? "NULL" : claims->aud); + return GRPC_JWT_VERIFIER_BAD_AUDIENCE; + } + return GRPC_JWT_VERIFIER_OK; +} + +/* --- verifier_cb_ctx object. --- */ + +typedef enum { + HTTP_RESPONSE_OPENID = 0, + HTTP_RESPONSE_KEYS, + HTTP_RESPONSE_COUNT /* must be last */ +} http_response_index; + +typedef struct { + grpc_jwt_verifier* verifier; + grpc_polling_entity pollent; + jose_header* header; + grpc_jwt_claims* claims; + char* audience; + grpc_slice signature; + grpc_slice signed_data; + void* user_data; + grpc_jwt_verification_done_cb user_cb; + grpc_http_response responses[HTTP_RESPONSE_COUNT]; +} 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, grpc_slice signature, + const char* signed_jwt, size_t signed_jwt_len, void* user_data, + grpc_jwt_verification_done_cb cb) { + grpc_core::ExecCtx exec_ctx; + verifier_cb_ctx* ctx = + static_cast(gpr_zalloc(sizeof(verifier_cb_ctx))); + ctx->verifier = verifier; + ctx->pollent = grpc_polling_entity_create_from_pollset(pollset); + ctx->header = header; + ctx->audience = gpr_strdup(audience); + ctx->claims = claims; + ctx->signature = signature; + ctx->signed_data = grpc_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 != nullptr) gpr_free(ctx->audience); + if (ctx->claims != nullptr) grpc_jwt_claims_destroy(ctx->claims); + grpc_slice_unref_internal(ctx->signature); + grpc_slice_unref_internal(ctx->signed_data); + jose_header_destroy(ctx->header); + for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) { + grpc_http_response_destroy(&ctx->responses[i]); + } + /* 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. */ +grpc_millis grpc_jwt_verifier_max_delay = 60 * GPR_MS_PER_SEC; + +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 = nullptr; + + if (response == nullptr) { + gpr_log(GPR_ERROR, "HTTP response is NULL."); + return nullptr; + } + if (response->status != 200) { + gpr_log(GPR_ERROR, "Call to http server failed with error %d.", + response->status); + return nullptr; + } + + json = grpc_json_parse_string_with_len(response->body, response->body_length); + if (json == nullptr) { + 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 != nullptr; cur = cur->next) { + if (strcmp(cur->key, name) == 0) return cur; + } + return nullptr; +} + +static EVP_PKEY* extract_pkey_from_x509(const char* x509_str) { + X509* x509 = nullptr; + EVP_PKEY* result = nullptr; + BIO* bio = BIO_new(BIO_s_mem()); + size_t len = strlen(x509_str); + GPR_ASSERT(len < INT_MAX); + BIO_write(bio, x509_str, static_cast(len)); + x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + if (x509 == nullptr) { + gpr_log(GPR_ERROR, "Unable to parse x509 cert."); + goto end; + } + result = X509_get_pubkey(x509); + if (result == nullptr) { + gpr_log(GPR_ERROR, "Cannot find public key in X509 cert."); + } + +end: + BIO_free(bio); + X509_free(x509); + return result; +} + +static BIGNUM* bignum_from_base64(const char* b64) { + BIGNUM* result = nullptr; + grpc_slice bin; + + if (b64 == nullptr) return nullptr; + bin = grpc_base64_decode(b64, 1); + if (GRPC_SLICE_IS_EMPTY(bin)) { + gpr_log(GPR_ERROR, "Invalid base64 for big num."); + return nullptr; + } + result = BN_bin2bn(GRPC_SLICE_START_PTR(bin), + TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), nullptr); + grpc_slice_unref_internal(bin); + return result; +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +// Provide compatibility across OpenSSL 1.02 and 1.1. +static int RSA_set0_key(RSA* r, BIGNUM* n, BIGNUM* e, BIGNUM* d) { + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == nullptr && n == nullptr) || (r->e == nullptr && e == nullptr)) { + return 0; + } + + if (n != nullptr) { + BN_free(r->n); + r->n = n; + } + if (e != nullptr) { + BN_free(r->e); + r->e = e; + } + if (d != nullptr) { + BN_free(r->d); + r->d = d; + } + + return 1; +} +#endif // OPENSSL_VERSION_NUMBER < 0x10100000L + +static EVP_PKEY* pkey_from_jwk(const grpc_json* json, const char* kty) { + const grpc_json* key_prop; + RSA* rsa = nullptr; + EVP_PKEY* result = nullptr; + BIGNUM* tmp_n = nullptr; + BIGNUM* tmp_e = nullptr; + + GPR_ASSERT(kty != nullptr && json != nullptr); + if (strcmp(kty, "RSA") != 0) { + gpr_log(GPR_ERROR, "Unsupported key type %s.", kty); + goto end; + } + rsa = RSA_new(); + if (rsa == nullptr) { + gpr_log(GPR_ERROR, "Could not create rsa key."); + goto end; + } + for (key_prop = json->child; key_prop != nullptr; key_prop = key_prop->next) { + if (strcmp(key_prop->key, "n") == 0) { + tmp_n = bignum_from_base64(validate_string_field(key_prop, "n")); + if (tmp_n == nullptr) goto end; + } else if (strcmp(key_prop->key, "e") == 0) { + tmp_e = bignum_from_base64(validate_string_field(key_prop, "e")); + if (tmp_e == nullptr) goto end; + } + } + if (tmp_e == nullptr || tmp_n == nullptr) { + gpr_log(GPR_ERROR, "Missing RSA public key field."); + goto end; + } + if (!RSA_set0_key(rsa, tmp_n, tmp_e, nullptr)) { + gpr_log(GPR_ERROR, "Cannot set RSA key from inputs."); + goto end; + } + /* RSA_set0_key takes ownership on success. */ + tmp_n = nullptr; + tmp_e = nullptr; + result = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */ + +end: + RSA_free(rsa); + BN_free(tmp_n); + BN_free(tmp_e); + 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 == nullptr) { + /* Use the google proprietary format which is: + { : , : , ... } */ + const grpc_json* cur = find_property_by_name(json, header_kid); + if (cur == nullptr) return nullptr; + 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 nullptr; + } + /* Key format is specified in: + https://tools.ietf.org/html/rfc7518#section-6. */ + for (jkey = jwk_keys->child; jkey != nullptr; jkey = jkey->next) { + grpc_json* key_prop; + const char* alg = nullptr; + const char* kid = nullptr; + const char* kty = nullptr; + + if (jkey->type != GRPC_JSON_OBJECT) continue; + for (key_prop = jkey->child; key_prop != nullptr; + 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 != nullptr && kid != nullptr && kty != nullptr && + 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 nullptr; +} + +static int verify_jwt_signature(EVP_PKEY* key, const char* alg, + grpc_slice signature, grpc_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 != nullptr); /* Checked before. */ + if (md_ctx == nullptr) { + gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX."); + goto end; + } + if (EVP_DigestVerifyInit(md_ctx, nullptr, md, nullptr, key) != 1) { + gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed."); + goto end; + } + if (EVP_DigestVerifyUpdate(md_ctx, GRPC_SLICE_START_PTR(signed_data), + GRPC_SLICE_LENGTH(signed_data)) != 1) { + gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed."); + goto end; + } + if (EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(signature), + GRPC_SLICE_LENGTH(signature)) != 1) { + gpr_log(GPR_ERROR, "JWT signature verification failed."); + goto end; + } + result = 1; + +end: + EVP_MD_CTX_destroy(md_ctx); + return result; +} + +static void on_keys_retrieved(void* user_data, grpc_error* error) { + verifier_cb_ctx* ctx = static_cast(user_data); + grpc_json* json = json_from_http(&ctx->responses[HTTP_RESPONSE_KEYS]); + EVP_PKEY* verification_key = nullptr; + grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR; + grpc_jwt_claims* claims = nullptr; + + if (json == nullptr) { + status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR; + goto end; + } + verification_key = + find_verification_key(json, ctx->header->alg, ctx->header->kid); + if (verification_key == nullptr) { + 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 = nullptr; + } + +end: + if (json != nullptr) grpc_json_destroy(json); + 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, grpc_error* error) { + const grpc_json* cur; + verifier_cb_ctx* ctx = static_cast(user_data); + const grpc_http_response* response = &ctx->responses[HTTP_RESPONSE_OPENID]; + grpc_json* json = json_from_http(response); + grpc_httpcli_request req; + const char* jwks_uri; + grpc_resource_quota* resource_quota = nullptr; + + /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */ + if (json == nullptr) goto error; + cur = find_property_by_name(json, "jwks_uri"); + if (cur == nullptr) { + 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 == nullptr) 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.http.path = const_cast(strchr(jwks_uri, '/')); + if (req.http.path == nullptr) { + req.http.path = (char*)""; + } else { + *(req.host + (req.http.path - jwks_uri)) = '\0'; + } + + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + resource_quota = grpc_resource_quota_create("jwt_verifier"); + grpc_httpcli_get( + &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req, + grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay, + GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx), + &ctx->responses[HTTP_RESPONSE_KEYS]); + grpc_resource_quota_unref_internal(resource_quota); + grpc_json_destroy(json); + gpr_free(req.host); + return; + +error: + if (json != nullptr) grpc_json_destroy(json); + ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr); + 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 == nullptr) return nullptr; + for (i = 0; i < v->num_mappings; i++) { + if (strcmp(email_domain, v->mappings[i].email_domain) == 0) { + return &v->mappings[i]; + } + } + return nullptr; +} + +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 != nullptr) { + 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); +} + +/* Very non-sophisticated way to detect an email address. Should be good + enough for now... */ +const char* grpc_jwt_issuer_email_domain(const char* issuer) { + const char* at_sign = strchr(issuer, '@'); + if (at_sign == nullptr) return nullptr; + const char* email_domain = at_sign + 1; + if (*email_domain == '\0') return nullptr; + const char* dot = strrchr(email_domain, '.'); + if (dot == nullptr || dot == email_domain) return email_domain; + GPR_ASSERT(dot > email_domain); + /* There may be a subdomain, we just want the domain. */ + dot = static_cast(gpr_memrchr( + (void*)email_domain, '.', static_cast(dot - email_domain))); + if (dot == nullptr) return email_domain; + return dot + 1; +} + +/* Takes ownership of ctx. */ +static void retrieve_key_and_verify(verifier_cb_ctx* ctx) { + const char* email_domain; + grpc_closure* http_cb; + char* path_prefix = nullptr; + const char* iss; + grpc_httpcli_request req; + grpc_resource_quota* resource_quota = nullptr; + memset(&req, 0, sizeof(grpc_httpcli_request)); + req.handshaker = &grpc_httpcli_ssl; + http_response_index rsp_idx; + + GPR_ASSERT(ctx != nullptr && ctx->header != nullptr && + ctx->claims != nullptr); + iss = ctx->claims->iss; + if (ctx->header->kid == nullptr) { + gpr_log(GPR_ERROR, "Missing kid in jose header."); + goto error; + } + if (iss == nullptr) { + 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. */ + email_domain = grpc_jwt_issuer_email_domain(iss); + if (email_domain != nullptr) { + email_key_mapping* mapping; + GPR_ASSERT(ctx->verifier != nullptr); + mapping = verifier_get_mapping(ctx->verifier, email_domain); + if (mapping == nullptr) { + 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 == nullptr) { + gpr_asprintf(&req.http.path, "/%s", iss); + } else { + *(path_prefix++) = '\0'; + gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss); + } + http_cb = + GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx); + rsp_idx = HTTP_RESPONSE_KEYS; + } else { + req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss); + path_prefix = strchr(req.host, '/'); + if (path_prefix == nullptr) { + req.http.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX); + } else { + *(path_prefix++) = 0; + gpr_asprintf(&req.http.path, "/%s%s", path_prefix, + GRPC_OPENID_CONFIG_URL_SUFFIX); + } + http_cb = GRPC_CLOSURE_CREATE(on_openid_config_retrieved, ctx, + grpc_schedule_on_exec_ctx); + rsp_idx = HTTP_RESPONSE_OPENID; + } + + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + resource_quota = grpc_resource_quota_create("jwt_verifier"); + grpc_httpcli_get( + &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req, + grpc_core::ExecCtx::Get()->Now() + grpc_jwt_verifier_max_delay, http_cb, + &ctx->responses[rsp_idx]); + grpc_resource_quota_unref_internal(resource_quota); + gpr_free(req.host); + gpr_free(req.http.path); + return; + +error: + ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, nullptr); + 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 = nullptr; + grpc_json* json; + jose_header* header = nullptr; + grpc_jwt_claims* claims = nullptr; + grpc_slice header_buffer; + grpc_slice claims_buffer; + grpc_slice signature; + size_t signed_jwt_len; + const char* cur = jwt; + + GPR_ASSERT(verifier != nullptr && jwt != nullptr && audience != nullptr && + cb != nullptr); + dot = strchr(cur, '.'); + if (dot == nullptr) goto error; + json = parse_json_part_from_jwt(cur, static_cast(dot - cur), + &header_buffer); + if (json == nullptr) goto error; + header = jose_header_from_json(json, header_buffer); + if (header == nullptr) goto error; + + cur = dot + 1; + dot = strchr(cur, '.'); + if (dot == nullptr) goto error; + json = parse_json_part_from_jwt(cur, static_cast(dot - cur), + &claims_buffer); + if (json == nullptr) goto error; + claims = grpc_jwt_claims_from_json(json, claims_buffer); + if (claims == nullptr) goto error; + + signed_jwt_len = static_cast(dot - jwt); + cur = dot + 1; + signature = grpc_base64_decode(cur, 1); + if (GRPC_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 != nullptr) jose_header_destroy(header); + if (claims != nullptr) grpc_jwt_claims_destroy(claims); + cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, nullptr); +} + +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 = + static_cast(gpr_zalloc(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 = static_cast( + 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 != nullptr) { + 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 == nullptr) return; + grpc_httpcli_context_destroy(&v->http_ctx); + if (v->mappings != nullptr) { + 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/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.h b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.h new file mode 100644 index 00000000000000..cdb09870bd5ea0 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/jwt/jwt_verifier.h @@ -0,0 +1,123 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H + +#include + +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/json/json.h" + +#include +#include + +/* --- Constants. --- */ + +#define GRPC_OPENID_CONFIG_URL_SUFFIX "/.well-known/openid-configuration" +#define GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN "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_BAD_SUBJECT, + 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 grpc_millis 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, grpc_slice buffer); +grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims* claims, + const char* audience); +const char* grpc_jwt_issuer_email_domain(const char* issuer); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.cc new file mode 100644 index 00000000000000..6f6f95a34a7c9d --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.cc @@ -0,0 +1,64 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/local/local_credentials.h" + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/security_connector/local/local_security_connector.h" + +#define GRPC_CREDENTIALS_TYPE_LOCAL "Local" + +grpc_core::RefCountedPtr +grpc_local_credentials::create_security_connector( + grpc_core::RefCountedPtr request_metadata_creds, + const char* target_name, const grpc_channel_args* args, + grpc_channel_args** new_args) { + return grpc_local_channel_security_connector_create( + this->Ref(), std::move(request_metadata_creds), args, target_name); +} + +grpc_core::RefCountedPtr +grpc_local_server_credentials::create_security_connector() { + return grpc_local_server_security_connector_create(this->Ref()); +} + +grpc_local_credentials::grpc_local_credentials( + grpc_local_connect_type connect_type) + : grpc_channel_credentials(GRPC_CREDENTIALS_TYPE_LOCAL), + connect_type_(connect_type) {} + +grpc_channel_credentials* grpc_local_credentials_create( + grpc_local_connect_type connect_type) { + return grpc_core::New(connect_type); +} + +grpc_local_server_credentials::grpc_local_server_credentials( + grpc_local_connect_type connect_type) + : grpc_server_credentials(GRPC_CREDENTIALS_TYPE_LOCAL), + connect_type_(connect_type) {} + +grpc_server_credentials* grpc_local_server_credentials_create( + grpc_local_connect_type connect_type) { + return grpc_core::New(connect_type); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.h b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.h new file mode 100644 index 00000000000000..60a8a4f64ca10f --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/local/local_credentials.h @@ -0,0 +1,61 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_LOCAL_LOCAL_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_LOCAL_LOCAL_CREDENTIALS_H + +#include + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +/* Main class for grpc local channel credential. */ +class grpc_local_credentials final : public grpc_channel_credentials { + public: + explicit grpc_local_credentials(grpc_local_connect_type connect_type); + ~grpc_local_credentials() override = default; + + grpc_core::RefCountedPtr + create_security_connector( + grpc_core::RefCountedPtr request_metadata_creds, + const char* target_name, const grpc_channel_args* args, + grpc_channel_args** new_args) override; + + grpc_local_connect_type connect_type() const { return connect_type_; } + + private: + grpc_local_connect_type connect_type_; +}; + +/* Main class for grpc local server credential. */ +class grpc_local_server_credentials final : public grpc_server_credentials { + public: + explicit grpc_local_server_credentials(grpc_local_connect_type connect_type); + ~grpc_local_server_credentials() override = default; + + grpc_core::RefCountedPtr + create_security_connector() override; + + grpc_local_connect_type connect_type() const { return connect_type_; } + + private: + grpc_local_connect_type connect_type_; +}; + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_LOCAL_LOCAL_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc new file mode 100644 index 00000000000000..ad63b01e754d79 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc @@ -0,0 +1,517 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" + +#include + +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/util/json_util.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include + +// +// Auth Refresh Token. +// + +int grpc_auth_refresh_token_is_valid( + const grpc_auth_refresh_token* refresh_token) { + return (refresh_token != nullptr) && + 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 == nullptr) { + gpr_log(GPR_ERROR, "Invalid json."); + goto end; + } + + prop_value = grpc_json_get_string_property(json, "type"); + if (prop_value == nullptr || + strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) { + goto end; + } + result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER; + + if (!grpc_copy_json_string_property(json, "client_secret", + &result.client_secret) || + !grpc_copy_json_string_property(json, "client_id", &result.client_id) || + !grpc_copy_json_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 != nullptr) grpc_json_destroy(json); + gpr_free(scratchpad); + return result; +} + +void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token* refresh_token) { + if (refresh_token == nullptr) return; + refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID; + if (refresh_token->client_id != nullptr) { + gpr_free(refresh_token->client_id); + refresh_token->client_id = nullptr; + } + if (refresh_token->client_secret != nullptr) { + gpr_free(refresh_token->client_secret); + refresh_token->client_secret = nullptr; + } + if (refresh_token->refresh_token != nullptr) { + gpr_free(refresh_token->refresh_token); + refresh_token->refresh_token = nullptr; + } +} + +// +// Oauth2 Token Fetcher credentials. +// + +grpc_oauth2_token_fetcher_credentials:: + ~grpc_oauth2_token_fetcher_credentials() { + GRPC_MDELEM_UNREF(access_token_md_); + gpr_mu_destroy(&mu_); + grpc_pollset_set_destroy(grpc_polling_entity_pollset_set(&pollent_)); + grpc_httpcli_context_destroy(&httpcli_context_); +} + +grpc_credentials_status +grpc_oauth2_token_fetcher_credentials_parse_server_response( + const grpc_http_response* response, grpc_mdelem* token_md, + grpc_millis* token_lifetime) { + char* null_terminated_body = nullptr; + char* new_access_token = nullptr; + grpc_credentials_status status = GRPC_CREDENTIALS_OK; + grpc_json* json = nullptr; + + if (response == nullptr) { + gpr_log(GPR_ERROR, "Received NULL response."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + + if (response->body_length > 0) { + null_terminated_body = + static_cast(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 != nullptr ? null_terminated_body : ""); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } else { + grpc_json* access_token = nullptr; + grpc_json* token_type = nullptr; + grpc_json* expires_in = nullptr; + grpc_json* ptr; + json = grpc_json_parse_string(null_terminated_body); + if (json == nullptr) { + 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 == nullptr || 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 == nullptr || 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 == nullptr || 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 = strtol(expires_in->value, nullptr, 10) * GPR_MS_PER_SEC; + if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(*token_md); + *token_md = grpc_mdelem_from_slices( + grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(new_access_token)); + status = GRPC_CREDENTIALS_OK; + } + +end: + if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) { + GRPC_MDELEM_UNREF(*token_md); + *token_md = GRPC_MDNULL; + } + if (null_terminated_body != nullptr) gpr_free(null_terminated_body); + if (new_access_token != nullptr) gpr_free(new_access_token); + if (json != nullptr) grpc_json_destroy(json); + return status; +} + +static void on_oauth2_token_fetcher_http_response(void* user_data, + grpc_error* error) { + GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error)); + grpc_credentials_metadata_request* r = + static_cast(user_data); + grpc_oauth2_token_fetcher_credentials* c = + reinterpret_cast(r->creds.get()); + c->on_http_response(r, error); +} + +void grpc_oauth2_token_fetcher_credentials::on_http_response( + grpc_credentials_metadata_request* r, grpc_error* error) { + grpc_mdelem access_token_md = GRPC_MDNULL; + grpc_millis token_lifetime; + grpc_credentials_status status = + grpc_oauth2_token_fetcher_credentials_parse_server_response( + &r->response, &access_token_md, &token_lifetime); + // Update cache and grab list of pending requests. + gpr_mu_lock(&mu_); + token_fetch_pending_ = false; + access_token_md_ = GRPC_MDELEM_REF(access_token_md); + token_expiration_ = + status == GRPC_CREDENTIALS_OK + ? gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(token_lifetime, GPR_TIMESPAN)) + : gpr_inf_past(GPR_CLOCK_MONOTONIC); + grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_; + pending_requests_ = nullptr; + gpr_mu_unlock(&mu_); + // Invoke callbacks for all pending requests. + while (pending_request != nullptr) { + if (status == GRPC_CREDENTIALS_OK) { + grpc_credentials_mdelem_array_add(pending_request->md_array, + access_token_md); + } else { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Error occurred when fetching oauth2 token.", &error, 1); + } + GRPC_CLOSURE_SCHED(pending_request->on_request_metadata, error); + grpc_polling_entity_del_from_pollset_set( + pending_request->pollent, grpc_polling_entity_pollset_set(&pollent_)); + grpc_oauth2_pending_get_request_metadata* prev = pending_request; + pending_request = pending_request->next; + gpr_free(prev); + } + GRPC_MDELEM_UNREF(access_token_md); + Unref(); + grpc_credentials_metadata_request_destroy(r); +} + +bool grpc_oauth2_token_fetcher_credentials::get_request_metadata( + grpc_polling_entity* pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, + grpc_error** error) { + // Check if we can use the cached token. + grpc_millis refresh_threshold = + GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS * GPR_MS_PER_SEC; + grpc_mdelem cached_access_token_md = GRPC_MDNULL; + gpr_mu_lock(&mu_); + if (!GRPC_MDISNULL(access_token_md_) && + gpr_time_cmp( + gpr_time_sub(token_expiration_, gpr_now(GPR_CLOCK_MONOTONIC)), + gpr_time_from_seconds(GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, + GPR_TIMESPAN)) > 0) { + cached_access_token_md = GRPC_MDELEM_REF(access_token_md_); + } + if (!GRPC_MDISNULL(cached_access_token_md)) { + gpr_mu_unlock(&mu_); + grpc_credentials_mdelem_array_add(md_array, cached_access_token_md); + GRPC_MDELEM_UNREF(cached_access_token_md); + return true; + } + // Couldn't get the token from the cache. + // Add request to pending_requests_ and start a new fetch if needed. + grpc_oauth2_pending_get_request_metadata* pending_request = + static_cast( + gpr_malloc(sizeof(*pending_request))); + pending_request->md_array = md_array; + pending_request->on_request_metadata = on_request_metadata; + pending_request->pollent = pollent; + grpc_polling_entity_add_to_pollset_set( + pollent, grpc_polling_entity_pollset_set(&pollent_)); + pending_request->next = pending_requests_; + pending_requests_ = pending_request; + bool start_fetch = false; + if (!token_fetch_pending_) { + token_fetch_pending_ = true; + start_fetch = true; + } + gpr_mu_unlock(&mu_); + if (start_fetch) { + Ref().release(); + fetch_oauth2(grpc_credentials_metadata_request_create(this->Ref()), + &httpcli_context_, &pollent_, + on_oauth2_token_fetcher_http_response, + grpc_core::ExecCtx::Get()->Now() + refresh_threshold); + } + return false; +} + +void grpc_oauth2_token_fetcher_credentials::cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) { + gpr_mu_lock(&mu_); + grpc_oauth2_pending_get_request_metadata* prev = nullptr; + grpc_oauth2_pending_get_request_metadata* pending_request = pending_requests_; + while (pending_request != nullptr) { + if (pending_request->md_array == md_array) { + // Remove matching pending request from the list. + if (prev != nullptr) { + prev->next = pending_request->next; + } else { + pending_requests_ = pending_request->next; + } + // Invoke the callback immediately with an error. + GRPC_CLOSURE_SCHED(pending_request->on_request_metadata, + GRPC_ERROR_REF(error)); + gpr_free(pending_request); + break; + } + prev = pending_request; + pending_request = pending_request->next; + } + gpr_mu_unlock(&mu_); + GRPC_ERROR_UNREF(error); +} + +grpc_oauth2_token_fetcher_credentials::grpc_oauth2_token_fetcher_credentials() + : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2), + token_expiration_(gpr_inf_past(GPR_CLOCK_MONOTONIC)), + pollent_(grpc_polling_entity_create_from_pollset_set( + grpc_pollset_set_create())) { + gpr_mu_init(&mu_); + grpc_httpcli_context_init(&httpcli_context_); +} + +// +// Google Compute Engine credentials. +// + +namespace { + +class grpc_compute_engine_token_fetcher_credentials + : public grpc_oauth2_token_fetcher_credentials { + public: + grpc_compute_engine_token_fetcher_credentials() = default; + ~grpc_compute_engine_token_fetcher_credentials() override = default; + + protected: + void fetch_oauth2(grpc_credentials_metadata_request* metadata_req, + grpc_httpcli_context* http_context, + grpc_polling_entity* pollent, + grpc_iomgr_cb_func response_cb, + grpc_millis deadline) override { + grpc_http_header header = {(char*)"Metadata-Flavor", (char*)"Google"}; + grpc_httpcli_request request; + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = (char*)GRPC_COMPUTE_ENGINE_METADATA_HOST; + request.http.path = (char*)GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH; + request.http.hdr_count = 1; + request.http.hdrs = &header; + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + grpc_resource_quota* resource_quota = + grpc_resource_quota_create("oauth2_credentials"); + grpc_httpcli_get(http_context, pollent, resource_quota, &request, deadline, + GRPC_CLOSURE_CREATE(response_cb, metadata_req, + grpc_schedule_on_exec_ctx), + &metadata_req->response); + grpc_resource_quota_unref_internal(resource_quota); + } +}; + +} // namespace + +grpc_call_credentials* grpc_google_compute_engine_credentials_create( + void* reserved) { + GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1, + (reserved)); + GPR_ASSERT(reserved == nullptr); + return grpc_core::MakeRefCounted< + grpc_compute_engine_token_fetcher_credentials>() + .release(); +} + +// +// Google Refresh Token credentials. +// + +grpc_google_refresh_token_credentials:: + ~grpc_google_refresh_token_credentials() { + grpc_auth_refresh_token_destruct(&refresh_token_); +} + +void grpc_google_refresh_token_credentials::fetch_oauth2( + grpc_credentials_metadata_request* metadata_req, + grpc_httpcli_context* httpcli_context, grpc_polling_entity* pollent, + grpc_iomgr_cb_func response_cb, grpc_millis deadline) { + grpc_http_header header = {(char*)"Content-Type", + (char*)"application/x-www-form-urlencoded"}; + grpc_httpcli_request request; + char* body = nullptr; + gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, + refresh_token_.client_id, refresh_token_.client_secret, + refresh_token_.refresh_token); + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_HOST; + request.http.path = (char*)GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH; + request.http.hdr_count = 1; + request.http.hdrs = &header; + request.handshaker = &grpc_httpcli_ssl; + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + grpc_resource_quota* resource_quota = + grpc_resource_quota_create("oauth2_credentials_refresh"); + grpc_httpcli_post( + httpcli_context, pollent, resource_quota, &request, body, strlen(body), + deadline, + GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), + &metadata_req->response); + grpc_resource_quota_unref_internal(resource_quota); + gpr_free(body); +} + +grpc_google_refresh_token_credentials::grpc_google_refresh_token_credentials( + grpc_auth_refresh_token refresh_token) + : refresh_token_(refresh_token) {} + +grpc_core::RefCountedPtr +grpc_refresh_token_credentials_create_from_auth_refresh_token( + grpc_auth_refresh_token refresh_token) { + if (!grpc_auth_refresh_token_is_valid(&refresh_token)) { + gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation"); + return nullptr; + } + return grpc_core::MakeRefCounted( + refresh_token); +} + +static char* create_loggable_refresh_token(grpc_auth_refresh_token* token) { + if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) { + return gpr_strdup(""); + } + char* loggable_token = nullptr; + gpr_asprintf(&loggable_token, + "{\n type: %s\n client_id: %s\n client_secret: " + "\n refresh_token: \n}", + token->type, token->client_id); + return loggable_token; +} + +grpc_call_credentials* grpc_google_refresh_token_credentials_create( + const char* json_refresh_token, void* reserved) { + grpc_auth_refresh_token token = + grpc_auth_refresh_token_create_from_string(json_refresh_token); + if (grpc_api_trace.enabled()) { + char* loggable_token = create_loggable_refresh_token(&token); + gpr_log(GPR_INFO, + "grpc_refresh_token_credentials_create(json_refresh_token=%s, " + "reserved=%p)", + loggable_token, reserved); + gpr_free(loggable_token); + } + GPR_ASSERT(reserved == nullptr); + return grpc_refresh_token_credentials_create_from_auth_refresh_token(token) + .release(); +} + +// +// Oauth2 Access Token credentials. +// + +grpc_access_token_credentials::~grpc_access_token_credentials() { + GRPC_MDELEM_UNREF(access_token_md_); +} + +bool grpc_access_token_credentials::get_request_metadata( + grpc_polling_entity* pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, + grpc_error** error) { + grpc_credentials_mdelem_array_add(md_array, access_token_md_); + return true; +} + +void grpc_access_token_credentials::cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) { + GRPC_ERROR_UNREF(error); +} + +grpc_access_token_credentials::grpc_access_token_credentials( + const char* access_token) + : grpc_call_credentials(GRPC_CALL_CREDENTIALS_TYPE_OAUTH2) { + char* token_md_value; + gpr_asprintf(&token_md_value, "Bearer %s", access_token); + grpc_core::ExecCtx exec_ctx; + access_token_md_ = grpc_mdelem_from_slices( + grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(token_md_value)); + gpr_free(token_md_value); +} + +grpc_call_credentials* grpc_access_token_credentials_create( + const char* access_token, void* reserved) { + GRPC_API_TRACE( + "grpc_access_token_credentials_create(access_token=, " + "reserved=%p)", + 1, (reserved)); + GPR_ASSERT(reserved == nullptr); + return grpc_core::MakeRefCounted(access_token) + .release(); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.h new file mode 100644 index 00000000000000..510a78b484adfb --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/oauth2/oauth2_credentials.h @@ -0,0 +1,151 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H + +#include + +#include "src/core/lib/json/json.h" +#include "src/core/lib/security/credentials/credentials.h" + +// 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); + +// -- Oauth2 Token Fetcher credentials -- +// +// This object is a base for credentials that need to acquire an oauth2 token +// from an http service. + +struct grpc_oauth2_pending_get_request_metadata { + grpc_credentials_mdelem_array* md_array; + grpc_closure* on_request_metadata; + grpc_polling_entity* pollent; + struct grpc_oauth2_pending_get_request_metadata* next; +}; + +class grpc_oauth2_token_fetcher_credentials : public grpc_call_credentials { + public: + grpc_oauth2_token_fetcher_credentials(); + ~grpc_oauth2_token_fetcher_credentials() override; + + bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) override; + + void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, + grpc_error* error) override; + + void on_http_response(grpc_credentials_metadata_request* r, + grpc_error* error); + + GRPC_ABSTRACT_BASE_CLASS + + protected: + virtual void fetch_oauth2(grpc_credentials_metadata_request* req, + grpc_httpcli_context* httpcli_context, + grpc_polling_entity* pollent, grpc_iomgr_cb_func cb, + grpc_millis deadline) GRPC_ABSTRACT; + + private: + gpr_mu mu_; + grpc_mdelem access_token_md_ = GRPC_MDNULL; + gpr_timespec token_expiration_; + bool token_fetch_pending_ = false; + grpc_oauth2_pending_get_request_metadata* pending_requests_ = nullptr; + grpc_httpcli_context httpcli_context_; + grpc_polling_entity pollent_; +}; + +// Google refresh token credentials. +class grpc_google_refresh_token_credentials final + : public grpc_oauth2_token_fetcher_credentials { + public: + grpc_google_refresh_token_credentials(grpc_auth_refresh_token refresh_token); + ~grpc_google_refresh_token_credentials() override; + + const grpc_auth_refresh_token& refresh_token() const { + return refresh_token_; + } + + protected: + void fetch_oauth2(grpc_credentials_metadata_request* req, + grpc_httpcli_context* httpcli_context, + grpc_polling_entity* pollent, grpc_iomgr_cb_func cb, + grpc_millis deadline) override; + + private: + grpc_auth_refresh_token refresh_token_; +}; + +// Access token credentials. +class grpc_access_token_credentials final : public grpc_call_credentials { + public: + grpc_access_token_credentials(const char* access_token); + ~grpc_access_token_credentials() override; + + bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) override; + + void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, + grpc_error* error) override; + + private: + grpc_mdelem access_token_md_; +}; + +// Private constructor for refresh token credentials from an already parsed +// refresh token. Takes ownership of the refresh token. +grpc_core::RefCountedPtr +grpc_refresh_token_credentials_create_from_auth_refresh_token( + grpc_auth_refresh_token token); + +// Exposed for testing only. +grpc_credentials_status +grpc_oauth2_token_fetcher_credentials_parse_server_response( + const struct grpc_http_response* response, grpc_mdelem* token_md, + grpc_millis* token_lifetime); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.cc new file mode 100644 index 00000000000000..52982fdb8f17f1 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.cc @@ -0,0 +1,252 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/plugin/plugin_credentials.h" + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/validate_metadata.h" + +grpc_core::TraceFlag grpc_plugin_credentials_trace(false, "plugin_credentials"); + +grpc_plugin_credentials::~grpc_plugin_credentials() { + gpr_mu_destroy(&mu_); + if (plugin_.state != nullptr && plugin_.destroy != nullptr) { + plugin_.destroy(plugin_.state); + } +} + +void grpc_plugin_credentials::pending_request_remove_locked( + pending_request* pending_request) { + if (pending_request->prev == nullptr) { + pending_requests_ = pending_request->next; + } else { + pending_request->prev->next = pending_request->next; + } + if (pending_request->next != nullptr) { + pending_request->next->prev = pending_request->prev; + } +} + +// Checks if the request has been cancelled. +// If not, removes it from the pending list, so that it cannot be +// cancelled out from under us. +// When this returns, r->cancelled indicates whether the request was +// cancelled before completion. +void grpc_plugin_credentials::pending_request_complete(pending_request* r) { + GPR_DEBUG_ASSERT(r->creds == this); + gpr_mu_lock(&mu_); + if (!r->cancelled) pending_request_remove_locked(r); + gpr_mu_unlock(&mu_); + // Ref to credentials not needed anymore. + Unref(); +} + +static grpc_error* process_plugin_result( + grpc_plugin_credentials::pending_request* r, const grpc_metadata* md, + size_t num_md, grpc_status_code status, const char* error_details) { + grpc_error* error = GRPC_ERROR_NONE; + if (status != GRPC_STATUS_OK) { + char* msg; + gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s", + error_details); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } else { + bool seen_illegal_header = false; + for (size_t i = 0; i < num_md; ++i) { + if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin", + grpc_validate_header_key_is_legal(md[i].key))) { + seen_illegal_header = true; + break; + } else if (!grpc_is_binary_header(md[i].key) && + !GRPC_LOG_IF_ERROR( + "validate_metadata_from_plugin", + grpc_validate_header_nonbin_value_is_legal(md[i].value))) { + gpr_log(GPR_ERROR, "Plugin added invalid metadata value."); + seen_illegal_header = true; + break; + } + } + if (seen_illegal_header) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata"); + } else { + for (size_t i = 0; i < num_md; ++i) { + grpc_mdelem mdelem = + grpc_mdelem_create(md[i].key, md[i].value, nullptr); + grpc_credentials_mdelem_array_add(r->md_array, mdelem); + GRPC_MDELEM_UNREF(mdelem); + } + } + } + return error; +} + +static void plugin_md_request_metadata_ready(void* request, + const grpc_metadata* md, + size_t num_md, + grpc_status_code status, + const char* error_details) { + /* called from application code */ + grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_FINISHED | + GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP); + grpc_plugin_credentials::pending_request* r = + static_cast(request); + if (grpc_plugin_credentials_trace.enabled()) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin returned " + "asynchronously", + r->creds, r); + } + // Remove request from pending list if not previously cancelled. + r->creds->pending_request_complete(r); + // If it has not been cancelled, process it. + if (!r->cancelled) { + grpc_error* error = + process_plugin_result(r, md, num_md, status, error_details); + GRPC_CLOSURE_SCHED(r->on_request_metadata, error); + } else if (grpc_plugin_credentials_trace.enabled()) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin was previously " + "cancelled", + r->creds, r); + } + gpr_free(r); +} + +bool grpc_plugin_credentials::get_request_metadata( + grpc_polling_entity* pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, grpc_closure* on_request_metadata, + grpc_error** error) { + bool retval = true; // Synchronous return. + if (plugin_.get_metadata != nullptr) { + // Create pending_request object. + pending_request* request = + static_cast(gpr_zalloc(sizeof(*request))); + request->creds = this; + request->md_array = md_array; + request->on_request_metadata = on_request_metadata; + // Add it to the pending list. + gpr_mu_lock(&mu_); + if (pending_requests_ != nullptr) { + pending_requests_->prev = request; + } + request->next = pending_requests_; + pending_requests_ = request; + gpr_mu_unlock(&mu_); + // Invoke the plugin. The callback holds a ref to us. + if (grpc_plugin_credentials_trace.enabled()) { + gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin", + this, request); + } + Ref().release(); + grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX]; + size_t num_creds_md = 0; + grpc_status_code status = GRPC_STATUS_OK; + const char* error_details = nullptr; + if (!plugin_.get_metadata( + plugin_.state, context, plugin_md_request_metadata_ready, request, + creds_md, &num_creds_md, &status, &error_details)) { + if (grpc_plugin_credentials_trace.enabled()) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin will return " + "asynchronously", + this, request); + } + return false; // Asynchronous return. + } + // Returned synchronously. + // Remove request from pending list if not previously cancelled. + request->creds->pending_request_complete(request); + // If the request was cancelled, the error will have been returned + // asynchronously by plugin_cancel_get_request_metadata(), so return + // false. Otherwise, process the result. + if (request->cancelled) { + if (grpc_plugin_credentials_trace.enabled()) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p was cancelled, error " + "will be returned asynchronously", + this, request); + } + retval = false; + } else { + if (grpc_plugin_credentials_trace.enabled()) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin returned " + "synchronously", + this, request); + } + *error = process_plugin_result(request, creds_md, num_creds_md, status, + error_details); + } + // Clean up. + for (size_t i = 0; i < num_creds_md; ++i) { + grpc_slice_unref_internal(creds_md[i].key); + grpc_slice_unref_internal(creds_md[i].value); + } + gpr_free((void*)error_details); + gpr_free(request); + } + return retval; +} + +void grpc_plugin_credentials::cancel_get_request_metadata( + grpc_credentials_mdelem_array* md_array, grpc_error* error) { + gpr_mu_lock(&mu_); + for (pending_request* pending_request = pending_requests_; + pending_request != nullptr; pending_request = pending_request->next) { + if (pending_request->md_array == md_array) { + if (grpc_plugin_credentials_trace.enabled()) { + gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", this, + pending_request); + } + pending_request->cancelled = true; + GRPC_CLOSURE_SCHED(pending_request->on_request_metadata, + GRPC_ERROR_REF(error)); + pending_request_remove_locked(pending_request); + break; + } + } + gpr_mu_unlock(&mu_); + GRPC_ERROR_UNREF(error); +} + +grpc_plugin_credentials::grpc_plugin_credentials( + grpc_metadata_credentials_plugin plugin) + : grpc_call_credentials(plugin.type), plugin_(plugin) { + gpr_mu_init(&mu_); +} + +grpc_call_credentials* grpc_metadata_credentials_create_from_plugin( + grpc_metadata_credentials_plugin plugin, void* reserved) { + GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1, + (reserved)); + GPR_ASSERT(reserved == nullptr); + return grpc_core::New(plugin); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.h b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.h new file mode 100644 index 00000000000000..77a957e5137110 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/plugin/plugin_credentials.h @@ -0,0 +1,69 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +extern grpc_core::TraceFlag grpc_plugin_credentials_trace; + +// This type is forward declared as a C struct and we cannot define it as a +// class. Otherwise, compiler will complain about type mismatch due to +// -Wmismatched-tags. +struct grpc_plugin_credentials final : public grpc_call_credentials { + public: + struct pending_request { + bool cancelled; + struct grpc_plugin_credentials* creds; + grpc_credentials_mdelem_array* md_array; + grpc_closure* on_request_metadata; + struct pending_request* prev; + struct pending_request* next; + }; + + explicit grpc_plugin_credentials(grpc_metadata_credentials_plugin plugin); + ~grpc_plugin_credentials() override; + + bool get_request_metadata(grpc_polling_entity* pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array* md_array, + grpc_closure* on_request_metadata, + grpc_error** error) override; + + void cancel_get_request_metadata(grpc_credentials_mdelem_array* md_array, + grpc_error* error) override; + + // Checks if the request has been cancelled. + // If not, removes it from the pending list, so that it cannot be + // cancelled out from under us. + // When this returns, r->cancelled indicates whether the request was + // cancelled before completion. + void pending_request_complete(pending_request* r); + + private: + void pending_request_remove_locked(pending_request* pending_request); + + grpc_metadata_credentials_plugin plugin_; + gpr_mu mu_; + pending_request* pending_requests_ = nullptr; +}; + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.cc new file mode 100644 index 00000000000000..83db86f1eac878 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.cc @@ -0,0 +1,346 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/credentials/ssl/ssl_credentials.h" + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/tsi/ssl_transport_security.h" + +#include +#include +#include + +// +// SSL Channel Credentials. +// + +void grpc_tsi_ssl_pem_key_cert_pairs_destroy(tsi_ssl_pem_key_cert_pair* kp, + size_t num_key_cert_pairs) { + if (kp == nullptr) return; + for (size_t i = 0; i < num_key_cert_pairs; i++) { + gpr_free((void*)kp[i].private_key); + gpr_free((void*)kp[i].cert_chain); + } + gpr_free(kp); +} + +grpc_ssl_credentials::grpc_ssl_credentials( + const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, + const verify_peer_options* verify_options) + : grpc_channel_credentials(GRPC_CHANNEL_CREDENTIALS_TYPE_SSL) { + build_config(pem_root_certs, pem_key_cert_pair, verify_options); +} + +grpc_ssl_credentials::~grpc_ssl_credentials() { + gpr_free(config_.pem_root_certs); + grpc_tsi_ssl_pem_key_cert_pairs_destroy(config_.pem_key_cert_pair, 1); + if (config_.verify_options.verify_peer_destruct != nullptr) { + config_.verify_options.verify_peer_destruct( + config_.verify_options.verify_peer_callback_userdata); + } +} + +grpc_core::RefCountedPtr +grpc_ssl_credentials::create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) { + const char* overridden_target_name = nullptr; + tsi_ssl_session_cache* ssl_session_cache = nullptr; + for (size_t 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; + } + if (strcmp(arg->key, GRPC_SSL_SESSION_CACHE_ARG) == 0 && + arg->type == GRPC_ARG_POINTER) { + ssl_session_cache = + static_cast(arg->value.pointer.p); + } + } + grpc_core::RefCountedPtr sc = + grpc_ssl_channel_security_connector_create( + this->Ref(), std::move(call_creds), &config_, target, + overridden_target_name, ssl_session_cache); + if (sc == nullptr) { + return sc; + } + grpc_arg new_arg = grpc_channel_arg_string_create( + (char*)GRPC_ARG_HTTP2_SCHEME, (char*)"https"); + *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1); + return sc; +} + +void grpc_ssl_credentials::build_config( + const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, + const verify_peer_options* verify_options) { + config_.pem_root_certs = gpr_strdup(pem_root_certs); + if (pem_key_cert_pair != nullptr) { + GPR_ASSERT(pem_key_cert_pair->private_key != nullptr); + GPR_ASSERT(pem_key_cert_pair->cert_chain != nullptr); + config_.pem_key_cert_pair = static_cast( + gpr_zalloc(sizeof(tsi_ssl_pem_key_cert_pair))); + config_.pem_key_cert_pair->cert_chain = + gpr_strdup(pem_key_cert_pair->cert_chain); + config_.pem_key_cert_pair->private_key = + gpr_strdup(pem_key_cert_pair->private_key); + } else { + config_.pem_key_cert_pair = nullptr; + } + if (verify_options != nullptr) { + memcpy(&config_.verify_options, verify_options, + sizeof(verify_peer_options)); + } else { + // Otherwise set all options to default values + memset(&config_.verify_options, 0, sizeof(verify_peer_options)); + } +} + +grpc_channel_credentials* grpc_ssl_credentials_create( + const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, + const verify_peer_options* verify_options, void* reserved) { + GRPC_API_TRACE( + "grpc_ssl_credentials_create(pem_root_certs=%s, " + "pem_key_cert_pair=%p, " + "verify_options=%p, " + "reserved=%p)", + 4, (pem_root_certs, pem_key_cert_pair, verify_options, reserved)); + GPR_ASSERT(reserved == nullptr); + + return grpc_core::New(pem_root_certs, pem_key_cert_pair, + verify_options); +} + +// +// SSL Server Credentials. +// + +struct grpc_ssl_server_credentials_options { + grpc_ssl_client_certificate_request_type client_certificate_request; + grpc_ssl_server_certificate_config* certificate_config; + grpc_ssl_server_certificate_config_fetcher* certificate_config_fetcher; +}; + +grpc_ssl_server_credentials::grpc_ssl_server_credentials( + const grpc_ssl_server_credentials_options& options) + : grpc_server_credentials(GRPC_CHANNEL_CREDENTIALS_TYPE_SSL) { + if (options.certificate_config_fetcher != nullptr) { + config_.client_certificate_request = options.client_certificate_request; + certificate_config_fetcher_ = *options.certificate_config_fetcher; + } else { + build_config(options.certificate_config->pem_root_certs, + options.certificate_config->pem_key_cert_pairs, + options.certificate_config->num_key_cert_pairs, + options.client_certificate_request); + } +} + +grpc_ssl_server_credentials::~grpc_ssl_server_credentials() { + grpc_tsi_ssl_pem_key_cert_pairs_destroy(config_.pem_key_cert_pairs, + config_.num_key_cert_pairs); + gpr_free(config_.pem_root_certs); +} +grpc_core::RefCountedPtr +grpc_ssl_server_credentials::create_security_connector() { + return grpc_ssl_server_security_connector_create(this->Ref()); +} + +tsi_ssl_pem_key_cert_pair* grpc_convert_grpc_to_tsi_cert_pairs( + const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs) { + tsi_ssl_pem_key_cert_pair* tsi_pairs = nullptr; + if (num_key_cert_pairs > 0) { + GPR_ASSERT(pem_key_cert_pairs != nullptr); + tsi_pairs = static_cast( + gpr_zalloc(num_key_cert_pairs * sizeof(tsi_ssl_pem_key_cert_pair))); + } + for (size_t i = 0; i < num_key_cert_pairs; i++) { + GPR_ASSERT(pem_key_cert_pairs[i].private_key != nullptr); + GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != nullptr); + tsi_pairs[i].cert_chain = gpr_strdup(pem_key_cert_pairs[i].cert_chain); + tsi_pairs[i].private_key = gpr_strdup(pem_key_cert_pairs[i].private_key); + } + return tsi_pairs; +} + +void grpc_ssl_server_credentials::build_config( + const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs, + grpc_ssl_client_certificate_request_type client_certificate_request) { + config_.client_certificate_request = client_certificate_request; + config_.pem_root_certs = gpr_strdup(pem_root_certs); + config_.pem_key_cert_pairs = grpc_convert_grpc_to_tsi_cert_pairs( + pem_key_cert_pairs, num_key_cert_pairs); + config_.num_key_cert_pairs = num_key_cert_pairs; +} + +grpc_ssl_server_certificate_config* grpc_ssl_server_certificate_config_create( + const char* pem_root_certs, + const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs) { + grpc_ssl_server_certificate_config* config = + static_cast( + gpr_zalloc(sizeof(grpc_ssl_server_certificate_config))); + config->pem_root_certs = gpr_strdup(pem_root_certs); + if (num_key_cert_pairs > 0) { + GPR_ASSERT(pem_key_cert_pairs != nullptr); + config->pem_key_cert_pairs = static_cast( + gpr_zalloc(num_key_cert_pairs * sizeof(grpc_ssl_pem_key_cert_pair))); + } + config->num_key_cert_pairs = num_key_cert_pairs; + for (size_t i = 0; i < num_key_cert_pairs; i++) { + GPR_ASSERT(pem_key_cert_pairs[i].private_key != nullptr); + GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != nullptr); + config->pem_key_cert_pairs[i].cert_chain = + gpr_strdup(pem_key_cert_pairs[i].cert_chain); + config->pem_key_cert_pairs[i].private_key = + gpr_strdup(pem_key_cert_pairs[i].private_key); + } + return config; +} + +void grpc_ssl_server_certificate_config_destroy( + grpc_ssl_server_certificate_config* config) { + if (config == nullptr) return; + for (size_t i = 0; i < config->num_key_cert_pairs; i++) { + gpr_free((void*)config->pem_key_cert_pairs[i].private_key); + gpr_free((void*)config->pem_key_cert_pairs[i].cert_chain); + } + gpr_free(config->pem_key_cert_pairs); + gpr_free(config->pem_root_certs); + gpr_free(config); +} + +grpc_ssl_server_credentials_options* +grpc_ssl_server_credentials_create_options_using_config( + grpc_ssl_client_certificate_request_type client_certificate_request, + grpc_ssl_server_certificate_config* config) { + grpc_ssl_server_credentials_options* options = nullptr; + if (config == nullptr) { + gpr_log(GPR_ERROR, "Certificate config must not be NULL."); + goto done; + } + options = static_cast( + gpr_zalloc(sizeof(grpc_ssl_server_credentials_options))); + options->client_certificate_request = client_certificate_request; + options->certificate_config = config; +done: + return options; +} + +grpc_ssl_server_credentials_options* +grpc_ssl_server_credentials_create_options_using_config_fetcher( + grpc_ssl_client_certificate_request_type client_certificate_request, + grpc_ssl_server_certificate_config_callback cb, void* user_data) { + if (cb == nullptr) { + gpr_log(GPR_ERROR, "Invalid certificate config callback parameter."); + return nullptr; + } + + grpc_ssl_server_certificate_config_fetcher* fetcher = + static_cast( + gpr_zalloc(sizeof(grpc_ssl_server_certificate_config_fetcher))); + fetcher->cb = cb; + fetcher->user_data = user_data; + + grpc_ssl_server_credentials_options* options = + static_cast( + gpr_zalloc(sizeof(grpc_ssl_server_credentials_options))); + options->client_certificate_request = client_certificate_request; + options->certificate_config_fetcher = fetcher; + + return options; +} + +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) { + return grpc_ssl_server_credentials_create_ex( + pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs, + force_client_auth + ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, + reserved); +} + +grpc_server_credentials* grpc_ssl_server_credentials_create_ex( + const char* pem_root_certs, grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs, + grpc_ssl_client_certificate_request_type client_certificate_request, + void* reserved) { + GRPC_API_TRACE( + "grpc_ssl_server_credentials_create_ex(" + "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, " + "client_certificate_request=%d, reserved=%p)", + 5, + (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs, + client_certificate_request, reserved)); + GPR_ASSERT(reserved == nullptr); + + grpc_ssl_server_certificate_config* cert_config = + grpc_ssl_server_certificate_config_create( + pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs); + grpc_ssl_server_credentials_options* options = + grpc_ssl_server_credentials_create_options_using_config( + client_certificate_request, cert_config); + + return grpc_ssl_server_credentials_create_with_options(options); +} + +grpc_server_credentials* grpc_ssl_server_credentials_create_with_options( + grpc_ssl_server_credentials_options* options) { + grpc_server_credentials* retval = nullptr; + + if (options == nullptr) { + gpr_log(GPR_ERROR, + "Invalid options trying to create SSL server credentials."); + goto done; + } + + if (options->certificate_config == nullptr && + options->certificate_config_fetcher == nullptr) { + gpr_log(GPR_ERROR, + "SSL server credentials options must specify either " + "certificate config or fetcher."); + goto done; + } else if (options->certificate_config_fetcher != nullptr && + options->certificate_config_fetcher->cb == nullptr) { + gpr_log(GPR_ERROR, "Certificate config fetcher callback must not be NULL."); + goto done; + } + + retval = grpc_core::New(*options); + +done: + grpc_ssl_server_credentials_options_destroy(options); + return retval; +} + +void grpc_ssl_server_credentials_options_destroy( + grpc_ssl_server_credentials_options* o) { + if (o == nullptr) return; + gpr_free(o->certificate_config_fetcher); + grpc_ssl_server_certificate_config_destroy(o->certificate_config); + gpr_free(o); +} diff --git a/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.h b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.h new file mode 100644 index 00000000000000..e1174327b30b33 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/credentials/ssl/ssl_credentials.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H +#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H + +#include + +#include "src/core/lib/security/credentials/credentials.h" + +#include "src/core/lib/security/security_connector/ssl/ssl_security_connector.h" + +class grpc_ssl_credentials : public grpc_channel_credentials { + public: + grpc_ssl_credentials(const char* pem_root_certs, + grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, + const verify_peer_options* verify_options); + + ~grpc_ssl_credentials() override; + + grpc_core::RefCountedPtr + create_security_connector( + grpc_core::RefCountedPtr call_creds, + const char* target, const grpc_channel_args* args, + grpc_channel_args** new_args) override; + + private: + void build_config(const char* pem_root_certs, + grpc_ssl_pem_key_cert_pair* pem_key_cert_pair, + const verify_peer_options* verify_options); + + grpc_ssl_config config_; +}; + +struct grpc_ssl_server_certificate_config { + grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs = nullptr; + size_t num_key_cert_pairs = 0; + char* pem_root_certs = nullptr; +}; + +struct grpc_ssl_server_certificate_config_fetcher { + grpc_ssl_server_certificate_config_callback cb = nullptr; + void* user_data; +}; + +class grpc_ssl_server_credentials final : public grpc_server_credentials { + public: + grpc_ssl_server_credentials( + const grpc_ssl_server_credentials_options& options); + ~grpc_ssl_server_credentials() override; + + grpc_core::RefCountedPtr + create_security_connector() override; + + bool has_cert_config_fetcher() const { + return certificate_config_fetcher_.cb != nullptr; + } + + grpc_ssl_certificate_config_reload_status FetchCertConfig( + grpc_ssl_server_certificate_config** config) { + GPR_DEBUG_ASSERT(has_cert_config_fetcher()); + return certificate_config_fetcher_.cb(certificate_config_fetcher_.user_data, + config); + } + + const grpc_ssl_server_config& config() const { return config_; } + + private: + void build_config( + const char* pem_root_certs, + grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, size_t num_key_cert_pairs, + grpc_ssl_client_certificate_request_type client_certificate_request); + + grpc_ssl_server_config config_; + grpc_ssl_server_certificate_config_fetcher certificate_config_fetcher_; +}; + +tsi_ssl_pem_key_cert_pair* grpc_convert_grpc_to_tsi_cert_pairs( + const grpc_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs); + +void grpc_tsi_ssl_pem_key_cert_pairs_destroy(tsi_ssl_pem_key_cert_pair* kp, + size_t num_key_cert_pairs); + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_SSL_SSL_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.cc new file mode 100644 index 00000000000000..3ad0cc353cb17a --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.cc @@ -0,0 +1,264 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/security_connector/alts/alts_security_connector.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/credentials/alts/alts_credentials.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/transport/transport.h" +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" +#include "src/core/tsi/transport_security.h" + +namespace { + +void alts_set_rpc_protocol_versions( + grpc_gcp_rpc_protocol_versions* rpc_versions) { + grpc_gcp_rpc_protocol_versions_set_max(rpc_versions, + GRPC_PROTOCOL_VERSION_MAX_MAJOR, + GRPC_PROTOCOL_VERSION_MAX_MINOR); + grpc_gcp_rpc_protocol_versions_set_min(rpc_versions, + GRPC_PROTOCOL_VERSION_MIN_MAJOR, + GRPC_PROTOCOL_VERSION_MIN_MINOR); +} + +void alts_check_peer(tsi_peer peer, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) { + *auth_context = + grpc_core::internal::grpc_alts_auth_context_from_tsi_peer(&peer); + tsi_peer_destruct(&peer); + grpc_error* error = + *auth_context != nullptr + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Could not get ALTS auth context from TSI peer"); + GRPC_CLOSURE_SCHED(on_peer_checked, error); +} + +class grpc_alts_channel_security_connector final + : public grpc_channel_security_connector { + public: + grpc_alts_channel_security_connector( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const char* target_name) + : grpc_channel_security_connector(/*url_scheme=*/nullptr, + std::move(channel_creds), + std::move(request_metadata_creds)), + target_name_(gpr_strdup(target_name)) { + grpc_alts_credentials* creds = + static_cast(mutable_channel_creds()); + alts_set_rpc_protocol_versions(&creds->mutable_options()->rpc_versions); + } + + ~grpc_alts_channel_security_connector() override { gpr_free(target_name_); } + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_manager) override { + tsi_handshaker* handshaker = nullptr; + const grpc_alts_credentials* creds = + static_cast(channel_creds()); + GPR_ASSERT(alts_tsi_handshaker_create(creds->options(), target_name_, + creds->handshaker_service_url(), true, + interested_parties, + &handshaker) == TSI_OK); + grpc_handshake_manager_add( + handshake_manager, grpc_security_handshaker_create(handshaker, this)); + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override { + alts_check_peer(peer, auth_context, on_peer_checked); + } + + int cmp(const grpc_security_connector* other_sc) const override { + auto* other = + reinterpret_cast(other_sc); + int c = channel_security_connector_cmp(other); + if (c != 0) return c; + return strcmp(target_name_, other->target_name_); + } + + bool check_call_host(const char* host, grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) override { + if (host == nullptr || strcmp(host, target_name_) != 0) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "ALTS call host does not match target name"); + } + return true; + } + + void cancel_check_call_host(grpc_closure* on_call_host_checked, + grpc_error* error) override { + GRPC_ERROR_UNREF(error); + } + + private: + char* target_name_; +}; + +class grpc_alts_server_security_connector final + : public grpc_server_security_connector { + public: + grpc_alts_server_security_connector( + grpc_core::RefCountedPtr server_creds) + : grpc_server_security_connector(/*url_scheme=*/nullptr, + std::move(server_creds)) { + grpc_alts_server_credentials* creds = + reinterpret_cast(mutable_server_creds()); + alts_set_rpc_protocol_versions(&creds->mutable_options()->rpc_versions); + } + ~grpc_alts_server_security_connector() override = default; + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_manager) override { + tsi_handshaker* handshaker = nullptr; + const grpc_alts_server_credentials* creds = + static_cast(server_creds()); + GPR_ASSERT(alts_tsi_handshaker_create( + creds->options(), nullptr, creds->handshaker_service_url(), + false, interested_parties, &handshaker) == TSI_OK); + grpc_handshake_manager_add( + handshake_manager, grpc_security_handshaker_create(handshaker, this)); + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override { + alts_check_peer(peer, auth_context, on_peer_checked); + } + + int cmp(const grpc_security_connector* other) const override { + return server_security_connector_cmp( + static_cast(other)); + } +}; +} // namespace + +namespace grpc_core { +namespace internal { +grpc_core::RefCountedPtr +grpc_alts_auth_context_from_tsi_peer(const tsi_peer* peer) { + if (peer == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to grpc_alts_auth_context_from_tsi_peer()"); + return nullptr; + } + /* Validate certificate type. */ + const tsi_peer_property* cert_type_prop = + tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY); + if (cert_type_prop == nullptr || + strncmp(cert_type_prop->value.data, TSI_ALTS_CERTIFICATE_TYPE, + cert_type_prop->value.length) != 0) { + gpr_log(GPR_ERROR, "Invalid or missing certificate type property."); + return nullptr; + } + /* Validate RPC protocol versions. */ + const tsi_peer_property* rpc_versions_prop = + tsi_peer_get_property_by_name(peer, TSI_ALTS_RPC_VERSIONS); + if (rpc_versions_prop == nullptr) { + gpr_log(GPR_ERROR, "Missing rpc protocol versions property."); + return nullptr; + } + grpc_gcp_rpc_protocol_versions local_versions, peer_versions; + alts_set_rpc_protocol_versions(&local_versions); + grpc_slice slice = grpc_slice_from_copied_buffer( + rpc_versions_prop->value.data, rpc_versions_prop->value.length); + bool decode_result = + grpc_gcp_rpc_protocol_versions_decode(slice, &peer_versions); + grpc_slice_unref_internal(slice); + if (!decode_result) { + gpr_log(GPR_ERROR, "Invalid peer rpc protocol versions."); + return nullptr; + } + /* TODO: Pass highest common rpc protocol version to grpc caller. */ + bool check_result = grpc_gcp_rpc_protocol_versions_check( + &local_versions, &peer_versions, nullptr); + if (!check_result) { + gpr_log(GPR_ERROR, "Mismatch of local and peer rpc protocol versions."); + return nullptr; + } + /* Create auth context. */ + auto ctx = grpc_core::MakeRefCounted(nullptr); + grpc_auth_context_add_cstring_property( + ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_ALTS_TRANSPORT_SECURITY_TYPE); + size_t i = 0; + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property* tsi_prop = &peer->properties[i]; + /* Add service account to auth context. */ + if (strcmp(tsi_prop->name, TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 0) { + grpc_auth_context_add_property( + ctx.get(), TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, + tsi_prop->value.data, tsi_prop->value.length); + GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( + ctx.get(), TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY) == 1); + } + } + if (!grpc_auth_context_peer_is_authenticated(ctx.get())) { + gpr_log(GPR_ERROR, "Invalid unauthenticated peer."); + ctx.reset(DEBUG_LOCATION, "test"); + return nullptr; + } + return ctx; +} + +} // namespace internal +} // namespace grpc_core + +grpc_core::RefCountedPtr +grpc_alts_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const char* target_name) { + if (channel_creds == nullptr || target_name == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_alts_channel_security_connector_create()"); + return nullptr; + } + return grpc_core::MakeRefCounted( + std::move(channel_creds), std::move(request_metadata_creds), target_name); +} + +grpc_core::RefCountedPtr +grpc_alts_server_security_connector_create( + grpc_core::RefCountedPtr server_creds) { + if (server_creds == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_alts_server_security_connector_create()"); + return nullptr; + } + return grpc_core::MakeRefCounted( + std::move(server_creds)); +} diff --git a/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.h new file mode 100644 index 00000000000000..b96dc36b302383 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/alts/alts_security_connector.h @@ -0,0 +1,71 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_ALTS_SECURITY_CONNECTOR_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_ALTS_SECURITY_CONNECTOR_H + +#include + +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" + +#define GRPC_ALTS_TRANSPORT_SECURITY_TYPE "alts" + +/** + * This method creates an ALTS channel security connector. + * + * - channel_creds: channel credential instance. + * - request_metadata_creds: credential object which will be sent with each + * request. This parameter can be nullptr. + * - target_name: the name of the endpoint that the channel is connecting to. + * - sc: address of ALTS channel security connector instance to be returned from + * the method. + * + * It returns nullptr on failure. + */ +grpc_core::RefCountedPtr +grpc_alts_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const char* target_name); + +/** + * This method creates an ALTS server security connector. + * + * - server_creds: server credential instance. + * - sc: address of ALTS server security connector instance to be returned from + * the method. + * + * It returns nullptr on failure. + */ +grpc_core::RefCountedPtr +grpc_alts_server_security_connector_create( + grpc_core::RefCountedPtr server_creds); + +namespace grpc_core { +namespace internal { + +/* Exposed only for testing. */ +grpc_core::RefCountedPtr +grpc_alts_auth_context_from_tsi_peer(const tsi_peer* peer); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_ALTS_ALTS_SECURITY_CONNECTOR_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.cc new file mode 100644 index 00000000000000..e3b8affb36057a --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.cc @@ -0,0 +1,305 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/security_connector/fake/fake_security_connector.h" + +#include + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/alpn/alpn.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/fake/fake_credentials.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/security/transport/target_authority_table.h" +#include "src/core/tsi/fake_transport_security.h" + +namespace { +class grpc_fake_channel_security_connector final + : public grpc_channel_security_connector { + public: + grpc_fake_channel_security_connector( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const char* target, const grpc_channel_args* args) + : grpc_channel_security_connector(GRPC_FAKE_SECURITY_URL_SCHEME, + std::move(channel_creds), + std::move(request_metadata_creds)), + target_(gpr_strdup(target)), + expected_targets_( + gpr_strdup(grpc_fake_transport_get_expected_targets(args))), + is_lb_channel_(grpc_core::FindTargetAuthorityTableInArgs(args) != + nullptr) { + const grpc_arg* target_name_override_arg = + grpc_channel_args_find(args, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); + if (target_name_override_arg != nullptr) { + target_name_override_ = + gpr_strdup(grpc_channel_arg_get_string(target_name_override_arg)); + } else { + target_name_override_ = nullptr; + } + } + + ~grpc_fake_channel_security_connector() override { + gpr_free(target_); + gpr_free(expected_targets_); + if (target_name_override_ != nullptr) gpr_free(target_name_override_); + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override; + + int cmp(const grpc_security_connector* other_sc) const override { + auto* other = + reinterpret_cast(other_sc); + int c = channel_security_connector_cmp(other); + if (c != 0) return c; + c = strcmp(target_, other->target_); + if (c != 0) return c; + if (expected_targets_ == nullptr || other->expected_targets_ == nullptr) { + c = GPR_ICMP(expected_targets_, other->expected_targets_); + } else { + c = strcmp(expected_targets_, other->expected_targets_); + } + if (c != 0) return c; + return GPR_ICMP(is_lb_channel_, other->is_lb_channel_); + } + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) override { + grpc_handshake_manager_add( + handshake_mgr, + grpc_security_handshaker_create( + tsi_create_fake_handshaker(/*is_client=*/true), this)); + } + + bool check_call_host(const char* host, grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) override { + char* authority_hostname = nullptr; + char* authority_ignored_port = nullptr; + char* target_hostname = nullptr; + char* target_ignored_port = nullptr; + gpr_split_host_port(host, &authority_hostname, &authority_ignored_port); + gpr_split_host_port(target_, &target_hostname, &target_ignored_port); + if (target_name_override_ != nullptr) { + char* fake_security_target_name_override_hostname = nullptr; + char* fake_security_target_name_override_ignored_port = nullptr; + gpr_split_host_port(target_name_override_, + &fake_security_target_name_override_hostname, + &fake_security_target_name_override_ignored_port); + if (strcmp(authority_hostname, + fake_security_target_name_override_hostname) != 0) { + gpr_log(GPR_ERROR, + "Authority (host) '%s' != Fake Security Target override '%s'", + host, fake_security_target_name_override_hostname); + abort(); + } + gpr_free(fake_security_target_name_override_hostname); + gpr_free(fake_security_target_name_override_ignored_port); + } else if (strcmp(authority_hostname, target_hostname) != 0) { + gpr_log(GPR_ERROR, "Authority (host) '%s' != Target '%s'", + authority_hostname, target_hostname); + abort(); + } + gpr_free(authority_hostname); + gpr_free(authority_ignored_port); + gpr_free(target_hostname); + gpr_free(target_ignored_port); + return true; + } + + void cancel_check_call_host(grpc_closure* on_call_host_checked, + grpc_error* error) override { + GRPC_ERROR_UNREF(error); + } + + char* target() const { return target_; } + char* expected_targets() const { return expected_targets_; } + bool is_lb_channel() const { return is_lb_channel_; } + char* target_name_override() const { return target_name_override_; } + + private: + bool fake_check_target(const char* target_type, const char* target, + const char* set_str) const { + GPR_ASSERT(target_type != nullptr); + GPR_ASSERT(target != nullptr); + char** set = nullptr; + size_t set_size = 0; + gpr_string_split(set_str, ",", &set, &set_size); + bool found = false; + for (size_t i = 0; i < set_size; ++i) { + if (set[i] != nullptr && strcmp(target, set[i]) == 0) found = true; + } + for (size_t i = 0; i < set_size; ++i) { + gpr_free(set[i]); + } + gpr_free(set); + return found; + } + + void fake_secure_name_check() const { + if (expected_targets_ == nullptr) return; + char** lbs_and_backends = nullptr; + size_t lbs_and_backends_size = 0; + bool success = false; + gpr_string_split(expected_targets_, ";", &lbs_and_backends, + &lbs_and_backends_size); + if (lbs_and_backends_size > 2 || lbs_and_backends_size == 0) { + gpr_log(GPR_ERROR, "Invalid expected targets arg value: '%s'", + expected_targets_); + goto done; + } + if (is_lb_channel_) { + if (lbs_and_backends_size != 2) { + gpr_log(GPR_ERROR, + "Invalid expected targets arg value: '%s'. Expectations for LB " + "channels must be of the form 'be1,be2,be3,...;lb1,lb2,...", + expected_targets_); + goto done; + } + if (!fake_check_target("LB", target_, lbs_and_backends[1])) { + gpr_log(GPR_ERROR, "LB target '%s' not found in expected set '%s'", + target_, lbs_and_backends[1]); + goto done; + } + success = true; + } else { + if (!fake_check_target("Backend", target_, lbs_and_backends[0])) { + gpr_log(GPR_ERROR, "Backend target '%s' not found in expected set '%s'", + target_, lbs_and_backends[0]); + goto done; + } + success = true; + } + done: + for (size_t i = 0; i < lbs_and_backends_size; ++i) { + gpr_free(lbs_and_backends[i]); + } + gpr_free(lbs_and_backends); + if (!success) abort(); + } + + char* target_; + char* expected_targets_; + bool is_lb_channel_; + char* target_name_override_; +}; + +static void fake_check_peer( + grpc_security_connector* sc, tsi_peer peer, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) { + const char* prop_name; + grpc_error* error = GRPC_ERROR_NONE; + *auth_context = nullptr; + if (peer.property_count != 1) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Fake peers should only have 1 property."); + goto end; + } + prop_name = peer.properties[0].name; + if (prop_name == nullptr || + strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) { + char* msg; + gpr_asprintf(&msg, "Unexpected property in fake peer: %s.", + prop_name == nullptr ? "" : prop_name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + goto end; + } + if (strncmp(peer.properties[0].value.data, TSI_FAKE_CERTIFICATE_TYPE, + peer.properties[0].value.length)) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid value for cert type property."); + goto end; + } + *auth_context = grpc_core::MakeRefCounted(nullptr); + grpc_auth_context_add_cstring_property( + auth_context->get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_FAKE_TRANSPORT_SECURITY_TYPE); +end: + GRPC_CLOSURE_SCHED(on_peer_checked, error); + tsi_peer_destruct(&peer); +} + +void grpc_fake_channel_security_connector::check_peer( + tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) { + fake_check_peer(this, peer, auth_context, on_peer_checked); + fake_secure_name_check(); +} + +class grpc_fake_server_security_connector + : public grpc_server_security_connector { + public: + grpc_fake_server_security_connector( + grpc_core::RefCountedPtr server_creds) + : grpc_server_security_connector(GRPC_FAKE_SECURITY_URL_SCHEME, + std::move(server_creds)) {} + ~grpc_fake_server_security_connector() override = default; + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override { + fake_check_peer(this, peer, auth_context, on_peer_checked); + } + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) override { + grpc_handshake_manager_add( + handshake_mgr, + grpc_security_handshaker_create( + tsi_create_fake_handshaker(/*=is_client*/ false), this)); + } + + int cmp(const grpc_security_connector* other) const override { + return server_security_connector_cmp( + static_cast(other)); + } +}; +} // namespace + +grpc_core::RefCountedPtr +grpc_fake_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const char* target, const grpc_channel_args* args) { + return grpc_core::MakeRefCounted( + std::move(channel_creds), std::move(request_metadata_creds), target, + args); +} + +grpc_core::RefCountedPtr +grpc_fake_server_security_connector_create( + grpc_core::RefCountedPtr server_creds) { + return grpc_core::MakeRefCounted( + std::move(server_creds)); +} diff --git a/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.h new file mode 100644 index 00000000000000..344a2349a49d71 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/fake/fake_security_connector.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_FAKE_FAKE_SECURITY_CONNECTOR_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_FAKE_FAKE_SECURITY_CONNECTOR_H + +#include + +#include + +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/security_connector/security_connector.h" + +#define GRPC_FAKE_SECURITY_URL_SCHEME "http+fake_security" + +/* Creates a fake connector that emulates real channel security. */ +grpc_core::RefCountedPtr +grpc_fake_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const char* target, const grpc_channel_args* args); + +/* Creates a fake connector that emulates real server security. */ +grpc_core::RefCountedPtr +grpc_fake_server_security_connector_create( + grpc_core::RefCountedPtr server_creds); + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_FAKE_FAKE_SECURITY_CONNECTOR_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots.h b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots.h new file mode 100644 index 00000000000000..5fdec154988afe --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_H + +namespace grpc_core { + +// Returns a slice containing roots from the OS trust store +grpc_slice LoadSystemRootCerts(); + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_H */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_fallback.cc b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_fallback.cc new file mode 100644 index 00000000000000..73d1245f33175a --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_fallback.cc @@ -0,0 +1,32 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include "src/core/lib/security/security_connector/load_system_roots.h" + +#ifndef GPR_LINUX + +namespace grpc_core { + +grpc_slice LoadSystemRootCerts() { return grpc_empty_slice(); } + +} // namespace grpc_core + +#endif /* GPR_LINUX */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.cc b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.cc new file mode 100644 index 00000000000000..924fa8a3e26874 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.cc @@ -0,0 +1,165 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include "src/core/lib/security/security_connector/load_system_roots_linux.h" + +#ifdef GPR_LINUX + +#include "src/core/lib/security/security_connector/load_system_roots.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/iomgr/load_file.h" + +namespace grpc_core { +namespace { + +const char* kLinuxCertFiles[] = { + "/etc/ssl/certs/ca-certificates.crt", "/etc/pki/tls/certs/ca-bundle.crt", + "/etc/ssl/ca-bundle.pem", "/etc/pki/tls/cacert.pem", + "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"}; +const char* kLinuxCertDirectories[] = { + "/etc/ssl/certs", "/system/etc/security/cacerts", "/usr/local/share/certs", + "/etc/pki/tls/certs", "/etc/openssl/certs"}; + +grpc_slice GetSystemRootCerts() { + grpc_slice valid_bundle_slice = grpc_empty_slice(); + size_t num_cert_files_ = GPR_ARRAY_SIZE(kLinuxCertFiles); + for (size_t i = 0; i < num_cert_files_; i++) { + grpc_error* error = + grpc_load_file(kLinuxCertFiles[i], 1, &valid_bundle_slice); + if (error == GRPC_ERROR_NONE) { + return valid_bundle_slice; + } + } + return grpc_empty_slice(); +} + +} // namespace + +void GetAbsoluteFilePath(const char* valid_file_dir, + const char* file_entry_name, char* path_buffer) { + if (valid_file_dir != nullptr && file_entry_name != nullptr) { + int path_len = snprintf(path_buffer, MAXPATHLEN, "%s/%s", valid_file_dir, + file_entry_name); + if (path_len == 0) { + gpr_log(GPR_ERROR, "failed to get absolute path for file: %s", + file_entry_name); + } + } +} + +grpc_slice CreateRootCertsBundle(const char* certs_directory) { + grpc_slice bundle_slice = grpc_empty_slice(); + if (certs_directory == nullptr) { + return bundle_slice; + } + DIR* ca_directory = opendir(certs_directory); + if (ca_directory == nullptr) { + return bundle_slice; + } + struct FileData { + char path[MAXPATHLEN]; + off_t size; + }; + InlinedVector roots_filenames; + size_t total_bundle_size = 0; + struct dirent* directory_entry; + while ((directory_entry = readdir(ca_directory)) != nullptr) { + struct stat dir_entry_stat; + const char* file_entry_name = directory_entry->d_name; + FileData file_data; + GetAbsoluteFilePath(certs_directory, file_entry_name, file_data.path); + int stat_return = stat(file_data.path, &dir_entry_stat); + if (stat_return == -1 || !S_ISREG(dir_entry_stat.st_mode)) { + // no subdirectories. + if (stat_return == -1) { + gpr_log(GPR_ERROR, "failed to get status for file: %s", file_data.path); + } + continue; + } + file_data.size = dir_entry_stat.st_size; + total_bundle_size += file_data.size; + roots_filenames.push_back(file_data); + } + closedir(ca_directory); + char* bundle_string = static_cast(gpr_zalloc(total_bundle_size + 1)); + size_t bytes_read = 0; + for (size_t i = 0; i < roots_filenames.size(); i++) { + int file_descriptor = open(roots_filenames[i].path, O_RDONLY); + if (file_descriptor != -1) { + // Read file into bundle. + size_t cert_file_size = roots_filenames[i].size; + int read_ret = + read(file_descriptor, bundle_string + bytes_read, cert_file_size); + if (read_ret != -1) { + bytes_read += read_ret; + } else { + gpr_log(GPR_ERROR, "failed to read file: %s", roots_filenames[i].path); + } + } + } + bundle_slice = grpc_slice_new(bundle_string, bytes_read, gpr_free); + return bundle_slice; +} + +grpc_slice LoadSystemRootCerts() { + grpc_slice result = grpc_empty_slice(); + // Prioritize user-specified custom directory if flag is set. + char* custom_dir = gpr_getenv("GRPC_SYSTEM_SSL_ROOTS_DIR"); + if (custom_dir != nullptr) { + result = CreateRootCertsBundle(custom_dir); + gpr_free(custom_dir); + } + // If the custom directory is empty/invalid/not specified, fallback to + // distribution-specific directory. + if (GRPC_SLICE_IS_EMPTY(result)) { + result = GetSystemRootCerts(); + } + if (GRPC_SLICE_IS_EMPTY(result)) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(kLinuxCertDirectories); i++) { + result = CreateRootCertsBundle(kLinuxCertDirectories[i]); + if (!GRPC_SLICE_IS_EMPTY(result)) { + break; + } + } + } + return result; +} + +} // namespace grpc_core + +#endif /* GPR_LINUX */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.h b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.h new file mode 100644 index 00000000000000..12617df4928051 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/load_system_roots_linux.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_LINUX_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_LINUX_H + +#include + +#ifdef GPR_LINUX + +namespace grpc_core { + +// Creates a bundle slice containing the contents of all certificate files in +// a directory. +// Returns such slice. +// Exposed for testing purposes only. +grpc_slice CreateRootCertsBundle(const char* certs_directory); + +// Gets the absolute file path needed to load a certificate file. +// Populates path_buffer, which must be of size MAXPATHLEN. +// Exposed for testing purposes only. +void GetAbsoluteFilePath(const char* valid_file_dir, + const char* file_entry_name, char* path_buffer); + +} // namespace grpc_core + +#endif /* GPR_LINUX */ +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOAD_SYSTEM_ROOTS_LINUX_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.cc new file mode 100644 index 00000000000000..7cc482c16c565b --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.cc @@ -0,0 +1,253 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/security_connector/local/local_security_connector.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_utils.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/security/credentials/local/local_credentials.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/tsi/local_transport_security.h" + +#define GRPC_UDS_URI_PATTERN "unix:" +#define GRPC_LOCAL_TRANSPORT_SECURITY_TYPE "local" + +namespace { + +grpc_core::RefCountedPtr local_auth_context_create() { + /* Create auth context. */ + grpc_core::RefCountedPtr ctx = + grpc_core::MakeRefCounted(nullptr); + grpc_auth_context_add_cstring_property( + ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_LOCAL_TRANSPORT_SECURITY_TYPE); + GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( + ctx.get(), GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME) == 1); + return ctx; +} + +void local_check_peer(grpc_security_connector* sc, tsi_peer peer, + grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked, + grpc_local_connect_type type) { + int fd = grpc_endpoint_get_fd(ep); + grpc_resolved_address resolved_addr; + memset(&resolved_addr, 0, sizeof(resolved_addr)); + resolved_addr.len = GRPC_MAX_SOCKADDR_SIZE; + bool is_endpoint_local = false; + if (getsockname(fd, reinterpret_cast(resolved_addr.addr), + &resolved_addr.len) == 0) { + grpc_resolved_address addr_normalized; + grpc_resolved_address* addr = + grpc_sockaddr_is_v4mapped(&resolved_addr, &addr_normalized) + ? &addr_normalized + : &resolved_addr; + grpc_sockaddr* sock_addr = reinterpret_cast(&addr->addr); + // UDS + if (type == UDS && grpc_is_unix_socket(addr)) { + is_endpoint_local = true; + // IPV4 + } else if (type == LOCAL_TCP && sock_addr->sa_family == GRPC_AF_INET) { + const grpc_sockaddr_in* addr4 = + reinterpret_cast(sock_addr); + if (grpc_htonl(addr4->sin_addr.s_addr) == INADDR_LOOPBACK) { + is_endpoint_local = true; + } + // IPv6 + } else if (type == LOCAL_TCP && sock_addr->sa_family == GRPC_AF_INET6) { + const grpc_sockaddr_in6* addr6 = + reinterpret_cast(addr); + if (memcmp(&addr6->sin6_addr, &in6addr_loopback, + sizeof(in6addr_loopback)) == 0) { + is_endpoint_local = true; + } + } + } + grpc_error* error = GRPC_ERROR_NONE; + if (!is_endpoint_local) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Endpoint is neither UDS or TCP loopback address."); + GRPC_CLOSURE_SCHED(on_peer_checked, error); + return; + } + /* Create an auth context which is necessary to pass the santiy check in + * {client, server}_auth_filter that verifies if the peer's auth context is + * obtained during handshakes. The auth context is only checked for its + * existence and not actually used. + */ + *auth_context = local_auth_context_create(); + error = *auth_context != nullptr ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Could not create local auth context"); + GRPC_CLOSURE_SCHED(on_peer_checked, error); +} + +class grpc_local_channel_security_connector final + : public grpc_channel_security_connector { + public: + grpc_local_channel_security_connector( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const char* target_name) + : grpc_channel_security_connector(nullptr, std::move(channel_creds), + std::move(request_metadata_creds)), + target_name_(gpr_strdup(target_name)) {} + + ~grpc_local_channel_security_connector() override { gpr_free(target_name_); } + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_manager) override { + tsi_handshaker* handshaker = nullptr; + GPR_ASSERT(local_tsi_handshaker_create(true /* is_client */, &handshaker) == + TSI_OK); + grpc_handshake_manager_add( + handshake_manager, grpc_security_handshaker_create(handshaker, this)); + } + + int cmp(const grpc_security_connector* other_sc) const override { + auto* other = + reinterpret_cast( + other_sc); + int c = channel_security_connector_cmp(other); + if (c != 0) return c; + return strcmp(target_name_, other->target_name_); + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override { + grpc_local_credentials* creds = + reinterpret_cast(mutable_channel_creds()); + local_check_peer(this, peer, ep, auth_context, on_peer_checked, + creds->connect_type()); + } + + bool check_call_host(const char* host, grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) override { + if (host == nullptr || strcmp(host, target_name_) != 0) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "local call host does not match target name"); + } + return true; + } + + void cancel_check_call_host(grpc_closure* on_call_host_checked, + grpc_error* error) override { + GRPC_ERROR_UNREF(error); + } + + const char* target_name() const { return target_name_; } + + private: + char* target_name_; +}; + +class grpc_local_server_security_connector final + : public grpc_server_security_connector { + public: + grpc_local_server_security_connector( + grpc_core::RefCountedPtr server_creds) + : grpc_server_security_connector(nullptr, std::move(server_creds)) {} + ~grpc_local_server_security_connector() override = default; + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_manager) override { + tsi_handshaker* handshaker = nullptr; + GPR_ASSERT(local_tsi_handshaker_create(false /* is_client */, + &handshaker) == TSI_OK); + grpc_handshake_manager_add( + handshake_manager, grpc_security_handshaker_create(handshaker, this)); + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override { + grpc_local_server_credentials* creds = + static_cast(mutable_server_creds()); + local_check_peer(this, peer, ep, auth_context, on_peer_checked, + creds->connect_type()); + } + + int cmp(const grpc_security_connector* other) const override { + return server_security_connector_cmp( + static_cast(other)); + } +}; +} // namespace + +grpc_core::RefCountedPtr +grpc_local_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const grpc_channel_args* args, const char* target_name) { + if (channel_creds == nullptr || target_name == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_local_channel_security_connector_create()"); + return nullptr; + } + // Perform sanity check on UDS address. For TCP local connection, the check + // will be done during check_peer procedure. + grpc_local_credentials* creds = + static_cast(channel_creds.get()); + const grpc_arg* server_uri_arg = + grpc_channel_args_find(args, GRPC_ARG_SERVER_URI); + const char* server_uri_str = grpc_channel_arg_get_string(server_uri_arg); + if (creds->connect_type() == UDS && + strncmp(GRPC_UDS_URI_PATTERN, server_uri_str, + strlen(GRPC_UDS_URI_PATTERN)) != 0) { + gpr_log(GPR_ERROR, + "Invalid UDS target name to " + "grpc_local_channel_security_connector_create()"); + return nullptr; + } + return grpc_core::MakeRefCounted( + channel_creds, request_metadata_creds, target_name); +} + +grpc_core::RefCountedPtr +grpc_local_server_security_connector_create( + grpc_core::RefCountedPtr server_creds) { + if (server_creds == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid arguments to grpc_local_server_security_connector_create()"); + return nullptr; + } + return grpc_core::MakeRefCounted( + std::move(server_creds)); +} diff --git a/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.h new file mode 100644 index 00000000000000..6eee0ca9a6c41c --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/local/local_security_connector.h @@ -0,0 +1,59 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOCAL_LOCAL_SECURITY_CONNECTOR_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOCAL_LOCAL_SECURITY_CONNECTOR_H + +#include + +#include "src/core/lib/security/context/security_context.h" + +/** + * This method creates a local channel security connector. + * + * - channel_creds: channel credential instance. + * - request_metadata_creds: credential object which will be sent with each + * request. This parameter can be nullptr. + * - target_name: the name of the endpoint that the channel is connecting to. + * - args: channel args passed from the caller. + * - sc: address of local channel security connector instance to be returned + * from the method. + * + * It returns nullptr on failure. + */ +grpc_core::RefCountedPtr +grpc_local_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const grpc_channel_args* args, const char* target_name); + +/** + * This method creates a local server security connector. + * + * - server_creds: server credential instance. + * - sc: address of local server security connector instance to be returned from + * the method. + * + * It returns nullptr on failure. + */ +grpc_core::RefCountedPtr +grpc_local_server_security_connector_create( + grpc_core::RefCountedPtr server_creds); + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_LOCAL_LOCAL_SECURITY_CONNECTOR_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/security_connector.cc new file mode 100644 index 00000000000000..96a19605466953 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/security_connector.cc @@ -0,0 +1,129 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/security_connector/security_connector.h" + +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/alpn/alpn.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/security_connector/load_system_roots.h" +#include "src/core/lib/security/security_connector/security_connector.h" +#include "src/core/lib/security/transport/security_handshaker.h" + +grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount( + false, "security_connector_refcount"); + +grpc_server_security_connector::grpc_server_security_connector( + const char* url_scheme, + grpc_core::RefCountedPtr server_creds) + : grpc_security_connector(url_scheme), + server_creds_(std::move(server_creds)) {} + +grpc_channel_security_connector::grpc_channel_security_connector( + const char* url_scheme, + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds) + : grpc_security_connector(url_scheme), + channel_creds_(std::move(channel_creds)), + request_metadata_creds_(std::move(request_metadata_creds)) {} +grpc_channel_security_connector::~grpc_channel_security_connector() {} + +int grpc_security_connector_cmp(const grpc_security_connector* sc, + const grpc_security_connector* other) { + if (sc == nullptr || other == nullptr) return GPR_ICMP(sc, other); + return sc->cmp(other); +} + +int grpc_channel_security_connector::channel_security_connector_cmp( + const grpc_channel_security_connector* other) const { + const grpc_channel_security_connector* other_sc = + static_cast(other); + GPR_ASSERT(channel_creds() != nullptr); + GPR_ASSERT(other_sc->channel_creds() != nullptr); + int c = GPR_ICMP(channel_creds(), other_sc->channel_creds()); + if (c != 0) return c; + return GPR_ICMP(request_metadata_creds(), other_sc->request_metadata_creds()); +} + +int grpc_server_security_connector::server_security_connector_cmp( + const grpc_server_security_connector* other) const { + const grpc_server_security_connector* other_sc = + static_cast(other); + GPR_ASSERT(server_creds() != nullptr); + GPR_ASSERT(other_sc->server_creds() != nullptr); + return GPR_ICMP(server_creds(), other_sc->server_creds()); +} + +static void connector_arg_destroy(void* p) { + static_cast(p)->Unref(DEBUG_LOCATION, + "connector_arg_destroy"); +} + +static void* connector_arg_copy(void* p) { + return static_cast(p) + ->Ref(DEBUG_LOCATION, "connector_arg_copy") + .release(); +} + +static int connector_cmp(void* a, void* b) { + return static_cast(a)->cmp( + static_cast(b)); +} + +static const grpc_arg_pointer_vtable connector_arg_vtable = { + connector_arg_copy, connector_arg_destroy, connector_cmp}; + +grpc_arg grpc_security_connector_to_arg(grpc_security_connector* sc) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_SECURITY_CONNECTOR, sc, + &connector_arg_vtable); +} + +grpc_security_connector* grpc_security_connector_from_arg(const grpc_arg* arg) { + if (strcmp(arg->key, GRPC_ARG_SECURITY_CONNECTOR)) return nullptr; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_ARG_SECURITY_CONNECTOR); + return nullptr; + } + return static_cast(arg->value.pointer.p); +} + +grpc_security_connector* grpc_security_connector_find_in_args( + const grpc_channel_args* args) { + size_t i; + if (args == nullptr) return nullptr; + for (i = 0; i < args->num_args; i++) { + grpc_security_connector* sc = + grpc_security_connector_from_arg(&args->args[i]); + if (sc != nullptr) return sc; + } + return nullptr; +} diff --git a/third_party/grpc/src/core/lib/security/security_connector/security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/security_connector.h new file mode 100644 index 00000000000000..74b0ef21a62ce4 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/security_connector.h @@ -0,0 +1,174 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H + +#include + +#include + +#include + +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/tsi/ssl_transport_security.h" +#include "src/core/tsi/transport_security_interface.h" + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_security_connector_refcount; + +typedef enum { GRPC_SECURITY_OK = 0, GRPC_SECURITY_ERROR } grpc_security_status; + +/* --- security_connector object. --- + + A security connector object represents away to configure the underlying + transport security mechanism and check the resulting trusted peer. */ + +#define GRPC_ARG_SECURITY_CONNECTOR "grpc.security_connector" + +class grpc_security_connector + : public grpc_core::RefCounted { + public: + explicit grpc_security_connector(const char* url_scheme) + : grpc_core::RefCounted( + &grpc_trace_security_connector_refcount), + url_scheme_(url_scheme) {} + virtual ~grpc_security_connector() = default; + + /* Check the peer. Callee takes ownership of the peer object. + When done, sets *auth_context and invokes on_peer_checked. */ + virtual void check_peer( + tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) GRPC_ABSTRACT; + + /* Compares two security connectors. */ + virtual int cmp(const grpc_security_connector* other) const GRPC_ABSTRACT; + + const char* url_scheme() const { return url_scheme_; } + + GRPC_ABSTRACT_BASE_CLASS + + private: + const char* url_scheme_; +}; + +/* 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_security_connector_find_in_args( + const grpc_channel_args* args); + +/* --- channel_security_connector object. --- + + A channel security connector object represents a way to configure the + underlying transport security mechanism on the client side. */ + +class grpc_channel_security_connector : public grpc_security_connector { + public: + grpc_channel_security_connector( + const char* url_scheme, + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds); + ~grpc_channel_security_connector() override; + + /// Checks that the host that will be set for a call is acceptable. + /// Returns true if completed synchronously, in which case \a error will + /// be set to indicate the result. Otherwise, \a on_call_host_checked + /// will be invoked when complete. + virtual bool check_call_host(const char* host, + grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) GRPC_ABSTRACT; + /// Cancels a pending asychronous call to + /// grpc_channel_security_connector_check_call_host() with + /// \a on_call_host_checked as its callback. + virtual void cancel_check_call_host(grpc_closure* on_call_host_checked, + grpc_error* error) GRPC_ABSTRACT; + /// Registers handshakers with \a handshake_mgr. + virtual void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) + GRPC_ABSTRACT; + + const grpc_channel_credentials* channel_creds() const { + return channel_creds_.get(); + } + grpc_channel_credentials* mutable_channel_creds() { + return channel_creds_.get(); + } + const grpc_call_credentials* request_metadata_creds() const { + return request_metadata_creds_.get(); + } + grpc_call_credentials* mutable_request_metadata_creds() { + return request_metadata_creds_.get(); + } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + // Helper methods to be used in subclasses. + int channel_security_connector_cmp( + const grpc_channel_security_connector* other) const; + + private: + grpc_core::RefCountedPtr channel_creds_; + grpc_core::RefCountedPtr request_metadata_creds_; +}; + +/* --- server_security_connector object. --- + + A server security connector object represents a way to configure the + underlying transport security mechanism on the server side. */ + +class grpc_server_security_connector : public grpc_security_connector { + public: + grpc_server_security_connector( + const char* url_scheme, + grpc_core::RefCountedPtr server_creds); + ~grpc_server_security_connector() override = default; + + virtual void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) + GRPC_ABSTRACT; + + const grpc_server_credentials* server_creds() const { + return server_creds_.get(); + } + grpc_server_credentials* mutable_server_creds() { + return server_creds_.get(); + } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + // Helper methods to be used in subclasses. + int server_security_connector_cmp( + const grpc_server_security_connector* other) const; + + private: + grpc_core::RefCountedPtr server_creds_; +}; + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SECURITY_CONNECTOR_H */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc new file mode 100644 index 00000000000000..7414ab1a37faf7 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.cc @@ -0,0 +1,452 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/security_connector/ssl/ssl_security_connector.h" + +#include + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/alpn/alpn.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/ssl/ssl_credentials.h" +#include "src/core/lib/security/security_connector/load_system_roots.h" +#include "src/core/lib/security/security_connector/ssl_utils.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/tsi/ssl_transport_security.h" +#include "src/core/tsi/transport_security.h" + +namespace { +grpc_error* ssl_check_peer( + const char* peer_name, const tsi_peer* peer, + grpc_core::RefCountedPtr* auth_context) { +#if TSI_OPENSSL_ALPN_SUPPORT + /* Check the ALPN if ALPN is supported. */ + const tsi_peer_property* p = + tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); + if (p == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: missing selected ALPN property."); + } + if (!grpc_chttp2_is_alpn_version_supported(p->value.data, p->value.length)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: invalid ALPN value."); + } +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + /* Check the peer name if specified. */ + if (peer_name != nullptr && !grpc_ssl_host_matches_name(peer, peer_name)) { + char* msg; + gpr_asprintf(&msg, "Peer name %s is not in peer certificate", peer_name); + grpc_error* error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + *auth_context = grpc_ssl_peer_to_auth_context(peer); + return GRPC_ERROR_NONE; +} + +class grpc_ssl_channel_security_connector final + : public grpc_channel_security_connector { + public: + grpc_ssl_channel_security_connector( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const grpc_ssl_config* config, const char* target_name, + const char* overridden_target_name) + : grpc_channel_security_connector(GRPC_SSL_URL_SCHEME, + std::move(channel_creds), + std::move(request_metadata_creds)), + overridden_target_name_(overridden_target_name == nullptr + ? nullptr + : gpr_strdup(overridden_target_name)), + verify_options_(&config->verify_options) { + char* port; + gpr_split_host_port(target_name, &target_name_, &port); + gpr_free(port); + } + + ~grpc_ssl_channel_security_connector() override { + tsi_ssl_client_handshaker_factory_unref(client_handshaker_factory_); + if (target_name_ != nullptr) gpr_free(target_name_); + if (overridden_target_name_ != nullptr) gpr_free(overridden_target_name_); + } + + grpc_security_status InitializeHandshakerFactory( + const grpc_ssl_config* config, const char* pem_root_certs, + const tsi_ssl_root_certs_store* root_store, + tsi_ssl_session_cache* ssl_session_cache) { + bool has_key_cert_pair = + config->pem_key_cert_pair != nullptr && + config->pem_key_cert_pair->private_key != nullptr && + config->pem_key_cert_pair->cert_chain != nullptr; + tsi_ssl_client_handshaker_options options; + memset(&options, 0, sizeof(options)); + GPR_DEBUG_ASSERT(pem_root_certs != nullptr); + options.pem_root_certs = pem_root_certs; + options.root_store = root_store; + options.alpn_protocols = + grpc_fill_alpn_protocol_strings(&options.num_alpn_protocols); + if (has_key_cert_pair) { + options.pem_key_cert_pair = config->pem_key_cert_pair; + } + options.cipher_suites = grpc_get_ssl_cipher_suites(); + options.session_cache = ssl_session_cache; + const tsi_result result = + tsi_create_ssl_client_handshaker_factory_with_options( + &options, &client_handshaker_factory_); + gpr_free((void*)options.alpn_protocols); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + return GRPC_SECURITY_ERROR; + } + return GRPC_SECURITY_OK; + } + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) override { + // Instantiate TSI handshaker. + tsi_handshaker* tsi_hs = nullptr; + tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker( + client_handshaker_factory_, + overridden_target_name_ != nullptr ? overridden_target_name_ + : target_name_, + &tsi_hs); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + return; + } + // Create handshakers. + grpc_handshake_manager_add(handshake_mgr, + grpc_security_handshaker_create(tsi_hs, this)); + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override { + const char* target_name = overridden_target_name_ != nullptr + ? overridden_target_name_ + : target_name_; + grpc_error* error = ssl_check_peer(target_name, &peer, auth_context); + if (error == GRPC_ERROR_NONE && + verify_options_->verify_peer_callback != nullptr) { + const tsi_peer_property* p = + tsi_peer_get_property_by_name(&peer, TSI_X509_PEM_CERT_PROPERTY); + if (p == nullptr) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: missing pem cert property."); + } else { + char* peer_pem = static_cast(gpr_malloc(p->value.length + 1)); + memcpy(peer_pem, p->value.data, p->value.length); + peer_pem[p->value.length] = '\0'; + int callback_status = verify_options_->verify_peer_callback( + target_name, peer_pem, + verify_options_->verify_peer_callback_userdata); + gpr_free(peer_pem); + if (callback_status) { + char* msg; + gpr_asprintf(&msg, "Verify peer callback returned a failure (%d)", + callback_status); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } + } + } + GRPC_CLOSURE_SCHED(on_peer_checked, error); + tsi_peer_destruct(&peer); + } + + int cmp(const grpc_security_connector* other_sc) const override { + auto* other = + reinterpret_cast(other_sc); + int c = channel_security_connector_cmp(other); + if (c != 0) return c; + c = strcmp(target_name_, other->target_name_); + if (c != 0) return c; + return (overridden_target_name_ == nullptr || + other->overridden_target_name_ == nullptr) + ? GPR_ICMP(overridden_target_name_, + other->overridden_target_name_) + : strcmp(overridden_target_name_, + other->overridden_target_name_); + } + + bool check_call_host(const char* host, grpc_auth_context* auth_context, + grpc_closure* on_call_host_checked, + grpc_error** error) override { + grpc_security_status status = GRPC_SECURITY_ERROR; + tsi_peer peer = grpc_shallow_peer_from_ssl_auth_context(auth_context); + if (grpc_ssl_host_matches_name(&peer, host)) status = 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 (overridden_target_name_ != nullptr && strcmp(host, target_name_) == 0) { + status = GRPC_SECURITY_OK; + } + if (status != GRPC_SECURITY_OK) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "call host does not match SSL server name"); + } + grpc_shallow_peer_destruct(&peer); + return true; + } + + void cancel_check_call_host(grpc_closure* on_call_host_checked, + grpc_error* error) override { + GRPC_ERROR_UNREF(error); + } + + private: + tsi_ssl_client_handshaker_factory* client_handshaker_factory_; + char* target_name_; + char* overridden_target_name_; + const verify_peer_options* verify_options_; +}; + +class grpc_ssl_server_security_connector + : public grpc_server_security_connector { + public: + grpc_ssl_server_security_connector( + grpc_core::RefCountedPtr server_creds) + : grpc_server_security_connector(GRPC_SSL_URL_SCHEME, + std::move(server_creds)) {} + + ~grpc_ssl_server_security_connector() override { + tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_); + } + + bool has_cert_config_fetcher() const { + return static_cast(server_creds()) + ->has_cert_config_fetcher(); + } + + const tsi_ssl_server_handshaker_factory* server_handshaker_factory() const { + return server_handshaker_factory_; + } + + grpc_security_status InitializeHandshakerFactory() { + if (has_cert_config_fetcher()) { + // Load initial credentials from certificate_config_fetcher: + if (!try_fetch_ssl_server_credentials()) { + gpr_log(GPR_ERROR, + "Failed loading SSL server credentials from fetcher."); + return GRPC_SECURITY_ERROR; + } + } else { + auto* server_credentials = + static_cast(server_creds()); + size_t num_alpn_protocols = 0; + const char** alpn_protocol_strings = + grpc_fill_alpn_protocol_strings(&num_alpn_protocols); + const tsi_result result = tsi_create_ssl_server_handshaker_factory_ex( + server_credentials->config().pem_key_cert_pairs, + server_credentials->config().num_key_cert_pairs, + server_credentials->config().pem_root_certs, + grpc_get_tsi_client_certificate_request_type( + server_credentials->config().client_certificate_request), + grpc_get_ssl_cipher_suites(), alpn_protocol_strings, + static_cast(num_alpn_protocols), + &server_handshaker_factory_); + gpr_free((void*)alpn_protocol_strings); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + return GRPC_SECURITY_ERROR; + } + } + return GRPC_SECURITY_OK; + } + + void add_handshakers(grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) override { + // Instantiate TSI handshaker. + try_fetch_ssl_server_credentials(); + tsi_handshaker* tsi_hs = nullptr; + tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker( + server_handshaker_factory_, &tsi_hs); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + return; + } + // Create handshakers. + grpc_handshake_manager_add(handshake_mgr, + grpc_security_handshaker_create(tsi_hs, this)); + } + + void check_peer(tsi_peer peer, grpc_endpoint* ep, + grpc_core::RefCountedPtr* auth_context, + grpc_closure* on_peer_checked) override { + grpc_error* error = ssl_check_peer(nullptr, &peer, auth_context); + tsi_peer_destruct(&peer); + GRPC_CLOSURE_SCHED(on_peer_checked, error); + } + + int cmp(const grpc_security_connector* other) const override { + return server_security_connector_cmp( + static_cast(other)); + } + + private: + /* Attempts to fetch the server certificate config if a callback is available. + * Current certificate config will continue to be used if the callback returns + * an error. Returns true if new credentials were sucessfully loaded. */ + bool try_fetch_ssl_server_credentials() { + grpc_ssl_server_certificate_config* certificate_config = nullptr; + bool status; + + if (!has_cert_config_fetcher()) return false; + + grpc_ssl_server_credentials* server_creds = + static_cast(this->mutable_server_creds()); + grpc_ssl_certificate_config_reload_status cb_result = + server_creds->FetchCertConfig(&certificate_config); + if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_UNCHANGED) { + gpr_log(GPR_DEBUG, "No change in SSL server credentials."); + status = false; + } else if (cb_result == GRPC_SSL_CERTIFICATE_CONFIG_RELOAD_NEW) { + status = try_replace_server_handshaker_factory(certificate_config); + } else { + // Log error, continue using previously-loaded credentials. + gpr_log(GPR_ERROR, + "Failed fetching new server credentials, continuing to " + "use previously-loaded credentials."); + status = false; + } + + if (certificate_config != nullptr) { + grpc_ssl_server_certificate_config_destroy(certificate_config); + } + return status; + } + + /* Attempts to replace the server_handshaker_factory with a new factory using + * the provided grpc_ssl_server_certificate_config. Should new factory + * creation fail, the existing factory will not be replaced. Returns true on + * success (new factory created). */ + bool try_replace_server_handshaker_factory( + const grpc_ssl_server_certificate_config* config) { + if (config == nullptr) { + gpr_log(GPR_ERROR, + "Server certificate config callback returned invalid (NULL) " + "config."); + return false; + } + gpr_log(GPR_DEBUG, "Using new server certificate config (%p).", config); + + size_t num_alpn_protocols = 0; + const char** alpn_protocol_strings = + grpc_fill_alpn_protocol_strings(&num_alpn_protocols); + tsi_ssl_pem_key_cert_pair* cert_pairs = grpc_convert_grpc_to_tsi_cert_pairs( + config->pem_key_cert_pairs, config->num_key_cert_pairs); + tsi_ssl_server_handshaker_factory* new_handshaker_factory = nullptr; + const grpc_ssl_server_credentials* server_creds = + static_cast(this->server_creds()); + GPR_DEBUG_ASSERT(config->pem_root_certs != nullptr); + tsi_result result = tsi_create_ssl_server_handshaker_factory_ex( + cert_pairs, config->num_key_cert_pairs, config->pem_root_certs, + grpc_get_tsi_client_certificate_request_type( + server_creds->config().client_certificate_request), + grpc_get_ssl_cipher_suites(), alpn_protocol_strings, + static_cast(num_alpn_protocols), &new_handshaker_factory); + gpr_free(cert_pairs); + gpr_free((void*)alpn_protocol_strings); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + return false; + } + set_server_handshaker_factory(new_handshaker_factory); + return true; + } + + void set_server_handshaker_factory( + tsi_ssl_server_handshaker_factory* new_factory) { + if (server_handshaker_factory_) { + tsi_ssl_server_handshaker_factory_unref(server_handshaker_factory_); + } + server_handshaker_factory_ = new_factory; + } + + tsi_ssl_server_handshaker_factory* server_handshaker_factory_ = nullptr; +}; +} // namespace + +grpc_core::RefCountedPtr +grpc_ssl_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const grpc_ssl_config* config, const char* target_name, + const char* overridden_target_name, + tsi_ssl_session_cache* ssl_session_cache) { + if (config == nullptr || target_name == nullptr) { + gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name."); + return nullptr; + } + + const char* pem_root_certs; + const tsi_ssl_root_certs_store* root_store; + if (config->pem_root_certs == nullptr) { + // Use default root certificates. + pem_root_certs = grpc_core::DefaultSslRootStore::GetPemRootCerts(); + if (pem_root_certs == nullptr) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + return nullptr; + } + root_store = grpc_core::DefaultSslRootStore::GetRootStore(); + } else { + pem_root_certs = config->pem_root_certs; + root_store = nullptr; + } + + grpc_core::RefCountedPtr c = + grpc_core::MakeRefCounted( + std::move(channel_creds), std::move(request_metadata_creds), config, + target_name, overridden_target_name); + const grpc_security_status result = c->InitializeHandshakerFactory( + config, pem_root_certs, root_store, ssl_session_cache); + if (result != GRPC_SECURITY_OK) { + return nullptr; + } + return c; +} + +grpc_core::RefCountedPtr +grpc_ssl_server_security_connector_create( + grpc_core::RefCountedPtr server_credentials) { + GPR_ASSERT(server_credentials != nullptr); + grpc_core::RefCountedPtr c = + grpc_core::MakeRefCounted( + std::move(server_credentials)); + const grpc_security_status retval = c->InitializeHandshakerFactory(); + if (retval != GRPC_SECURITY_OK) { + return nullptr; + } + return c; +} diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.h b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.h new file mode 100644 index 00000000000000..70e26e338aa4f4 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/ssl/ssl_security_connector.h @@ -0,0 +1,79 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_SSL_SECURITY_CONNECTOR_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_SSL_SECURITY_CONNECTOR_H + +#include + +#include + +#include "src/core/lib/security/security_connector/security_connector.h" + +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/tsi/ssl_transport_security.h" +#include "src/core/tsi/transport_security_interface.h" + +typedef struct { + tsi_ssl_pem_key_cert_pair* pem_key_cert_pair; + char* pem_root_certs; + verify_peer_options verify_options; +} 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_core::RefCountedPtr +grpc_ssl_channel_security_connector_create( + grpc_core::RefCountedPtr channel_creds, + grpc_core::RefCountedPtr request_metadata_creds, + const grpc_ssl_config* config, const char* target_name, + const char* overridden_target_name, + tsi_ssl_session_cache* ssl_session_cache); + +/* Config for ssl servers. */ +typedef struct { + tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs = nullptr; + size_t num_key_cert_pairs = 0; + char* pem_root_certs = nullptr; + grpc_ssl_client_certificate_request_type client_certificate_request = + GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE; +} 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_core::RefCountedPtr +grpc_ssl_server_security_connector_create( + grpc_core::RefCountedPtr server_credentials); + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_SSL_SECURITY_CONNECTOR_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.cc b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.cc new file mode 100644 index 00000000000000..29030f07ad6980 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.cc @@ -0,0 +1,349 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/security_connector/ssl_utils.h" + +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/alpn/alpn.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/security_connector/load_system_roots.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 + +/** Environment variable used as a flag to enable/disable loading system root + certificates from the OS trust store. */ +#ifndef GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR +#define GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR "GRPC_NOT_USE_SYSTEM_SSL_ROOTS" +#endif + +#ifndef TSI_OPENSSL_ALPN_SUPPORT +#define TSI_OPENSSL_ALPN_SUPPORT 1 +#endif + +/* -- Overridden default roots. -- */ + +static grpc_ssl_roots_override_callback ssl_roots_override_cb = nullptr; + +void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) { + ssl_roots_override_cb = cb; +} + +/* -- 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-ECDSA-AES128-GCM-SHA256:" \ + "ECDHE-ECDSA-AES256-GCM-SHA384:" \ + "ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES256-GCM-SHA384" + +static gpr_once cipher_suites_once = GPR_ONCE_INIT; +static const char* cipher_suites = nullptr; + +static void init_cipher_suites(void) { + char* overridden = gpr_getenv("GRPC_SSL_CIPHER_SUITES"); + cipher_suites = overridden != nullptr ? overridden : GRPC_SSL_CIPHER_SUITES; +} + +/* --- Util --- */ + +const char* grpc_get_ssl_cipher_suites(void) { + gpr_once_init(&cipher_suites_once, init_cipher_suites); + return cipher_suites; +} + +tsi_client_certificate_request_type +grpc_get_tsi_client_certificate_request_type( + grpc_ssl_client_certificate_request_type grpc_request_type) { + switch (grpc_request_type) { + case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE: + return TSI_DONT_REQUEST_CLIENT_CERTIFICATE; + + case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY; + + case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY: + return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY; + + case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY; + + case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: + return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY; + + default: + return TSI_DONT_REQUEST_CLIENT_CERTIFICATE; + } +} + +const char** grpc_fill_alpn_protocol_strings(size_t* num_alpn_protocols) { + GPR_ASSERT(num_alpn_protocols != nullptr); + *num_alpn_protocols = grpc_chttp2_num_alpn_versions(); + const char** alpn_protocol_strings = static_cast( + gpr_malloc(sizeof(const char*) * (*num_alpn_protocols))); + for (size_t i = 0; i < *num_alpn_protocols; i++) { + alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i); + } + return alpn_protocol_strings; +} + +int grpc_ssl_host_matches_name(const tsi_peer* peer, const char* peer_name) { + char* allocated_name = nullptr; + int r; + + 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; + + // IPv6 zone-id should not be included in comparisons. + char* const zone_id = strchr(allocated_name, '%'); + if (zone_id != nullptr) *zone_id = '\0'; + + r = tsi_ssl_peer_matches_name(peer, peer_name); + gpr_free(allocated_name); + return r; +} + +grpc_core::RefCountedPtr grpc_ssl_peer_to_auth_context( + const tsi_peer* peer) { + size_t i; + const char* peer_identity_property_name = nullptr; + + /* The caller has checked the certificate type property. */ + GPR_ASSERT(peer->property_count >= 1); + grpc_core::RefCountedPtr ctx = + grpc_core::MakeRefCounted(nullptr); + grpc_auth_context_add_cstring_property( + ctx.get(), 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 == nullptr) 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 == nullptr) { + peer_identity_property_name = GRPC_X509_CN_PROPERTY_NAME; + } + grpc_auth_context_add_property(ctx.get(), 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.get(), GRPC_X509_SAN_PROPERTY_NAME, + prop->value.data, prop->value.length); + } else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) { + grpc_auth_context_add_property(ctx.get(), + GRPC_X509_PEM_CERT_PROPERTY_NAME, + prop->value.data, prop->value.length); + } else if (strcmp(prop->name, TSI_SSL_SESSION_REUSED_PEER_PROPERTY) == 0) { + grpc_auth_context_add_property(ctx.get(), + GRPC_SSL_SESSION_REUSED_PROPERTY, + prop->value.data, prop->value.length); + } + } + if (peer_identity_property_name != nullptr) { + GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( + ctx.get(), peer_identity_property_name) == 1); + } + return ctx; +} + +static void add_shallow_auth_property_to_peer(tsi_peer* peer, + const grpc_auth_property* prop, + const char* tsi_prop_name) { + tsi_peer_property* tsi_prop = &peer->properties[peer->property_count++]; + tsi_prop->name = const_cast(tsi_prop_name); + tsi_prop->value.data = prop->value; + tsi_prop->value.length = prop->value_length; +} + +tsi_peer grpc_shallow_peer_from_ssl_auth_context( + const grpc_auth_context* auth_context) { + size_t max_num_props = 0; + grpc_auth_property_iterator it; + const grpc_auth_property* prop; + tsi_peer peer; + memset(&peer, 0, sizeof(peer)); + + it = grpc_auth_context_property_iterator(auth_context); + while (grpc_auth_property_iterator_next(&it) != nullptr) max_num_props++; + + if (max_num_props > 0) { + peer.properties = static_cast( + gpr_malloc(max_num_props * sizeof(tsi_peer_property))); + it = grpc_auth_context_property_iterator(auth_context); + while ((prop = grpc_auth_property_iterator_next(&it)) != nullptr) { + if (strcmp(prop->name, GRPC_X509_SAN_PROPERTY_NAME) == 0) { + add_shallow_auth_property_to_peer( + &peer, prop, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY); + } else if (strcmp(prop->name, GRPC_X509_CN_PROPERTY_NAME) == 0) { + add_shallow_auth_property_to_peer( + &peer, prop, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); + } else if (strcmp(prop->name, GRPC_X509_PEM_CERT_PROPERTY_NAME) == 0) { + add_shallow_auth_property_to_peer(&peer, prop, + TSI_X509_PEM_CERT_PROPERTY); + } + } + } + return peer; +} + +void grpc_shallow_peer_destruct(tsi_peer* peer) { + if (peer->properties != nullptr) gpr_free(peer->properties); +} + +/* --- Ssl cache implementation. --- */ + +grpc_ssl_session_cache* grpc_ssl_session_cache_create_lru(size_t capacity) { + tsi_ssl_session_cache* cache = tsi_ssl_session_cache_create_lru(capacity); + return reinterpret_cast(cache); +} + +void grpc_ssl_session_cache_destroy(grpc_ssl_session_cache* cache) { + tsi_ssl_session_cache* tsi_cache = + reinterpret_cast(cache); + tsi_ssl_session_cache_unref(tsi_cache); +} + +static void* grpc_ssl_session_cache_arg_copy(void* p) { + tsi_ssl_session_cache* tsi_cache = + reinterpret_cast(p); + // destroy call below will unref the pointer. + tsi_ssl_session_cache_ref(tsi_cache); + return p; +} + +static void grpc_ssl_session_cache_arg_destroy(void* p) { + tsi_ssl_session_cache* tsi_cache = + reinterpret_cast(p); + tsi_ssl_session_cache_unref(tsi_cache); +} + +static int grpc_ssl_session_cache_arg_cmp(void* p, void* q) { + return GPR_ICMP(p, q); +} + +grpc_arg grpc_ssl_session_cache_create_channel_arg( + grpc_ssl_session_cache* cache) { + static const grpc_arg_pointer_vtable vtable = { + grpc_ssl_session_cache_arg_copy, + grpc_ssl_session_cache_arg_destroy, + grpc_ssl_session_cache_arg_cmp, + }; + return grpc_channel_arg_pointer_create( + const_cast(GRPC_SSL_SESSION_CACHE_ARG), cache, &vtable); +} + +/* --- Default SSL root store implementation. --- */ + +namespace grpc_core { + +tsi_ssl_root_certs_store* DefaultSslRootStore::default_root_store_; +grpc_slice DefaultSslRootStore::default_pem_root_certs_; + +const tsi_ssl_root_certs_store* DefaultSslRootStore::GetRootStore() { + InitRootStore(); + return default_root_store_; +} + +const char* DefaultSslRootStore::GetPemRootCerts() { + InitRootStore(); + return GRPC_SLICE_IS_EMPTY(default_pem_root_certs_) + ? nullptr + : reinterpret_cast + GRPC_SLICE_START_PTR(default_pem_root_certs_); +} + +grpc_slice DefaultSslRootStore::ComputePemRootCerts() { + grpc_slice result = grpc_empty_slice(); + char* not_use_system_roots_env_value = + gpr_getenv(GRPC_NOT_USE_SYSTEM_SSL_ROOTS_ENV_VAR); + const bool not_use_system_roots = gpr_is_true(not_use_system_roots_env_value); + gpr_free(not_use_system_roots_env_value); + // 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 != nullptr) { + GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(default_root_certs_path, 1, &result)); + gpr_free(default_root_certs_path); + } + // Try overridden roots if needed. + grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL; + if (GRPC_SLICE_IS_EMPTY(result) && ssl_roots_override_cb != nullptr) { + char* pem_root_certs = nullptr; + ovrd_res = ssl_roots_override_cb(&pem_root_certs); + if (ovrd_res == GRPC_SSL_ROOTS_OVERRIDE_OK) { + GPR_ASSERT(pem_root_certs != nullptr); + result = grpc_slice_from_copied_buffer( + pem_root_certs, + strlen(pem_root_certs) + 1); // nullptr terminator. + } + gpr_free(pem_root_certs); + } + // Try loading roots from OS trust store if flag is enabled. + if (GRPC_SLICE_IS_EMPTY(result) && !not_use_system_roots) { + result = LoadSystemRootCerts(); + } + // Fallback to roots manually shipped with gRPC. + if (GRPC_SLICE_IS_EMPTY(result) && + ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) { + GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(installed_roots_path, 1, &result)); + } + return result; +} + +void DefaultSslRootStore::InitRootStore() { + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, DefaultSslRootStore::InitRootStoreOnce); +} + +void DefaultSslRootStore::InitRootStoreOnce() { + default_pem_root_certs_ = ComputePemRootCerts(); + if (!GRPC_SLICE_IS_EMPTY(default_pem_root_certs_)) { + default_root_store_ = + tsi_ssl_root_certs_store_create(reinterpret_cast( + GRPC_SLICE_START_PTR(default_pem_root_certs_))); + } +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.h b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.h new file mode 100644 index 00000000000000..c9cd1a1d9c546f --- /dev/null +++ b/third_party/grpc/src/core/lib/security/security_connector/ssl_utils.h @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_UTILS_H +#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_UTILS_H + +#include + +#include + +#include +#include + +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/tsi/ssl_transport_security.h" +#include "src/core/tsi/transport_security_interface.h" + +/* --- Util. --- */ + +/* --- URL schemes. --- */ +#define GRPC_SSL_URL_SCHEME "https" + +/* Return HTTP2-compliant cipher suites that gRPC accepts by default. */ +const char* grpc_get_ssl_cipher_suites(void); + +/* Map from grpc_ssl_client_certificate_request_type to + * tsi_client_certificate_request_type. */ +tsi_client_certificate_request_type +grpc_get_tsi_client_certificate_request_type( + grpc_ssl_client_certificate_request_type grpc_request_type); + +/* Return an array of strings containing alpn protocols. */ +const char** grpc_fill_alpn_protocol_strings(size_t* num_alpn_protocols); + +/* Exposed for testing only. */ +grpc_core::RefCountedPtr grpc_ssl_peer_to_auth_context( + const tsi_peer* peer); +tsi_peer grpc_shallow_peer_from_ssl_auth_context( + const grpc_auth_context* auth_context); +void grpc_shallow_peer_destruct(tsi_peer* peer); +int grpc_ssl_host_matches_name(const tsi_peer* peer, const char* peer_name); + +/* --- Default SSL Root Store. --- */ +namespace grpc_core { + +// The class implements default SSL root store. +class DefaultSslRootStore { + public: + // Gets the default SSL root store. Returns nullptr if not found. + static const tsi_ssl_root_certs_store* GetRootStore(); + + // Gets the default PEM root certificate. + static const char* GetPemRootCerts(); + + protected: + // Returns default PEM root certificates in nullptr terminated grpc_slice. + // This function is protected instead of private, so that it can be tested. + static grpc_slice ComputePemRootCerts(); + + private: + // Construct me not! + DefaultSslRootStore(); + + // Initialization of default SSL root store. + static void InitRootStore(); + + // One-time initialization of default SSL root store. + static void InitRootStoreOnce(); + + // SSL root store in tsi_ssl_root_certs_store object. + static tsi_ssl_root_certs_store* default_root_store_; + + // Default PEM root certificates. + static grpc_slice default_pem_root_certs_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_SSL_UTILS_H \ + */ diff --git a/third_party/grpc/src/core/lib/security/transport/auth_filters.h b/third_party/grpc/src/core/lib/security/transport/auth_filters.h new file mode 100644 index 00000000000000..af2104cfbcda85 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/auth_filters.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H +#define GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H + +#include + +#include +#include "src/core/lib/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_client_auth_filter; +extern const grpc_channel_filter grpc_server_auth_filter; + +void grpc_auth_metadata_context_build( + const char* url_scheme, grpc_slice call_host, grpc_slice call_method, + grpc_auth_context* auth_context, + grpc_auth_metadata_context* auth_md_context); + +void grpc_auth_metadata_context_reset(grpc_auth_metadata_context* context); + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_AUTH_FILTERS_H */ diff --git a/third_party/grpc/src/core/lib/security/transport/client_auth_filter.cc b/third_party/grpc/src/core/lib/security/transport/client_auth_filter.cc new file mode 100644 index 00000000000000..66f86b8bc52668 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/client_auth_filter.cc @@ -0,0 +1,429 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/transport/auth_filters.h" + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/security_connector/security_connector.h" +#include "src/core/lib/security/security_connector/ssl_utils.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/transport/static_metadata.h" + +#define MAX_CREDENTIALS_METADATA_COUNT 4 + +namespace { +/* We can have a per-call credentials. */ +struct call_data { + call_data(grpc_call_element* elem, const grpc_call_element_args& args) + : arena(args.arena), + owning_call(args.call_stack), + call_combiner(args.call_combiner) {} + + // This method is technically the dtor of this class. However, since + // `get_request_metadata_cancel_closure` can run in parallel to + // `destroy_call_elem`, we cannot call the dtor in them. Otherwise, + // fields will be accessed after calling dtor, and msan correctly complains + // that the memory is not initialized. + void destroy() { + grpc_credentials_mdelem_array_destroy(&md_array); + creds.reset(); + grpc_slice_unref_internal(host); + grpc_slice_unref_internal(method); + grpc_auth_metadata_context_reset(&auth_md_context); + } + + gpr_arena* arena; + grpc_call_stack* owning_call; + grpc_call_combiner* call_combiner; + grpc_core::RefCountedPtr creds; + grpc_slice host = grpc_empty_slice(); + grpc_slice method = grpc_empty_slice(); + /* pollset{_set} bound to this call; if we need to make external + network requests, they should be done under a pollset added to this + pollset_set so that work can progress when this call wants work to progress + */ + grpc_polling_entity* pollent = nullptr; + grpc_credentials_mdelem_array md_array; + grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT] = {}; + grpc_auth_metadata_context auth_md_context = + grpc_auth_metadata_context(); // Zero-initialize the C struct. + grpc_closure async_result_closure; + grpc_closure check_call_host_cancel_closure; + grpc_closure get_request_metadata_cancel_closure; +}; + +/* We can have a per-channel credentials. */ +struct channel_data { + channel_data(grpc_channel_security_connector* security_connector, + grpc_auth_context* auth_context) + : security_connector( + security_connector->Ref(DEBUG_LOCATION, "client_auth_filter")), + auth_context(auth_context->Ref(DEBUG_LOCATION, "client_auth_filter")) {} + ~channel_data() { + security_connector.reset(DEBUG_LOCATION, "client_auth_filter"); + auth_context.reset(DEBUG_LOCATION, "client_auth_filter"); + } + + grpc_core::RefCountedPtr security_connector; + grpc_core::RefCountedPtr auth_context; +}; +} // namespace + +void grpc_auth_metadata_context_reset( + grpc_auth_metadata_context* auth_md_context) { + if (auth_md_context->service_url != nullptr) { + gpr_free(const_cast(auth_md_context->service_url)); + auth_md_context->service_url = nullptr; + } + if (auth_md_context->method_name != nullptr) { + gpr_free(const_cast(auth_md_context->method_name)); + auth_md_context->method_name = nullptr; + } + if (auth_md_context->channel_auth_context != nullptr) { + const_cast(auth_md_context->channel_auth_context) + ->Unref(DEBUG_LOCATION, "grpc_auth_metadata_context"); + auth_md_context->channel_auth_context = nullptr; + } +} + +static void add_error(grpc_error** combined, grpc_error* error) { + if (error == GRPC_ERROR_NONE) return; + if (*combined == GRPC_ERROR_NONE) { + *combined = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Client auth metadata plugin error"); + } + *combined = grpc_error_add_child(*combined, error); +} + +static void on_credentials_metadata(void* arg, grpc_error* input_error) { + grpc_transport_stream_op_batch* batch = + static_cast(arg); + grpc_call_element* elem = + static_cast(batch->handler_private.extra_arg); + call_data* calld = static_cast(elem->call_data); + grpc_auth_metadata_context_reset(&calld->auth_md_context); + grpc_error* error = GRPC_ERROR_REF(input_error); + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(calld->md_array.size <= MAX_CREDENTIALS_METADATA_COUNT); + GPR_ASSERT(batch->send_initial_metadata); + grpc_metadata_batch* mdb = + batch->payload->send_initial_metadata.send_initial_metadata; + for (size_t i = 0; i < calld->md_array.size; ++i) { + add_error(&error, grpc_metadata_batch_add_tail( + mdb, &calld->md_links[i], + GRPC_MDELEM_REF(calld->md_array.md[i]))); + } + } + if (error == GRPC_ERROR_NONE) { + grpc_call_next_op(elem, batch); + } else { + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + grpc_transport_stream_op_batch_finish_with_failure(batch, error, + calld->call_combiner); + } + GRPC_CALL_STACK_UNREF(calld->owning_call, "get_request_metadata"); +} + +void grpc_auth_metadata_context_build( + const char* url_scheme, grpc_slice call_host, grpc_slice call_method, + grpc_auth_context* auth_context, + grpc_auth_metadata_context* auth_md_context) { + char* service = grpc_slice_to_c_string(call_method); + char* last_slash = strrchr(service, '/'); + char* method_name = nullptr; + char* service_url = nullptr; + grpc_auth_metadata_context_reset(auth_md_context); + if (last_slash == nullptr) { + gpr_log(GPR_ERROR, "No '/' found in fully qualified method name"); + service[0] = '\0'; + method_name = gpr_strdup(""); + } else if (last_slash == service) { + method_name = gpr_strdup(""); + } else { + *last_slash = '\0'; + method_name = gpr_strdup(last_slash + 1); + } + char* host_and_port = grpc_slice_to_c_string(call_host); + if (url_scheme != nullptr && strcmp(url_scheme, GRPC_SSL_URL_SCHEME) == 0) { + /* Remove the port if it is 443. */ + char* port_delimiter = strrchr(host_and_port, ':'); + if (port_delimiter != nullptr && strcmp(port_delimiter + 1, "443") == 0) { + *port_delimiter = '\0'; + } + } + gpr_asprintf(&service_url, "%s://%s%s", + url_scheme == nullptr ? "" : url_scheme, host_and_port, service); + auth_md_context->service_url = service_url; + auth_md_context->method_name = method_name; + auth_md_context->channel_auth_context = + auth_context == nullptr + ? nullptr + : auth_context->Ref(DEBUG_LOCATION, "grpc_auth_metadata_context") + .release(); + gpr_free(service); + gpr_free(host_and_port); +} + +static void cancel_get_request_metadata(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + if (error != GRPC_ERROR_NONE) { + calld->creds->cancel_get_request_metadata(&calld->md_array, + GRPC_ERROR_REF(error)); + } +} + +static void send_security_metadata(grpc_call_element* elem, + grpc_transport_stream_op_batch* batch) { + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + grpc_client_security_context* ctx = + static_cast( + batch->payload->context[GRPC_CONTEXT_SECURITY].value); + grpc_call_credentials* channel_call_creds = + chand->security_connector->mutable_request_metadata_creds(); + int call_creds_has_md = (ctx != nullptr) && (ctx->creds != nullptr); + + if (channel_call_creds == nullptr && !call_creds_has_md) { + /* Skip sending metadata altogether. */ + grpc_call_next_op(elem, batch); + return; + } + + if (channel_call_creds != nullptr && call_creds_has_md) { + calld->creds = grpc_core::RefCountedPtr( + grpc_composite_call_credentials_create(channel_call_creds, + ctx->creds.get(), nullptr)); + if (calld->creds == nullptr) { + grpc_transport_stream_op_batch_finish_with_failure( + batch, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Incompatible credentials set on channel and call."), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); + return; + } + } else { + calld->creds = + call_creds_has_md ? ctx->creds->Ref() : channel_call_creds->Ref(); + } + + grpc_auth_metadata_context_build( + chand->security_connector->url_scheme(), calld->host, calld->method, + chand->auth_context.get(), &calld->auth_md_context); + + GPR_ASSERT(calld->pollent != nullptr); + GRPC_CALL_STACK_REF(calld->owning_call, "get_request_metadata"); + GRPC_CLOSURE_INIT(&calld->async_result_closure, on_credentials_metadata, + batch, grpc_schedule_on_exec_ctx); + grpc_error* error = GRPC_ERROR_NONE; + if (calld->creds->get_request_metadata( + calld->pollent, calld->auth_md_context, &calld->md_array, + &calld->async_result_closure, &error)) { + // Synchronous return; invoke on_credentials_metadata() directly. + on_credentials_metadata(batch, error); + GRPC_ERROR_UNREF(error); + } else { + // Async return; register cancellation closure with call combiner. + grpc_call_combiner_set_notify_on_cancel( + calld->call_combiner, + GRPC_CLOSURE_INIT(&calld->get_request_metadata_cancel_closure, + cancel_get_request_metadata, elem, + grpc_schedule_on_exec_ctx)); + } +} + +static void on_host_checked(void* arg, grpc_error* error) { + grpc_transport_stream_op_batch* batch = + static_cast(arg); + grpc_call_element* elem = + static_cast(batch->handler_private.extra_arg); + call_data* calld = static_cast(elem->call_data); + if (error == GRPC_ERROR_NONE) { + send_security_metadata(elem, batch); + } else { + char* error_msg; + char* host = grpc_slice_to_c_string(calld->host); + gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.", + host); + gpr_free(host); + grpc_transport_stream_op_batch_finish_with_failure( + batch, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); + gpr_free(error_msg); + } + GRPC_CALL_STACK_UNREF(calld->owning_call, "check_call_host"); +} + +static void cancel_check_call_host(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + if (error != GRPC_ERROR_NONE) { + chand->security_connector->cancel_check_call_host( + &calld->async_result_closure, GRPC_ERROR_REF(error)); + } +} + +static void auth_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + GPR_TIMER_SCOPE("auth_start_transport_stream_op_batch", 0); + + /* grab pointers to our data from the call element */ + call_data* calld = static_cast(elem->call_data); + channel_data* chand = static_cast(elem->channel_data); + + if (!batch->cancel_stream) { + // TODO(hcaseyal): move this to init_call_elem once issue #15927 is + // resolved. + GPR_ASSERT(batch->payload->context != nullptr); + if (batch->payload->context[GRPC_CONTEXT_SECURITY].value == nullptr) { + batch->payload->context[GRPC_CONTEXT_SECURITY].value = + grpc_client_security_context_create(calld->arena, /*creds=*/nullptr); + batch->payload->context[GRPC_CONTEXT_SECURITY].destroy = + grpc_client_security_context_destroy; + } + grpc_client_security_context* sec_ctx = + static_cast( + batch->payload->context[GRPC_CONTEXT_SECURITY].value); + sec_ctx->auth_context.reset(DEBUG_LOCATION, "client_auth_filter"); + sec_ctx->auth_context = + chand->auth_context->Ref(DEBUG_LOCATION, "client_auth_filter"); + } + + if (batch->send_initial_metadata) { + grpc_metadata_batch* metadata = + batch->payload->send_initial_metadata.send_initial_metadata; + if (metadata->idx.named.path != nullptr) { + calld->method = + grpc_slice_ref_internal(GRPC_MDVALUE(metadata->idx.named.path->md)); + } + if (metadata->idx.named.authority != nullptr) { + calld->host = grpc_slice_ref_internal( + GRPC_MDVALUE(metadata->idx.named.authority->md)); + batch->handler_private.extra_arg = elem; + GRPC_CALL_STACK_REF(calld->owning_call, "check_call_host"); + GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch, + grpc_schedule_on_exec_ctx); + char* call_host = grpc_slice_to_c_string(calld->host); + grpc_error* error = GRPC_ERROR_NONE; + if (chand->security_connector->check_call_host( + call_host, chand->auth_context.get(), + &calld->async_result_closure, &error)) { + // Synchronous return; invoke on_host_checked() directly. + on_host_checked(batch, error); + GRPC_ERROR_UNREF(error); + } else { + // Async return; register cancellation closure with call combiner. + grpc_call_combiner_set_notify_on_cancel( + calld->call_combiner, + GRPC_CLOSURE_INIT(&calld->check_call_host_cancel_closure, + cancel_check_call_host, elem, + grpc_schedule_on_exec_ctx)); + } + gpr_free(call_host); + return; /* early exit */ + } + } + + /* pass control down the stack */ + grpc_call_next_op(elem, batch); +} + +/* Constructor for call_data */ +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + new (elem->call_data) call_data(elem, *args); + return GRPC_ERROR_NONE; +} + +static void set_pollset_or_pollset_set(grpc_call_element* elem, + grpc_polling_entity* pollent) { + call_data* calld = static_cast(elem->call_data); + calld->pollent = pollent; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = static_cast(elem->call_data); + calld->destroy(); +} + +/* Constructor for channel_data */ +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + /* 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(!args->is_last); + grpc_security_connector* sc = + grpc_security_connector_find_in_args(args->channel_args); + if (sc == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Security connector missing from client auth filter args"); + } + grpc_auth_context* auth_context = + grpc_find_auth_context_in_args(args->channel_args); + if (auth_context == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Auth context missing from client auth filter args"); + } + new (elem->channel_data) channel_data( + static_cast(sc), auth_context); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + chand->~channel_data(); +} + +const grpc_channel_filter grpc_client_auth_filter = { + auth_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "client-auth"}; diff --git a/third_party/grpc/src/core/lib/security/transport/secure_endpoint.cc b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.cc new file mode 100644 index 00000000000000..14fb55884f1a90 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.cc @@ -0,0 +1,445 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when + using that endpoint. Because of various transitive includes in uv.h, + including windows.h on Windows, uv.h must be included before other system + headers. Therefore, sockaddr.h must always be included first */ +#include + +#include + +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include +#include +#include +#include +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/security/transport/secure_endpoint.h" +#include "src/core/lib/security/transport/tsi_error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/tsi/transport_security_grpc.h" + +#define STAGING_BUFFER_SIZE 8192 + +static void on_read(void* user_data, grpc_error* error); + +namespace { +struct secure_endpoint { + secure_endpoint(const grpc_endpoint_vtable* vtable, + tsi_frame_protector* protector, + tsi_zero_copy_grpc_protector* zero_copy_protector, + grpc_endpoint* transport, grpc_slice* leftover_slices, + size_t leftover_nslices) + : wrapped_ep(transport), + protector(protector), + zero_copy_protector(zero_copy_protector) { + base.vtable = vtable; + gpr_mu_init(&protector_mu); + GRPC_CLOSURE_INIT(&on_read, ::on_read, this, grpc_schedule_on_exec_ctx); + grpc_slice_buffer_init(&source_buffer); + grpc_slice_buffer_init(&leftover_bytes); + for (size_t i = 0; i < leftover_nslices; i++) { + grpc_slice_buffer_add(&leftover_bytes, + grpc_slice_ref_internal(leftover_slices[i])); + } + grpc_slice_buffer_init(&output_buffer); + gpr_ref_init(&ref, 1); + } + + ~secure_endpoint() { + grpc_endpoint_destroy(wrapped_ep); + tsi_frame_protector_destroy(protector); + tsi_zero_copy_grpc_protector_destroy(zero_copy_protector); + grpc_slice_buffer_destroy_internal(&source_buffer); + grpc_slice_buffer_destroy_internal(&leftover_bytes); + grpc_slice_unref_internal(read_staging_buffer); + grpc_slice_unref_internal(write_staging_buffer); + grpc_slice_buffer_destroy_internal(&output_buffer); + gpr_mu_destroy(&protector_mu); + } + + grpc_endpoint base; + grpc_endpoint* wrapped_ep; + struct tsi_frame_protector* protector; + struct tsi_zero_copy_grpc_protector* zero_copy_protector; + gpr_mu protector_mu; + /* saved upper level callbacks and user_data. */ + grpc_closure* read_cb = nullptr; + grpc_closure* write_cb = nullptr; + grpc_closure on_read; + grpc_slice_buffer* read_buffer = nullptr; + grpc_slice_buffer source_buffer; + /* saved handshaker leftover data to unprotect. */ + grpc_slice_buffer leftover_bytes; + /* buffers for read and write */ + grpc_slice read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + grpc_slice write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + grpc_slice_buffer output_buffer; + + gpr_refcount ref; +}; +} // namespace + +grpc_core::TraceFlag grpc_trace_secure_endpoint(false, "secure_endpoint"); + +static void destroy(secure_endpoint* ep) { grpc_core::Delete(ep); } + +#ifndef NDEBUG +#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) { + if (grpc_trace_secure_endpoint.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECENDP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val, + val - 1); + } + if (gpr_unref(&ep->ref)) { + destroy(ep); + } +} + +static void secure_endpoint_ref(secure_endpoint* ep, const char* reason, + const char* file, int line) { + if (grpc_trace_secure_endpoint.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECENDP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val, + val + 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, uint8_t** cur, + uint8_t** end) { + grpc_slice_buffer_add(ep->read_buffer, ep->read_staging_buffer); + ep->read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + *cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer); + *end = GRPC_SLICE_END_PTR(ep->read_staging_buffer); +} + +static void call_read_cb(secure_endpoint* ep, grpc_error* error) { + if (grpc_trace_secure_endpoint.enabled()) { + size_t i; + for (i = 0; i < ep->read_buffer->count; i++) { + char* data = grpc_dump_slice(ep->read_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "READ %p: %s", ep, data); + gpr_free(data); + } + } + ep->read_buffer = nullptr; + GRPC_CLOSURE_SCHED(ep->read_cb, error); + SECURE_ENDPOINT_UNREF(ep, "read"); +} + +static void on_read(void* user_data, grpc_error* error) { + unsigned i; + uint8_t keep_looping = 0; + tsi_result result = TSI_OK; + secure_endpoint* ep = static_cast(user_data); + uint8_t* cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer); + uint8_t* end = GRPC_SLICE_END_PTR(ep->read_staging_buffer); + + if (error != GRPC_ERROR_NONE) { + grpc_slice_buffer_reset_and_unref_internal(ep->read_buffer); + call_read_cb(ep, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Secure read failed", &error, 1)); + return; + } + + if (ep->zero_copy_protector != nullptr) { + // Use zero-copy grpc protector to unprotect. + result = tsi_zero_copy_grpc_protector_unprotect( + ep->zero_copy_protector, &ep->source_buffer, ep->read_buffer); + } else { + // Use frame protector to unprotect. + /* TODO(yangg) check error, maybe bail out early */ + for (i = 0; i < ep->source_buffer.count; i++) { + grpc_slice encrypted = ep->source_buffer.slices[i]; + uint8_t* message_bytes = GRPC_SLICE_START_PTR(encrypted); + size_t message_size = GRPC_SLICE_LENGTH(encrypted); + + while (message_size > 0 || keep_looping) { + size_t unprotected_buffer_size_written = static_cast(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 != GRPC_SLICE_START_PTR(ep->read_staging_buffer)) { + grpc_slice_buffer_add( + ep->read_buffer, + grpc_slice_split_head( + &ep->read_staging_buffer, + static_cast( + cur - GRPC_SLICE_START_PTR(ep->read_staging_buffer)))); + } + } + + /* TODO(yangg) experiment with moving this block after read_cb to see if it + helps latency */ + grpc_slice_buffer_reset_and_unref_internal(&ep->source_buffer); + + if (result != TSI_OK) { + grpc_slice_buffer_reset_and_unref_internal(ep->read_buffer); + call_read_cb( + ep, grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unwrap failed"), result)); + return; + } + + call_read_cb(ep, GRPC_ERROR_NONE); +} + +static void endpoint_read(grpc_endpoint* secure_ep, grpc_slice_buffer* slices, + grpc_closure* cb) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + ep->read_cb = cb; + ep->read_buffer = slices; + grpc_slice_buffer_reset_and_unref_internal(ep->read_buffer); + + SECURE_ENDPOINT_REF(ep, "read"); + if (ep->leftover_bytes.count) { + grpc_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer); + GPR_ASSERT(ep->leftover_bytes.count == 0); + on_read(ep, GRPC_ERROR_NONE); + return; + } + + grpc_endpoint_read(ep->wrapped_ep, &ep->source_buffer, &ep->on_read); +} + +static void flush_write_staging_buffer(secure_endpoint* ep, uint8_t** cur, + uint8_t** end) { + grpc_slice_buffer_add(&ep->output_buffer, ep->write_staging_buffer); + ep->write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + *cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer); + *end = GRPC_SLICE_END_PTR(ep->write_staging_buffer); +} + +static void endpoint_write(grpc_endpoint* secure_ep, grpc_slice_buffer* slices, + grpc_closure* cb, void* arg) { + GPR_TIMER_SCOPE("secure_endpoint.endpoint_write", 0); + + unsigned i; + tsi_result result = TSI_OK; + secure_endpoint* ep = reinterpret_cast(secure_ep); + uint8_t* cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer); + uint8_t* end = GRPC_SLICE_END_PTR(ep->write_staging_buffer); + + grpc_slice_buffer_reset_and_unref_internal(&ep->output_buffer); + + if (grpc_trace_secure_endpoint.enabled()) { + for (i = 0; i < slices->count; i++) { + char* data = + grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "WRITE %p: %s", ep, data); + gpr_free(data); + } + } + + if (ep->zero_copy_protector != nullptr) { + // Use zero-copy grpc protector to protect. + result = tsi_zero_copy_grpc_protector_protect(ep->zero_copy_protector, + slices, &ep->output_buffer); + } else { + // Use frame protector to protect. + for (i = 0; i < slices->count; i++) { + grpc_slice plain = slices->slices[i]; + uint8_t* message_bytes = GRPC_SLICE_START_PTR(plain); + size_t message_size = GRPC_SLICE_LENGTH(plain); + while (message_size > 0) { + size_t protected_buffer_size_to_send = static_cast(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 = static_cast(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 != GRPC_SLICE_START_PTR(ep->write_staging_buffer)) { + grpc_slice_buffer_add( + &ep->output_buffer, + grpc_slice_split_head( + &ep->write_staging_buffer, + static_cast( + cur - GRPC_SLICE_START_PTR(ep->write_staging_buffer)))); + } + } + } + + if (result != TSI_OK) { + /* TODO(yangg) do different things according to the error type? */ + grpc_slice_buffer_reset_and_unref_internal(&ep->output_buffer); + GRPC_CLOSURE_SCHED( + cb, grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Wrap failed"), result)); + return; + } + + grpc_endpoint_write(ep->wrapped_ep, &ep->output_buffer, cb, arg); +} + +static void endpoint_shutdown(grpc_endpoint* secure_ep, grpc_error* why) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + grpc_endpoint_shutdown(ep->wrapped_ep, why); +} + +static void endpoint_destroy(grpc_endpoint* secure_ep) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + SECURE_ENDPOINT_UNREF(ep, "destroy"); +} + +static void endpoint_add_to_pollset(grpc_endpoint* secure_ep, + grpc_pollset* pollset) { + secure_endpoint* ep = reinterpret_cast(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 = reinterpret_cast(secure_ep); + grpc_endpoint_add_to_pollset_set(ep->wrapped_ep, pollset_set); +} + +static void endpoint_delete_from_pollset_set(grpc_endpoint* secure_ep, + grpc_pollset_set* pollset_set) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + grpc_endpoint_delete_from_pollset_set(ep->wrapped_ep, pollset_set); +} + +static char* endpoint_get_peer(grpc_endpoint* secure_ep) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + return grpc_endpoint_get_peer(ep->wrapped_ep); +} + +static int endpoint_get_fd(grpc_endpoint* secure_ep) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + return grpc_endpoint_get_fd(ep->wrapped_ep); +} + +static grpc_resource_user* endpoint_get_resource_user( + grpc_endpoint* secure_ep) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + return grpc_endpoint_get_resource_user(ep->wrapped_ep); +} + +static bool endpoint_can_track_err(grpc_endpoint* secure_ep) { + secure_endpoint* ep = reinterpret_cast(secure_ep); + return grpc_endpoint_can_track_err(ep->wrapped_ep); +} + +static const grpc_endpoint_vtable vtable = {endpoint_read, + endpoint_write, + endpoint_add_to_pollset, + endpoint_add_to_pollset_set, + endpoint_delete_from_pollset_set, + endpoint_shutdown, + endpoint_destroy, + endpoint_get_resource_user, + endpoint_get_peer, + endpoint_get_fd, + endpoint_can_track_err}; + +grpc_endpoint* grpc_secure_endpoint_create( + struct tsi_frame_protector* protector, + struct tsi_zero_copy_grpc_protector* zero_copy_protector, + grpc_endpoint* transport, grpc_slice* leftover_slices, + size_t leftover_nslices) { + secure_endpoint* ep = grpc_core::New( + &vtable, protector, zero_copy_protector, transport, leftover_slices, + leftover_nslices); + return &ep->base; +} diff --git a/third_party/grpc/src/core/lib/security/transport/secure_endpoint.h b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.h new file mode 100644 index 00000000000000..e7e335167893db --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/secure_endpoint.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H +#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H + +#include + +#include +#include "src/core/lib/iomgr/endpoint.h" + +struct tsi_frame_protector; +struct tsi_zero_copy_grpc_protector; + +extern grpc_core::TraceFlag grpc_trace_secure_endpoint; + +/* Takes ownership of protector, zero_copy_protector, and to_wrap, and refs + * leftover_slices. If zero_copy_protector is not NULL, protector will never be + * used. */ +grpc_endpoint* grpc_secure_endpoint_create( + struct tsi_frame_protector* protector, + struct tsi_zero_copy_grpc_protector* zero_copy_protector, + grpc_endpoint* to_wrap, grpc_slice* leftover_slices, + size_t leftover_nslices); + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H */ diff --git a/third_party/grpc/src/core/lib/security/transport/security_handshaker.cc b/third_party/grpc/src/core/lib/security/transport/security_handshaker.cc new file mode 100644 index 00000000000000..01831dab10ff31 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/security_handshaker.cc @@ -0,0 +1,546 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/transport/security_handshaker.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/transport/secure_endpoint.h" +#include "src/core/lib/security/transport/tsi_error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/transport_security_grpc.h" + +#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 + +namespace { +struct security_handshaker { + security_handshaker(tsi_handshaker* handshaker, + grpc_security_connector* connector); + ~security_handshaker() { + gpr_mu_destroy(&mu); + tsi_handshaker_destroy(handshaker); + tsi_handshaker_result_destroy(handshaker_result); + if (endpoint_to_destroy != nullptr) { + grpc_endpoint_destroy(endpoint_to_destroy); + } + if (read_buffer_to_destroy != nullptr) { + grpc_slice_buffer_destroy_internal(read_buffer_to_destroy); + gpr_free(read_buffer_to_destroy); + } + gpr_free(handshake_buffer); + grpc_slice_buffer_destroy_internal(&outgoing); + auth_context.reset(DEBUG_LOCATION, "handshake"); + connector.reset(DEBUG_LOCATION, "handshake"); + } + + void Ref() { refs.Ref(); } + void Unref() { + if (refs.Unref()) { + grpc_core::Delete(this); + } + } + + grpc_handshaker base; + + // State set at creation time. + tsi_handshaker* handshaker; + grpc_core::RefCountedPtr connector; + + gpr_mu mu; + grpc_core::RefCount refs; + + bool shutdown = false; + // Endpoint and read buffer to destroy after a shutdown. + grpc_endpoint* endpoint_to_destroy = nullptr; + grpc_slice_buffer* read_buffer_to_destroy = nullptr; + + // State saved while performing the handshake. + grpc_handshaker_args* args = nullptr; + grpc_closure* on_handshake_done = nullptr; + + size_t handshake_buffer_size; + unsigned char* handshake_buffer; + grpc_slice_buffer outgoing; + grpc_closure on_handshake_data_sent_to_peer; + grpc_closure on_handshake_data_received_from_peer; + grpc_closure on_peer_checked; + grpc_core::RefCountedPtr auth_context; + tsi_handshaker_result* handshaker_result = nullptr; +}; +} // namespace + +static size_t move_read_buffer_into_handshake_buffer(security_handshaker* h) { + size_t bytes_in_read_buffer = h->args->read_buffer->length; + if (h->handshake_buffer_size < bytes_in_read_buffer) { + h->handshake_buffer = static_cast( + gpr_realloc(h->handshake_buffer, bytes_in_read_buffer)); + h->handshake_buffer_size = bytes_in_read_buffer; + } + size_t offset = 0; + while (h->args->read_buffer->count > 0) { + grpc_slice next_slice = grpc_slice_buffer_take_first(h->args->read_buffer); + memcpy(h->handshake_buffer + offset, GRPC_SLICE_START_PTR(next_slice), + GRPC_SLICE_LENGTH(next_slice)); + offset += GRPC_SLICE_LENGTH(next_slice); + grpc_slice_unref_internal(next_slice); + } + return bytes_in_read_buffer; +} + +// Set args fields to NULL, saving the endpoint and read buffer for +// later destruction. +static void cleanup_args_for_failure_locked(security_handshaker* h) { + h->endpoint_to_destroy = h->args->endpoint; + h->args->endpoint = nullptr; + h->read_buffer_to_destroy = h->args->read_buffer; + h->args->read_buffer = nullptr; + grpc_channel_args_destroy(h->args->args); + h->args->args = nullptr; +} + +// If the handshake failed or we're shutting down, clean up and invoke the +// callback with the error. +static void security_handshake_failed_locked(security_handshaker* h, + grpc_error* error) { + if (error == GRPC_ERROR_NONE) { + // If we were shut down after the handshake succeeded but before an + // endpoint callback was invoked, we need to generate our own error. + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown"); + } + const char* msg = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg); + + if (!h->shutdown) { + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(h->args->endpoint, GRPC_ERROR_REF(error)); + // Not shutting down, so the write failed. Clean up before + // invoking the callback. + cleanup_args_for_failure_locked(h); + // Set shutdown to true so that subsequent calls to + // security_handshaker_shutdown() do nothing. + h->shutdown = true; + } + // Invoke callback. + GRPC_CLOSURE_SCHED(h->on_handshake_done, error); +} + +static void on_peer_checked_inner(security_handshaker* h, grpc_error* error) { + if (error != GRPC_ERROR_NONE || h->shutdown) { + security_handshake_failed_locked(h, GRPC_ERROR_REF(error)); + return; + } + // Create zero-copy frame protector, if implemented. + tsi_zero_copy_grpc_protector* zero_copy_protector = nullptr; + tsi_result result = tsi_handshaker_result_create_zero_copy_grpc_protector( + h->handshaker_result, nullptr, &zero_copy_protector); + if (result != TSI_OK && result != TSI_UNIMPLEMENTED) { + error = grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Zero-copy frame protector creation failed"), + result); + security_handshake_failed_locked(h, error); + return; + } + // Create frame protector if zero-copy frame protector is NULL. + tsi_frame_protector* protector = nullptr; + if (zero_copy_protector == nullptr) { + result = tsi_handshaker_result_create_frame_protector(h->handshaker_result, + nullptr, &protector); + if (result != TSI_OK) { + error = grpc_set_tsi_error_result(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Frame protector creation failed"), + result); + security_handshake_failed_locked(h, error); + return; + } + } + // Get unused bytes. + const unsigned char* unused_bytes = nullptr; + size_t unused_bytes_size = 0; + result = tsi_handshaker_result_get_unused_bytes( + h->handshaker_result, &unused_bytes, &unused_bytes_size); + // Create secure endpoint. + if (unused_bytes_size > 0) { + grpc_slice slice = + grpc_slice_from_copied_buffer((char*)unused_bytes, unused_bytes_size); + h->args->endpoint = grpc_secure_endpoint_create( + protector, zero_copy_protector, h->args->endpoint, &slice, 1); + grpc_slice_unref_internal(slice); + } else { + h->args->endpoint = grpc_secure_endpoint_create( + protector, zero_copy_protector, h->args->endpoint, nullptr, 0); + } + tsi_handshaker_result_destroy(h->handshaker_result); + h->handshaker_result = nullptr; + // Add auth context to channel args. + grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context.get()); + grpc_channel_args* tmp_args = h->args->args; + h->args->args = + grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1); + grpc_channel_args_destroy(tmp_args); + // Invoke callback. + GRPC_CLOSURE_SCHED(h->on_handshake_done, GRPC_ERROR_NONE); + // Set shutdown to true so that subsequent calls to + // security_handshaker_shutdown() do nothing. + h->shutdown = true; +} + +static void on_peer_checked(void* arg, grpc_error* error) { + security_handshaker* h = static_cast(arg); + gpr_mu_lock(&h->mu); + on_peer_checked_inner(h, error); + gpr_mu_unlock(&h->mu); + h->Unref(); +} + +static grpc_error* check_peer_locked(security_handshaker* h) { + tsi_peer peer; + tsi_result result = + tsi_handshaker_result_extract_peer(h->handshaker_result, &peer); + if (result != TSI_OK) { + return grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Peer extraction failed"), result); + } + h->connector->check_peer(peer, h->args->endpoint, &h->auth_context, + &h->on_peer_checked); + return GRPC_ERROR_NONE; +} + +static grpc_error* on_handshake_next_done_locked( + security_handshaker* h, tsi_result result, + const unsigned char* bytes_to_send, size_t bytes_to_send_size, + tsi_handshaker_result* handshaker_result) { + grpc_error* error = GRPC_ERROR_NONE; + // Handshaker was shutdown. + if (h->shutdown) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown"); + } + // Read more if we need to. + if (result == TSI_INCOMPLETE_DATA) { + GPR_ASSERT(bytes_to_send_size == 0); + grpc_endpoint_read(h->args->endpoint, h->args->read_buffer, + &h->on_handshake_data_received_from_peer); + return error; + } + if (result != TSI_OK) { + return grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake failed"), result); + } + // Update handshaker result. + if (handshaker_result != nullptr) { + GPR_ASSERT(h->handshaker_result == nullptr); + h->handshaker_result = handshaker_result; + } + if (bytes_to_send_size > 0) { + // Send data to peer, if needed. + grpc_slice to_send = grpc_slice_from_copied_buffer( + reinterpret_cast(bytes_to_send), bytes_to_send_size); + grpc_slice_buffer_reset_and_unref_internal(&h->outgoing); + grpc_slice_buffer_add(&h->outgoing, to_send); + grpc_endpoint_write(h->args->endpoint, &h->outgoing, + &h->on_handshake_data_sent_to_peer, nullptr); + } else if (handshaker_result == nullptr) { + // There is nothing to send, but need to read from peer. + grpc_endpoint_read(h->args->endpoint, h->args->read_buffer, + &h->on_handshake_data_received_from_peer); + } else { + // Handshake has finished, check peer and so on. + error = check_peer_locked(h); + } + return error; +} + +static void on_handshake_next_done_grpc_wrapper( + tsi_result result, void* user_data, const unsigned char* bytes_to_send, + size_t bytes_to_send_size, tsi_handshaker_result* handshaker_result) { + security_handshaker* h = static_cast(user_data); + gpr_mu_lock(&h->mu); + grpc_error* error = on_handshake_next_done_locked( + h, result, bytes_to_send, bytes_to_send_size, handshaker_result); + if (error != GRPC_ERROR_NONE) { + security_handshake_failed_locked(h, error); + gpr_mu_unlock(&h->mu); + h->Unref(); + } else { + gpr_mu_unlock(&h->mu); + } +} + +static grpc_error* do_handshaker_next_locked( + security_handshaker* h, const unsigned char* bytes_received, + size_t bytes_received_size) { + // Invoke TSI handshaker. + const unsigned char* bytes_to_send = nullptr; + size_t bytes_to_send_size = 0; + tsi_handshaker_result* handshaker_result = nullptr; + tsi_result result = tsi_handshaker_next( + h->handshaker, bytes_received, bytes_received_size, &bytes_to_send, + &bytes_to_send_size, &handshaker_result, + &on_handshake_next_done_grpc_wrapper, h); + if (result == TSI_ASYNC) { + // Handshaker operating asynchronously. Nothing else to do here; + // callback will be invoked in a TSI thread. + return GRPC_ERROR_NONE; + } + // Handshaker returned synchronously. Invoke callback directly in + // this thread with our existing exec_ctx. + return on_handshake_next_done_locked(h, result, bytes_to_send, + bytes_to_send_size, handshaker_result); +} + +static void on_handshake_data_received_from_peer(void* arg, grpc_error* error) { + security_handshaker* h = static_cast(arg); + gpr_mu_lock(&h->mu); + if (error != GRPC_ERROR_NONE || h->shutdown) { + security_handshake_failed_locked( + h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Handshake read failed", &error, 1)); + gpr_mu_unlock(&h->mu); + h->Unref(); + return; + } + // Copy all slices received. + size_t bytes_received_size = move_read_buffer_into_handshake_buffer(h); + // Call TSI handshaker. + error = + do_handshaker_next_locked(h, h->handshake_buffer, bytes_received_size); + + if (error != GRPC_ERROR_NONE) { + security_handshake_failed_locked(h, error); + gpr_mu_unlock(&h->mu); + h->Unref(); + } else { + gpr_mu_unlock(&h->mu); + } +} + +static void on_handshake_data_sent_to_peer(void* arg, grpc_error* error) { + security_handshaker* h = static_cast(arg); + gpr_mu_lock(&h->mu); + if (error != GRPC_ERROR_NONE || h->shutdown) { + security_handshake_failed_locked( + h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Handshake write failed", &error, 1)); + gpr_mu_unlock(&h->mu); + h->Unref(); + return; + } + // We may be done. + if (h->handshaker_result == nullptr) { + grpc_endpoint_read(h->args->endpoint, h->args->read_buffer, + &h->on_handshake_data_received_from_peer); + } else { + error = check_peer_locked(h); + if (error != GRPC_ERROR_NONE) { + security_handshake_failed_locked(h, error); + gpr_mu_unlock(&h->mu); + h->Unref(); + return; + } + } + gpr_mu_unlock(&h->mu); +} + +// +// public handshaker API +// + +static void security_handshaker_destroy(grpc_handshaker* handshaker) { + security_handshaker* h = reinterpret_cast(handshaker); + h->Unref(); +} + +static void security_handshaker_shutdown(grpc_handshaker* handshaker, + grpc_error* why) { + security_handshaker* h = reinterpret_cast(handshaker); + gpr_mu_lock(&h->mu); + if (!h->shutdown) { + h->shutdown = true; + tsi_handshaker_shutdown(h->handshaker); + grpc_endpoint_shutdown(h->args->endpoint, GRPC_ERROR_REF(why)); + cleanup_args_for_failure_locked(h); + } + gpr_mu_unlock(&h->mu); + GRPC_ERROR_UNREF(why); +} + +static void security_handshaker_do_handshake(grpc_handshaker* handshaker, + grpc_tcp_server_acceptor* acceptor, + grpc_closure* on_handshake_done, + grpc_handshaker_args* args) { + security_handshaker* h = reinterpret_cast(handshaker); + gpr_mu_lock(&h->mu); + h->args = args; + h->on_handshake_done = on_handshake_done; + h->Ref(); + size_t bytes_received_size = move_read_buffer_into_handshake_buffer(h); + grpc_error* error = + do_handshaker_next_locked(h, h->handshake_buffer, bytes_received_size); + if (error != GRPC_ERROR_NONE) { + security_handshake_failed_locked(h, error); + gpr_mu_unlock(&h->mu); + h->Unref(); + return; + } + gpr_mu_unlock(&h->mu); +} + +static const grpc_handshaker_vtable security_handshaker_vtable = { + security_handshaker_destroy, security_handshaker_shutdown, + security_handshaker_do_handshake, "security"}; + +namespace { +security_handshaker::security_handshaker(tsi_handshaker* handshaker, + grpc_security_connector* connector) + : handshaker(handshaker), + connector(connector->Ref(DEBUG_LOCATION, "handshake")), + handshake_buffer_size(GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE), + handshake_buffer( + static_cast(gpr_malloc(handshake_buffer_size))) { + grpc_handshaker_init(&security_handshaker_vtable, &base); + gpr_mu_init(&mu); + grpc_slice_buffer_init(&outgoing); + GRPC_CLOSURE_INIT(&on_handshake_data_sent_to_peer, + ::on_handshake_data_sent_to_peer, this, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_handshake_data_received_from_peer, + ::on_handshake_data_received_from_peer, this, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_peer_checked, ::on_peer_checked, this, + grpc_schedule_on_exec_ctx); +} +} // namespace + +static grpc_handshaker* security_handshaker_create( + tsi_handshaker* handshaker, grpc_security_connector* connector) { + security_handshaker* h = + grpc_core::New(handshaker, connector); + return &h->base; +} + +// +// fail_handshaker +// + +static void fail_handshaker_destroy(grpc_handshaker* handshaker) { + gpr_free(handshaker); +} + +static void fail_handshaker_shutdown(grpc_handshaker* handshaker, + grpc_error* why) { + GRPC_ERROR_UNREF(why); +} + +static void fail_handshaker_do_handshake(grpc_handshaker* handshaker, + grpc_tcp_server_acceptor* acceptor, + grpc_closure* on_handshake_done, + grpc_handshaker_args* args) { + GRPC_CLOSURE_SCHED(on_handshake_done, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to create security handshaker")); +} + +static const grpc_handshaker_vtable fail_handshaker_vtable = { + fail_handshaker_destroy, fail_handshaker_shutdown, + fail_handshaker_do_handshake, "security_fail"}; + +static grpc_handshaker* fail_handshaker_create() { + grpc_handshaker* h = static_cast(gpr_malloc(sizeof(*h))); + grpc_handshaker_init(&fail_handshaker_vtable, h); + return h; +} + +// +// handshaker factories +// + +static void client_handshaker_factory_add_handshakers( + grpc_handshaker_factory* handshaker_factory, const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) { + grpc_channel_security_connector* security_connector = + reinterpret_cast( + grpc_security_connector_find_in_args(args)); + if (security_connector) { + security_connector->add_handshakers(interested_parties, handshake_mgr); + } +} + +static void server_handshaker_factory_add_handshakers( + grpc_handshaker_factory* hf, const grpc_channel_args* args, + grpc_pollset_set* interested_parties, + grpc_handshake_manager* handshake_mgr) { + grpc_server_security_connector* security_connector = + reinterpret_cast( + grpc_security_connector_find_in_args(args)); + if (security_connector) { + security_connector->add_handshakers(interested_parties, handshake_mgr); + } +} + +static void handshaker_factory_destroy( + grpc_handshaker_factory* handshaker_factory) {} + +static const grpc_handshaker_factory_vtable client_handshaker_factory_vtable = { + client_handshaker_factory_add_handshakers, handshaker_factory_destroy}; + +static grpc_handshaker_factory client_handshaker_factory = { + &client_handshaker_factory_vtable}; + +static const grpc_handshaker_factory_vtable server_handshaker_factory_vtable = { + server_handshaker_factory_add_handshakers, handshaker_factory_destroy}; + +static grpc_handshaker_factory server_handshaker_factory = { + &server_handshaker_factory_vtable}; + +// +// exported functions +// + +grpc_handshaker* grpc_security_handshaker_create( + tsi_handshaker* handshaker, grpc_security_connector* connector) { + // If no TSI handshaker was created, return a handshaker that always fails. + // Otherwise, return a real security handshaker. + if (handshaker == nullptr) { + return fail_handshaker_create(); + } else { + return security_handshaker_create(handshaker, connector); + } +} + +void grpc_security_register_handshaker_factories() { + grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_CLIENT, + &client_handshaker_factory); + grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_SERVER, + &server_handshaker_factory); +} diff --git a/third_party/grpc/src/core/lib/security/transport/security_handshaker.h b/third_party/grpc/src/core/lib/security/transport/security_handshaker.h new file mode 100644 index 00000000000000..88483b02e745e1 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/security_handshaker.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H +#define GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H + +#include + +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/security/security_connector/security_connector.h" + +/// Creates a security handshaker using \a handshaker. +grpc_handshaker* grpc_security_handshaker_create( + tsi_handshaker* handshaker, grpc_security_connector* connector); + +/// Registers security handshaker factories. +void grpc_security_register_handshaker_factories(); + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H */ diff --git a/third_party/grpc/src/core/lib/security/transport/server_auth_filter.cc b/third_party/grpc/src/core/lib/security/transport/server_auth_filter.cc new file mode 100644 index 00000000000000..f93eb4275e3bde --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/server_auth_filter.cc @@ -0,0 +1,325 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/transport/auth_filters.h" +#include "src/core/lib/slice/slice_internal.h" + +static void recv_initial_metadata_ready(void* arg, grpc_error* error); +static void recv_trailing_metadata_ready(void* user_data, grpc_error* error); + +namespace { +enum async_state { + STATE_INIT = 0, + STATE_DONE, + STATE_CANCELLED, +}; + +struct channel_data { + channel_data(grpc_auth_context* auth_context, grpc_server_credentials* creds) + : auth_context(auth_context->Ref()), creds(creds->Ref()) {} + ~channel_data() { auth_context.reset(DEBUG_LOCATION, "server_auth_filter"); } + + grpc_core::RefCountedPtr auth_context; + grpc_core::RefCountedPtr creds; +}; + +struct call_data { + call_data(grpc_call_element* elem, const grpc_call_element_args& args) + : call_combiner(args.call_combiner), owning_call(args.call_stack) { + GRPC_CLOSURE_INIT(&recv_initial_metadata_ready, + ::recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready, + ::recv_trailing_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + // Create server security context. Set its auth context from channel + // data and save it in the call context. + grpc_server_security_context* server_ctx = + grpc_server_security_context_create(args.arena); + channel_data* chand = static_cast(elem->channel_data); + server_ctx->auth_context = + chand->auth_context->Ref(DEBUG_LOCATION, "server_auth_filter"); + if (args.context[GRPC_CONTEXT_SECURITY].value != nullptr) { + args.context[GRPC_CONTEXT_SECURITY].destroy( + args.context[GRPC_CONTEXT_SECURITY].value); + } + args.context[GRPC_CONTEXT_SECURITY].value = server_ctx; + args.context[GRPC_CONTEXT_SECURITY].destroy = + grpc_server_security_context_destroy; + } + + ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); } + + grpc_call_combiner* call_combiner; + grpc_call_stack* owning_call; + grpc_transport_stream_op_batch* recv_initial_metadata_batch; + grpc_closure* original_recv_initial_metadata_ready; + grpc_closure recv_initial_metadata_ready; + grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE; + grpc_closure recv_trailing_metadata_ready; + grpc_closure* original_recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_error; + bool seen_recv_trailing_metadata_ready = false; + grpc_metadata_array md; + const grpc_metadata* consumed_md; + size_t num_consumed_md; + grpc_closure cancel_closure; + gpr_atm state = STATE_INIT; // async_state +}; + +} // namespace + +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 != nullptr; l = l->next) { + grpc_metadata* usr_md = nullptr; + grpc_mdelem md = l->md; + grpc_slice key = GRPC_MDKEY(md); + grpc_slice value = GRPC_MDVALUE(md); + if (result.count == result.capacity) { + result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2); + result.metadata = static_cast(gpr_realloc( + result.metadata, result.capacity * sizeof(grpc_metadata))); + } + usr_md = &result.metadata[result.count++]; + usr_md->key = grpc_slice_ref_internal(key); + usr_md->value = grpc_slice_ref_internal(value); + } + return result; +} + +static grpc_filtered_mdelem remove_consumed_md(void* user_data, + grpc_mdelem md) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + size_t i; + for (i = 0; i < calld->num_consumed_md; i++) { + const grpc_metadata* consumed_md = &calld->consumed_md[i]; + if (grpc_slice_eq(GRPC_MDKEY(md), consumed_md->key) && + grpc_slice_eq(GRPC_MDVALUE(md), consumed_md->value)) + return GRPC_FILTERED_REMOVE(); + } + return GRPC_FILTERED_MDELEM(md); +} + +static void on_md_processing_done_inner(grpc_call_element* elem, + const grpc_metadata* consumed_md, + size_t num_consumed_md, + const grpc_metadata* response_md, + size_t num_response_md, + grpc_error* error) { + call_data* calld = static_cast(elem->call_data); + grpc_transport_stream_op_batch* batch = calld->recv_initial_metadata_batch; + /* TODO(jboeuf): Implement support for response_md. */ + if (response_md != nullptr && num_response_md > 0) { + gpr_log(GPR_INFO, + "response_md in auth metadata processing not supported for now. " + "Ignoring..."); + } + if (error == GRPC_ERROR_NONE) { + calld->consumed_md = consumed_md; + calld->num_consumed_md = num_consumed_md; + error = grpc_metadata_batch_filter( + batch->payload->recv_initial_metadata.recv_initial_metadata, + remove_consumed_md, elem, "Response metadata filtering error"); + } + calld->recv_initial_metadata_error = GRPC_ERROR_REF(error); + grpc_closure* closure = calld->original_recv_initial_metadata_ready; + calld->original_recv_initial_metadata_ready = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_SCHED(closure, error); +} + +// Called from application code. +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 = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + grpc_core::ExecCtx exec_ctx; + // If the call was not cancelled while we were in flight, process the result. + if (gpr_atm_full_cas(&calld->state, static_cast(STATE_INIT), + static_cast(STATE_DONE))) { + grpc_error* error = GRPC_ERROR_NONE; + if (status != GRPC_STATUS_OK) { + if (error_details == nullptr) { + error_details = "Authentication metadata processing failed."; + } + error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details), + GRPC_ERROR_INT_GRPC_STATUS, status); + } + on_md_processing_done_inner(elem, consumed_md, num_consumed_md, response_md, + num_response_md, error); + } + // Clean up. + for (size_t i = 0; i < calld->md.count; i++) { + grpc_slice_unref_internal(calld->md.metadata[i].key); + grpc_slice_unref_internal(calld->md.metadata[i].value); + } + grpc_metadata_array_destroy(&calld->md); + GRPC_CALL_STACK_UNREF(calld->owning_call, "server_auth_metadata"); +} + +static void cancel_call(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + call_data* calld = static_cast(elem->call_data); + // If the result was not already processed, invoke the callback now. + if (error != GRPC_ERROR_NONE && + gpr_atm_full_cas(&calld->state, static_cast(STATE_INIT), + static_cast(STATE_CANCELLED))) { + on_md_processing_done_inner(elem, nullptr, 0, nullptr, 0, + GRPC_ERROR_REF(error)); + } +} + +static void recv_initial_metadata_ready(void* arg, grpc_error* error) { + grpc_call_element* elem = static_cast(arg); + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + grpc_transport_stream_op_batch* batch = calld->recv_initial_metadata_batch; + if (error == GRPC_ERROR_NONE) { + if (chand->creds != nullptr && + chand->creds->auth_metadata_processor().process != nullptr) { + // We're calling out to the application, so we need to make sure + // to drop the call combiner early if we get cancelled. + GRPC_CLOSURE_INIT(&calld->cancel_closure, cancel_call, elem, + grpc_schedule_on_exec_ctx); + grpc_call_combiner_set_notify_on_cancel(calld->call_combiner, + &calld->cancel_closure); + GRPC_CALL_STACK_REF(calld->owning_call, "server_auth_metadata"); + calld->md = metadata_batch_to_md_array( + batch->payload->recv_initial_metadata.recv_initial_metadata); + chand->creds->auth_metadata_processor().process( + chand->creds->auth_metadata_processor().state, + chand->auth_context.get(), calld->md.metadata, calld->md.count, + on_md_processing_done, elem); + return; + } + } + grpc_closure* closure = calld->original_recv_initial_metadata_ready; + calld->original_recv_initial_metadata_ready = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_RUN(closure, GRPC_ERROR_REF(error)); +} + +static void recv_trailing_metadata_ready(void* user_data, grpc_error* err) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + if (calld->original_recv_initial_metadata_ready != nullptr) { + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(err); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring recv_trailing_metadata_ready until " + "after recv_initial_metadata_ready"); + return; + } + err = grpc_error_add_child( + GRPC_ERROR_REF(err), GRPC_ERROR_REF(calld->recv_initial_metadata_error)); + GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err); +} + +static void auth_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* batch) { + call_data* calld = static_cast(elem->call_data); + if (batch->recv_initial_metadata) { + // Inject our callback. + calld->recv_initial_metadata_batch = batch; + calld->original_recv_initial_metadata_ready = + batch->payload->recv_initial_metadata.recv_initial_metadata_ready; + batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + if (batch->recv_trailing_metadata) { + calld->original_recv_trailing_metadata_ready = + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &calld->recv_trailing_metadata_ready; + } + grpc_call_next_op(elem, batch); +} + +/* Constructor for call_data */ +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + new (elem->call_data) call_data(elem, *args); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = static_cast(elem->call_data); + calld->~call_data(); +} + +/* Constructor for channel_data */ +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + GPR_ASSERT(!args->is_last); + grpc_auth_context* auth_context = + grpc_find_auth_context_in_args(args->channel_args); + GPR_ASSERT(auth_context != nullptr); + grpc_server_credentials* creds = + grpc_find_server_credentials_in_args(args->channel_args); + new (elem->channel_data) channel_data(auth_context, creds); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_channel_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + chand->~channel_data(); +} + +const grpc_channel_filter grpc_server_auth_filter = { + auth_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "server-auth"}; diff --git a/third_party/grpc/src/core/lib/security/transport/target_authority_table.cc b/third_party/grpc/src/core/lib/security/transport/target_authority_table.cc new file mode 100644 index 00000000000000..1eeb557f6a4b79 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/target_authority_table.cc @@ -0,0 +1,75 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/transport/target_authority_table.h" + +// Channel arg key for the mapping of target addresses to their authorities. +#define GRPC_ARG_TARGET_AUTHORITY_TABLE "grpc.target_authority_table" + +namespace grpc_core { +namespace { + +void* target_authority_table_copy(void* p) { + TargetAuthorityTable* table = static_cast(p); + // TODO(roth): When channel_args are converted to C++, pass the + // RefCountedPtr<> directly instead of managing the ref manually. + table->Ref().release(); + return p; +} +void target_authority_table_destroy(void* p) { + TargetAuthorityTable* table = static_cast(p); + table->Unref(); +} +int target_authority_table_cmp(void* a, void* b) { + return TargetAuthorityTable::Cmp( + *static_cast(a), + *static_cast(b)); +} +const grpc_arg_pointer_vtable target_authority_table_arg_vtable = { + target_authority_table_copy, target_authority_table_destroy, + target_authority_table_cmp}; + +} // namespace + +grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_TARGET_AUTHORITY_TABLE, + table, + &target_authority_table_arg_vtable); +} + +TargetAuthorityTable* FindTargetAuthorityTableInArgs( + const grpc_channel_args* args) { + const grpc_arg* arg = + grpc_channel_args_find(args, GRPC_ARG_TARGET_AUTHORITY_TABLE); + if (arg != nullptr) { + if (arg->type == GRPC_ARG_POINTER) { + return static_cast(arg->value.pointer.p); + } else { + gpr_log(GPR_ERROR, "value of " GRPC_ARG_TARGET_AUTHORITY_TABLE + " channel arg was not pointer type; ignoring"); + } + } + return nullptr; +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/security/transport/target_authority_table.h b/third_party/grpc/src/core/lib/security/transport/target_authority_table.h new file mode 100644 index 00000000000000..a2e7dc6ac23b41 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/target_authority_table.h @@ -0,0 +1,40 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H +#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H + +#include + +#include "src/core/lib/slice/slice_hash_table.h" + +namespace grpc_core { + +/// A hash table mapping target addresses to authorities. +typedef SliceHashTable> TargetAuthorityTable; + +/// Returns a channel argument containing \a table. +grpc_arg CreateTargetAuthorityTableChannelArg(TargetAuthorityTable* table); + +/// Returns the target authority table from \a args or nullptr. +TargetAuthorityTable* FindTargetAuthorityTableInArgs( + const grpc_channel_args* args); + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TARGET_AUTHORITY_TABLE_H */ diff --git a/third_party/grpc/src/core/lib/security/transport/tsi_error.cc b/third_party/grpc/src/core/lib/security/transport/tsi_error.cc new file mode 100644 index 00000000000000..f78bb8df38fadc --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/tsi_error.cc @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/transport/tsi_error.h" + +grpc_error* grpc_set_tsi_error_result(grpc_error* error, tsi_result result) { + return grpc_error_set_int( + grpc_error_set_str( + error, GRPC_ERROR_STR_TSI_ERROR, + grpc_slice_from_static_string(tsi_result_to_string(result))), + GRPC_ERROR_INT_TSI_CODE, result); +} diff --git a/third_party/grpc/src/core/lib/security/transport/tsi_error.h b/third_party/grpc/src/core/lib/security/transport/tsi_error.h new file mode 100644 index 00000000000000..16e04f70f1e929 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/transport/tsi_error.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H +#define GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H + +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/tsi/transport_security_interface.h" + +grpc_error* grpc_set_tsi_error_result(grpc_error* error, tsi_result result); + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H */ diff --git a/third_party/grpc/src/core/lib/security/util/json_util.cc b/third_party/grpc/src/core/lib/security/util/json_util.cc new file mode 100644 index 00000000000000..fe9f5fe3d357b3 --- /dev/null +++ b/third_party/grpc/src/core/lib/security/util/json_util.cc @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/security/util/json_util.h" + +#include + +#include +#include + +const char* grpc_json_get_string_property(const grpc_json* json, + const char* prop_name) { + grpc_json* child; + for (child = json->child; child != nullptr; child = child->next) { + if (child->key == nullptr) { + gpr_log(GPR_ERROR, "Invalid (null) JSON key encountered"); + return nullptr; + } + if (strcmp(child->key, prop_name) == 0) break; + } + if (child == nullptr || child->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name); + return nullptr; + } + return child->value; +} + +bool grpc_copy_json_string_property(const grpc_json* json, + const char* prop_name, + char** copied_value) { + const char* prop_value = grpc_json_get_string_property(json, prop_name); + if (prop_value == nullptr) return false; + *copied_value = gpr_strdup(prop_value); + return true; +} diff --git a/third_party/grpc/src/core/lib/security/util/json_util.h b/third_party/grpc/src/core/lib/security/util/json_util.h new file mode 100644 index 00000000000000..89deffcc08192f --- /dev/null +++ b/third_party/grpc/src/core/lib/security/util/json_util.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H +#define GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H + +#include + +#include + +#include "src/core/lib/json/json.h" + +// Constants. +#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" + +// Gets a child property from a json node. +const char* grpc_json_get_string_property(const grpc_json* json, + const char* prop_name); + +// Copies the value of the json child property specified by prop_name. +// Returns false if the property was not found. +bool grpc_copy_json_string_property(const grpc_json* json, + const char* prop_name, char** copied_value); + +#endif /* GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H */ diff --git a/third_party/grpc/src/core/security/base64.c b/third_party/grpc/src/core/lib/slice/b64.cc similarity index 59% rename from third_party/grpc/src/core/security/base64.c rename to third_party/grpc/src/core/lib/slice/b64.cc index 8367c160c3672f..27f2724002ef53 100644 --- a/third_party/grpc/src/core/security/base64.c +++ b/third_party/grpc/src/core/lib/slice/b64.cc @@ -1,44 +1,33 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include "src/core/security/base64.h" +#include + +#include "src/core/lib/slice/b64.h" #include #include #include #include -#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/slice/slice_internal.h" /* --- Constants. --- */ @@ -67,18 +56,34 @@ static const char base64_url_safe_chars[] = /* --- base64 functions. --- */ -char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe, +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 = + grpc_base64_estimate_encoded_size(data_size, url_safe, multiline); + char* result = static_cast(gpr_malloc(result_projected_size)); + grpc_base64_encode_core(result, vdata, data_size, url_safe, multiline); + return result; +} + +size_t grpc_base64_estimate_encoded_size(size_t data_size, int url_safe, + int multiline) { 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; + return result_projected_size; +} + +void grpc_base64_encode_core(char* result, const void* vdata, size_t data_size, + int url_safe, int multiline) { + const unsigned char* data = static_cast(vdata); + const char* base64_chars = + url_safe ? base64_url_safe_chars : base64_url_unsafe_chars; + const size_t result_projected_size = + grpc_base64_estimate_encoded_size(data_size, url_safe, multiline); + + char* current = result; size_t num_blocks = 0; size_t i = 0; @@ -117,29 +122,30 @@ char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe, GPR_ASSERT(current >= result); GPR_ASSERT((uintptr_t)(current - result) < result_projected_size); result[current - result] = '\0'; - return result; } -gpr_slice grpc_base64_decode(const char *b64, int url_safe) { +grpc_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) { - uint32_t packed = ((uint32_t)codes[0] << 2) | ((uint32_t)codes[1] >> 4); - result[(*result_offset)++] = (unsigned char)packed; +static void decode_one_char(const unsigned char* codes, unsigned char* result, + size_t* result_offset) { + uint32_t packed = (static_cast(codes[0]) << 2) | + (static_cast(codes[1]) >> 4); + result[(*result_offset)++] = static_cast(packed); } -static void decode_two_chars(const unsigned char *codes, unsigned char *result, - size_t *result_offset) { - uint32_t packed = ((uint32_t)codes[0] << 10) | ((uint32_t)codes[1] << 4) | - ((uint32_t)codes[2] >> 2); - result[(*result_offset)++] = (unsigned char)(packed >> 8); - result[(*result_offset)++] = (unsigned char)(packed); +static void decode_two_chars(const unsigned char* codes, unsigned char* result, + size_t* result_offset) { + uint32_t packed = (static_cast(codes[0]) << 10) | + (static_cast(codes[1]) << 4) | + (static_cast(codes[2]) >> 2); + result[(*result_offset)++] = static_cast(packed >> 8); + result[(*result_offset)++] = static_cast(packed); } -static int decode_group(const unsigned char *codes, size_t num_codes, - unsigned char *result, size_t *result_offset) { +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. */ @@ -173,25 +179,26 @@ static int decode_group(const unsigned char *codes, size_t num_codes, decode_two_chars(codes, result, result_offset); } else { /* No padding. */ - uint32_t packed = ((uint32_t)codes[0] << 18) | ((uint32_t)codes[1] << 12) | - ((uint32_t)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); + uint32_t packed = (static_cast(codes[0]) << 18) | + (static_cast(codes[1]) << 12) | + (static_cast(codes[2]) << 6) | codes[3]; + result[(*result_offset)++] = static_cast(packed >> 16); + result[(*result_offset)++] = static_cast(packed >> 8); + result[(*result_offset)++] = static_cast(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); +grpc_slice grpc_base64_decode_with_len(const char* b64, size_t b64_len, + int url_safe) { + grpc_slice result = GRPC_SLICE_MALLOC(b64_len); + unsigned char* current = GRPC_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++); + unsigned char c = static_cast(*b64++); signed char code; if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue; if (url_safe) { @@ -212,7 +219,7 @@ gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len, goto fail; } } else { - codes[num_codes++] = (unsigned char)code; + codes[num_codes++] = static_cast(code); if (num_codes == 4) { if (!decode_group(codes, num_codes, current, &result_size)) goto fail; num_codes = 0; @@ -224,10 +231,10 @@ gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len, !decode_group(codes, num_codes, current, &result_size)) { goto fail; } - GPR_SLICE_SET_LENGTH(result, result_size); + GRPC_SLICE_SET_LENGTH(result, result_size); return result; fail: - gpr_slice_unref(result); - return gpr_empty_slice(); + grpc_slice_unref_internal(result); + return grpc_empty_slice(); } diff --git a/third_party/grpc/src/core/lib/slice/b64.h b/third_party/grpc/src/core/lib/slice/b64.h new file mode 100644 index 00000000000000..4475568c25b512 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/b64.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SLICE_B64_H +#define GRPC_CORE_LIB_SLICE_B64_H + +#include + +#include + +/* Encodes data using base64. It is the caller's responsability to free + the returned char * using gpr_free. Returns NULL on NULL input. + TODO(makdharma) : change the flags to bool from int */ +char* grpc_base64_encode(const void* data, size_t data_size, int url_safe, + int multiline); + +/* estimate the upper bound on size of base64 encoded data. The actual size + * is guaranteed to be less than or equal to the size returned here. */ +size_t grpc_base64_estimate_encoded_size(size_t data_size, int url_safe, + int multiline); + +/* Encodes data using base64 and write it to memory pointed to by result. It is + * the caller's responsiblity to allocate enough memory in |result| to fit the + * encoded data. */ +void grpc_base64_encode_core(char* result, const void* vdata, size_t data_size, + int url_safe, int multiline); + +/* Decodes data according to the base64 specification. Returns an empty + slice in case of failure. */ +grpc_slice grpc_base64_decode(const char* b64, int url_safe); + +/* Same as above except that the length is provided by the caller. */ +grpc_slice grpc_base64_decode_with_len(const char* b64, size_t b64_len, + int url_safe); + +#endif /* GRPC_CORE_LIB_SLICE_B64_H */ diff --git a/third_party/grpc/src/core/lib/slice/percent_encoding.cc b/third_party/grpc/src/core/lib/slice/percent_encoding.cc new file mode 100644 index 00000000000000..45cd2cc47f4cd0 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/percent_encoding.cc @@ -0,0 +1,169 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/slice/percent_encoding.h" + +#include + +#include "src/core/lib/slice/slice_internal.h" + +const uint8_t grpc_url_percent_encoding_unreserved_bytes[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0xfe, 0xff, 0xff, + 0x87, 0xfe, 0xff, 0xff, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t grpc_compatible_percent_encoding_unreserved_bytes[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 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}; + +static bool is_unreserved_character(uint8_t c, + const uint8_t* unreserved_bytes) { + return ((unreserved_bytes[c / 8] >> (c % 8)) & 1) != 0; +} + +grpc_slice grpc_percent_encode_slice(grpc_slice slice, + const uint8_t* unreserved_bytes) { + static const uint8_t hex[] = "0123456789ABCDEF"; + + // first pass: count the number of bytes needed to output this string + size_t output_length = 0; + const uint8_t* slice_start = GRPC_SLICE_START_PTR(slice); + const uint8_t* slice_end = GRPC_SLICE_END_PTR(slice); + const uint8_t* p; + bool any_reserved_bytes = false; + for (p = slice_start; p < slice_end; p++) { + bool unres = is_unreserved_character(*p, unreserved_bytes); + output_length += unres ? 1 : 3; + any_reserved_bytes |= !unres; + } + // no unreserved bytes: return the string unmodified + if (!any_reserved_bytes) { + return grpc_slice_ref_internal(slice); + } + // second pass: actually encode + grpc_slice out = GRPC_SLICE_MALLOC(output_length); + uint8_t* q = GRPC_SLICE_START_PTR(out); + for (p = slice_start; p < slice_end; p++) { + if (is_unreserved_character(*p, unreserved_bytes)) { + *q++ = *p; + } else { + *q++ = '%'; + *q++ = hex[*p >> 4]; + *q++ = hex[*p & 15]; + } + } + GPR_ASSERT(q == GRPC_SLICE_END_PTR(out)); + return out; +} + +static bool valid_hex(const uint8_t* p, const uint8_t* end) { + if (p >= end) return false; + return (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || + (*p >= 'A' && *p <= 'F'); +} + +static uint8_t dehex(uint8_t c) { + if (c >= '0' && c <= '9') return static_cast(c - '0'); + if (c >= 'A' && c <= 'F') return static_cast(c - 'A' + 10); + if (c >= 'a' && c <= 'f') return static_cast(c - 'a' + 10); + GPR_UNREACHABLE_CODE(return 255); +} + +bool grpc_strict_percent_decode_slice(grpc_slice slice_in, + const uint8_t* unreserved_bytes, + grpc_slice* slice_out) { + const uint8_t* p = GRPC_SLICE_START_PTR(slice_in); + const uint8_t* in_end = GRPC_SLICE_END_PTR(slice_in); + size_t out_length = 0; + bool any_percent_encoded_stuff = false; + while (p != in_end) { + if (*p == '%') { + if (!valid_hex(++p, in_end)) return false; + if (!valid_hex(++p, in_end)) return false; + p++; + out_length++; + any_percent_encoded_stuff = true; + } else if (is_unreserved_character(*p, unreserved_bytes)) { + p++; + out_length++; + } else { + return false; + } + } + if (!any_percent_encoded_stuff) { + *slice_out = grpc_slice_ref_internal(slice_in); + return true; + } + p = GRPC_SLICE_START_PTR(slice_in); + *slice_out = GRPC_SLICE_MALLOC(out_length); + uint8_t* q = GRPC_SLICE_START_PTR(*slice_out); + while (p != in_end) { + if (*p == '%') { + *q++ = static_cast(dehex(p[1]) << 4) | (dehex(p[2])); + p += 3; + } else { + *q++ = *p++; + } + } + GPR_ASSERT(q == GRPC_SLICE_END_PTR(*slice_out)); + return true; +} + +grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in) { + const uint8_t* p = GRPC_SLICE_START_PTR(slice_in); + const uint8_t* in_end = GRPC_SLICE_END_PTR(slice_in); + size_t out_length = 0; + bool any_percent_encoded_stuff = false; + while (p != in_end) { + if (*p == '%') { + if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) { + p++; + out_length++; + } else { + p += 3; + out_length++; + any_percent_encoded_stuff = true; + } + } else { + p++; + out_length++; + } + } + if (!any_percent_encoded_stuff) { + return grpc_slice_ref_internal(slice_in); + } + p = GRPC_SLICE_START_PTR(slice_in); + grpc_slice out = GRPC_SLICE_MALLOC(out_length); + uint8_t* q = GRPC_SLICE_START_PTR(out); + while (p != in_end) { + if (*p == '%') { + if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) { + *q++ = *p++; + } else { + *q++ = static_cast(dehex(p[1]) << 4) | (dehex(p[2])); + p += 3; + } + } else { + *q++ = *p++; + } + } + GPR_ASSERT(q == GRPC_SLICE_END_PTR(out)); + return out; +} diff --git a/third_party/grpc/src/core/lib/slice/percent_encoding.h b/third_party/grpc/src/core/lib/slice/percent_encoding.h new file mode 100644 index 00000000000000..6b13ffc3fee05b --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/percent_encoding.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SLICE_PERCENT_ENCODING_H +#define GRPC_CORE_LIB_SLICE_PERCENT_ENCODING_H + +/* Percent encoding and decoding of slices. + Transforms arbitrary strings into safe-for-transmission strings by using + variants of percent encoding (RFC 3986). + Two major variants are supplied: one that strictly matches URL encoding, + and another which applies percent encoding only to non-http2 header + bytes (the 'compatible' variant) */ + +#include + +#include + +#include + +/* URL percent encoding spec bitfield (usabel as 'unreserved_bytes' in + grpc_percent_encode_slice, grpc_strict_percent_decode_slice). + Flags [A-Za-z0-9-_.~] as unreserved bytes for the percent encoding routines + */ +extern const uint8_t grpc_url_percent_encoding_unreserved_bytes[256 / 8]; +/* URL percent encoding spec bitfield (usabel as 'unreserved_bytes' in + grpc_percent_encode_slice, grpc_strict_percent_decode_slice). + Flags ascii7 non-control characters excluding '%' as unreserved bytes for the + percent encoding routines */ +extern const uint8_t grpc_compatible_percent_encoding_unreserved_bytes[256 / 8]; + +/* Percent-encode a slice, returning the new slice (this cannot fail): + unreserved_bytes is a bitfield indicating which bytes are considered + unreserved and thus do not need percent encoding */ +grpc_slice grpc_percent_encode_slice(grpc_slice slice, + const uint8_t* unreserved_bytes); +/* Percent-decode a slice, strictly. + If the input is legal (contains no unreserved bytes, and legal % encodings), + returns true and sets *slice_out to the decoded slice. + If the input is not legal, returns false and leaves *slice_out untouched. + unreserved_bytes is a bitfield indicating which bytes are considered + unreserved and thus do not need percent encoding */ +bool grpc_strict_percent_decode_slice(grpc_slice slice_in, + const uint8_t* unreserved_bytes, + grpc_slice* slice_out); +/* Percent-decode a slice, permissively. + If a % triplet can not be decoded, pass it through verbatim. + This cannot fail. */ +grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in); + +#endif /* GRPC_CORE_LIB_SLICE_PERCENT_ENCODING_H */ diff --git a/third_party/grpc/src/core/lib/slice/slice.cc b/third_party/grpc/src/core/lib/slice/slice.cc new file mode 100644 index 00000000000000..e842d84f11fe5b --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice.cc @@ -0,0 +1,501 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/slice/slice_internal.h" + +#include +#include +#include + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" + +char* grpc_slice_to_c_string(grpc_slice slice) { + char* out = static_cast(gpr_malloc(GRPC_SLICE_LENGTH(slice) + 1)); + memcpy(out, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice)); + out[GRPC_SLICE_LENGTH(slice)] = 0; + return out; +} + +grpc_slice grpc_empty_slice(void) { + grpc_slice out; + out.refcount = nullptr; + out.data.inlined.length = 0; + return out; +} + +grpc_slice grpc_slice_copy(grpc_slice s) { + grpc_slice out = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(s)); + memcpy(GRPC_SLICE_START_PTR(out), GRPC_SLICE_START_PTR(s), + GRPC_SLICE_LENGTH(s)); + return out; +} + +grpc_slice grpc_slice_ref_internal(grpc_slice slice) { + if (slice.refcount) { + slice.refcount->vtable->ref(slice.refcount); + } + return slice; +} + +void grpc_slice_unref_internal(grpc_slice slice) { + if (slice.refcount) { + slice.refcount->vtable->unref(slice.refcount); + } +} + +/* Public API */ +grpc_slice grpc_slice_ref(grpc_slice slice) { + return grpc_slice_ref_internal(slice); +} + +/* Public API */ +void grpc_slice_unref(grpc_slice slice) { + if (grpc_core::ExecCtx::Get() == nullptr) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_unref_internal(slice); + } else { + grpc_slice_unref_internal(slice); + } +} + +/* grpc_slice_from_static_string support structure - a refcount that does + nothing */ +static void noop_ref(void* unused) {} +static void noop_unref(void* unused) {} + +static const grpc_slice_refcount_vtable noop_refcount_vtable = { + noop_ref, noop_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; +static grpc_slice_refcount noop_refcount = {&noop_refcount_vtable, + &noop_refcount}; + +size_t grpc_slice_memory_usage(grpc_slice s) { + if (s.refcount == nullptr || s.refcount == &noop_refcount) { + return 0; + } else { + return s.data.refcounted.length; + } +} + +grpc_slice grpc_slice_from_static_buffer(const void* s, size_t len) { + grpc_slice slice; + slice.refcount = &noop_refcount; + slice.data.refcounted.bytes = (uint8_t*)s; + slice.data.refcounted.length = len; + return slice; +} + +grpc_slice grpc_slice_from_static_string(const char* s) { + return grpc_slice_from_static_buffer(s, strlen(s)); +} + +/* grpc_slice_new support structures - we create a refcount object extended + with the user provided data pointer & destroy function */ +typedef struct new_slice_refcount { + grpc_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 = static_cast(p); + gpr_ref(&r->refs); +} + +static void new_slice_unref(void* p) { + new_slice_refcount* r = static_cast(p); + if (gpr_unref(&r->refs)) { + r->user_destroy(r->user_data); + gpr_free(r); + } +} + +static const grpc_slice_refcount_vtable new_slice_vtable = { + new_slice_ref, new_slice_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +grpc_slice grpc_slice_new_with_user_data(void* p, size_t len, + void (*destroy)(void*), + void* user_data) { + grpc_slice slice; + new_slice_refcount* rc = + static_cast(gpr_malloc(sizeof(new_slice_refcount))); + gpr_ref_init(&rc->refs, 1); + rc->rc.vtable = &new_slice_vtable; + rc->rc.sub_refcount = &rc->rc; + rc->user_destroy = destroy; + rc->user_data = user_data; + + slice.refcount = &rc->rc; + slice.data.refcounted.bytes = static_cast(p); + slice.data.refcounted.length = len; + return slice; +} + +grpc_slice grpc_slice_new(void* p, size_t len, void (*destroy)(void*)) { + /* Pass "p" to *destroy when the slice is no longer needed. */ + return grpc_slice_new_with_user_data(p, len, destroy, p); +} + +/* grpc_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 { + grpc_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 = static_cast(p); + gpr_ref(&r->refs); +} + +static void new_with_len_unref(void* p) { + new_with_len_slice_refcount* r = static_cast(p); + if (gpr_unref(&r->refs)) { + r->user_destroy(r->user_data, r->user_length); + gpr_free(r); + } +} + +static const grpc_slice_refcount_vtable new_with_len_vtable = { + new_with_len_ref, new_with_len_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +grpc_slice grpc_slice_new_with_len(void* p, size_t len, + void (*destroy)(void*, size_t)) { + grpc_slice slice; + new_with_len_slice_refcount* rc = static_cast( + gpr_malloc(sizeof(new_with_len_slice_refcount))); + gpr_ref_init(&rc->refs, 1); + rc->rc.vtable = &new_with_len_vtable; + rc->rc.sub_refcount = &rc->rc; + rc->user_destroy = destroy; + rc->user_data = p; + rc->user_length = len; + + slice.refcount = &rc->rc; + slice.data.refcounted.bytes = static_cast(p); + slice.data.refcounted.length = len; + return slice; +} + +grpc_slice grpc_slice_from_copied_buffer(const char* source, size_t length) { + if (length == 0) return grpc_empty_slice(); + grpc_slice slice = GRPC_SLICE_MALLOC(length); + memcpy(GRPC_SLICE_START_PTR(slice), source, length); + return slice; +} + +grpc_slice grpc_slice_from_copied_string(const char* source) { + return grpc_slice_from_copied_buffer(source, strlen(source)); +} + +typedef struct { + grpc_slice_refcount base; + gpr_refcount refs; +} malloc_refcount; + +static void malloc_ref(void* p) { + malloc_refcount* r = static_cast(p); + gpr_ref(&r->refs); +} + +static void malloc_unref(void* p) { + malloc_refcount* r = static_cast(p); + if (gpr_unref(&r->refs)) { + gpr_free(r); + } +} + +static const grpc_slice_refcount_vtable malloc_vtable = { + malloc_ref, malloc_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +grpc_slice grpc_slice_malloc_large(size_t length) { + grpc_slice slice; + + /* 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 = static_cast( + 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.vtable = &malloc_vtable; + rc->base.sub_refcount = &rc->base; + + /* 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 = reinterpret_cast(rc + 1); + /* And the length of the block is set to the requested length */ + slice.data.refcounted.length = length; + return slice; +} + +grpc_slice grpc_slice_malloc(size_t length) { + grpc_slice slice; + + if (length > sizeof(slice.data.inlined.bytes)) { + return grpc_slice_malloc_large(length); + } else { + /* small slice: just inline the data */ + slice.refcount = nullptr; + slice.data.inlined.length = static_cast(length); + } + return slice; +} + +grpc_slice grpc_slice_sub_no_ref(grpc_slice source, size_t begin, size_t end) { + grpc_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->sub_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 = nullptr; + subset.data.inlined.length = static_cast(end - begin); + memcpy(subset.data.inlined.bytes, source.data.inlined.bytes + begin, + end - begin); + } + return subset; +} + +grpc_slice grpc_slice_sub(grpc_slice source, size_t begin, size_t end) { + grpc_slice subset; + + if (end - begin <= sizeof(subset.data.inlined.bytes)) { + subset.refcount = nullptr; + subset.data.inlined.length = static_cast(end - begin); + memcpy(subset.data.inlined.bytes, GRPC_SLICE_START_PTR(source) + begin, + end - begin); + } else { + subset = grpc_slice_sub_no_ref(source, begin, end); + /* Bump the refcount */ + subset.refcount->vtable->ref(subset.refcount); + } + return subset; +} + +grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice* source, size_t split, + grpc_slice_ref_whom ref_whom) { + grpc_slice tail; + + if (source->refcount == nullptr) { + /* inlined data, copy it out */ + GPR_ASSERT(source->data.inlined.length >= split); + tail.refcount = nullptr; + tail.data.inlined.length = + static_cast(source->data.inlined.length - split); + memcpy(tail.data.inlined.bytes, source->data.inlined.bytes + split, + tail.data.inlined.length); + source->data.inlined.length = static_cast(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) && + ref_whom != GRPC_SLICE_REF_TAIL) { + /* Copy out the bytes - it'll be cheaper than refcounting */ + tail.refcount = nullptr; + tail.data.inlined.length = static_cast(tail_length); + memcpy(tail.data.inlined.bytes, source->data.refcounted.bytes + split, + tail_length); + source->refcount = source->refcount->sub_refcount; + } else { + /* Build the result */ + switch (ref_whom) { + case GRPC_SLICE_REF_TAIL: + tail.refcount = source->refcount->sub_refcount; + source->refcount = &noop_refcount; + break; + case GRPC_SLICE_REF_HEAD: + tail.refcount = &noop_refcount; + source->refcount = source->refcount->sub_refcount; + break; + case GRPC_SLICE_REF_BOTH: + tail.refcount = source->refcount->sub_refcount; + source->refcount = source->refcount->sub_refcount; + /* Bump the refcount */ + tail.refcount->vtable->ref(tail.refcount); + break; + } + /* 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; +} + +grpc_slice grpc_slice_split_tail(grpc_slice* source, size_t split) { + return grpc_slice_split_tail_maybe_ref(source, split, GRPC_SLICE_REF_BOTH); +} + +grpc_slice grpc_slice_split_head(grpc_slice* source, size_t split) { + grpc_slice head; + + if (source->refcount == nullptr) { + GPR_ASSERT(source->data.inlined.length >= split); + + head.refcount = nullptr; + head.data.inlined.length = static_cast(split); + memcpy(head.data.inlined.bytes, source->data.inlined.bytes, split); + source->data.inlined.length = + static_cast(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 = nullptr; + head.data.inlined.length = static_cast(split); + memcpy(head.data.inlined.bytes, source->data.refcounted.bytes, split); + source->refcount = source->refcount->sub_refcount; + 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->sub_refcount; + /* Bump the refcount */ + head.refcount->vtable->ref(head.refcount); + /* Point into the source array */ + head.data.refcounted.bytes = source->data.refcounted.bytes; + head.data.refcounted.length = split; + source->refcount = source->refcount->sub_refcount; + source->data.refcounted.bytes += split; + source->data.refcounted.length -= split; + } + + return head; +} + +int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b) { + if (GRPC_SLICE_LENGTH(a) != GRPC_SLICE_LENGTH(b)) return false; + if (GRPC_SLICE_LENGTH(a) == 0) return true; + return 0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b), + GRPC_SLICE_LENGTH(a)); +} + +int grpc_slice_eq(grpc_slice a, grpc_slice b) { + if (a.refcount && b.refcount && a.refcount->vtable == b.refcount->vtable) { + return a.refcount->vtable->eq(a, b); + } + return grpc_slice_default_eq_impl(a, b); +} + +int grpc_slice_cmp(grpc_slice a, grpc_slice b) { + int d = static_cast(GRPC_SLICE_LENGTH(a) - GRPC_SLICE_LENGTH(b)); + if (d != 0) return d; + return memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b), + GRPC_SLICE_LENGTH(a)); +} + +int grpc_slice_str_cmp(grpc_slice a, const char* b) { + size_t b_length = strlen(b); + int d = static_cast(GRPC_SLICE_LENGTH(a) - b_length); + if (d != 0) return d; + return memcmp(GRPC_SLICE_START_PTR(a), b, b_length); +} + +int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b) { + if (a.refcount == nullptr || b.refcount == nullptr) { + return grpc_slice_eq(a, b); + } + return a.data.refcounted.length == b.data.refcounted.length && + a.data.refcounted.bytes == b.data.refcounted.bytes; +} + +int grpc_slice_buf_start_eq(grpc_slice a, const void* b, size_t len) { + if (GRPC_SLICE_LENGTH(a) < len) return 0; + return 0 == memcmp(GRPC_SLICE_START_PTR(a), b, len); +} + +int grpc_slice_rchr(grpc_slice s, char c) { + const char* b = reinterpret_cast GRPC_SLICE_START_PTR(s); + int i; + for (i = static_cast GRPC_SLICE_LENGTH(s) - 1; i != -1 && b[i] != c; i--) + ; + return i; +} + +int grpc_slice_chr(grpc_slice s, char c) { + const char* b = reinterpret_cast GRPC_SLICE_START_PTR(s); + const char* p = static_cast(memchr(b, c, GRPC_SLICE_LENGTH(s))); + return p == nullptr ? -1 : static_cast(p - b); +} + +int grpc_slice_slice(grpc_slice haystack, grpc_slice needle) { + size_t haystack_len = GRPC_SLICE_LENGTH(haystack); + const uint8_t* haystack_bytes = GRPC_SLICE_START_PTR(haystack); + size_t needle_len = GRPC_SLICE_LENGTH(needle); + const uint8_t* needle_bytes = GRPC_SLICE_START_PTR(needle); + + if (haystack_len == 0 || needle_len == 0) return -1; + if (haystack_len < needle_len) return -1; + if (haystack_len == needle_len) + return grpc_slice_eq(haystack, needle) ? 0 : -1; + if (needle_len == 1) + return grpc_slice_chr(haystack, static_cast(*needle_bytes)); + + const uint8_t* last = haystack_bytes + haystack_len - needle_len; + for (const uint8_t* cur = haystack_bytes; cur != last; ++cur) { + if (0 == memcmp(cur, needle_bytes, needle_len)) { + return static_cast(cur - haystack_bytes); + } + } + return -1; +} + +grpc_slice grpc_slice_dup(grpc_slice a) { + grpc_slice copy = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(a)); + memcpy(GRPC_SLICE_START_PTR(copy), GRPC_SLICE_START_PTR(a), + GRPC_SLICE_LENGTH(a)); + return copy; +} diff --git a/third_party/grpc/src/core/lib/slice/slice_buffer.cc b/third_party/grpc/src/core/lib/slice/slice_buffer.cc new file mode 100644 index 00000000000000..1f1c08b1594c25 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_buffer.cc @@ -0,0 +1,379 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" + +/* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */ +#define GROW(x) (3 * (x) / 2) + +static void maybe_embiggen(grpc_slice_buffer* sb) { + /* How far away from sb->base_slices is sb->slices pointer */ + size_t slice_offset = static_cast(sb->slices - sb->base_slices); + size_t slice_count = sb->count + slice_offset; + + if (slice_count == sb->capacity) { + if (sb->base_slices != sb->slices) { + /* Make room by moving elements if there's still space unused */ + memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice)); + sb->slices = sb->base_slices; + } else { + /* Allocate more memory if no more space is available */ + sb->capacity = GROW(sb->capacity); + GPR_ASSERT(sb->capacity > slice_count); + if (sb->base_slices == sb->inlined) { + sb->base_slices = static_cast( + gpr_malloc(sb->capacity * sizeof(grpc_slice))); + memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice)); + } else { + sb->base_slices = static_cast( + gpr_realloc(sb->base_slices, sb->capacity * sizeof(grpc_slice))); + } + + sb->slices = sb->base_slices + slice_offset; + } + } +} + +void grpc_slice_buffer_init(grpc_slice_buffer* sb) { + sb->count = 0; + sb->length = 0; + sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS; + sb->base_slices = sb->slices = sb->inlined; +} + +void grpc_slice_buffer_destroy_internal(grpc_slice_buffer* sb) { + grpc_slice_buffer_reset_and_unref_internal(sb); + if (sb->base_slices != sb->inlined) { + gpr_free(sb->base_slices); + } +} + +void grpc_slice_buffer_destroy(grpc_slice_buffer* sb) { + if (grpc_core::ExecCtx::Get() == nullptr) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_buffer_destroy_internal(sb); + } else { + grpc_slice_buffer_destroy_internal(sb); + } +} + +uint8_t* grpc_slice_buffer_tiny_add(grpc_slice_buffer* sb, size_t n) { + grpc_slice* back; + uint8_t* 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 = + static_cast(back->data.inlined.length + n); + return out; + +add_new: + maybe_embiggen(sb); + back = &sb->slices[sb->count]; + sb->count++; + back->refcount = nullptr; + back->data.inlined.length = static_cast(n); + return back->data.inlined.bytes; +} + +size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer* sb, grpc_slice s) { + size_t out = sb->count; + maybe_embiggen(sb); + sb->slices[out] = s; + sb->length += GRPC_SLICE_LENGTH(s); + sb->count = out + 1; + return out; +} + +void grpc_slice_buffer_add(grpc_slice_buffer* sb, grpc_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) { + grpc_slice* back = &sb->slices[n - 1]; + if (!back->refcount && + back->data.inlined.length < GRPC_SLICE_INLINED_SIZE) { + if (s.data.inlined.length + back->data.inlined.length <= + GRPC_SLICE_INLINED_SIZE) { + memcpy(back->data.inlined.bytes + back->data.inlined.length, + s.data.inlined.bytes, s.data.inlined.length); + back->data.inlined.length = static_cast( + back->data.inlined.length + s.data.inlined.length); + } else { + size_t cp1 = GRPC_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 = GRPC_SLICE_INLINED_SIZE; + maybe_embiggen(sb); + back = &sb->slices[n]; + sb->count = n + 1; + back->refcount = nullptr; + back->data.inlined.length = + static_cast(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 */ + } + } + grpc_slice_buffer_add_indexed(sb, s); +} + +void grpc_slice_buffer_addn(grpc_slice_buffer* sb, grpc_slice* s, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + grpc_slice_buffer_add(sb, s[i]); + } +} + +void grpc_slice_buffer_pop(grpc_slice_buffer* sb) { + if (sb->count != 0) { + size_t count = --sb->count; + sb->length -= GRPC_SLICE_LENGTH(sb->slices[count]); + } +} + +void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer* sb) { + size_t i; + for (i = 0; i < sb->count; i++) { + grpc_slice_unref_internal(sb->slices[i]); + } + + sb->count = 0; + sb->length = 0; +} + +void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer* sb) { + if (grpc_core::ExecCtx::Get() == nullptr) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_buffer_reset_and_unref_internal(sb); + } else { + grpc_slice_buffer_reset_and_unref_internal(sb); + } +} + +void grpc_slice_buffer_swap(grpc_slice_buffer* a, grpc_slice_buffer* b) { + size_t a_offset = static_cast(a->slices - a->base_slices); + size_t b_offset = static_cast(b->slices - b->base_slices); + + size_t a_count = a->count + a_offset; + size_t b_count = b->count + b_offset; + + if (a->base_slices == a->inlined) { + if (b->base_slices == b->inlined) { + /* swap contents of inlined buffer */ + grpc_slice temp[GRPC_SLICE_BUFFER_INLINE_ELEMENTS]; + memcpy(temp, a->base_slices, a_count * sizeof(grpc_slice)); + memcpy(a->base_slices, b->base_slices, b_count * sizeof(grpc_slice)); + memcpy(b->base_slices, temp, a_count * sizeof(grpc_slice)); + } else { + /* a is inlined, b is not - copy a inlined into b, fix pointers */ + a->base_slices = b->base_slices; + b->base_slices = b->inlined; + memcpy(b->base_slices, a->inlined, a_count * sizeof(grpc_slice)); + } + } else if (b->base_slices == b->inlined) { + /* b is inlined, a is not - copy b inlined int a, fix pointers */ + b->base_slices = a->base_slices; + a->base_slices = a->inlined; + memcpy(a->base_slices, b->inlined, b_count * sizeof(grpc_slice)); + } else { + /* no inlining: easy swap */ + GPR_SWAP(grpc_slice*, a->base_slices, b->base_slices); + } + + /* Update the slices pointers (cannot do a GPR_SWAP on slices fields here). + * Also note that since the base_slices pointers are already swapped we need + * use 'b_offset' for 'a->base_slices' and vice versa */ + a->slices = a->base_slices + b_offset; + b->slices = b->base_slices + a_offset; + + /* base_slices and slices fields are correctly set. Swap all other fields */ + GPR_SWAP(size_t, a->count, b->count); + GPR_SWAP(size_t, a->capacity, b->capacity); + GPR_SWAP(size_t, a->length, b->length); +} + +void grpc_slice_buffer_move_into(grpc_slice_buffer* src, + grpc_slice_buffer* dst) { + /* anything to move? */ + if (src->count == 0) { + return; + } + /* anything in dst? */ + if (dst->count == 0) { + grpc_slice_buffer_swap(src, dst); + return; + } + /* both buffers have data - copy, and reset src */ + grpc_slice_buffer_addn(dst, src->slices, src->count); + src->count = 0; + src->length = 0; +} + +static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer* src, size_t n, + grpc_slice_buffer* dst, + bool incref) { + GPR_ASSERT(src->length >= n); + if (src->length == n) { + grpc_slice_buffer_move_into(src, dst); + return; + } + + size_t output_len = dst->length + n; + size_t new_input_len = src->length - n; + + while (src->count > 0) { + grpc_slice slice = grpc_slice_buffer_take_first(src); + size_t slice_len = GRPC_SLICE_LENGTH(slice); + if (n > slice_len) { + grpc_slice_buffer_add(dst, slice); + n -= slice_len; + } else if (n == slice_len) { + grpc_slice_buffer_add(dst, slice); + break; + } else if (incref) { /* n < slice_len */ + grpc_slice_buffer_undo_take_first( + src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_BOTH)); + GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n); + grpc_slice_buffer_add(dst, slice); + break; + } else { /* n < slice_len */ + grpc_slice_buffer_undo_take_first( + src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_TAIL)); + GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n); + grpc_slice_buffer_add_indexed(dst, slice); + break; + } + } + GPR_ASSERT(dst->length == output_len); + GPR_ASSERT(src->length == new_input_len); + GPR_ASSERT(src->count > 0); +} + +void grpc_slice_buffer_move_first(grpc_slice_buffer* src, size_t n, + grpc_slice_buffer* dst) { + slice_buffer_move_first_maybe_ref(src, n, dst, true); +} + +void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer* src, size_t n, + grpc_slice_buffer* dst) { + slice_buffer_move_first_maybe_ref(src, n, dst, false); +} + +void grpc_slice_buffer_move_first_into_buffer(grpc_slice_buffer* src, size_t n, + void* dst) { + char* dstp = static_cast(dst); + GPR_ASSERT(src->length >= n); + + while (n > 0) { + grpc_slice slice = grpc_slice_buffer_take_first(src); + size_t slice_len = GRPC_SLICE_LENGTH(slice); + if (slice_len > n) { + memcpy(dstp, GRPC_SLICE_START_PTR(slice), n); + grpc_slice_buffer_undo_take_first( + src, grpc_slice_sub_no_ref(slice, n, slice_len)); + n = 0; + } else if (slice_len == n) { + memcpy(dstp, GRPC_SLICE_START_PTR(slice), n); + grpc_slice_unref_internal(slice); + n = 0; + } else { + memcpy(dstp, GRPC_SLICE_START_PTR(slice), slice_len); + dstp += slice_len; + n -= slice_len; + grpc_slice_unref_internal(slice); + } + } +} + +void grpc_slice_buffer_trim_end(grpc_slice_buffer* sb, size_t n, + grpc_slice_buffer* garbage) { + GPR_ASSERT(n <= sb->length); + sb->length -= n; + for (;;) { + size_t idx = sb->count - 1; + grpc_slice slice = sb->slices[idx]; + size_t slice_len = GRPC_SLICE_LENGTH(slice); + if (slice_len > n) { + sb->slices[idx] = grpc_slice_split_head(&slice, slice_len - n); + if (garbage) { + grpc_slice_buffer_add_indexed(garbage, slice); + } else { + grpc_slice_unref_internal(slice); + } + return; + } else if (slice_len == n) { + if (garbage) { + grpc_slice_buffer_add_indexed(garbage, slice); + } else { + grpc_slice_unref_internal(slice); + } + sb->count = idx; + return; + } else { + if (garbage) { + grpc_slice_buffer_add_indexed(garbage, slice); + } else { + grpc_slice_unref_internal(slice); + } + n -= slice_len; + sb->count = idx; + } + } +} + +grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer* sb) { + grpc_slice slice; + GPR_ASSERT(sb->count > 0); + slice = sb->slices[0]; + sb->slices++; + sb->count--; + sb->length -= GRPC_SLICE_LENGTH(slice); + + return slice; +} + +void grpc_slice_buffer_undo_take_first(grpc_slice_buffer* sb, + grpc_slice slice) { + sb->slices--; + sb->slices[0] = slice; + sb->count++; + sb->length += GRPC_SLICE_LENGTH(slice); +} diff --git a/third_party/grpc/src/core/lib/slice/slice_hash_table.h b/third_party/grpc/src/core/lib/slice/slice_hash_table.h new file mode 100644 index 00000000000000..4bbcf88e895533 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_hash_table.h @@ -0,0 +1,205 @@ +/* + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H +#define GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H + +#include + +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/slice/slice_internal.h" + +/// Hash table implementation. +/// +/// This implementation uses open addressing +/// (https://en.wikipedia.org/wiki/Open_addressing) with linear +/// probing (https://en.wikipedia.org/wiki/Linear_probing). +/// +/// The keys are \a grpc_slice objects. The values can be any type. +/// +/// Hash tables are intentionally immutable, to avoid the need for locking. + +namespace grpc_core { + +template +class SliceHashTable : public RefCounted> { + public: + struct Entry { + grpc_slice key; + T value; + bool is_set; + }; + + // Function for comparing values. + // TODO(roth): Eliminate this and the Cmp() method from this API once + // grpc_channel_args is redesigned to require that keys are unique. + typedef int (*ValueCmp)(const T&, const T&); + + /// Creates a new hash table containing \a entries, which is an array + /// of length \a num_entries. Takes ownership of all keys and values in \a + /// entries. If not null, \a value_cmp will be used to compare values in + /// the context of \a Cmp(). If null, raw pointer (\a GPR_ICMP) comparison + /// will be used. + static RefCountedPtr Create(size_t num_entries, + Entry* entries, + ValueCmp value_cmp); + + /// Returns the value from the table associated with \a key. + /// Returns null if \a key is not found. + const T* Get(const grpc_slice& key) const; + + /// Compares \a a vs. \a b. + /// A table is considered "smaller" (resp. "greater") if: + /// - GPR_ICMP(a->value_cmp, b->value_cmp) < 1 (resp. > 1), + /// - else, it contains fewer (resp. more) entries, + /// - else, if strcmp(a_key, b_key) < 1 (resp. > 1), + /// - else, if value_cmp(a_value, b_value) < 1 (resp. > 1). + static int Cmp(const SliceHashTable& a, const SliceHashTable& b); + + private: + // So New() can call our private ctor. + template + friend T2* New(Args&&... args); + + // So Delete() can call our private dtor. + template + friend void Delete(T2*); + + SliceHashTable(size_t num_entries, Entry* entries, ValueCmp value_cmp); + virtual ~SliceHashTable(); + + void Add(grpc_slice key, T& value); + + // Default value comparison function, if none specified by caller. + static int DefaultValueCmp(const T& a, const T& b) { return GPR_ICMP(a, b); } + + const ValueCmp value_cmp_; + const size_t size_; + size_t max_num_probes_; + Entry* entries_; +}; + +// +// implementation -- no user-serviceable parts below +// + +template +RefCountedPtr> SliceHashTable::Create(size_t num_entries, + Entry* entries, + ValueCmp value_cmp) { + return MakeRefCounted>(num_entries, entries, value_cmp); +} + +template +SliceHashTable::SliceHashTable(size_t num_entries, Entry* entries, + ValueCmp value_cmp) + : value_cmp_(value_cmp), + // Keep load factor low to improve performance of lookups. + size_(num_entries * 2), + max_num_probes_(0) { + entries_ = static_cast(gpr_zalloc(sizeof(Entry) * size_)); + for (size_t i = 0; i < num_entries; ++i) { + Entry* entry = &entries[i]; + Add(entry->key, entry->value); + } +} + +template +SliceHashTable::~SliceHashTable() { + for (size_t i = 0; i < size_; ++i) { + Entry& entry = entries_[i]; + if (entry.is_set) { + grpc_slice_unref_internal(entry.key); + entry.value.~T(); + } + } + gpr_free(entries_); +} + +template +void SliceHashTable::Add(grpc_slice key, T& value) { + const size_t hash = grpc_slice_hash(key); + for (size_t offset = 0; offset < size_; ++offset) { + const size_t idx = (hash + offset) % size_; + if (!entries_[idx].is_set) { + entries_[idx].is_set = true; + entries_[idx].key = key; + entries_[idx].value = std::move(value); + // Keep track of the maximum number of probes needed, since this + // provides an upper bound for lookups. + if (offset > max_num_probes_) max_num_probes_ = offset; + return; + } + } + GPR_ASSERT(false); // Table should never be full. +} + +template +const T* SliceHashTable::Get(const grpc_slice& key) const { + const size_t hash = grpc_slice_hash(key); + // We cap the number of probes at the max number recorded when + // populating the table. + for (size_t offset = 0; offset <= max_num_probes_; ++offset) { + const size_t idx = (hash + offset) % size_; + if (!entries_[idx].is_set) break; + if (grpc_slice_eq(entries_[idx].key, key)) { + return &entries_[idx].value; + } + } + return nullptr; // Not found. +} + +template +int SliceHashTable::Cmp(const SliceHashTable& a, const SliceHashTable& b) { + ValueCmp value_cmp_a = + a.value_cmp_ != nullptr ? a.value_cmp_ : DefaultValueCmp; + ValueCmp value_cmp_b = + b.value_cmp_ != nullptr ? b.value_cmp_ : DefaultValueCmp; + // Compare value_fns + const int value_fns_cmp = GPR_ICMP((void*)value_cmp_a, (void*)value_cmp_b); + if (value_fns_cmp != 0) return value_fns_cmp; + // Compare sizes + if (a.size_ < b.size_) return -1; + if (a.size_ > b.size_) return 1; + // Compare rows. + for (size_t i = 0; i < a.size_; ++i) { + if (!a.entries_[i].is_set) { + if (b.entries_[i].is_set) { + return -1; // a empty but b non-empty + } + continue; // both empty, no need to check key or value + } else if (!b.entries_[i].is_set) { + return 1; // a non-empty but b empty + } + // neither entry is empty + const int key_cmp = grpc_slice_cmp(a.entries_[i].key, b.entries_[i].key); + if (key_cmp != 0) return key_cmp; + const int value_cmp = value_cmp_a(a.entries_[i].value, b.entries_[i].value); + if (value_cmp != 0) return value_cmp; + } + return 0; +} + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */ diff --git a/third_party/grpc/src/core/lib/slice/slice_intern.cc b/third_party/grpc/src/core/lib/slice/slice_intern.cc new file mode 100644 index 00000000000000..e53c040e1aaef6 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_intern.cc @@ -0,0 +1,332 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/slice/slice_internal.h" + +#include +#include + +#include +#include + +#include "src/core/lib/gpr/murmur_hash.h" +#include "src/core/lib/iomgr/iomgr_internal.h" /* for iomgr_abort_on_leaks() */ +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" + +#define LOG2_SHARD_COUNT 5 +#define SHARD_COUNT (1 << LOG2_SHARD_COUNT) +#define INITIAL_SHARD_CAPACITY 8 + +#define TABLE_IDX(hash, capacity) (((hash) >> LOG2_SHARD_COUNT) % (capacity)) +#define SHARD_IDX(hash) ((hash) & ((1 << LOG2_SHARD_COUNT) - 1)) + +typedef struct interned_slice_refcount { + grpc_slice_refcount base; + grpc_slice_refcount sub; + size_t length; + gpr_atm refcnt; + uint32_t hash; + struct interned_slice_refcount* bucket_next; +} interned_slice_refcount; + +typedef struct slice_shard { + gpr_mu mu; + interned_slice_refcount** strs; + size_t count; + size_t capacity; +} slice_shard; + +/* hash seed: decided at initialization time */ +static uint32_t g_hash_seed; +static int g_forced_hash_seed = 0; + +static slice_shard g_shards[SHARD_COUNT]; + +typedef struct { + uint32_t hash; + uint32_t idx; +} static_metadata_hash_ent; + +static static_metadata_hash_ent + static_metadata_hash[4 * GRPC_STATIC_MDSTR_COUNT]; +static uint32_t max_static_metadata_hash_probe; +static uint32_t static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT]; + +static void interned_slice_ref(void* p) { + interned_slice_refcount* s = static_cast(p); + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) > 0); +} + +static void interned_slice_destroy(interned_slice_refcount* s) { + slice_shard* shard = &g_shards[SHARD_IDX(s->hash)]; + gpr_mu_lock(&shard->mu); + GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt)); + interned_slice_refcount** prev_next; + interned_slice_refcount* cur; + for (prev_next = &shard->strs[TABLE_IDX(s->hash, shard->capacity)], + cur = *prev_next; + cur != s; prev_next = &cur->bucket_next, cur = cur->bucket_next) + ; + *prev_next = cur->bucket_next; + shard->count--; + gpr_free(s); + gpr_mu_unlock(&shard->mu); +} + +static void interned_slice_unref(void* p) { + interned_slice_refcount* s = static_cast(p); + if (1 == gpr_atm_full_fetch_add(&s->refcnt, -1)) { + interned_slice_destroy(s); + } +} + +static void interned_slice_sub_ref(void* p) { + interned_slice_ref((static_cast(p)) - + offsetof(interned_slice_refcount, sub)); +} + +static void interned_slice_sub_unref(void* p) { + interned_slice_unref((static_cast(p)) - + offsetof(interned_slice_refcount, sub)); +} + +static uint32_t interned_slice_hash(grpc_slice slice) { + interned_slice_refcount* s = + reinterpret_cast(slice.refcount); + return s->hash; +} + +static int interned_slice_eq(grpc_slice a, grpc_slice b) { + return a.refcount == b.refcount; +} + +static const grpc_slice_refcount_vtable interned_slice_vtable = { + interned_slice_ref, interned_slice_unref, interned_slice_eq, + interned_slice_hash}; +static const grpc_slice_refcount_vtable interned_slice_sub_vtable = { + interned_slice_sub_ref, interned_slice_sub_unref, + grpc_slice_default_eq_impl, grpc_slice_default_hash_impl}; + +static void grow_shard(slice_shard* shard) { + GPR_TIMER_SCOPE("grow_strtab", 0); + + size_t capacity = shard->capacity * 2; + size_t i; + interned_slice_refcount** strtab; + interned_slice_refcount *s, *next; + + strtab = static_cast( + gpr_zalloc(sizeof(interned_slice_refcount*) * capacity)); + + for (i = 0; i < shard->capacity; i++) { + for (s = shard->strs[i]; s; s = next) { + size_t idx = TABLE_IDX(s->hash, capacity); + next = s->bucket_next; + s->bucket_next = strtab[idx]; + strtab[idx] = s; + } + } + gpr_free(shard->strs); + shard->strs = strtab; + shard->capacity = capacity; +} + +static grpc_slice materialize(interned_slice_refcount* s) { + grpc_slice slice; + slice.refcount = &s->base; + slice.data.refcounted.bytes = reinterpret_cast(s + 1); + slice.data.refcounted.length = s->length; + return slice; +} + +uint32_t grpc_slice_default_hash_impl(grpc_slice s) { + return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), + g_hash_seed); +} + +uint32_t grpc_static_slice_hash(grpc_slice s) { + return static_metadata_hash_values[GRPC_STATIC_METADATA_INDEX(s)]; +} + +int grpc_static_slice_eq(grpc_slice a, grpc_slice b) { + return GRPC_STATIC_METADATA_INDEX(a) == GRPC_STATIC_METADATA_INDEX(b); +} + +uint32_t grpc_slice_hash(grpc_slice s) { + return s.refcount == nullptr ? grpc_slice_default_hash_impl(s) + : s.refcount->vtable->hash(s); +} + +grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice, + bool* returned_slice_is_different) { + if (GRPC_IS_STATIC_METADATA_STRING(slice)) { + return slice; + } + + uint32_t hash = grpc_slice_hash(slice); + for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) { + static_metadata_hash_ent ent = + static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)]; + if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT && + grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) { + *returned_slice_is_different = true; + return grpc_static_slice_table[ent.idx]; + } + } + + return slice; +} + +bool grpc_slice_is_interned(grpc_slice slice) { + return (slice.refcount && slice.refcount->vtable == &interned_slice_vtable) || + GRPC_IS_STATIC_METADATA_STRING(slice); +} + +grpc_slice grpc_slice_intern(grpc_slice slice) { + GPR_TIMER_SCOPE("grpc_slice_intern", 0); + if (GRPC_IS_STATIC_METADATA_STRING(slice)) { + return slice; + } + + uint32_t hash = grpc_slice_hash(slice); + for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) { + static_metadata_hash_ent ent = + static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)]; + if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT && + grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) { + return grpc_static_slice_table[ent.idx]; + } + } + + interned_slice_refcount* s; + slice_shard* shard = &g_shards[SHARD_IDX(hash)]; + + gpr_mu_lock(&shard->mu); + + /* search for an existing string */ + size_t idx = TABLE_IDX(hash, shard->capacity); + for (s = shard->strs[idx]; s; s = s->bucket_next) { + if (s->hash == hash && grpc_slice_eq(slice, materialize(s))) { + if (gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) == 0) { + /* If we get here, we've added a ref to something that was about to + * die - drop it immediately. + * The *only* possible path here (given the shard mutex) should be to + * drop from one ref back to zero - assert that with a CAS */ + GPR_ASSERT(gpr_atm_rel_cas(&s->refcnt, 1, 0)); + /* and treat this as if we were never here... sshhh */ + } else { + gpr_mu_unlock(&shard->mu); + return materialize(s); + } + } + } + + /* not found: create a new string */ + /* string data goes after the internal_string header */ + s = static_cast( + gpr_malloc(sizeof(*s) + GRPC_SLICE_LENGTH(slice))); + gpr_atm_rel_store(&s->refcnt, 1); + s->length = GRPC_SLICE_LENGTH(slice); + s->hash = hash; + s->base.vtable = &interned_slice_vtable; + s->base.sub_refcount = &s->sub; + s->sub.vtable = &interned_slice_sub_vtable; + s->sub.sub_refcount = &s->sub; + s->bucket_next = shard->strs[idx]; + shard->strs[idx] = s; + memcpy(s + 1, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice)); + + shard->count++; + + if (shard->count > shard->capacity * 2) { + grow_shard(shard); + } + + gpr_mu_unlock(&shard->mu); + + return materialize(s); +} + +void grpc_test_only_set_slice_hash_seed(uint32_t seed) { + g_hash_seed = seed; + g_forced_hash_seed = 1; +} + +void grpc_slice_intern_init(void) { + if (!g_forced_hash_seed) { + g_hash_seed = static_cast(gpr_now(GPR_CLOCK_REALTIME).tv_nsec); + } + for (size_t i = 0; i < SHARD_COUNT; i++) { + slice_shard* shard = &g_shards[i]; + gpr_mu_init(&shard->mu); + shard->count = 0; + shard->capacity = INITIAL_SHARD_CAPACITY; + shard->strs = static_cast( + gpr_zalloc(sizeof(*shard->strs) * shard->capacity)); + } + for (size_t i = 0; i < GPR_ARRAY_SIZE(static_metadata_hash); i++) { + static_metadata_hash[i].hash = 0; + static_metadata_hash[i].idx = GRPC_STATIC_MDSTR_COUNT; + } + max_static_metadata_hash_probe = 0; + for (size_t i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) { + static_metadata_hash_values[i] = + grpc_slice_default_hash_impl(grpc_static_slice_table[i]); + for (size_t j = 0; j < GPR_ARRAY_SIZE(static_metadata_hash); j++) { + size_t slot = (static_metadata_hash_values[i] + j) % + GPR_ARRAY_SIZE(static_metadata_hash); + if (static_metadata_hash[slot].idx == GRPC_STATIC_MDSTR_COUNT) { + static_metadata_hash[slot].hash = static_metadata_hash_values[i]; + static_metadata_hash[slot].idx = static_cast(i); + if (j > max_static_metadata_hash_probe) { + max_static_metadata_hash_probe = static_cast(j); + } + break; + } + } + } +} + +void grpc_slice_intern_shutdown(void) { + for (size_t i = 0; i < SHARD_COUNT; i++) { + slice_shard* shard = &g_shards[i]; + gpr_mu_destroy(&shard->mu); + /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ + if (shard->count != 0) { + gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked", + shard->count); + for (size_t j = 0; j < shard->capacity; j++) { + for (interned_slice_refcount* s = shard->strs[j]; s; + s = s->bucket_next) { + char* text = + grpc_dump_slice(materialize(s), GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "LEAKED: %s", text); + gpr_free(text); + } + } + if (grpc_iomgr_abort_on_leaks()) { + abort(); + } + } + gpr_free(shard->strs); + } +} diff --git a/third_party/grpc/src/core/lib/slice/slice_internal.h b/third_party/grpc/src/core/lib/slice/slice_internal.h new file mode 100644 index 00000000000000..5b05951522f3a5 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_internal.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H +#define GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H + +#include + +#include +#include + +grpc_slice grpc_slice_ref_internal(grpc_slice slice); +void grpc_slice_unref_internal(grpc_slice slice); +void grpc_slice_buffer_reset_and_unref_internal(grpc_slice_buffer* sb); +void grpc_slice_buffer_partial_unref_internal(grpc_slice_buffer* sb, + size_t idx); +void grpc_slice_buffer_destroy_internal(grpc_slice_buffer* sb); + +/* Check if a slice is interned */ +bool grpc_slice_is_interned(grpc_slice slice); + +void grpc_slice_intern_init(void); +void grpc_slice_intern_shutdown(void); +void grpc_test_only_set_slice_hash_seed(uint32_t key); +// if slice matches a static slice, returns the static slice +// otherwise returns the passed in slice (without reffing it) +// used for surface boundaries where we might receive an un-interned static +// string +grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice, + bool* returned_slice_is_different); +uint32_t grpc_static_slice_hash(grpc_slice s); +int grpc_static_slice_eq(grpc_slice a, grpc_slice b); + +// Returns the memory used by this slice, not counting the slice structure +// itself. This means that inlined and slices from static strings will return +// 0. All other slices will return the size of the allocated chars. +size_t grpc_slice_memory_usage(grpc_slice s); + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H */ diff --git a/third_party/grpc/src/core/lib/slice/slice_string_helpers.cc b/third_party/grpc/src/core/lib/slice/slice_string_helpers.cc new file mode 100644 index 00000000000000..6af9c33eb5609b --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_string_helpers.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/slice/slice_string_helpers.h" + +#include + +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/slice/slice_internal.h" + +char* grpc_dump_slice(grpc_slice s, uint32_t flags) { + return gpr_dump(reinterpret_cast GRPC_SLICE_START_PTR(s), + GRPC_SLICE_LENGTH(s), flags); +} + +/** 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 grpc_slice str, const char* sep, + const size_t read_offset, size_t* begin, + size_t* end) { + size_t i; + const uint8_t* str_ptr = GRPC_SLICE_START_PTR(str) + read_offset; + const size_t str_len = GRPC_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; +} + +static void skip_leading_trailing_spaces(const uint8_t* str_buffer, + size_t* begin, size_t* end) { + while (*begin < *end && str_buffer[*begin] == ' ') { + (*begin)++; + } + while (*begin < *end && str_buffer[*end - 1] == ' ') { + (*end)--; + } +} + +static void grpc_slice_split_inner(grpc_slice str, const char* sep, + grpc_slice_buffer* dst, bool no_space) { + const size_t sep_len = strlen(sep); + size_t begin, end; + const uint8_t* str_buffer = GRPC_SLICE_START_PTR(str); + size_t sep_pos; + + GPR_ASSERT(sep_len > 0); + + if (slice_find_separator_offset(str, sep, 0, &begin, &end) != 0) { + do { + sep_pos = end; + if (no_space) { + skip_leading_trailing_spaces(str_buffer, &begin, &end); + } + grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end)); + } while (slice_find_separator_offset(str, sep, sep_pos + sep_len, &begin, + &end) != 0); + begin = sep_pos + sep_len; + end = GRPC_SLICE_LENGTH(str); + if (no_space) { + skip_leading_trailing_spaces(str_buffer, &begin, &end); + } + grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end)); + } else { /* no sep found, add whole input */ + begin = 0; + end = GRPC_SLICE_LENGTH(str); + if (no_space) { + skip_leading_trailing_spaces(str_buffer, &begin, &end); + } + grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end)); + } +} + +void grpc_slice_split(grpc_slice str, const char* sep, grpc_slice_buffer* dst) { + grpc_slice_split_inner(str, sep, dst, false); +} + +void grpc_slice_split_without_space(grpc_slice str, const char* sep, + grpc_slice_buffer* dst) { + grpc_slice_split_inner(str, sep, dst, true); +} + +bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t* result) { + return gpr_parse_bytes_to_uint32( + reinterpret_cast GRPC_SLICE_START_PTR(str), + GRPC_SLICE_LENGTH(str), result) != 0; +} diff --git a/third_party/grpc/src/core/lib/slice/slice_string_helpers.h b/third_party/grpc/src/core/lib/slice/slice_string_helpers.h new file mode 100644 index 00000000000000..976f72411a8aa2 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_string_helpers.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H +#define GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H + +#include + +#include +#include + +#include +#include + +#include "src/core/lib/gpr/string.h" + +/* Calls gpr_dump on a slice. */ +char* grpc_dump_slice(grpc_slice slice, uint32_t flags); + +/** Split \a str by the separator \a sep. Results are stored in \a dst, which + * should be a properly initialized instance. */ +void grpc_slice_split(grpc_slice str, const char* sep, grpc_slice_buffer* dst); + +/** Split \a str by the separator \a sep and remove the leading and trailing + * spaces of each resulting token. Results are stored in \a dst, which should be + * a properly initialized instance. */ +void grpc_slice_split_without_space(grpc_slice str, const char* sep, + grpc_slice_buffer* dst); + +bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t* result); + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_STRING_HELPERS_H */ diff --git a/third_party/grpc/src/core/lib/slice/slice_traits.h b/third_party/grpc/src/core/lib/slice/slice_traits.h new file mode 100644 index 00000000000000..ee01916525e340 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_traits.h @@ -0,0 +1,31 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H +#define GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H + +#include + +#include +#include + +bool grpc_slice_is_legal_header(grpc_slice s); +bool grpc_slice_is_legal_nonbin_header(grpc_slice s); +bool grpc_slice_is_bin_suffixed(grpc_slice s); + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H */ diff --git a/third_party/grpc/src/core/lib/slice/slice_weak_hash_table.h b/third_party/grpc/src/core/lib/slice/slice_weak_hash_table.h new file mode 100644 index 00000000000000..dc3ccc5dadd2d8 --- /dev/null +++ b/third_party/grpc/src/core/lib/slice/slice_weak_hash_table.h @@ -0,0 +1,109 @@ +/* + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H +#define GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H + +#include + +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/slice/slice_internal.h" + +/// Weak hash table implementation. +/// +/// This entries in this table are weak: an entry may be removed at any time due +/// to a number of reasons: memory pressure, hash collisions, etc. +/// +/// The keys are \a grpc_slice objects. The values are of arbitrary type. +/// +/// This class is thread unsafe. It's the caller's responsibility to ensure +/// proper locking when accessing its methods. + +namespace grpc_core { + +template +class SliceWeakHashTable : public RefCounted> { + public: + /// Creates a new table of at most \a size entries. + static RefCountedPtr Create() { + return MakeRefCounted>(); + } + + /// Add a mapping from \a key to \a value, taking ownership of \a key. This + /// operation will always succeed. It may discard older entries. + void Add(grpc_slice key, T value) { + const size_t idx = grpc_slice_hash(key) % Size; + entries_[idx].Set(key, std::move(value)); + return; + } + + /// Returns the value from the table associated with / \a key or null if not + /// found. + const T* Get(const grpc_slice key) const { + const size_t idx = grpc_slice_hash(key) % Size; + const auto& entry = entries_[idx]; + return grpc_slice_eq(entry.key(), key) ? entry.value() : nullptr; + } + + private: + // So New() can call our private ctor. + template + friend T2* New(Args&&... args); + + // So Delete() can call our private dtor. + template + friend void Delete(T2*); + + SliceWeakHashTable() = default; + ~SliceWeakHashTable() = default; + + /// The type of the table "rows". + class Entry { + public: + Entry() = default; + ~Entry() { + if (is_set_) grpc_slice_unref_internal(key_); + } + grpc_slice key() const { return key_; } + + /// Return the entry's value, or null if unset. + const T* value() const { + if (!is_set_) return nullptr; + return &value_; + } + + /// Set the \a key and \a value (which is moved) for the entry. + void Set(grpc_slice key, T&& value) { + if (is_set_) grpc_slice_unref_internal(key_); + key_ = key; + value_ = std::move(value); + is_set_ = true; + } + + private: + grpc_slice key_; + T value_; + bool is_set_ = false; + }; + + Entry entries_[Size]; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H */ diff --git a/third_party/grpc/src/core/lib/surface/README.md b/third_party/grpc/src/core/lib/surface/README.md new file mode 100644 index 00000000000000..74cbd711315073 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/README.md @@ -0,0 +1,4 @@ +# Surface + +Surface provides the bulk of the gRPC Core public API, and translates it into +calls against core components. diff --git a/third_party/grpc/src/core/lib/surface/api_trace.cc b/third_party/grpc/src/core/lib/surface/api_trace.cc new file mode 100644 index 00000000000000..bab5a7910c0ef8 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/api_trace.cc @@ -0,0 +1,24 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/surface/api_trace.h" + +grpc_core::TraceFlag grpc_api_trace(false, "api"); diff --git a/third_party/grpc/src/core/lib/surface/api_trace.h b/third_party/grpc/src/core/lib/surface/api_trace.h new file mode 100644 index 00000000000000..72ed830554fa51 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/api_trace.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_API_TRACE_H +#define GRPC_CORE_LIB_SURFACE_API_TRACE_H + +#include + +#include +#include "src/core/lib/debug/trace.h" + +extern grpc_core::TraceFlag grpc_api_trace; + +/* Provide unwrapping macros because we're in C89 and variadic macros weren't + introduced until C99... */ +#define GRPC_API_TRACE_UNWRAP0() +#define GRPC_API_TRACE_UNWRAP1(a) , a +#define GRPC_API_TRACE_UNWRAP2(a, b) , a, b +#define GRPC_API_TRACE_UNWRAP3(a, b, c) , a, b, c +#define GRPC_API_TRACE_UNWRAP4(a, b, c, d) , a, b, c, d +#define GRPC_API_TRACE_UNWRAP5(a, b, c, d, e) , a, b, c, d, e +#define GRPC_API_TRACE_UNWRAP6(a, b, c, d, e, f) , a, b, c, d, e, f +#define GRPC_API_TRACE_UNWRAP7(a, b, c, d, e, f, g) , a, b, c, d, e, f, g +#define GRPC_API_TRACE_UNWRAP8(a, b, c, d, e, f, g, h) , a, b, c, d, e, f, g, h +#define GRPC_API_TRACE_UNWRAP9(a, b, c, d, e, f, g, h, i) \ + , a, b, c, d, e, f, g, h, i +#define GRPC_API_TRACE_UNWRAP10(a, b, c, d, e, f, g, h, i, j) \ + , a, b, c, d, e, f, g, h, i, j + +/* Due to the limitations of C89's preprocessor, the arity of the var-arg list + 'nargs' must be specified. */ +#define GRPC_API_TRACE(fmt, nargs, args) \ + if (grpc_api_trace.enabled()) { \ + gpr_log(GPR_INFO, fmt GRPC_API_TRACE_UNWRAP##nargs args); \ + } + +#endif /* GRPC_CORE_LIB_SURFACE_API_TRACE_H */ diff --git a/third_party/grpc/src/core/lib/surface/byte_buffer.cc b/third_party/grpc/src/core/lib/surface/byte_buffer.cc new file mode 100644 index 00000000000000..6246796e46c93f --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/byte_buffer.cc @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" + +grpc_byte_buffer* grpc_raw_byte_buffer_create(grpc_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( + grpc_slice* slices, size_t nslices, + grpc_compression_algorithm compression) { + size_t i; + grpc_byte_buffer* bb = + static_cast(gpr_malloc(sizeof(grpc_byte_buffer))); + bb->type = GRPC_BB_RAW; + bb->data.raw.compression = compression; + grpc_slice_buffer_init(&bb->data.raw.slice_buffer); + for (i = 0; i < nslices; i++) { + grpc_slice_ref_internal(slices[i]); + grpc_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 = + static_cast(gpr_malloc(sizeof(grpc_byte_buffer))); + grpc_slice slice; + bb->type = GRPC_BB_RAW; + bb->data.raw.compression = GRPC_COMPRESS_NONE; + grpc_slice_buffer_init(&bb->data.raw.slice_buffer); + + while (grpc_byte_buffer_reader_next(reader, &slice)) { + grpc_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_compressed_byte_buffer_create( + bb->data.raw.slice_buffer.slices, bb->data.raw.slice_buffer.count, + bb->data.raw.compression); + } + GPR_UNREACHABLE_CODE(return nullptr); +} + +void grpc_byte_buffer_destroy(grpc_byte_buffer* bb) { + if (!bb) return; + grpc_core::ExecCtx exec_ctx; + switch (bb->type) { + case GRPC_BB_RAW: + grpc_slice_buffer_destroy_internal(&bb->data.raw.slice_buffer); + break; + } + gpr_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_UNREACHABLE_CODE(return 0); +} diff --git a/third_party/grpc/src/core/lib/surface/byte_buffer_reader.cc b/third_party/grpc/src/core/lib/surface/byte_buffer_reader.cc new file mode 100644 index 00000000000000..1debc98ea0c4d6 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/byte_buffer_reader.cc @@ -0,0 +1,129 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/compression/message_compress.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.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 */; +} + +int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader, + grpc_byte_buffer* buffer) { + grpc_core::ExecCtx exec_ctx; + grpc_slice_buffer decompressed_slices_buffer; + reader->buffer_in = buffer; + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: + grpc_slice_buffer_init(&decompressed_slices_buffer); + if (is_compressed(reader->buffer_in)) { + if (grpc_msg_decompress( + + grpc_compression_algorithm_to_message_compression_algorithm( + reader->buffer_in->data.raw.compression), + &reader->buffer_in->data.raw.slice_buffer, + &decompressed_slices_buffer) == 0) { + gpr_log(GPR_ERROR, + "Unexpected error decompressing data for algorithm with enum " + "value '%d'.", + reader->buffer_in->data.raw.compression); + memset(reader, 0, sizeof(*reader)); + return 0; + } else { /* all fine */ + reader->buffer_out = + grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices, + decompressed_slices_buffer.count); + } + grpc_slice_buffer_destroy_internal(&decompressed_slices_buffer); + } else { /* not compressed, use the input buffer as output */ + reader->buffer_out = reader->buffer_in; + } + reader->current.index = 0; + break; + } + + return 1; +} + +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, + grpc_slice* slice) { + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: { + grpc_slice_buffer* slice_buffer; + slice_buffer = &reader->buffer_out->data.raw.slice_buffer; + if (reader->current.index < slice_buffer->count) { + *slice = grpc_slice_ref_internal( + slice_buffer->slices[reader->current.index]); + reader->current.index += 1; + return 1; + } + break; + } + } + return 0; +} + +grpc_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader* reader) { + grpc_slice in_slice; + size_t bytes_read = 0; + const size_t input_size = grpc_byte_buffer_length(reader->buffer_out); + grpc_slice out_slice = GRPC_SLICE_MALLOC(input_size); + uint8_t* const outbuf = GRPC_SLICE_START_PTR(out_slice); /* just an alias */ + + grpc_core::ExecCtx exec_ctx; + while (grpc_byte_buffer_reader_next(reader, &in_slice) != 0) { + const size_t slice_length = GRPC_SLICE_LENGTH(in_slice); + memcpy(&(outbuf[bytes_read]), GRPC_SLICE_START_PTR(in_slice), slice_length); + bytes_read += slice_length; + grpc_slice_unref_internal(in_slice); + GPR_ASSERT(bytes_read <= input_size); + } + + return out_slice; +} diff --git a/third_party/grpc/src/core/lib/surface/call.cc b/third_party/grpc/src/core/lib/surface/call.cc new file mode 100644 index 00000000000000..89b3f77822c41d --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/call.cc @@ -0,0 +1,1979 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/compression/algorithm_metadata.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/alloc.h" +#include "src/core/lib/gpr/arena.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/call_test_only.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/server.h" +#include "src/core/lib/surface/validate_metadata.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_metadata.h" +#include "src/core/lib/transport/transport.h" + +/** The maximum number of concurrent batches 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_BATCHES 6 + +#define MAX_SEND_EXTRA_METADATA_COUNT 3 + +// Used to create arena for the first call. +#define ESTIMATED_MDELEM_COUNT 16 + +struct batch_control { + batch_control() { gpr_ref_init(&steps_to_complete, 0); } + + grpc_call* call = nullptr; + grpc_transport_stream_op_batch op; + /* Share memory for cq_completion and notify_tag as they are never needed + simultaneously. Each byte used in this data structure count as six bytes + per call, so any savings we can make are worthwhile, + + We use notify_tag to determine whether or not to send notification to the + completion queue. Once we've made that determination, we can reuse the + memory for cq_completion. */ + union { + grpc_cq_completion cq_completion; + struct { + /* Any given op indicates completion by either (a) calling a closure or + (b) sending a notification on the call's completion queue. If + \a is_closure is true, \a tag indicates a closure to be invoked; + otherwise, \a tag indicates the tag to be used in the notification to + be sent to the completion queue. */ + void* tag; + bool is_closure; + } notify_tag; + } completion_data; + grpc_closure start_batch; + grpc_closure finish_batch; + gpr_refcount steps_to_complete; + gpr_atm batch_error = reinterpret_cast(GRPC_ERROR_NONE); +}; + +struct parent_call { + parent_call() { gpr_mu_init(&child_list_mu); } + ~parent_call() { gpr_mu_destroy(&child_list_mu); } + + gpr_mu child_list_mu; + grpc_call* first_child = nullptr; +}; + +struct child_call { + child_call(grpc_call* parent) : parent(parent) {} + grpc_call* parent; + /** siblings: children of the same parent form a list, and this list is + protected under + parent->mu */ + grpc_call* sibling_next = nullptr; + grpc_call* sibling_prev = nullptr; +}; + +#define RECV_NONE ((gpr_atm)0) +#define RECV_INITIAL_METADATA_FIRST ((gpr_atm)1) + +struct grpc_call { + grpc_call(gpr_arena* arena, const grpc_call_create_args& args) + : arena(arena), + cq(args.cq), + channel(args.channel), + is_client(args.server_transport_data == nullptr), + stream_op_payload(context) { + gpr_ref_init(&ext_ref, 1); + grpc_call_combiner_init(&call_combiner); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + metadata_batch[i][j].deadline = GRPC_MILLIS_INF_FUTURE; + } + } + } + + ~grpc_call() { + gpr_free(static_cast(const_cast(final_info.error_string))); + grpc_call_combiner_destroy(&call_combiner); + } + + gpr_refcount ext_ref; + gpr_arena* arena; + grpc_call_combiner call_combiner; + grpc_completion_queue* cq; + grpc_polling_entity pollent; + grpc_channel* channel; + gpr_timespec start_time = gpr_now(GPR_CLOCK_MONOTONIC); + /* parent_call* */ gpr_atm parent_call_atm = 0; + child_call* child = nullptr; + + /* client or server call */ + bool is_client; + /** has grpc_call_unref been called */ + bool destroy_called = false; + /** flag indicating that cancellation is inherited */ + bool cancellation_is_inherited = false; + /** which ops are in-flight */ + bool sent_initial_metadata = false; + bool sending_message = false; + bool sent_final_op = false; + bool received_initial_metadata = false; + bool receiving_message = false; + bool requested_final_op = false; + gpr_atm any_ops_sent_atm = 0; + gpr_atm received_final_op_atm = 0; + + batch_control* active_batches[MAX_CONCURRENT_BATCHES] = {}; + grpc_transport_stream_op_batch_payload stream_op_payload; + + /* first idx: is_receiving, second idx: is_trailing */ + grpc_metadata_batch metadata_batch[2][2] = {}; + + /* 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] = {}; + + grpc_metadata compression_md; + + // A char* indicating the peer name. + gpr_atm peer_string = 0; + + /* Call data useful used for reporting. Only valid after the call has + * completed */ + grpc_call_final_info final_info; + + /* Compression algorithm for *incoming* data */ + grpc_message_compression_algorithm incoming_message_compression_algorithm = + GRPC_MESSAGE_COMPRESS_NONE; + /* Stream compression algorithm for *incoming* data */ + grpc_stream_compression_algorithm incoming_stream_compression_algorithm = + GRPC_STREAM_COMPRESS_NONE; + /* Supported encodings (compression algorithms), a bitset. + * Always support no compression. */ + uint32_t encodings_accepted_by_peer = 1 << GRPC_MESSAGE_COMPRESS_NONE; + /* Supported stream encodings (stream compression algorithms), a bitset */ + uint32_t stream_encodings_accepted_by_peer = 0; + + /* Contexts for various subsystems (security, tracing, ...). */ + grpc_call_context_element context[GRPC_CONTEXT_COUNT] = {}; + + /* for the client, extra metadata is initial metadata; for the + server, it's trailing metadata */ + grpc_linked_mdelem send_extra_metadata[MAX_SEND_EXTRA_METADATA_COUNT]; + int send_extra_metadata_count; + grpc_millis send_deadline; + + grpc_core::ManualConstructor sending_stream; + + grpc_core::OrphanablePtr receiving_stream; + grpc_byte_buffer** receiving_buffer = nullptr; + grpc_slice receiving_slice = grpc_empty_slice(); + grpc_closure receiving_slice_ready; + grpc_closure receiving_stream_ready; + grpc_closure receiving_initial_metadata_ready; + grpc_closure receiving_trailing_metadata_ready; + uint32_t test_only_last_message_flags = 0; + gpr_atm cancelled = 0; + + grpc_closure release_call; + + union { + struct { + grpc_status_code* status; + grpc_slice* status_details; + const char** error_string; + } client; + struct { + int* cancelled; + // backpointer to owning server if this is a server side call. + grpc_server* server; + } server; + } final_op; + gpr_atm status_error = 0; + + /* recv_state can contain one of the following values: + RECV_NONE : : no initial metadata and messages received + RECV_INITIAL_METADATA_FIRST : received initial metadata first + a batch_control* : received messages first + + +------1------RECV_NONE------3-----+ + | | + | | + v v + RECV_INITIAL_METADATA_FIRST receiving_stream_ready_bctlp + | ^ | ^ + | | | | + +-----2-----+ +-----4-----+ + + For 1, 4: See receiving_initial_metadata_ready() function + For 2, 3: See receiving_stream_ready() function */ + gpr_atm recv_state = 0; +}; + +grpc_core::TraceFlag grpc_call_error_trace(false, "call_error"); +grpc_core::TraceFlag grpc_compression_trace(false, "compression"); + +#define CALL_STACK_FROM_CALL(call) \ + (grpc_call_stack*)((char*)(call) + \ + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call))) +#define CALL_FROM_CALL_STACK(call_stack) \ + (grpc_call*)(((char*)(call_stack)) - \ + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call))) + +#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 execute_batch(grpc_call* call, grpc_transport_stream_op_batch* op, + grpc_closure* start_batch_closure); + +static void cancel_with_status(grpc_call* c, grpc_status_code status, + const char* description); +static void cancel_with_error(grpc_call* c, grpc_error* error); +static void destroy_call(void* call_stack, grpc_error* error); +static void receiving_slice_ready(void* bctlp, grpc_error* error); +static void set_final_status(grpc_call* call, grpc_error* error); +static void process_data_after_md(batch_control* bctl); +static void post_batch_completion(batch_control* bctl); + +static void add_init_error(grpc_error** composite, grpc_error* new_err) { + if (new_err == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) + *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Call creation failed"); + *composite = grpc_error_add_child(*composite, new_err); +} + +void* grpc_call_arena_alloc(grpc_call* call, size_t size) { + return gpr_arena_alloc(call->arena, size); +} + +static parent_call* get_or_create_parent_call(grpc_call* call) { + parent_call* p = (parent_call*)gpr_atm_acq_load(&call->parent_call_atm); + if (p == nullptr) { + p = new (gpr_arena_alloc(call->arena, sizeof(*p))) parent_call(); + if (!gpr_atm_rel_cas(&call->parent_call_atm, (gpr_atm) nullptr, + (gpr_atm)p)) { + p->~parent_call(); + p = (parent_call*)gpr_atm_acq_load(&call->parent_call_atm); + } + } + return p; +} + +static parent_call* get_parent_call(grpc_call* call) { + return (parent_call*)gpr_atm_acq_load(&call->parent_call_atm); +} + +size_t grpc_call_get_initial_size_estimate() { + return sizeof(grpc_call) + sizeof(batch_control) * MAX_CONCURRENT_BATCHES + + sizeof(grpc_linked_mdelem) * ESTIMATED_MDELEM_COUNT; +} + +grpc_error* grpc_call_create(const grpc_call_create_args* args, + grpc_call** out_call) { + GPR_TIMER_SCOPE("grpc_call_create", 0); + + GRPC_CHANNEL_INTERNAL_REF(args->channel, "call"); + + grpc_error* error = GRPC_ERROR_NONE; + grpc_channel_stack* channel_stack = + grpc_channel_get_channel_stack(args->channel); + grpc_call* call; + size_t initial_size = grpc_channel_get_call_size_estimate(args->channel); + GRPC_STATS_INC_CALL_INITIAL_SIZE(initial_size); + gpr_arena* arena = gpr_arena_create(initial_size); + call = new (gpr_arena_alloc( + arena, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call)) + + channel_stack->call_stack_size)) grpc_call(arena, *args); + *out_call = call; + grpc_slice path = grpc_empty_slice(); + if (call->is_client) { + call->final_op.client.status_details = nullptr; + call->final_op.client.status = nullptr; + call->final_op.client.error_string = nullptr; + GRPC_STATS_INC_CLIENT_CALLS_CREATED(); + GPR_ASSERT(args->add_initial_metadata_count < + MAX_SEND_EXTRA_METADATA_COUNT); + for (size_t i = 0; i < args->add_initial_metadata_count; i++) { + call->send_extra_metadata[i].md = args->add_initial_metadata[i]; + if (grpc_slice_eq(GRPC_MDKEY(args->add_initial_metadata[i]), + GRPC_MDSTR_PATH)) { + path = grpc_slice_ref_internal( + GRPC_MDVALUE(args->add_initial_metadata[i])); + } + } + call->send_extra_metadata_count = + static_cast(args->add_initial_metadata_count); + } else { + GRPC_STATS_INC_SERVER_CALLS_CREATED(); + call->final_op.server.cancelled = nullptr; + call->final_op.server.server = args->server; + GPR_ASSERT(args->add_initial_metadata_count == 0); + call->send_extra_metadata_count = 0; + } + + grpc_millis send_deadline = args->send_deadline; + bool immediately_cancel = false; + + if (args->parent != nullptr) { + call->child = new (gpr_arena_alloc(arena, sizeof(child_call))) + child_call(args->parent); + + GRPC_CALL_INTERNAL_REF(args->parent, "child"); + GPR_ASSERT(call->is_client); + GPR_ASSERT(!args->parent->is_client); + + if (args->propagation_mask & GRPC_PROPAGATE_DEADLINE) { + send_deadline = GPR_MIN(send_deadline, args->parent->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 (args->propagation_mask & GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT) { + if (0 == (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)) { + add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Census tracing propagation requested " + "without Census context propagation")); + } + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, + args->parent->context[GRPC_CONTEXT_TRACING].value, + nullptr); + } else if (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT) { + add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Census context propagation requested " + "without Census tracing propagation")); + } + if (args->propagation_mask & GRPC_PROPAGATE_CANCELLATION) { + call->cancellation_is_inherited = 1; + if (gpr_atm_acq_load(&args->parent->received_final_op_atm)) { + immediately_cancel = true; + } + } + } + call->send_deadline = send_deadline; + /* initial refcount dropped by grpc_call_unref */ + grpc_call_element_args call_args = {CALL_STACK_FROM_CALL(call), + args->server_transport_data, + call->context, + path, + call->start_time, + send_deadline, + call->arena, + &call->call_combiner}; + add_init_error(&error, grpc_call_stack_init(channel_stack, 1, destroy_call, + call, &call_args)); + // Publish this call to parent only after the call stack has been initialized. + if (args->parent != nullptr) { + child_call* cc = call->child; + parent_call* pc = get_or_create_parent_call(args->parent); + gpr_mu_lock(&pc->child_list_mu); + if (pc->first_child == nullptr) { + pc->first_child = call; + cc->sibling_next = cc->sibling_prev = call; + } else { + cc->sibling_next = pc->first_child; + cc->sibling_prev = pc->first_child->child->sibling_prev; + cc->sibling_next->child->sibling_prev = + cc->sibling_prev->child->sibling_next = call; + } + gpr_mu_unlock(&pc->child_list_mu); + } + + if (error != GRPC_ERROR_NONE) { + cancel_with_error(call, GRPC_ERROR_REF(error)); + } + if (immediately_cancel) { + cancel_with_error(call, GRPC_ERROR_CANCELLED); + } + if (args->cq != nullptr) { + GPR_ASSERT(args->pollset_set_alternative == nullptr && + "Only one of 'cq' and 'pollset_set_alternative' should be " + "non-nullptr."); + GRPC_CQ_INTERNAL_REF(args->cq, "bind"); + call->pollent = + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(args->cq)); + } + if (args->pollset_set_alternative != nullptr) { + call->pollent = grpc_polling_entity_create_from_pollset_set( + args->pollset_set_alternative); + } + if (!grpc_polling_entity_is_empty(&call->pollent)) { + grpc_call_stack_set_pollset_or_pollset_set(CALL_STACK_FROM_CALL(call), + &call->pollent); + } + + if (call->is_client) { + grpc_core::channelz::ChannelNode* channelz_channel = + grpc_channel_get_channelz_node(call->channel); + if (channelz_channel != nullptr) { + channelz_channel->RecordCallStarted(); + } + } else { + grpc_core::channelz::ServerNode* channelz_server = + grpc_server_get_channelz_node(call->final_op.server.server); + if (channelz_server != nullptr) { + channelz_server->RecordCallStarted(); + } + } + + grpc_slice_unref_internal(path); + + return error; +} + +void grpc_call_set_completion_queue(grpc_call* call, + grpc_completion_queue* cq) { + GPR_ASSERT(cq); + + if (grpc_polling_entity_pollset_set(&call->pollent) != nullptr) { + gpr_log(GPR_ERROR, "A pollset_set is already registered for this call."); + abort(); + } + call->cq = cq; + GRPC_CQ_INTERNAL_REF(cq, "bind"); + call->pollent = grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)); + grpc_call_stack_set_pollset_or_pollset_set(CALL_STACK_FROM_CALL(call), + &call->pollent); +} + +#ifndef NDEBUG +#define REF_REASON reason +#define REF_ARG , const char* reason +#else +#define REF_REASON "" +#define REF_ARG +#endif +void grpc_call_internal_ref(grpc_call* c REF_ARG) { + GRPC_CALL_STACK_REF(CALL_STACK_FROM_CALL(c), REF_REASON); +} +void grpc_call_internal_unref(grpc_call* c REF_ARG) { + GRPC_CALL_STACK_UNREF(CALL_STACK_FROM_CALL(c), REF_REASON); +} + +static void release_call(void* call, grpc_error* error) { + grpc_call* c = static_cast(call); + grpc_channel* channel = c->channel; + gpr_arena* arena = c->arena; + c->~grpc_call(); + grpc_channel_update_call_size_estimate(channel, gpr_arena_destroy(arena)); + GRPC_CHANNEL_INTERNAL_UNREF(channel, "call"); +} + +static void destroy_call(void* call, grpc_error* error) { + GPR_TIMER_SCOPE("destroy_call", 0); + size_t i; + int ii; + grpc_call* c = static_cast(call); + for (i = 0; i < 2; i++) { + grpc_metadata_batch_destroy( + &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]); + } + c->receiving_stream.reset(); + parent_call* pc = get_parent_call(c); + if (pc != nullptr) { + pc->~parent_call(); + } + for (ii = 0; ii < c->send_extra_metadata_count; ii++) { + GRPC_MDELEM_UNREF(c->send_extra_metadata[ii].md); + } + for (i = 0; i < GRPC_CONTEXT_COUNT; i++) { + if (c->context[i].destroy) { + c->context[i].destroy(c->context[i].value); + } + } + if (c->cq) { + GRPC_CQ_INTERNAL_UNREF(c->cq, "bind"); + } + + grpc_error* status_error = + reinterpret_cast(gpr_atm_acq_load(&c->status_error)); + grpc_error_get_status(status_error, c->send_deadline, + &c->final_info.final_status, nullptr, nullptr, + &(c->final_info.error_string)); + GRPC_ERROR_UNREF(status_error); + c->final_info.stats.latency = + gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time); + + grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c), &c->final_info, + GRPC_CLOSURE_INIT(&c->release_call, release_call, c, + grpc_schedule_on_exec_ctx)); +} + +void grpc_call_ref(grpc_call* c) { gpr_ref(&c->ext_ref); } + +void grpc_call_unref(grpc_call* c) { + if (!gpr_unref(&c->ext_ref)) return; + + GPR_TIMER_SCOPE("grpc_call_unref", 0); + + child_call* cc = c->child; + grpc_core::ExecCtx exec_ctx; + + GRPC_API_TRACE("grpc_call_unref(c=%p)", 1, (c)); + + if (cc) { + parent_call* pc = get_parent_call(cc->parent); + gpr_mu_lock(&pc->child_list_mu); + if (c == pc->first_child) { + pc->first_child = cc->sibling_next; + if (c == pc->first_child) { + pc->first_child = nullptr; + } + } + cc->sibling_prev->child->sibling_next = cc->sibling_next; + cc->sibling_next->child->sibling_prev = cc->sibling_prev; + gpr_mu_unlock(&pc->child_list_mu); + GRPC_CALL_INTERNAL_UNREF(cc->parent, "child"); + } + + GPR_ASSERT(!c->destroy_called); + c->destroy_called = 1; + bool cancel = gpr_atm_acq_load(&c->any_ops_sent_atm) != 0 && + gpr_atm_acq_load(&c->received_final_op_atm) == 0; + if (cancel) { + cancel_with_error(c, GRPC_ERROR_CANCELLED); + } else { + // Unset the call combiner cancellation closure. This has the + // effect of scheduling the previously set cancellation closure, if + // any, so that it can release any internal references it may be + // holding to the call stack. Also flush the closures on exec_ctx so that + // filters that schedule cancel notification closures on exec_ctx do not + // need to take a ref of the call stack to guarantee closure liveness. + grpc_call_combiner_set_notify_on_cancel(&c->call_combiner, nullptr); + grpc_core::ExecCtx::Get()->Flush(); + } + GRPC_CALL_INTERNAL_UNREF(c, "destroy"); +} + +grpc_call_error grpc_call_cancel(grpc_call* call, void* reserved) { + GRPC_API_TRACE("grpc_call_cancel(call=%p, reserved=%p)", 2, (call, reserved)); + GPR_ASSERT(!reserved); + grpc_core::ExecCtx exec_ctx; + cancel_with_error(call, GRPC_ERROR_CANCELLED); + return GRPC_CALL_OK; +} + +// This is called via the call combiner to start sending a batch down +// the filter stack. +static void execute_batch_in_call_combiner(void* arg, grpc_error* ignored) { + GPR_TIMER_SCOPE("execute_batch_in_call_combiner", 0); + grpc_transport_stream_op_batch* batch = + static_cast(arg); + grpc_call* call = static_cast(batch->handler_private.extra_arg); + grpc_call_element* elem = CALL_ELEM_FROM_CALL(call, 0); + GRPC_CALL_LOG_OP(GPR_INFO, elem, batch); + elem->filter->start_transport_stream_op_batch(elem, batch); +} + +// start_batch_closure points to a caller-allocated closure to be used +// for entering the call combiner. +static void execute_batch(grpc_call* call, + grpc_transport_stream_op_batch* batch, + grpc_closure* start_batch_closure) { + batch->handler_private.extra_arg = call; + GRPC_CLOSURE_INIT(start_batch_closure, execute_batch_in_call_combiner, batch, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(&call->call_combiner, start_batch_closure, + GRPC_ERROR_NONE, "executing batch"); +} + +char* grpc_call_get_peer(grpc_call* call) { + char* peer_string = (char*)gpr_atm_acq_load(&call->peer_string); + if (peer_string != nullptr) return gpr_strdup(peer_string); + peer_string = grpc_channel_get_target(call->channel); + if (peer_string != nullptr) return peer_string; + return gpr_strdup("unknown"); +} + +grpc_call* grpc_call_from_top_element(grpc_call_element* elem) { + return CALL_FROM_TOP_ELEM(elem); +} + +/******************************************************************************* + * CANCELLATION + */ + +grpc_call_error grpc_call_cancel_with_status(grpc_call* c, + grpc_status_code status, + const char* description, + void* reserved) { + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE( + "grpc_call_cancel_with_status(" + "c=%p, status=%d, description=%s, reserved=%p)", + 4, (c, (int)status, description, reserved)); + GPR_ASSERT(reserved == nullptr); + cancel_with_status(c, status, description); + return GRPC_CALL_OK; +} + +typedef struct { + grpc_call* call; + grpc_closure start_batch; + grpc_closure finish_batch; +} cancel_state; + +// The on_complete callback used when sending a cancel_stream batch down +// the filter stack. Yields the call combiner when the batch is done. +static void done_termination(void* arg, grpc_error* error) { + cancel_state* state = static_cast(arg); + GRPC_CALL_COMBINER_STOP(&state->call->call_combiner, + "on_complete for cancel_stream op"); + GRPC_CALL_INTERNAL_UNREF(state->call, "termination"); + gpr_free(state); +} + +static void cancel_with_error(grpc_call* c, grpc_error* error) { + if (!gpr_atm_rel_cas(&c->cancelled, 0, 1)) { + GRPC_ERROR_UNREF(error); + return; + } + GRPC_CALL_INTERNAL_REF(c, "termination"); + // Inform the call combiner of the cancellation, so that it can cancel + // any in-flight asynchronous actions that may be holding the call + // combiner. This ensures that the cancel_stream batch can be sent + // down the filter stack in a timely manner. + grpc_call_combiner_cancel(&c->call_combiner, GRPC_ERROR_REF(error)); + cancel_state* state = static_cast(gpr_malloc(sizeof(*state))); + state->call = c; + GRPC_CLOSURE_INIT(&state->finish_batch, done_termination, state, + grpc_schedule_on_exec_ctx); + grpc_transport_stream_op_batch* op = + grpc_make_transport_stream_op(&state->finish_batch); + op->cancel_stream = true; + op->payload->cancel_stream.cancel_error = error; + execute_batch(c, op, &state->start_batch); +} + +void grpc_call_cancel_internal(grpc_call* call) { + cancel_with_error(call, GRPC_ERROR_CANCELLED); +} + +static grpc_error* error_from_status(grpc_status_code status, + const char* description) { + // copying 'description' is needed to ensure the grpc_call_cancel_with_status + // guarantee that can be short-lived. + return grpc_error_set_int( + grpc_error_set_str(GRPC_ERROR_CREATE_FROM_COPIED_STRING(description), + GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_from_copied_string(description)), + GRPC_ERROR_INT_GRPC_STATUS, status); +} + +static void cancel_with_status(grpc_call* c, grpc_status_code status, + const char* description) { + cancel_with_error(c, error_from_status(status, description)); +} + +static void set_final_status(grpc_call* call, grpc_error* error) { + if (grpc_call_error_trace.enabled()) { + gpr_log(GPR_DEBUG, "set_final_status %s", call->is_client ? "CLI" : "SVR"); + gpr_log(GPR_DEBUG, "%s", grpc_error_string(error)); + } + if (call->is_client) { + grpc_error_get_status(error, call->send_deadline, + call->final_op.client.status, + call->final_op.client.status_details, nullptr, + call->final_op.client.error_string); + // explicitly take a ref + grpc_slice_ref_internal(*call->final_op.client.status_details); + gpr_atm_rel_store(&call->status_error, reinterpret_cast(error)); + grpc_core::channelz::ChannelNode* channelz_channel = + grpc_channel_get_channelz_node(call->channel); + if (channelz_channel != nullptr) { + if (*call->final_op.client.status != GRPC_STATUS_OK) { + channelz_channel->RecordCallFailed(); + } else { + channelz_channel->RecordCallSucceeded(); + } + } + } else { + *call->final_op.server.cancelled = + error != GRPC_ERROR_NONE || + reinterpret_cast(gpr_atm_acq_load(&call->status_error)) != + GRPC_ERROR_NONE; + grpc_core::channelz::ServerNode* channelz_server = + grpc_server_get_channelz_node(call->final_op.server.server); + if (channelz_server != nullptr) { + if (*call->final_op.server.cancelled) { + channelz_server->RecordCallFailed(); + } else { + channelz_server->RecordCallSucceeded(); + } + } + GRPC_ERROR_UNREF(error); + } +} + +/******************************************************************************* + * COMPRESSION + */ + +static void set_incoming_message_compression_algorithm( + grpc_call* call, grpc_message_compression_algorithm algo) { + GPR_ASSERT(algo < GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT); + call->incoming_message_compression_algorithm = algo; +} + +static void set_incoming_stream_compression_algorithm( + grpc_call* call, grpc_stream_compression_algorithm algo) { + GPR_ASSERT(algo < GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT); + call->incoming_stream_compression_algorithm = algo; +} + +grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( + grpc_call* call) { + grpc_compression_algorithm algorithm = GRPC_COMPRESS_NONE; + grpc_compression_algorithm_from_message_stream_compression_algorithm( + &algorithm, call->incoming_message_compression_algorithm, + call->incoming_stream_compression_algorithm); + return algorithm; +} + +static grpc_compression_algorithm compression_algorithm_for_level_locked( + grpc_call* call, grpc_compression_level level) { + return grpc_compression_algorithm_for_level(level, + call->encodings_accepted_by_peer); +} + +uint32_t grpc_call_test_only_get_message_flags(grpc_call* call) { + uint32_t flags; + flags = call->test_only_last_message_flags; + return flags; +} + +static void destroy_encodings_accepted_by_peer(void* p) { return; } + +static void set_encodings_accepted_by_peer(grpc_call* call, grpc_mdelem mdel, + uint32_t* encodings_accepted_by_peer, + bool stream_encoding) { + size_t i; + uint32_t algorithm; + grpc_slice_buffer accept_encoding_parts; + grpc_slice accept_encoding_slice; + void* accepted_user_data; + + accepted_user_data = + grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer); + if (accepted_user_data != nullptr) { + *encodings_accepted_by_peer = + static_cast(((uintptr_t)accepted_user_data) - 1); + return; + } + + *encodings_accepted_by_peer = 0; + + accept_encoding_slice = GRPC_MDVALUE(mdel); + grpc_slice_buffer_init(&accept_encoding_parts); + grpc_slice_split_without_space(accept_encoding_slice, ",", + &accept_encoding_parts); + + GPR_BITSET(encodings_accepted_by_peer, GRPC_COMPRESS_NONE); + for (i = 0; i < accept_encoding_parts.count; i++) { + int r; + grpc_slice accept_encoding_entry_slice = accept_encoding_parts.slices[i]; + if (!stream_encoding) { + r = grpc_message_compression_algorithm_parse( + accept_encoding_entry_slice, + reinterpret_cast(&algorithm)); + } else { + r = grpc_stream_compression_algorithm_parse( + accept_encoding_entry_slice, + reinterpret_cast(&algorithm)); + } + if (r) { + GPR_BITSET(encodings_accepted_by_peer, algorithm); + } else { + char* accept_encoding_entry_str = + grpc_slice_to_c_string(accept_encoding_entry_slice); + gpr_log(GPR_DEBUG, + "Unknown entry in accept encoding metadata: '%s'. Ignoring.", + accept_encoding_entry_str); + gpr_free(accept_encoding_entry_str); + } + } + + grpc_slice_buffer_destroy_internal(&accept_encoding_parts); + + grpc_mdelem_set_user_data( + mdel, destroy_encodings_accepted_by_peer, + (void*)((static_cast(*encodings_accepted_by_peer)) + 1)); +} + +uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call* call) { + uint32_t encodings_accepted_by_peer; + encodings_accepted_by_peer = call->encodings_accepted_by_peer; + return encodings_accepted_by_peer; +} + +grpc_stream_compression_algorithm +grpc_call_test_only_get_incoming_stream_encodings(grpc_call* call) { + return call->incoming_stream_compression_algorithm; +} + +static grpc_linked_mdelem* linked_from_md(const grpc_metadata* md) { + return (grpc_linked_mdelem*)&md->internal_data; +} + +static grpc_metadata* get_md_elem(grpc_metadata* metadata, + grpc_metadata* additional_metadata, int i, + int count) { + grpc_metadata* res = + i < count ? &metadata[i] : &additional_metadata[i - count]; + GPR_ASSERT(res); + return res; +} + +static int prepare_application_metadata(grpc_call* call, int count, + grpc_metadata* metadata, + int is_trailing, + int prepend_extra_metadata, + grpc_metadata* additional_metadata, + int additional_metadata_count) { + int total_count = count + additional_metadata_count; + int i; + grpc_metadata_batch* batch = + &call->metadata_batch[0 /* is_receiving */][is_trailing]; + for (i = 0; i < total_count; i++) { + const grpc_metadata* md = + get_md_elem(metadata, additional_metadata, i, count); + grpc_linked_mdelem* l = linked_from_md(md); + GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data)); + if (!GRPC_LOG_IF_ERROR("validate_metadata", + grpc_validate_header_key_is_legal(md->key))) { + break; + } else if (!grpc_is_binary_header(md->key) && + !GRPC_LOG_IF_ERROR( + "validate_metadata", + grpc_validate_header_nonbin_value_is_legal(md->value))) { + break; + } + l->md = grpc_mdelem_from_grpc_metadata(const_cast(md)); + } + if (i != total_count) { + for (int j = 0; j < i; j++) { + const grpc_metadata* md = + get_md_elem(metadata, additional_metadata, j, count); + grpc_linked_mdelem* l = linked_from_md(md); + GRPC_MDELEM_UNREF(l->md); + } + return 0; + } + if (prepend_extra_metadata) { + if (call->send_extra_metadata_count == 0) { + prepend_extra_metadata = 0; + } else { + for (i = 0; i < call->send_extra_metadata_count; i++) { + GRPC_LOG_IF_ERROR("prepare_application_metadata", + grpc_metadata_batch_link_tail( + batch, &call->send_extra_metadata[i])); + } + } + } + for (i = 0; i < total_count; i++) { + grpc_metadata* md = get_md_elem(metadata, additional_metadata, i, count); + grpc_linked_mdelem* l = linked_from_md(md); + grpc_error* error = grpc_metadata_batch_link_tail(batch, l); + if (error != GRPC_ERROR_NONE) { + GRPC_MDELEM_UNREF(l->md); + } + GRPC_LOG_IF_ERROR("prepare_application_metadata", error); + } + call->send_extra_metadata_count = 0; + + return 1; +} + +static grpc_message_compression_algorithm decode_message_compression( + grpc_mdelem md) { + grpc_message_compression_algorithm algorithm = + grpc_message_compression_algorithm_from_slice(GRPC_MDVALUE(md)); + if (algorithm == GRPC_MESSAGE_COMPRESS_ALGORITHMS_COUNT) { + char* md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid incoming message compression algorithm: '%s'. " + "Interpreting incoming data as uncompressed.", + md_c_str); + gpr_free(md_c_str); + return GRPC_MESSAGE_COMPRESS_NONE; + } + return algorithm; +} + +static grpc_stream_compression_algorithm decode_stream_compression( + grpc_mdelem md) { + grpc_stream_compression_algorithm algorithm = + grpc_stream_compression_algorithm_from_slice(GRPC_MDVALUE(md)); + if (algorithm == GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) { + char* md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid incoming stream compression algorithm: '%s'. Interpreting " + "incoming data as uncompressed.", + md_c_str); + gpr_free(md_c_str); + return GRPC_STREAM_COMPRESS_NONE; + } + return algorithm; +} + +static void publish_app_metadata(grpc_call* call, grpc_metadata_batch* b, + int is_trailing) { + if (b->list.count == 0) return; + if (!call->is_client && is_trailing) return; + if (is_trailing && call->buffered_metadata[1] == nullptr) return; + GPR_TIMER_SCOPE("publish_app_metadata", 0); + grpc_metadata_array* dest; + grpc_metadata* mdusr; + dest = call->buffered_metadata[is_trailing]; + if (dest->count + b->list.count > dest->capacity) { + dest->capacity = + GPR_MAX(dest->capacity + b->list.count, dest->capacity * 3 / 2); + dest->metadata = static_cast( + gpr_realloc(dest->metadata, sizeof(grpc_metadata) * dest->capacity)); + } + for (grpc_linked_mdelem* l = b->list.head; l != nullptr; l = l->next) { + mdusr = &dest->metadata[dest->count++]; + /* we pass back borrowed slices that are valid whilst the call is valid */ + mdusr->key = GRPC_MDKEY(l->md); + mdusr->value = GRPC_MDVALUE(l->md); + } +} + +static void recv_initial_filter(grpc_call* call, grpc_metadata_batch* b) { + if (b->idx.named.content_encoding != nullptr) { + GPR_TIMER_SCOPE("incoming_stream_compression_algorithm", 0); + set_incoming_stream_compression_algorithm( + call, decode_stream_compression(b->idx.named.content_encoding->md)); + grpc_metadata_batch_remove(b, b->idx.named.content_encoding); + } + if (b->idx.named.grpc_encoding != nullptr) { + GPR_TIMER_SCOPE("incoming_message_compression_algorithm", 0); + set_incoming_message_compression_algorithm( + call, decode_message_compression(b->idx.named.grpc_encoding->md)); + grpc_metadata_batch_remove(b, b->idx.named.grpc_encoding); + } + uint32_t message_encodings_accepted_by_peer = 1u; + uint32_t stream_encodings_accepted_by_peer = 1u; + if (b->idx.named.grpc_accept_encoding != nullptr) { + GPR_TIMER_SCOPE("encodings_accepted_by_peer", 0); + set_encodings_accepted_by_peer(call, b->idx.named.grpc_accept_encoding->md, + &message_encodings_accepted_by_peer, false); + grpc_metadata_batch_remove(b, b->idx.named.grpc_accept_encoding); + } + if (b->idx.named.accept_encoding != nullptr) { + GPR_TIMER_SCOPE("stream_encodings_accepted_by_peer", 0); + set_encodings_accepted_by_peer(call, b->idx.named.accept_encoding->md, + &stream_encodings_accepted_by_peer, true); + grpc_metadata_batch_remove(b, b->idx.named.accept_encoding); + } + call->encodings_accepted_by_peer = + grpc_compression_bitset_from_message_stream_compression_bitset( + message_encodings_accepted_by_peer, + stream_encodings_accepted_by_peer); + publish_app_metadata(call, b, false); +} + +static void recv_trailing_filter(void* args, grpc_metadata_batch* b, + grpc_error* batch_error) { + grpc_call* call = static_cast(args); + if (batch_error != GRPC_ERROR_NONE) { + set_final_status(call, batch_error); + } else if (b->idx.named.grpc_status != nullptr) { + grpc_status_code status_code = + grpc_get_status_code_from_metadata(b->idx.named.grpc_status->md); + grpc_error* error = GRPC_ERROR_NONE; + if (status_code != GRPC_STATUS_OK) { + error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Error received from peer"), + GRPC_ERROR_INT_GRPC_STATUS, static_cast(status_code)); + } + if (b->idx.named.grpc_message != nullptr) { + error = grpc_error_set_str( + error, GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_message->md))); + grpc_metadata_batch_remove(b, b->idx.named.grpc_message); + } else if (error != GRPC_ERROR_NONE) { + error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_empty_slice()); + } + set_final_status(call, GRPC_ERROR_REF(error)); + grpc_metadata_batch_remove(b, b->idx.named.grpc_status); + GRPC_ERROR_UNREF(error); + } else if (!call->is_client) { + set_final_status(call, GRPC_ERROR_NONE); + } else { + gpr_log(GPR_DEBUG, + "Received trailing metadata with no error and no status"); + set_final_status( + call, grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("No status received"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNKNOWN)); + } + publish_app_metadata(call, b, true); +} + +gpr_arena* grpc_call_get_arena(grpc_call* call) { return call->arena; } + +grpc_call_stack* grpc_call_get_call_stack(grpc_call* call) { + return CALL_STACK_FROM_CALL(call); +} + +/******************************************************************************* + * BATCH API IMPLEMENTATION + */ + +static bool are_write_flags_valid(uint32_t flags) { + /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ + const uint32_t allowed_write_positions = + (GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK); + const uint32_t invalid_positions = ~allowed_write_positions; + return !(flags & invalid_positions); +} + +static bool are_initial_metadata_flags_valid(uint32_t flags, bool is_client) { + /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ + uint32_t invalid_positions = ~GRPC_INITIAL_METADATA_USED_MASK; + if (!is_client) { + invalid_positions |= GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + } + return !(flags & invalid_positions); +} + +static size_t batch_slot_for_op(grpc_op_type type) { + switch (type) { + case GRPC_OP_SEND_INITIAL_METADATA: + return 0; + case GRPC_OP_SEND_MESSAGE: + return 1; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + case GRPC_OP_SEND_STATUS_FROM_SERVER: + return 2; + case GRPC_OP_RECV_INITIAL_METADATA: + return 3; + case GRPC_OP_RECV_MESSAGE: + return 4; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + case GRPC_OP_RECV_STATUS_ON_CLIENT: + return 5; + } + GPR_UNREACHABLE_CODE(return 123456789); +} + +static batch_control* reuse_or_allocate_batch_control(grpc_call* call, + const grpc_op* ops, + size_t num_ops) { + size_t slot_idx = batch_slot_for_op(ops[0].op); + batch_control** pslot = &call->active_batches[slot_idx]; + batch_control* bctl; + if (*pslot != nullptr) { + bctl = *pslot; + if (bctl->call != nullptr) { + return nullptr; + } + bctl->~batch_control(); + bctl->op = {}; + } else { + bctl = new (gpr_arena_alloc(call->arena, sizeof(batch_control))) + batch_control(); + *pslot = bctl; + } + bctl->call = call; + bctl->op.payload = &call->stream_op_payload; + return bctl; +} + +static void finish_batch_completion(void* user_data, + grpc_cq_completion* storage) { + batch_control* bctl = static_cast(user_data); + grpc_call* call = bctl->call; + bctl->call = nullptr; + GRPC_CALL_INTERNAL_UNREF(call, "completion"); +} + +static void reset_batch_errors(batch_control* bctl) { + GRPC_ERROR_UNREF( + reinterpret_cast(gpr_atm_acq_load(&bctl->batch_error))); + gpr_atm_rel_store(&bctl->batch_error, + reinterpret_cast(GRPC_ERROR_NONE)); +} + +static void post_batch_completion(batch_control* bctl) { + grpc_call* next_child_call; + grpc_call* call = bctl->call; + grpc_error* error = GRPC_ERROR_REF( + reinterpret_cast(gpr_atm_acq_load(&bctl->batch_error))); + + if (bctl->op.send_initial_metadata) { + grpc_metadata_batch_destroy( + &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]); + } + if (bctl->op.send_message) { + call->sending_message = false; + } + if (bctl->op.send_trailing_metadata) { + grpc_metadata_batch_destroy( + &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]); + } + if (bctl->op.recv_trailing_metadata) { + /* propagate cancellation to any interested children */ + gpr_atm_rel_store(&call->received_final_op_atm, 1); + parent_call* pc = get_parent_call(call); + if (pc != nullptr) { + grpc_call* child; + gpr_mu_lock(&pc->child_list_mu); + child = pc->first_child; + if (child != nullptr) { + do { + next_child_call = child->child->sibling_next; + if (child->cancellation_is_inherited) { + GRPC_CALL_INTERNAL_REF(child, "propagate_cancel"); + cancel_with_error(child, GRPC_ERROR_CANCELLED); + GRPC_CALL_INTERNAL_UNREF(child, "propagate_cancel"); + } + child = next_child_call; + } while (child != pc->first_child); + } + gpr_mu_unlock(&pc->child_list_mu); + } + GRPC_ERROR_UNREF(error); + error = GRPC_ERROR_NONE; + } + if (error != GRPC_ERROR_NONE && bctl->op.recv_message && + *call->receiving_buffer != nullptr) { + grpc_byte_buffer_destroy(*call->receiving_buffer); + *call->receiving_buffer = nullptr; + } + reset_batch_errors(bctl); + + if (bctl->completion_data.notify_tag.is_closure) { + /* unrefs error */ + bctl->call = nullptr; + /* This closure may be meant to be run within some combiner. Since we aren't + * running in any combiner here, we need to use GRPC_CLOSURE_SCHED instead + * of GRPC_CLOSURE_RUN. + */ + GRPC_CLOSURE_SCHED((grpc_closure*)bctl->completion_data.notify_tag.tag, + error); + GRPC_CALL_INTERNAL_UNREF(call, "completion"); + } else { + /* unrefs error */ + grpc_cq_end_op(bctl->call->cq, bctl->completion_data.notify_tag.tag, error, + finish_batch_completion, bctl, + &bctl->completion_data.cq_completion); + } +} + +static void finish_batch_step(batch_control* bctl) { + if (gpr_unref(&bctl->steps_to_complete)) { + post_batch_completion(bctl); + } +} + +static void continue_receiving_slices(batch_control* bctl) { + grpc_error* error; + grpc_call* call = bctl->call; + for (;;) { + size_t remaining = call->receiving_stream->length() - + (*call->receiving_buffer)->data.raw.slice_buffer.length; + if (remaining == 0) { + call->receiving_message = 0; + call->receiving_stream.reset(); + finish_batch_step(bctl); + return; + } + if (call->receiving_stream->Next(remaining, &call->receiving_slice_ready)) { + error = call->receiving_stream->Pull(&call->receiving_slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, + call->receiving_slice); + } else { + call->receiving_stream.reset(); + grpc_byte_buffer_destroy(*call->receiving_buffer); + *call->receiving_buffer = nullptr; + call->receiving_message = 0; + finish_batch_step(bctl); + return; + } + } else { + return; + } + } +} + +static void receiving_slice_ready(void* bctlp, grpc_error* error) { + batch_control* bctl = static_cast(bctlp); + grpc_call* call = bctl->call; + bool release_error = false; + + if (error == GRPC_ERROR_NONE) { + grpc_slice slice; + error = call->receiving_stream->Pull(&slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, + slice); + continue_receiving_slices(bctl); + } else { + /* Error returned by ByteStream::Pull() needs to be released manually */ + release_error = true; + } + } + + if (error != GRPC_ERROR_NONE) { + if (grpc_trace_operation_failures.enabled()) { + GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error)); + } + call->receiving_stream.reset(); + grpc_byte_buffer_destroy(*call->receiving_buffer); + *call->receiving_buffer = nullptr; + call->receiving_message = 0; + finish_batch_step(bctl); + if (release_error) { + GRPC_ERROR_UNREF(error); + } + } +} + +static void process_data_after_md(batch_control* bctl) { + grpc_call* call = bctl->call; + if (call->receiving_stream == nullptr) { + *call->receiving_buffer = nullptr; + call->receiving_message = 0; + finish_batch_step(bctl); + } else { + call->test_only_last_message_flags = call->receiving_stream->flags(); + if ((call->receiving_stream->flags() & GRPC_WRITE_INTERNAL_COMPRESS) && + (call->incoming_message_compression_algorithm > + GRPC_MESSAGE_COMPRESS_NONE)) { + grpc_compression_algorithm algo; + GPR_ASSERT( + grpc_compression_algorithm_from_message_stream_compression_algorithm( + &algo, call->incoming_message_compression_algorithm, + (grpc_stream_compression_algorithm)0)); + *call->receiving_buffer = + grpc_raw_compressed_byte_buffer_create(nullptr, 0, algo); + } else { + *call->receiving_buffer = grpc_raw_byte_buffer_create(nullptr, 0); + } + GRPC_CLOSURE_INIT(&call->receiving_slice_ready, receiving_slice_ready, bctl, + grpc_schedule_on_exec_ctx); + continue_receiving_slices(bctl); + } +} + +static void receiving_stream_ready(void* bctlp, grpc_error* error) { + batch_control* bctl = static_cast(bctlp); + grpc_call* call = bctl->call; + if (error != GRPC_ERROR_NONE) { + call->receiving_stream.reset(); + if (reinterpret_cast(gpr_atm_acq_load(&bctl->batch_error)) == + GRPC_ERROR_NONE) { + gpr_atm_rel_store(&bctl->batch_error, + reinterpret_cast(GRPC_ERROR_REF(error))); + } + cancel_with_error(call, GRPC_ERROR_REF(error)); + } + /* If recv_state is RECV_NONE, we will save the batch_control + * object with rel_cas, and will not use it after the cas. Its corresponding + * acq_load is in receiving_initial_metadata_ready() */ + if (error != GRPC_ERROR_NONE || call->receiving_stream == nullptr || + !gpr_atm_rel_cas(&call->recv_state, RECV_NONE, (gpr_atm)bctlp)) { + process_data_after_md(bctl); + } +} + +// The recv_message_ready callback used when sending a batch containing +// a recv_message op down the filter stack. Yields the call combiner +// before processing the received message. +static void receiving_stream_ready_in_call_combiner(void* bctlp, + grpc_error* error) { + batch_control* bctl = static_cast(bctlp); + grpc_call* call = bctl->call; + GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_message_ready"); + receiving_stream_ready(bctlp, error); +} + +static void validate_filtered_metadata(batch_control* bctl) { + grpc_compression_algorithm compression_algorithm; + grpc_call* call = bctl->call; + if (call->incoming_stream_compression_algorithm != + GRPC_STREAM_COMPRESS_NONE && + call->incoming_message_compression_algorithm != + GRPC_MESSAGE_COMPRESS_NONE) { + char* error_msg = nullptr; + gpr_asprintf(&error_msg, + "Incoming stream has both stream compression (%d) and message " + "compression (%d).", + call->incoming_stream_compression_algorithm, + call->incoming_message_compression_algorithm); + gpr_log(GPR_ERROR, "%s", error_msg); + cancel_with_status(call, GRPC_STATUS_INTERNAL, error_msg); + gpr_free(error_msg); + } else if ( + grpc_compression_algorithm_from_message_stream_compression_algorithm( + &compression_algorithm, call->incoming_message_compression_algorithm, + call->incoming_stream_compression_algorithm) == 0) { + char* error_msg = nullptr; + gpr_asprintf(&error_msg, + "Error in incoming message compression (%d) or stream " + "compression (%d).", + call->incoming_stream_compression_algorithm, + call->incoming_message_compression_algorithm); + cancel_with_status(call, GRPC_STATUS_INTERNAL, error_msg); + gpr_free(error_msg); + } else { + char* error_msg = nullptr; + const grpc_compression_options compression_options = + grpc_channel_compression_options(call->channel); + if (compression_algorithm >= GRPC_COMPRESS_ALGORITHMS_COUNT) { + gpr_asprintf(&error_msg, "Invalid compression algorithm value '%d'.", + compression_algorithm); + gpr_log(GPR_ERROR, "%s", error_msg); + cancel_with_status(call, GRPC_STATUS_UNIMPLEMENTED, error_msg); + } else if (grpc_compression_options_is_algorithm_enabled( + &compression_options, compression_algorithm) == 0) { + /* check if algorithm is supported by current channel config */ + const char* algo_name = nullptr; + grpc_compression_algorithm_name(compression_algorithm, &algo_name); + gpr_asprintf(&error_msg, "Compression algorithm '%s' is disabled.", + algo_name); + gpr_log(GPR_ERROR, "%s", error_msg); + cancel_with_status(call, GRPC_STATUS_UNIMPLEMENTED, error_msg); + } + gpr_free(error_msg); + + GPR_ASSERT(call->encodings_accepted_by_peer != 0); + if (!GPR_BITGET(call->encodings_accepted_by_peer, compression_algorithm)) { + if (grpc_compression_trace.enabled()) { + const char* algo_name = nullptr; + grpc_compression_algorithm_name(compression_algorithm, &algo_name); + gpr_log(GPR_ERROR, + "Compression algorithm ('%s') not present in the bitset of " + "accepted encodings ('0x%x')", + algo_name, call->encodings_accepted_by_peer); + } + } + } +} + +static void receiving_initial_metadata_ready(void* bctlp, grpc_error* error) { + batch_control* bctl = static_cast(bctlp); + grpc_call* call = bctl->call; + + GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_initial_metadata_ready"); + + if (error == GRPC_ERROR_NONE) { + grpc_metadata_batch* md = + &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; + recv_initial_filter(call, md); + + /* TODO(ctiller): this could be moved into recv_initial_filter now */ + GPR_TIMER_SCOPE("validate_filtered_metadata", 0); + validate_filtered_metadata(bctl); + + if (md->deadline != GRPC_MILLIS_INF_FUTURE && !call->is_client) { + call->send_deadline = md->deadline; + } + } else { + if (reinterpret_cast(gpr_atm_acq_load(&bctl->batch_error)) == + GRPC_ERROR_NONE) { + gpr_atm_rel_store(&bctl->batch_error, + reinterpret_cast(GRPC_ERROR_REF(error))); + } + cancel_with_error(call, GRPC_ERROR_REF(error)); + } + + grpc_closure* saved_rsr_closure = nullptr; + while (true) { + gpr_atm rsr_bctlp = gpr_atm_acq_load(&call->recv_state); + /* Should only receive initial metadata once */ + GPR_ASSERT(rsr_bctlp != 1); + if (rsr_bctlp == 0) { + /* We haven't seen initial metadata and messages before, thus initial + * metadata is received first. + * no_barrier_cas is used, as this function won't access the batch_control + * object saved by receiving_stream_ready() if the initial metadata is + * received first. */ + if (gpr_atm_no_barrier_cas(&call->recv_state, RECV_NONE, + RECV_INITIAL_METADATA_FIRST)) { + break; + } + } else { + /* Already received messages */ + saved_rsr_closure = + GRPC_CLOSURE_CREATE(receiving_stream_ready, (batch_control*)rsr_bctlp, + grpc_schedule_on_exec_ctx); + /* No need to modify recv_state */ + break; + } + } + if (saved_rsr_closure != nullptr) { + GRPC_CLOSURE_RUN(saved_rsr_closure, GRPC_ERROR_REF(error)); + } + + finish_batch_step(bctl); +} + +static void receiving_trailing_metadata_ready(void* bctlp, grpc_error* error) { + batch_control* bctl = static_cast(bctlp); + grpc_call* call = bctl->call; + GRPC_CALL_COMBINER_STOP(&call->call_combiner, "recv_trailing_metadata_ready"); + grpc_metadata_batch* md = + &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; + recv_trailing_filter(call, md, GRPC_ERROR_REF(error)); + finish_batch_step(bctl); +} + +static void finish_batch(void* bctlp, grpc_error* error) { + batch_control* bctl = static_cast(bctlp); + grpc_call* call = bctl->call; + GRPC_CALL_COMBINER_STOP(&call->call_combiner, "on_complete"); + if (reinterpret_cast(gpr_atm_acq_load(&bctl->batch_error)) == + GRPC_ERROR_NONE) { + gpr_atm_rel_store(&bctl->batch_error, + reinterpret_cast(GRPC_ERROR_REF(error))); + } + if (error != GRPC_ERROR_NONE) { + cancel_with_error(call, GRPC_ERROR_REF(error)); + } + finish_batch_step(bctl); +} + +static void free_no_op_completion(void* p, grpc_cq_completion* completion) { + gpr_free(completion); +} + +static grpc_call_error call_start_batch(grpc_call* call, const grpc_op* ops, + size_t nops, void* notify_tag, + int is_notify_tag_closure) { + GPR_TIMER_SCOPE("call_start_batch", 0); + + size_t i; + const grpc_op* op; + batch_control* bctl; + bool has_send_ops = false; + int num_recv_ops = 0; + grpc_call_error error = GRPC_CALL_OK; + grpc_transport_stream_op_batch* stream_op; + grpc_transport_stream_op_batch_payload* stream_op_payload; + + GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, notify_tag); + + if (nops == 0) { + if (!is_notify_tag_closure) { + GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag)); + grpc_cq_end_op(call->cq, notify_tag, GRPC_ERROR_NONE, + free_no_op_completion, nullptr, + static_cast( + gpr_malloc(sizeof(grpc_cq_completion)))); + } else { + GRPC_CLOSURE_SCHED((grpc_closure*)notify_tag, GRPC_ERROR_NONE); + } + error = GRPC_CALL_OK; + goto done; + } + + bctl = reuse_or_allocate_batch_control(call, ops, nops); + if (bctl == nullptr) { + return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + } + bctl->completion_data.notify_tag.tag = notify_tag; + bctl->completion_data.notify_tag.is_closure = + static_cast(is_notify_tag_closure != 0); + + stream_op = &bctl->op; + stream_op_payload = &call->stream_op_payload; + + /* rewrite batch ops into a transport op */ + for (i = 0; i < nops; i++) { + op = &ops[i]; + if (op->reserved != nullptr) { + error = GRPC_CALL_ERROR; + goto done_with_error; + } + switch (op->op) { + case GRPC_OP_SEND_INITIAL_METADATA: { + /* Flag validation: currently allow no flags */ + if (!are_initial_metadata_flags_valid(op->flags, call->is_client)) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->sent_initial_metadata) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + /* process compression level */ + memset(&call->compression_md, 0, sizeof(call->compression_md)); + size_t additional_metadata_count = 0; + grpc_compression_level effective_compression_level = + GRPC_COMPRESS_LEVEL_NONE; + bool level_set = false; + if (op->data.send_initial_metadata.maybe_compression_level.is_set) { + effective_compression_level = + op->data.send_initial_metadata.maybe_compression_level.level; + level_set = true; + } else { + const grpc_compression_options copts = + grpc_channel_compression_options(call->channel); + if (copts.default_level.is_set) { + level_set = true; + effective_compression_level = copts.default_level.level; + } + } + if (level_set && !call->is_client) { + const grpc_compression_algorithm calgo = + compression_algorithm_for_level_locked( + call, effective_compression_level); + /* the following will be picked up by the compress filter and used + * as the call's compression algorithm. */ + call->compression_md.key = GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST; + call->compression_md.value = grpc_compression_algorithm_slice(calgo); + additional_metadata_count++; + } + + if (op->data.send_initial_metadata.count + additional_metadata_count > + INT_MAX) { + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + stream_op->send_initial_metadata = true; + call->sent_initial_metadata = true; + if (!prepare_application_metadata( + call, static_cast(op->data.send_initial_metadata.count), + op->data.send_initial_metadata.metadata, 0, call->is_client, + &call->compression_md, + static_cast(additional_metadata_count))) { + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + /* TODO(ctiller): just make these the same variable? */ + if (call->is_client) { + call->metadata_batch[0][0].deadline = call->send_deadline; + } + stream_op_payload->send_initial_metadata.send_initial_metadata = + &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]; + stream_op_payload->send_initial_metadata.send_initial_metadata_flags = + op->flags; + if (call->is_client) { + stream_op_payload->send_initial_metadata.peer_string = + &call->peer_string; + } + has_send_ops = true; + break; + } + case GRPC_OP_SEND_MESSAGE: { + if (!are_write_flags_valid(op->flags)) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (op->data.send_message.send_message == nullptr) { + error = GRPC_CALL_ERROR_INVALID_MESSAGE; + goto done_with_error; + } + if (call->sending_message) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + uint32_t flags = op->flags; + /* If the outgoing buffer is already compressed, mark it as so in the + flags. These will be picked up by the compression filter and further + (wasteful) attempts at compression skipped. */ + if (op->data.send_message.send_message->data.raw.compression > + GRPC_COMPRESS_NONE) { + flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } + stream_op->send_message = true; + call->sending_message = true; + call->sending_stream.Init( + &op->data.send_message.send_message->data.raw.slice_buffer, flags); + stream_op_payload->send_message.send_message.reset( + call->sending_stream.get()); + has_send_ops = true; + break; + } + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (!call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_SERVER; + goto done_with_error; + } + if (call->sent_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + stream_op->send_trailing_metadata = true; + call->sent_final_op = true; + stream_op_payload->send_trailing_metadata.send_trailing_metadata = + &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; + has_send_ops = true; + break; + } + case GRPC_OP_SEND_STATUS_FROM_SERVER: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_CLIENT; + goto done_with_error; + } + if (call->sent_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + if (op->data.send_status_from_server.trailing_metadata_count > + INT_MAX) { + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + stream_op->send_trailing_metadata = true; + call->sent_final_op = true; + GPR_ASSERT(call->send_extra_metadata_count == 0); + call->send_extra_metadata_count = 1; + call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem( + call->channel, op->data.send_status_from_server.status); + grpc_error* status_error = + op->data.send_status_from_server.status == GRPC_STATUS_OK + ? GRPC_ERROR_NONE + : grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Server returned error"), + GRPC_ERROR_INT_GRPC_STATUS, + static_cast( + op->data.send_status_from_server.status)); + if (op->data.send_status_from_server.status_details != nullptr) { + call->send_extra_metadata[1].md = grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_MESSAGE, + grpc_slice_ref_internal( + *op->data.send_status_from_server.status_details)); + call->send_extra_metadata_count++; + if (status_error != GRPC_ERROR_NONE) { + char* msg = grpc_slice_to_c_string( + GRPC_MDVALUE(call->send_extra_metadata[1].md)); + status_error = + grpc_error_set_str(status_error, GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_from_copied_string(msg)); + gpr_free(msg); + } + } + + gpr_atm_rel_store(&call->status_error, + reinterpret_cast(status_error)); + if (!prepare_application_metadata( + call, + static_cast( + op->data.send_status_from_server.trailing_metadata_count), + op->data.send_status_from_server.trailing_metadata, 1, 1, + nullptr, 0)) { + for (int n = 0; n < call->send_extra_metadata_count; n++) { + GRPC_MDELEM_UNREF(call->send_extra_metadata[n].md); + } + call->send_extra_metadata_count = 0; + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + stream_op_payload->send_trailing_metadata.send_trailing_metadata = + &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; + has_send_ops = true; + break; + } + case GRPC_OP_RECV_INITIAL_METADATA: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->received_initial_metadata) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->received_initial_metadata = true; + call->buffered_metadata[0] = + op->data.recv_initial_metadata.recv_initial_metadata; + GRPC_CLOSURE_INIT(&call->receiving_initial_metadata_ready, + receiving_initial_metadata_ready, bctl, + grpc_schedule_on_exec_ctx); + stream_op->recv_initial_metadata = true; + stream_op_payload->recv_initial_metadata.recv_initial_metadata = + &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; + stream_op_payload->recv_initial_metadata.recv_initial_metadata_ready = + &call->receiving_initial_metadata_ready; + if (!call->is_client) { + stream_op_payload->recv_initial_metadata.peer_string = + &call->peer_string; + } + ++num_recv_ops; + break; + } + case GRPC_OP_RECV_MESSAGE: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->receiving_message) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->receiving_message = true; + stream_op->recv_message = true; + call->receiving_buffer = op->data.recv_message.recv_message; + stream_op_payload->recv_message.recv_message = &call->receiving_stream; + GRPC_CLOSURE_INIT(&call->receiving_stream_ready, + receiving_stream_ready_in_call_combiner, bctl, + grpc_schedule_on_exec_ctx); + stream_op_payload->recv_message.recv_message_ready = + &call->receiving_stream_ready; + ++num_recv_ops; + break; + } + case GRPC_OP_RECV_STATUS_ON_CLIENT: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (!call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_SERVER; + goto done_with_error; + } + if (call->requested_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->requested_final_op = true; + call->buffered_metadata[1] = + op->data.recv_status_on_client.trailing_metadata; + call->final_op.client.status = op->data.recv_status_on_client.status; + call->final_op.client.status_details = + op->data.recv_status_on_client.status_details; + call->final_op.client.error_string = + op->data.recv_status_on_client.error_string; + stream_op->recv_trailing_metadata = true; + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = + &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; + stream_op_payload->recv_trailing_metadata.collect_stats = + &call->final_info.stats.transport_stream_stats; + GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready, + receiving_trailing_metadata_ready, bctl, + grpc_schedule_on_exec_ctx); + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &call->receiving_trailing_metadata_ready; + ++num_recv_ops; + break; + } + case GRPC_OP_RECV_CLOSE_ON_SERVER: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_CLIENT; + goto done_with_error; + } + if (call->requested_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->requested_final_op = true; + call->final_op.server.cancelled = + op->data.recv_close_on_server.cancelled; + stream_op->recv_trailing_metadata = true; + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = + &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; + stream_op_payload->recv_trailing_metadata.collect_stats = + &call->final_info.stats.transport_stream_stats; + GRPC_CLOSURE_INIT(&call->receiving_trailing_metadata_ready, + receiving_trailing_metadata_ready, bctl, + grpc_schedule_on_exec_ctx); + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &call->receiving_trailing_metadata_ready; + ++num_recv_ops; + break; + } + } + } + + GRPC_CALL_INTERNAL_REF(call, "completion"); + if (!is_notify_tag_closure) { + GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag)); + } + gpr_ref_init(&bctl->steps_to_complete, (has_send_ops ? 1 : 0) + num_recv_ops); + + if (has_send_ops) { + GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl, + grpc_schedule_on_exec_ctx); + stream_op->on_complete = &bctl->finish_batch; + } + + gpr_atm_rel_store(&call->any_ops_sent_atm, 1); + execute_batch(call, stream_op, &bctl->start_batch); + +done: + return error; + +done_with_error: + /* reverse any mutations that occurred */ + if (stream_op->send_initial_metadata) { + call->sent_initial_metadata = false; + grpc_metadata_batch_clear(&call->metadata_batch[0][0]); + } + if (stream_op->send_message) { + call->sending_message = false; + call->sending_stream->Orphan(); + } + if (stream_op->send_trailing_metadata) { + call->sent_final_op = false; + grpc_metadata_batch_clear(&call->metadata_batch[0][1]); + } + if (stream_op->recv_initial_metadata) { + call->received_initial_metadata = false; + } + if (stream_op->recv_message) { + call->receiving_message = false; + } + if (stream_op->recv_trailing_metadata) { + call->requested_final_op = false; + } + goto done; +} + +grpc_call_error grpc_call_start_batch(grpc_call* call, const grpc_op* ops, + size_t nops, void* tag, void* reserved) { + grpc_core::ExecCtx exec_ctx; + grpc_call_error err; + + GRPC_API_TRACE( + "grpc_call_start_batch(call=%p, ops=%p, nops=%lu, tag=%p, " + "reserved=%p)", + 5, (call, ops, (unsigned long)nops, tag, reserved)); + + if (reserved != nullptr) { + err = GRPC_CALL_ERROR; + } else { + err = call_start_batch(call, ops, nops, tag, 0); + } + + return err; +} + +grpc_call_error grpc_call_start_batch_and_execute(grpc_call* call, + const grpc_op* ops, + size_t nops, + grpc_closure* closure) { + return call_start_batch(call, ops, nops, closure, 1); +} + +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; +} + +uint8_t grpc_call_is_client(grpc_call* call) { return call->is_client; } + +grpc_compression_algorithm grpc_call_compression_for_level( + grpc_call* call, grpc_compression_level level) { + grpc_compression_algorithm algo = + compression_algorithm_for_level_locked(call, level); + return algo; +} + +const char* grpc_call_error_to_string(grpc_call_error error) { + switch (error) { + case GRPC_CALL_ERROR: + return "GRPC_CALL_ERROR"; + case GRPC_CALL_ERROR_ALREADY_ACCEPTED: + return "GRPC_CALL_ERROR_ALREADY_ACCEPTED"; + case GRPC_CALL_ERROR_ALREADY_FINISHED: + return "GRPC_CALL_ERROR_ALREADY_FINISHED"; + case GRPC_CALL_ERROR_ALREADY_INVOKED: + return "GRPC_CALL_ERROR_ALREADY_INVOKED"; + case GRPC_CALL_ERROR_BATCH_TOO_BIG: + return "GRPC_CALL_ERROR_BATCH_TOO_BIG"; + case GRPC_CALL_ERROR_INVALID_FLAGS: + return "GRPC_CALL_ERROR_INVALID_FLAGS"; + case GRPC_CALL_ERROR_INVALID_MESSAGE: + return "GRPC_CALL_ERROR_INVALID_MESSAGE"; + case GRPC_CALL_ERROR_INVALID_METADATA: + return "GRPC_CALL_ERROR_INVALID_METADATA"; + case GRPC_CALL_ERROR_NOT_INVOKED: + return "GRPC_CALL_ERROR_NOT_INVOKED"; + case GRPC_CALL_ERROR_NOT_ON_CLIENT: + return "GRPC_CALL_ERROR_NOT_ON_CLIENT"; + case GRPC_CALL_ERROR_NOT_ON_SERVER: + return "GRPC_CALL_ERROR_NOT_ON_SERVER"; + case GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE: + return "GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE"; + case GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH: + return "GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH"; + case GRPC_CALL_ERROR_TOO_MANY_OPERATIONS: + return "GRPC_CALL_ERROR_TOO_MANY_OPERATIONS"; + case GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN: + return "GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN"; + case GRPC_CALL_OK: + return "GRPC_CALL_OK"; + } + GPR_UNREACHABLE_CODE(return "GRPC_CALL_ERROR_UNKNOW"); +} diff --git a/third_party/grpc/src/core/lib/surface/call.h b/third_party/grpc/src/core/lib/surface/call.h new file mode 100644 index 00000000000000..bd7295fe1106dd --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/call.h @@ -0,0 +1,121 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_CALL_H +#define GRPC_CORE_LIB_SURFACE_CALL_H + +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/channel/context.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include + +typedef void (*grpc_ioreq_completion_func)(grpc_call* call, int success, + void* user_data); + +typedef struct grpc_call_create_args { + grpc_channel* channel; + grpc_server* server; + + grpc_call* parent; + uint32_t propagation_mask; + + grpc_completion_queue* cq; + /* if not NULL, it'll be used in lieu of cq */ + grpc_pollset_set* pollset_set_alternative; + + const void* server_transport_data; + + grpc_mdelem* add_initial_metadata; + size_t add_initial_metadata_count; + + grpc_millis send_deadline; +} grpc_call_create_args; + +/* Create a new call based on \a args. + Regardless of success or failure, always returns a valid new call into *call + */ +grpc_error* grpc_call_create(const grpc_call_create_args* args, + grpc_call** call); + +void grpc_call_set_completion_queue(grpc_call* call, grpc_completion_queue* cq); + +#ifndef NDEBUG +void grpc_call_internal_ref(grpc_call* call, const char* reason); +void grpc_call_internal_unref(grpc_call* call, const char* reason); +#define GRPC_CALL_INTERNAL_REF(call, reason) \ + grpc_call_internal_ref(call, reason) +#define GRPC_CALL_INTERNAL_UNREF(call, reason) \ + grpc_call_internal_unref(call, reason) +#else +void grpc_call_internal_ref(grpc_call* call); +void grpc_call_internal_unref(grpc_call* call); +#define GRPC_CALL_INTERNAL_REF(call, reason) grpc_call_internal_ref(call) +#define GRPC_CALL_INTERNAL_UNREF(call, reason) grpc_call_internal_unref(call) +#endif + +gpr_arena* grpc_call_get_arena(grpc_call* call); + +grpc_call_stack* grpc_call_get_call_stack(grpc_call* call); + +grpc_call_error grpc_call_start_batch_and_execute(grpc_call* call, + const grpc_op* ops, + size_t nops, + grpc_closure* closure); + +/* gRPC core internal version of grpc_call_cancel that does not create + * exec_ctx. */ +void grpc_call_cancel_internal(grpc_call* call); + +/* Given the top call_element, get the call object. */ +grpc_call* grpc_call_from_top_element(grpc_call_element* surface_element); + +void grpc_call_log_batch(const char* file, int line, gpr_log_severity severity, + grpc_call* call, const grpc_op* ops, size_t nops, + void* tag); + +/* Set a context pointer. + No thread safety guarantees are made wrt this value. */ +/* TODO(#9731): add exec_ctx to destroy */ +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_api_trace.enabled()) grpc_call_log_batch(sev, call, ops, nops, tag) + +uint8_t grpc_call_is_client(grpc_call* call); + +/* Get the estimated memory size for a call BESIDES the call stack. Combined + * with the size of the call stack, it helps estimate the arena size for the + * initial call. */ +size_t grpc_call_get_initial_size_estimate(); + +/* Return an appropriate compression algorithm for the requested compression \a + * level in the context of \a call. */ +grpc_compression_algorithm grpc_call_compression_for_level( + grpc_call* call, grpc_compression_level level); + +extern grpc_core::TraceFlag grpc_call_error_trace; +extern grpc_core::TraceFlag grpc_compression_trace; + +#endif /* GRPC_CORE_LIB_SURFACE_CALL_H */ diff --git a/third_party/grpc/src/core/lib/surface/call_details.cc b/third_party/grpc/src/core/lib/surface/call_details.cc new file mode 100644 index 00000000000000..7f20b1dae740ea --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/call_details.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" + +void grpc_call_details_init(grpc_call_details* cd) { + GRPC_API_TRACE("grpc_call_details_init(cd=%p)", 1, (cd)); + memset(cd, 0, sizeof(*cd)); + cd->method = grpc_empty_slice(); + cd->host = grpc_empty_slice(); +} + +void grpc_call_details_destroy(grpc_call_details* cd) { + GRPC_API_TRACE("grpc_call_details_destroy(cd=%p)", 1, (cd)); + grpc_core::ExecCtx exec_ctx; + grpc_slice_unref_internal(cd->method); + grpc_slice_unref_internal(cd->host); +} diff --git a/third_party/grpc/src/core/lib/surface/call_log_batch.cc b/third_party/grpc/src/core/lib/surface/call_log_batch.cc new file mode 100644 index 00000000000000..f0c82c03570d81 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/call_log_batch.cc @@ -0,0 +1,120 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/call.h" + +#include + +#include +#include +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +static void add_metadata(gpr_strvec* b, const grpc_metadata* md, size_t count) { + size_t i; + if (md == nullptr) { + gpr_strvec_add(b, gpr_strdup("(nil)")); + return; + } + for (i = 0; i < count; i++) { + gpr_strvec_add(b, gpr_strdup("\nkey=")); + gpr_strvec_add(b, grpc_slice_to_c_string(md[i].key)); + + gpr_strvec_add(b, gpr_strdup(" value=")); + gpr_strvec_add(b, + grpc_dump_slice(md[i].value, 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.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=", + op->data.send_status_from_server.status); + gpr_strvec_add(&b, tmp); + if (op->data.send_status_from_server.status_details != nullptr) { + gpr_strvec_add(&b, grpc_dump_slice( + *op->data.send_status_from_server.status_details, + GPR_DUMP_ASCII)); + } else { + gpr_strvec_add(&b, gpr_strdup("(null)")); + } + 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.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.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, nullptr); + gpr_strvec_destroy(&b); + + return out; +} + +void grpc_call_log_batch(const 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; + for (i = 0; i < nops; i++) { + tmp = grpc_op_string(&ops[i]); + gpr_log(file, line, severity, "ops[%" PRIuPTR "]: %s", i, tmp); + gpr_free(tmp); + } +} diff --git a/third_party/grpc/src/core/lib/surface/call_test_only.h b/third_party/grpc/src/core/lib/surface/call_test_only.h new file mode 100644 index 00000000000000..dbd1a866cae480 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/call_test_only.h @@ -0,0 +1,43 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H +#define GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H + +#include + +#include + +/** Return the message compression algorithm from \a call. + * + * \warning This function should \b only be used in test code. */ +grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( + grpc_call* call); + +/** Return the message flags from \a call. + * + * \warning This function should \b only be used in test code. */ +uint32_t grpc_call_test_only_get_message_flags(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. */ +uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call* call); + +#endif /* GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H */ diff --git a/third_party/grpc/src/core/lib/surface/channel.cc b/third_party/grpc/src/core/lib/surface/channel.cc new file mode 100644 index 00000000000000..e47cb4360ea39b --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/channel.cc @@ -0,0 +1,500 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/channel.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_trace.h" +#include "src/core/lib/channel/channelz.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/manual_constructor.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/static_metadata.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; + grpc_compression_options compression_options; + + gpr_atm call_size_estimate; + grpc_resource_user* resource_user; + + gpr_mu registered_call_mu; + registered_call* registered_calls; + + grpc_core::RefCountedPtr channelz_channel; + + char* target; +}; + +#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack*)((c) + 1)) + +static void destroy_channel(void* arg, grpc_error* error); + +grpc_channel* grpc_channel_create_with_builder( + grpc_channel_stack_builder* builder, + grpc_channel_stack_type channel_stack_type) { + char* target = gpr_strdup(grpc_channel_stack_builder_get_target(builder)); + grpc_channel_args* args = grpc_channel_args_copy( + grpc_channel_stack_builder_get_channel_arguments(builder)); + grpc_resource_user* resource_user = + grpc_channel_stack_builder_get_resource_user(builder); + grpc_channel* channel; + if (channel_stack_type == GRPC_SERVER_CHANNEL) { + GRPC_STATS_INC_SERVER_CHANNELS_CREATED(); + } else { + GRPC_STATS_INC_CLIENT_CHANNELS_CREATED(); + } + grpc_error* error = grpc_channel_stack_builder_finish( + builder, sizeof(grpc_channel), 1, destroy_channel, nullptr, + reinterpret_cast(&channel)); + if (error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "channel stack builder failed: %s", + grpc_error_string(error)); + GRPC_ERROR_UNREF(error); + gpr_free(target); + grpc_channel_args_destroy(args); + return channel; + } + + channel->target = target; + channel->resource_user = resource_user; + channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type); + bool channelz_enabled = GRPC_ENABLE_CHANNELZ_DEFAULT; + size_t channel_tracer_max_memory = + GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT; + bool internal_channel = false; + // this creates the default ChannelNode. Different types of channels may + // override this to ensure a correct ChannelNode is created. + grpc_core::channelz::ChannelNodeCreationFunc channel_node_create_func = + grpc_core::channelz::ChannelNode::MakeChannelNode; + gpr_mu_init(&channel->registered_call_mu); + channel->registered_calls = nullptr; + + gpr_atm_no_barrier_store( + &channel->call_size_estimate, + (gpr_atm)CHANNEL_STACK_FROM_CHANNEL(channel)->call_stack_size + + grpc_call_get_initial_size_estimate()); + + grpc_compression_options_init(&channel->compression_options); + for (size_t i = 0; i < args->num_args; i++) { + if (0 == + strcmp(args->args[i].key, GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) { + channel->compression_options.default_level.is_set = true; + channel->compression_options.default_level.level = + static_cast(grpc_channel_arg_get_integer( + &args->args[i], + {GRPC_COMPRESS_LEVEL_NONE, GRPC_COMPRESS_LEVEL_NONE, + GRPC_COMPRESS_LEVEL_COUNT - 1})); + } else if (0 == strcmp(args->args[i].key, + GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) { + channel->compression_options.default_algorithm.is_set = true; + channel->compression_options.default_algorithm.algorithm = + static_cast(grpc_channel_arg_get_integer( + &args->args[i], {GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, + GRPC_COMPRESS_ALGORITHMS_COUNT - 1})); + } else if (0 == + strcmp(args->args[i].key, + GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) { + channel->compression_options.enabled_algorithms_bitset = + static_cast(args->args[i].value.integer) | + 0x1; /* always support no compression */ + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE)) { + const grpc_integer_options options = { + GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX}; + channel_tracer_max_memory = + (size_t)grpc_channel_arg_get_integer(&args->args[i], options); + } else if (0 == strcmp(args->args[i].key, GRPC_ARG_ENABLE_CHANNELZ)) { + // channelz will not be enabled by default until all concerns in + // https://github.com/grpc/grpc/issues/15986 are addressed. + channelz_enabled = grpc_channel_arg_get_bool( + &args->args[i], GRPC_ENABLE_CHANNELZ_DEFAULT); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_CHANNELZ_CHANNEL_NODE_CREATION_FUNC)) { + GPR_ASSERT(args->args[i].type == GRPC_ARG_POINTER); + GPR_ASSERT(args->args[i].value.pointer.p != nullptr); + channel_node_create_func = + reinterpret_cast( + args->args[i].value.pointer.p); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_CHANNELZ_CHANNEL_IS_INTERNAL_CHANNEL)) { + internal_channel = grpc_channel_arg_get_bool(&args->args[i], false); + } + } + + grpc_channel_args_destroy(args); + // we only need to do the channelz bookkeeping for clients here. The channelz + // bookkeeping for server channels occurs in src/core/lib/surface/server.cc + if (channelz_enabled && channel->is_client) { + channel->channelz_channel = channel_node_create_func( + channel, channel_tracer_max_memory, !internal_channel); + channel->channelz_channel->AddTraceEvent( + grpc_core::channelz::ChannelTrace::Severity::Info, + grpc_slice_from_static_string("Channel created")); + } + return channel; +} + +static grpc_core::UniquePtr get_default_authority( + const grpc_channel_args* input_args) { + bool has_default_authority = false; + char* ssl_override = nullptr; + grpc_core::UniquePtr default_authority; + const size_t num_args = input_args != nullptr ? input_args->num_args : 0; + for (size_t i = 0; i < num_args; ++i) { + if (0 == strcmp(input_args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) { + has_default_authority = true; + } else if (0 == strcmp(input_args->args[i].key, + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) { + ssl_override = grpc_channel_arg_get_string(&input_args->args[i]); + } + } + if (!has_default_authority && ssl_override != nullptr) { + default_authority.reset(gpr_strdup(ssl_override)); + } + return default_authority; +} + +static grpc_channel_args* build_channel_args( + const grpc_channel_args* input_args, char* default_authority) { + grpc_arg new_args[1]; + size_t num_new_args = 0; + if (default_authority != nullptr) { + new_args[num_new_args++] = grpc_channel_arg_string_create( + const_cast(GRPC_ARG_DEFAULT_AUTHORITY), default_authority); + } + return grpc_channel_args_copy_and_add(input_args, new_args, num_new_args); +} + +grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node( + grpc_channel* channel) { + return channel->channelz_channel.get(); +} + +grpc_channel* grpc_channel_create(const char* target, + const grpc_channel_args* input_args, + grpc_channel_stack_type channel_stack_type, + grpc_transport* optional_transport, + grpc_resource_user* resource_user) { + grpc_channel_stack_builder* builder = grpc_channel_stack_builder_create(); + const grpc_core::UniquePtr default_authority = + get_default_authority(input_args); + grpc_channel_args* args = + build_channel_args(input_args, default_authority.get()); + grpc_channel_stack_builder_set_channel_arguments(builder, args); + grpc_channel_args_destroy(args); + grpc_channel_stack_builder_set_target(builder, target); + grpc_channel_stack_builder_set_transport(builder, optional_transport); + grpc_channel_stack_builder_set_resource_user(builder, resource_user); + if (!grpc_channel_init_create_stack(builder, channel_stack_type)) { + grpc_channel_stack_builder_destroy(builder); + if (resource_user != nullptr) { + grpc_resource_user_free(resource_user, GRPC_RESOURCE_QUOTA_CHANNEL_SIZE); + } + return nullptr; + } + grpc_channel* channel = + grpc_channel_create_with_builder(builder, channel_stack_type); + return channel; +} + +size_t grpc_channel_get_call_size_estimate(grpc_channel* channel) { +#define ROUND_UP_SIZE 256 + /* We round up our current estimate to the NEXT value of ROUND_UP_SIZE. + This ensures: + 1. a consistent size allocation when our estimate is drifting slowly + (which is common) - which tends to help most allocators reuse memory + 2. a small amount of allowed growth over the estimate without hitting + the arena size doubling case, reducing overall memory usage */ + return (static_cast( + gpr_atm_no_barrier_load(&channel->call_size_estimate)) + + 2 * ROUND_UP_SIZE) & + ~static_cast(ROUND_UP_SIZE - 1); +} + +void grpc_channel_update_call_size_estimate(grpc_channel* channel, + size_t size) { + size_t cur = static_cast( + gpr_atm_no_barrier_load(&channel->call_size_estimate)); + if (cur < size) { + /* size grew: update estimate */ + gpr_atm_no_barrier_cas(&channel->call_size_estimate, + static_cast(cur), + static_cast(size)); + /* if we lose: never mind, something else will likely update soon enough */ + } else if (cur == size) { + /* no change: holding pattern */ + } else if (cur > 0) { + /* size shrank: decrease estimate */ + gpr_atm_no_barrier_cas( + &channel->call_size_estimate, static_cast(cur), + static_cast(GPR_MIN(cur - 1, (255 * cur + size) / 256))); + /* if we lose: never mind, something else will likely update soon enough */ + } +} + +char* grpc_channel_get_target(grpc_channel* channel) { + GRPC_API_TRACE("grpc_channel_get_target(channel=%p)", 1, (channel)); + return gpr_strdup(channel->target); +} + +void grpc_channel_get_info(grpc_channel* channel, + const grpc_channel_info* channel_info) { + grpc_core::ExecCtx exec_ctx; + grpc_channel_element* elem = + grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); + elem->filter->get_channel_info(elem, channel_info); +} + +void grpc_channel_reset_connect_backoff(grpc_channel* channel) { + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE("grpc_channel_reset_connect_backoff(channel=%p)", 1, + (channel)); + grpc_transport_op* op = grpc_make_transport_op(nullptr); + op->reset_connect_backoff = true; + grpc_channel_element* elem = + grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); + elem->filter->start_transport_op(elem, op); +} + +static grpc_call* grpc_channel_create_call_internal( + grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask, + grpc_completion_queue* cq, grpc_pollset_set* pollset_set_alternative, + grpc_mdelem path_mdelem, grpc_mdelem authority_mdelem, + grpc_millis deadline) { + grpc_mdelem send_metadata[2]; + size_t num_metadata = 0; + + GPR_ASSERT(channel->is_client); + GPR_ASSERT(!(cq != nullptr && pollset_set_alternative != nullptr)); + + send_metadata[num_metadata++] = path_mdelem; + if (!GRPC_MDISNULL(authority_mdelem)) { + send_metadata[num_metadata++] = authority_mdelem; + } + + grpc_call_create_args args; + args.channel = channel; + args.server = nullptr; + args.parent = parent_call; + args.propagation_mask = propagation_mask; + args.cq = cq; + args.pollset_set_alternative = pollset_set_alternative; + args.server_transport_data = nullptr; + args.add_initial_metadata = send_metadata; + args.add_initial_metadata_count = num_metadata; + args.send_deadline = deadline; + + grpc_call* call; + GRPC_LOG_IF_ERROR("call_create", grpc_call_create(&args, &call)); + return call; +} + +grpc_call* grpc_channel_create_call(grpc_channel* channel, + grpc_call* parent_call, + uint32_t propagation_mask, + grpc_completion_queue* cq, + grpc_slice method, const grpc_slice* host, + gpr_timespec deadline, void* reserved) { + GPR_ASSERT(!reserved); + grpc_core::ExecCtx exec_ctx; + grpc_call* call = grpc_channel_create_call_internal( + channel, parent_call, propagation_mask, cq, nullptr, + grpc_mdelem_create(GRPC_MDSTR_PATH, method, nullptr), + host != nullptr ? grpc_mdelem_create(GRPC_MDSTR_AUTHORITY, *host, nullptr) + : GRPC_MDNULL, + grpc_timespec_to_millis_round_up(deadline)); + + return call; +} + +grpc_call* grpc_channel_create_pollset_set_call( + grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask, + grpc_pollset_set* pollset_set, const grpc_slice& method, + const grpc_slice* host, grpc_millis deadline, void* reserved) { + GPR_ASSERT(!reserved); + return grpc_channel_create_call_internal( + channel, parent_call, propagation_mask, nullptr, pollset_set, + grpc_mdelem_create(GRPC_MDSTR_PATH, method, nullptr), + host != nullptr ? grpc_mdelem_create(GRPC_MDSTR_AUTHORITY, *host, nullptr) + : GRPC_MDNULL, + deadline); +} + +void* grpc_channel_register_call(grpc_channel* channel, const char* method, + const char* host, void* reserved) { + registered_call* rc = + static_cast(gpr_malloc(sizeof(registered_call))); + GRPC_API_TRACE( + "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)", + 4, (channel, method, host, reserved)); + GPR_ASSERT(!reserved); + grpc_core::ExecCtx exec_ctx; + + rc->path = grpc_mdelem_from_slices( + GRPC_MDSTR_PATH, + grpc_slice_intern(grpc_slice_from_static_string(method))); + rc->authority = + host ? grpc_mdelem_from_slices( + GRPC_MDSTR_AUTHORITY, + grpc_slice_intern(grpc_slice_from_static_string(host))) + : GRPC_MDNULL; + 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, uint32_t propagation_mask, + grpc_completion_queue* completion_queue, void* registered_call_handle, + gpr_timespec deadline, void* reserved) { + registered_call* rc = static_cast(registered_call_handle); + GRPC_API_TRACE( + "grpc_channel_create_registered_call(" + "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, " + "registered_call_handle=%p, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 9, + (channel, parent_call, (unsigned)propagation_mask, completion_queue, + registered_call_handle, deadline.tv_sec, deadline.tv_nsec, + (int)deadline.clock_type, reserved)); + GPR_ASSERT(!reserved); + grpc_core::ExecCtx exec_ctx; + grpc_call* call = grpc_channel_create_call_internal( + channel, parent_call, propagation_mask, completion_queue, nullptr, + GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority), + grpc_timespec_to_millis_round_up(deadline)); + + return call; +} + +#ifndef NDEBUG +#define REF_REASON reason +#define REF_ARG , const char* reason +#else +#define REF_REASON "" +#define REF_ARG +#endif +void grpc_channel_internal_ref(grpc_channel* c REF_ARG) { + GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); +} + +void grpc_channel_internal_unref(grpc_channel* c REF_ARG) { + GRPC_CHANNEL_STACK_UNREF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); +} + +static void destroy_channel(void* arg, grpc_error* error) { + grpc_channel* channel = static_cast(arg); + if (channel->channelz_channel != nullptr) { + channel->channelz_channel->AddTraceEvent( + grpc_core::channelz::ChannelTrace::Severity::Info, + grpc_slice_from_static_string("Channel destroyed")); + channel->channelz_channel->MarkChannelDestroyed(); + channel->channelz_channel.reset(); + } + grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel)); + while (channel->registered_calls) { + registered_call* rc = channel->registered_calls; + channel->registered_calls = rc->next; + GRPC_MDELEM_UNREF(rc->path); + GRPC_MDELEM_UNREF(rc->authority); + gpr_free(rc); + } + if (channel->resource_user != nullptr) { + grpc_resource_user_free(channel->resource_user, + GRPC_RESOURCE_QUOTA_CHANNEL_SIZE); + } + gpr_mu_destroy(&channel->registered_call_mu); + gpr_free(channel->target); + gpr_free(channel); +} + +void grpc_channel_destroy(grpc_channel* channel) { + grpc_transport_op* op = grpc_make_transport_op(nullptr); + grpc_channel_element* elem; + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel)); + op->disconnect_with_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Destroyed"); + 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_compression_options grpc_channel_compression_options( + const grpc_channel* channel) { + return channel->compression_options; +} + +grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_channel* channel, int i) { + char tmp[GPR_LTOA_MIN_BUFSIZE]; + switch (i) { + case 0: + return GRPC_MDELEM_GRPC_STATUS_0; + case 1: + return GRPC_MDELEM_GRPC_STATUS_1; + case 2: + return GRPC_MDELEM_GRPC_STATUS_2; + } + gpr_ltoa(i, tmp); + return grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_STATUS, + grpc_slice_from_copied_string(tmp)); +} diff --git a/third_party/grpc/src/core/lib/surface/channel.h b/third_party/grpc/src/core/lib/surface/channel.h new file mode 100644 index 00000000000000..ab00b8e94f7c74 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/channel.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_H +#define GRPC_CORE_LIB_SURFACE_CHANNEL_H + +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/channel/channelz.h" +#include "src/core/lib/surface/channel_stack_type.h" + +grpc_channel* grpc_channel_create(const char* target, + const grpc_channel_args* args, + grpc_channel_stack_type channel_stack_type, + grpc_transport* optional_transport, + grpc_resource_user* resource_user = nullptr); + +grpc_channel* grpc_channel_create_with_builder( + grpc_channel_stack_builder* builder, + grpc_channel_stack_type channel_stack_type); + +/** Create a call given a grpc_channel, in order to call \a method. + Progress is tied to activity on \a pollset_set. The returned call object is + meant to be used with \a grpc_call_start_batch_and_execute, which relies on + callbacks to signal completions. \a method and \a host need + only live through the invocation of this function. If \a 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, depending on the + value of \a propagation_mask (see propagation_bits.h for possible values) */ +grpc_call* grpc_channel_create_pollset_set_call( + grpc_channel* channel, grpc_call* parent_call, uint32_t propagation_mask, + grpc_pollset_set* pollset_set, const grpc_slice& method, + const grpc_slice* host, grpc_millis deadline, void* reserved); + +/** Get a (borrowed) pointer to this channels underlying channel stack */ +grpc_channel_stack* grpc_channel_get_channel_stack(grpc_channel* channel); + +grpc_core::channelz::ChannelNode* grpc_channel_get_channelz_node( + 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); + +size_t grpc_channel_get_call_size_estimate(grpc_channel* channel); +void grpc_channel_update_call_size_estimate(grpc_channel* channel, size_t size); + +#ifndef NDEBUG +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 + +/** Return the channel's compression options. */ +grpc_compression_options grpc_channel_compression_options( + const grpc_channel* channel); + +#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_H */ diff --git a/third_party/grpc/src/core/lib/surface/channel_init.cc b/third_party/grpc/src/core/lib/surface/channel_init.cc new file mode 100644 index 00000000000000..62eb1c3f9d6c49 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/channel_init.cc @@ -0,0 +1,109 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/channel_init.h" + +#include + +typedef struct stage_slot { + grpc_channel_init_stage fn; + void* arg; + int priority; + size_t insertion_order; +} stage_slot; + +typedef struct stage_slots { + stage_slot* slots; + size_t num_slots; + size_t cap_slots; +} stage_slots; + +static stage_slots g_slots[GRPC_NUM_CHANNEL_STACK_TYPES]; +static bool g_finalized; + +void grpc_channel_init_init(void) { + for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { + g_slots[i].slots = nullptr; + g_slots[i].num_slots = 0; + g_slots[i].cap_slots = 0; + } + g_finalized = false; +} + +void grpc_channel_init_register_stage(grpc_channel_stack_type type, + int priority, + grpc_channel_init_stage stage, + void* stage_arg) { + GPR_ASSERT(!g_finalized); + if (g_slots[type].cap_slots == g_slots[type].num_slots) { + g_slots[type].cap_slots = GPR_MAX(8, 3 * g_slots[type].cap_slots / 2); + g_slots[type].slots = static_cast( + gpr_realloc(g_slots[type].slots, + g_slots[type].cap_slots * sizeof(*g_slots[type].slots))); + } + stage_slot* s = &g_slots[type].slots[g_slots[type].num_slots++]; + s->insertion_order = g_slots[type].num_slots; + s->priority = priority; + s->fn = stage; + s->arg = stage_arg; +} + +static int compare_slots(const void* a, const void* b) { + const stage_slot* sa = static_cast(a); + const stage_slot* sb = static_cast(b); + + int c = GPR_ICMP(sa->priority, sb->priority); + if (c != 0) return c; + return GPR_ICMP(sa->insertion_order, sb->insertion_order); +} + +void grpc_channel_init_finalize(void) { + GPR_ASSERT(!g_finalized); + for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { + qsort(g_slots[i].slots, g_slots[i].num_slots, sizeof(*g_slots[i].slots), + compare_slots); + } + g_finalized = true; +} + +void grpc_channel_init_shutdown(void) { + for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { + gpr_free(g_slots[i].slots); + g_slots[i].slots = + static_cast((void*)static_cast(0xdeadbeef)); + } +} + +bool grpc_channel_init_create_stack(grpc_channel_stack_builder* builder, + grpc_channel_stack_type type) { + GPR_ASSERT(g_finalized); + + grpc_channel_stack_builder_set_name(builder, + grpc_channel_stack_type_string(type)); + + for (size_t i = 0; i < g_slots[type].num_slots; i++) { + const stage_slot* slot = &g_slots[type].slots[i]; + if (!slot->fn(builder, slot->arg)) { + return false; + } + } + + return true; +} diff --git a/third_party/grpc/src/core/lib/surface/channel_init.h b/third_party/grpc/src/core/lib/surface/channel_init.h new file mode 100644 index 00000000000000..f01852473ba30d --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/channel_init.h @@ -0,0 +1,73 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H +#define GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H + +#include + +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/surface/channel_stack_type.h" +#include "src/core/lib/transport/transport.h" + +#define GRPC_CHANNEL_INIT_BUILTIN_PRIORITY 10000 + +/// This module provides a way for plugins (and the grpc core library itself) +/// to register mutators for channel stacks. +/// It also provides a universal entry path to run those mutators to build +/// a channel stack for various subsystems. + +/// One stage of mutation: call functions against \a builder to influence the +/// finally constructed channel stack +typedef bool (*grpc_channel_init_stage)(grpc_channel_stack_builder* builder, + void* arg); + +/// Global initialization of the system +void grpc_channel_init_init(void); + +/// Register one stage of mutators. +/// Stages are run in priority order (lowest to highest), and then in +/// registration order (in the case of a tie). +/// Stages are registered against one of the pre-determined channel stack +/// types. +void grpc_channel_init_register_stage(grpc_channel_stack_type type, + int priority, + grpc_channel_init_stage stage_fn, + void* stage_arg); + +/// Finalize registration. No more calls to grpc_channel_init_register_stage are +/// allowed. +void grpc_channel_init_finalize(void); +/// Shutdown the channel init system +void grpc_channel_init_shutdown(void); + +/// Construct a channel stack of some sort: see channel_stack.h for details +/// \a type is the type of channel stack to create +/// \a prefix_bytes is the number of bytes before the channel stack to allocate +/// \a args are configuration arguments for the channel stack +/// \a initial_refs is the initial refcount to give the channel stack +/// \a destroy and \a destroy_arg specify how to destroy the channel stack +/// if destroy_arg is NULL, the returned value from this function will be +/// substituted +/// \a optional_transport is either NULL or a constructed transport object +/// Returns a pointer to the base of the memory allocated (the actual channel +/// stack object will be prefix_bytes past that pointer) +bool grpc_channel_init_create_stack(grpc_channel_stack_builder* builder, + grpc_channel_stack_type type); + +#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H */ diff --git a/third_party/grpc/src/core/lib/surface/channel_ping.cc b/third_party/grpc/src/core/lib/surface/channel_ping.cc new file mode 100644 index 00000000000000..bae945942f210c --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/channel_ping.cc @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/channel.h" + +#include + +#include +#include + +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/completion_queue.h" + +typedef struct { + grpc_closure closure; + void* tag; + grpc_completion_queue* cq; + grpc_cq_completion completion_storage; +} ping_result; + +static void ping_destroy(void* arg, grpc_cq_completion* storage) { + gpr_free(arg); +} + +static void ping_done(void* arg, grpc_error* error) { + ping_result* pr = static_cast(arg); + grpc_cq_end_op(pr->cq, pr->tag, GRPC_ERROR_REF(error), ping_destroy, pr, + &pr->completion_storage); +} + +void grpc_channel_ping(grpc_channel* channel, grpc_completion_queue* cq, + void* tag, void* reserved) { + GRPC_API_TRACE("grpc_channel_ping(channel=%p, cq=%p, tag=%p, reserved=%p)", 4, + (channel, cq, tag, reserved)); + grpc_transport_op* op = grpc_make_transport_op(nullptr); + ping_result* pr = static_cast(gpr_malloc(sizeof(*pr))); + grpc_channel_element* top_elem = + grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + grpc_core::ExecCtx exec_ctx; + GPR_ASSERT(reserved == nullptr); + pr->tag = tag; + pr->cq = cq; + GRPC_CLOSURE_INIT(&pr->closure, ping_done, pr, grpc_schedule_on_exec_ctx); + op->send_ping.on_ack = &pr->closure; + op->bind_pollset = grpc_cq_pollset(cq); + GPR_ASSERT(grpc_cq_begin_op(cq, tag)); + top_elem->filter->start_transport_op(top_elem, op); +} diff --git a/third_party/grpc/src/core/lib/surface/channel_stack_type.cc b/third_party/grpc/src/core/lib/surface/channel_stack_type.cc new file mode 100644 index 00000000000000..fcf96ddc9fba74 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/channel_stack_type.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include "src/core/lib/surface/channel_stack_type.h" + +bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type) { + switch (type) { + case GRPC_CLIENT_CHANNEL: + return true; + case GRPC_CLIENT_SUBCHANNEL: + return true; + case GRPC_CLIENT_LAME_CHANNEL: + return true; + case GRPC_CLIENT_DIRECT_CHANNEL: + return true; + case GRPC_SERVER_CHANNEL: + return false; + case GRPC_NUM_CHANNEL_STACK_TYPES: + break; + } + GPR_UNREACHABLE_CODE(return true;); +} + +const char* grpc_channel_stack_type_string(grpc_channel_stack_type type) { + switch (type) { + case GRPC_CLIENT_CHANNEL: + return "CLIENT_CHANNEL"; + case GRPC_CLIENT_SUBCHANNEL: + return "CLIENT_SUBCHANNEL"; + case GRPC_SERVER_CHANNEL: + return "SERVER_CHANNEL"; + case GRPC_CLIENT_LAME_CHANNEL: + return "CLIENT_LAME_CHANNEL"; + case GRPC_CLIENT_DIRECT_CHANNEL: + return "CLIENT_DIRECT_CHANNEL"; + case GRPC_NUM_CHANNEL_STACK_TYPES: + break; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} diff --git a/third_party/grpc/src/core/lib/surface/channel_stack_type.h b/third_party/grpc/src/core/lib/surface/channel_stack_type.h new file mode 100644 index 00000000000000..8a3c08e1cce279 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/channel_stack_type.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H +#define GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H + +#include + +#include + +typedef enum { + // normal top-half client channel with load-balancing, connection management + GRPC_CLIENT_CHANNEL, + // bottom-half of a client channel: everything that happens post-load + // balancing (bound to a specific transport) + GRPC_CLIENT_SUBCHANNEL, + // a permanently broken client channel + GRPC_CLIENT_LAME_CHANNEL, + // a directly connected client channel (without load-balancing, directly talks + // to a transport) + GRPC_CLIENT_DIRECT_CHANNEL, + // server side channel + GRPC_SERVER_CHANNEL, + // must be last + GRPC_NUM_CHANNEL_STACK_TYPES +} grpc_channel_stack_type; + +bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type); + +const char* grpc_channel_stack_type_string(grpc_channel_stack_type type); + +#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H */ diff --git a/third_party/grpc/src/core/lib/surface/completion_queue.cc b/third_party/grpc/src/core/lib/surface/completion_queue.cc new file mode 100644 index 00000000000000..661022ec5f1350 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/completion_queue.cc @@ -0,0 +1,1408 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "src/core/lib/surface/completion_queue.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/spinlock.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/tls.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/event_string.h" + +grpc_core::TraceFlag grpc_trace_operation_failures(false, "op_failure"); +grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags(false, "pending_tags"); +grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount(false, "cq_refcount"); + +// Specifies a cq thread local cache. +// The first event that occurs on a thread +// with a cq cache will go into that cache, and +// will only be returned on the thread that initialized the cache. +// NOTE: Only one event will ever be cached. +GPR_TLS_DECL(g_cached_event); +GPR_TLS_DECL(g_cached_cq); + +typedef struct { + grpc_pollset_worker** worker; + void* tag; +} plucker; + +typedef struct { + bool can_get_pollset; + bool can_listen; + size_t (*size)(void); + void (*init)(grpc_pollset* pollset, gpr_mu** mu); + grpc_error* (*kick)(grpc_pollset* pollset, + grpc_pollset_worker* specific_worker); + grpc_error* (*work)(grpc_pollset* pollset, grpc_pollset_worker** worker, + grpc_millis deadline); + void (*shutdown)(grpc_pollset* pollset, grpc_closure* closure); + void (*destroy)(grpc_pollset* pollset); +} cq_poller_vtable; + +typedef struct non_polling_worker { + gpr_cv cv; + bool kicked; + struct non_polling_worker* next; + struct non_polling_worker* prev; +} non_polling_worker; + +typedef struct { + gpr_mu mu; + bool kicked_without_poller; + non_polling_worker* root; + grpc_closure* shutdown; +} non_polling_poller; + +static size_t non_polling_poller_size(void) { + return sizeof(non_polling_poller); +} + +static void non_polling_poller_init(grpc_pollset* pollset, gpr_mu** mu) { + non_polling_poller* npp = reinterpret_cast(pollset); + gpr_mu_init(&npp->mu); + *mu = &npp->mu; +} + +static void non_polling_poller_destroy(grpc_pollset* pollset) { + non_polling_poller* npp = reinterpret_cast(pollset); + gpr_mu_destroy(&npp->mu); +} + +static grpc_error* non_polling_poller_work(grpc_pollset* pollset, + grpc_pollset_worker** worker, + grpc_millis deadline) { + non_polling_poller* npp = reinterpret_cast(pollset); + if (npp->shutdown) return GRPC_ERROR_NONE; + if (npp->kicked_without_poller) { + npp->kicked_without_poller = false; + return GRPC_ERROR_NONE; + } + non_polling_worker w; + gpr_cv_init(&w.cv); + if (worker != nullptr) *worker = reinterpret_cast(&w); + if (npp->root == nullptr) { + npp->root = w.next = w.prev = &w; + } else { + w.next = npp->root; + w.prev = w.next->prev; + w.next->prev = w.prev->next = &w; + } + w.kicked = false; + gpr_timespec deadline_ts = + grpc_millis_to_timespec(deadline, GPR_CLOCK_MONOTONIC); + while (!npp->shutdown && !w.kicked && + !gpr_cv_wait(&w.cv, &npp->mu, deadline_ts)) + ; + grpc_core::ExecCtx::Get()->InvalidateNow(); + if (&w == npp->root) { + npp->root = w.next; + if (&w == npp->root) { + if (npp->shutdown) { + GRPC_CLOSURE_SCHED(npp->shutdown, GRPC_ERROR_NONE); + } + npp->root = nullptr; + } + } + w.next->prev = w.prev; + w.prev->next = w.next; + gpr_cv_destroy(&w.cv); + if (worker != nullptr) *worker = nullptr; + return GRPC_ERROR_NONE; +} + +static grpc_error* non_polling_poller_kick( + grpc_pollset* pollset, grpc_pollset_worker* specific_worker) { + non_polling_poller* p = reinterpret_cast(pollset); + if (specific_worker == nullptr) + specific_worker = reinterpret_cast(p->root); + if (specific_worker != nullptr) { + non_polling_worker* w = + reinterpret_cast(specific_worker); + if (!w->kicked) { + w->kicked = true; + gpr_cv_signal(&w->cv); + } + } else { + p->kicked_without_poller = true; + } + return GRPC_ERROR_NONE; +} + +static void non_polling_poller_shutdown(grpc_pollset* pollset, + grpc_closure* closure) { + non_polling_poller* p = reinterpret_cast(pollset); + GPR_ASSERT(closure != nullptr); + p->shutdown = closure; + if (p->root == nullptr) { + GRPC_CLOSURE_SCHED(closure, GRPC_ERROR_NONE); + } else { + non_polling_worker* w = p->root; + do { + gpr_cv_signal(&w->cv); + w = w->next; + } while (w != p->root); + } +} + +static const cq_poller_vtable g_poller_vtable_by_poller_type[] = { + /* GRPC_CQ_DEFAULT_POLLING */ + {true, true, grpc_pollset_size, grpc_pollset_init, grpc_pollset_kick, + grpc_pollset_work, grpc_pollset_shutdown, grpc_pollset_destroy}, + /* GRPC_CQ_NON_LISTENING */ + {true, false, grpc_pollset_size, grpc_pollset_init, grpc_pollset_kick, + grpc_pollset_work, grpc_pollset_shutdown, grpc_pollset_destroy}, + /* GRPC_CQ_NON_POLLING */ + {false, false, non_polling_poller_size, non_polling_poller_init, + non_polling_poller_kick, non_polling_poller_work, + non_polling_poller_shutdown, non_polling_poller_destroy}, +}; + +typedef struct cq_vtable { + grpc_cq_completion_type cq_completion_type; + size_t data_size; + void (*init)(void* data, + grpc_experimental_completion_queue_functor* shutdown_callback); + void (*shutdown)(grpc_completion_queue* cq); + void (*destroy)(void* data); + bool (*begin_op)(grpc_completion_queue* cq, void* tag); + void (*end_op)(grpc_completion_queue* cq, void* tag, grpc_error* error, + void (*done)(void* done_arg, grpc_cq_completion* storage), + void* done_arg, grpc_cq_completion* storage); + grpc_event (*next)(grpc_completion_queue* cq, gpr_timespec deadline, + void* reserved); + grpc_event (*pluck)(grpc_completion_queue* cq, void* tag, + gpr_timespec deadline, void* reserved); +} cq_vtable; + +/* Queue that holds the cq_completion_events. Internally uses gpr_mpscq queue + * (a lockfree multiproducer single consumer queue). It uses a queue_lock + * to support multiple consumers. + * Only used in completion queues whose completion_type is GRPC_CQ_NEXT */ +typedef struct grpc_cq_event_queue { + /* Spinlock to serialize consumers i.e pop() operations */ + gpr_spinlock queue_lock; + + gpr_mpscq queue; + + /* A lazy counter of number of items in the queue. This is NOT atomically + incremented/decremented along with push/pop operations and hence is only + eventually consistent */ + gpr_atm num_queue_items; +} grpc_cq_event_queue; + +typedef struct cq_next_data { + /** Completed events for completion-queues of type GRPC_CQ_NEXT */ + grpc_cq_event_queue queue; + + /** Counter of how many things have ever been queued on this completion queue + useful for avoiding locks to check the queue */ + gpr_atm things_queued_ever; + + /* Number of outstanding events (+1 if not shut down) */ + gpr_atm pending_events; + + /** 0 initially. 1 once we initiated shutdown */ + bool shutdown_called; +} cq_next_data; + +typedef struct cq_pluck_data { + /** Completed events for completion-queues of type GRPC_CQ_PLUCK */ + grpc_cq_completion completed_head; + grpc_cq_completion* completed_tail; + + /** Number of pending events (+1 if we're not shutdown) */ + gpr_atm pending_events; + + /** Counter of how many things have ever been queued on this completion queue + useful for avoiding locks to check the queue */ + gpr_atm things_queued_ever; + + /** 0 initially. 1 once we completed shutting */ + /* TODO: (sreek) This is not needed since (shutdown == 1) if and only if + * (pending_events == 0). So consider removing this in future and use + * pending_events */ + gpr_atm shutdown; + + /** 0 initially. 1 once we initiated shutdown */ + bool shutdown_called; + + int num_pluckers; + plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS]; +} cq_pluck_data; + +typedef struct cq_callback_data { + /** No actual completed events queue, unlike other types */ + + /** Number of pending events (+1 if we're not shutdown) */ + gpr_atm pending_events; + + /** Counter of how many things have ever been queued on this completion queue + useful for avoiding locks to check the queue */ + gpr_atm things_queued_ever; + + /** 0 initially. 1 once we initiated shutdown */ + bool shutdown_called; + + /** A callback that gets invoked when the CQ completes shutdown */ + grpc_experimental_completion_queue_functor* shutdown_callback; +} cq_callback_data; + +/* Completion queue structure */ +struct grpc_completion_queue { + /** Once owning_refs drops to zero, we will destroy the cq */ + gpr_refcount owning_refs; + + gpr_mu* mu; + + const cq_vtable* vtable; + const cq_poller_vtable* poller_vtable; + +#ifndef NDEBUG + void** outstanding_tags; + size_t outstanding_tag_count; + size_t outstanding_tag_capacity; +#endif + + grpc_closure pollset_shutdown_done; + int num_polls; +}; + +/* Forward declarations */ +static void cq_finish_shutdown_next(grpc_completion_queue* cq); +static void cq_finish_shutdown_pluck(grpc_completion_queue* cq); +static void cq_finish_shutdown_callback(grpc_completion_queue* cq); +static void cq_shutdown_next(grpc_completion_queue* cq); +static void cq_shutdown_pluck(grpc_completion_queue* cq); +static void cq_shutdown_callback(grpc_completion_queue* cq); + +static bool cq_begin_op_for_next(grpc_completion_queue* cq, void* tag); +static bool cq_begin_op_for_pluck(grpc_completion_queue* cq, void* tag); +static bool cq_begin_op_for_callback(grpc_completion_queue* cq, void* tag); + +// A cq_end_op function is called when an operation on a given CQ with +// a given tag has completed. The storage argument is a reference to the +// space reserved for this completion as it is placed into the corresponding +// queue. The done argument is a callback that will be invoked when it is +// safe to free up that storage. The storage MUST NOT be freed until the +// done callback is invoked. +static void cq_end_op_for_next(grpc_completion_queue* cq, void* tag, + grpc_error* error, + void (*done)(void* done_arg, + grpc_cq_completion* storage), + void* done_arg, grpc_cq_completion* storage); + +static void cq_end_op_for_pluck(grpc_completion_queue* cq, void* tag, + grpc_error* error, + void (*done)(void* done_arg, + grpc_cq_completion* storage), + void* done_arg, grpc_cq_completion* storage); + +static void cq_end_op_for_callback(grpc_completion_queue* cq, void* tag, + grpc_error* error, + void (*done)(void* done_arg, + grpc_cq_completion* storage), + void* done_arg, grpc_cq_completion* storage); + +static grpc_event cq_next(grpc_completion_queue* cq, gpr_timespec deadline, + void* reserved); + +static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag, + gpr_timespec deadline, void* reserved); + +// Note that cq_init_next and cq_init_pluck do not use the shutdown_callback +static void cq_init_next( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback); +static void cq_init_pluck( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback); +static void cq_init_callback( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback); +static void cq_destroy_next(void* data); +static void cq_destroy_pluck(void* data); +static void cq_destroy_callback(void* data); + +/* Completion queue vtables based on the completion-type */ +static const cq_vtable g_cq_vtable[] = { + /* GRPC_CQ_NEXT */ + {GRPC_CQ_NEXT, sizeof(cq_next_data), cq_init_next, cq_shutdown_next, + cq_destroy_next, cq_begin_op_for_next, cq_end_op_for_next, cq_next, + nullptr}, + /* GRPC_CQ_PLUCK */ + {GRPC_CQ_PLUCK, sizeof(cq_pluck_data), cq_init_pluck, cq_shutdown_pluck, + cq_destroy_pluck, cq_begin_op_for_pluck, cq_end_op_for_pluck, nullptr, + cq_pluck}, + /* GRPC_CQ_CALLBACK */ + {GRPC_CQ_CALLBACK, sizeof(cq_callback_data), cq_init_callback, + cq_shutdown_callback, cq_destroy_callback, cq_begin_op_for_callback, + cq_end_op_for_callback, nullptr, nullptr}, +}; + +#define DATA_FROM_CQ(cq) ((void*)(cq + 1)) +#define POLLSET_FROM_CQ(cq) \ + ((grpc_pollset*)(cq->vtable->data_size + (char*)DATA_FROM_CQ(cq))) + +grpc_core::TraceFlag grpc_cq_pluck_trace(false, "queue_pluck"); + +#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \ + if (grpc_api_trace.enabled() && (grpc_cq_pluck_trace.enabled() || \ + (event)->type != GRPC_QUEUE_TIMEOUT)) { \ + char* _ev = grpc_event_string(event); \ + gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \ + gpr_free(_ev); \ + } + +static void on_pollset_shutdown_done(void* cq, grpc_error* error); + +void grpc_cq_global_init() { + gpr_tls_init(&g_cached_event); + gpr_tls_init(&g_cached_cq); +} + +void grpc_completion_queue_thread_local_cache_init(grpc_completion_queue* cq) { + if ((grpc_completion_queue*)gpr_tls_get(&g_cached_cq) == nullptr) { + gpr_tls_set(&g_cached_event, (intptr_t)0); + gpr_tls_set(&g_cached_cq, (intptr_t)cq); + } +} + +int grpc_completion_queue_thread_local_cache_flush(grpc_completion_queue* cq, + void** tag, int* ok) { + grpc_cq_completion* storage = + (grpc_cq_completion*)gpr_tls_get(&g_cached_event); + int ret = 0; + if (storage != nullptr && + (grpc_completion_queue*)gpr_tls_get(&g_cached_cq) == cq) { + *tag = storage->tag; + grpc_core::ExecCtx exec_ctx; + *ok = (storage->next & static_cast(1)) == 1; + storage->done(storage->done_arg, storage); + ret = 1; + cq_next_data* cqd = static_cast DATA_FROM_CQ(cq); + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); + gpr_mu_lock(cq->mu); + cq_finish_shutdown_next(cq); + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down"); + } + } + gpr_tls_set(&g_cached_event, (intptr_t)0); + gpr_tls_set(&g_cached_cq, (intptr_t)0); + + return ret; +} + +static void cq_event_queue_init(grpc_cq_event_queue* q) { + gpr_mpscq_init(&q->queue); + q->queue_lock = GPR_SPINLOCK_INITIALIZER; + gpr_atm_no_barrier_store(&q->num_queue_items, 0); +} + +static void cq_event_queue_destroy(grpc_cq_event_queue* q) { + gpr_mpscq_destroy(&q->queue); +} + +static bool cq_event_queue_push(grpc_cq_event_queue* q, grpc_cq_completion* c) { + gpr_mpscq_push(&q->queue, reinterpret_cast(c)); + return gpr_atm_no_barrier_fetch_add(&q->num_queue_items, 1) == 0; +} + +static grpc_cq_completion* cq_event_queue_pop(grpc_cq_event_queue* q) { + grpc_cq_completion* c = nullptr; + + if (gpr_spinlock_trylock(&q->queue_lock)) { + GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_SUCCESSES(); + + bool is_empty = false; + c = reinterpret_cast( + gpr_mpscq_pop_and_check_end(&q->queue, &is_empty)); + gpr_spinlock_unlock(&q->queue_lock); + + if (c == nullptr && !is_empty) { + GRPC_STATS_INC_CQ_EV_QUEUE_TRANSIENT_POP_FAILURES(); + } + } else { + GRPC_STATS_INC_CQ_EV_QUEUE_TRYLOCK_FAILURES(); + } + + if (c) { + gpr_atm_no_barrier_fetch_add(&q->num_queue_items, -1); + } + + return c; +} + +/* Note: The counter is not incremented/decremented atomically with push/pop. + * The count is only eventually consistent */ +static long cq_event_queue_num_items(grpc_cq_event_queue* q) { + return static_cast(gpr_atm_no_barrier_load(&q->num_queue_items)); +} + +grpc_completion_queue* grpc_completion_queue_create_internal( + grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type, + grpc_experimental_completion_queue_functor* shutdown_callback) { + GPR_TIMER_SCOPE("grpc_completion_queue_create_internal", 0); + + grpc_completion_queue* cq; + + GRPC_API_TRACE( + "grpc_completion_queue_create_internal(completion_type=%d, " + "polling_type=%d)", + 2, (completion_type, polling_type)); + + const cq_vtable* vtable = &g_cq_vtable[completion_type]; + const cq_poller_vtable* poller_vtable = + &g_poller_vtable_by_poller_type[polling_type]; + + grpc_core::ExecCtx exec_ctx; + GRPC_STATS_INC_CQS_CREATED(); + + cq = static_cast( + gpr_zalloc(sizeof(grpc_completion_queue) + vtable->data_size + + poller_vtable->size())); + + cq->vtable = vtable; + cq->poller_vtable = poller_vtable; + + /* One for destroy(), one for pollset_shutdown */ + gpr_ref_init(&cq->owning_refs, 2); + + poller_vtable->init(POLLSET_FROM_CQ(cq), &cq->mu); + vtable->init(DATA_FROM_CQ(cq), shutdown_callback); + + GRPC_CLOSURE_INIT(&cq->pollset_shutdown_done, on_pollset_shutdown_done, cq, + grpc_schedule_on_exec_ctx); + return cq; +} + +static void cq_init_next( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback) { + cq_next_data* cqd = static_cast(data); + /* Initial count is dropped by grpc_completion_queue_shutdown */ + gpr_atm_no_barrier_store(&cqd->pending_events, 1); + cqd->shutdown_called = false; + gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); + cq_event_queue_init(&cqd->queue); +} + +static void cq_destroy_next(void* data) { + cq_next_data* cqd = static_cast(data); + GPR_ASSERT(cq_event_queue_num_items(&cqd->queue) == 0); + cq_event_queue_destroy(&cqd->queue); +} + +static void cq_init_pluck( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback) { + cq_pluck_data* cqd = static_cast(data); + /* Initial count is dropped by grpc_completion_queue_shutdown */ + gpr_atm_no_barrier_store(&cqd->pending_events, 1); + cqd->completed_tail = &cqd->completed_head; + cqd->completed_head.next = (uintptr_t)cqd->completed_tail; + gpr_atm_no_barrier_store(&cqd->shutdown, 0); + cqd->shutdown_called = false; + cqd->num_pluckers = 0; + gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); +} + +static void cq_destroy_pluck(void* data) { + cq_pluck_data* cqd = static_cast(data); + GPR_ASSERT(cqd->completed_head.next == (uintptr_t)&cqd->completed_head); +} + +static void cq_init_callback( + void* data, grpc_experimental_completion_queue_functor* shutdown_callback) { + cq_callback_data* cqd = static_cast(data); + /* Initial count is dropped by grpc_completion_queue_shutdown */ + gpr_atm_no_barrier_store(&cqd->pending_events, 1); + cqd->shutdown_called = false; + gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); + cqd->shutdown_callback = shutdown_callback; +} + +static void cq_destroy_callback(void* data) {} + +grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue* cq) { + return cq->vtable->cq_completion_type; +} + +int grpc_get_cq_poll_num(grpc_completion_queue* cq) { + int cur_num_polls; + gpr_mu_lock(cq->mu); + cur_num_polls = cq->num_polls; + gpr_mu_unlock(cq->mu); + return cur_num_polls; +} + +#ifndef NDEBUG +void grpc_cq_internal_ref(grpc_completion_queue* cq, const char* reason, + const char* file, int line) { + if (grpc_trace_cq_refcount.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "CQ:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val + 1, + reason); + } +#else +void grpc_cq_internal_ref(grpc_completion_queue* cq) { +#endif + gpr_ref(&cq->owning_refs); +} + +static void on_pollset_shutdown_done(void* arg, grpc_error* error) { + grpc_completion_queue* cq = static_cast(arg); + GRPC_CQ_INTERNAL_UNREF(cq, "pollset_destroy"); +} + +#ifndef NDEBUG +void grpc_cq_internal_unref(grpc_completion_queue* cq, const char* reason, + const char* file, int line) { + if (grpc_trace_cq_refcount.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "CQ:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val - 1, + reason); + } +#else +void grpc_cq_internal_unref(grpc_completion_queue* cq) { +#endif + if (gpr_unref(&cq->owning_refs)) { + cq->vtable->destroy(DATA_FROM_CQ(cq)); + cq->poller_vtable->destroy(POLLSET_FROM_CQ(cq)); +#ifndef NDEBUG + gpr_free(cq->outstanding_tags); +#endif + gpr_free(cq); + } +} + +#ifndef NDEBUG +static void cq_check_tag(grpc_completion_queue* cq, void* tag, bool lock_cq) { + int found = 0; + if (lock_cq) { + gpr_mu_lock(cq->mu); + } + + for (int i = 0; i < static_cast(cq->outstanding_tag_count); i++) { + if (cq->outstanding_tags[i] == tag) { + cq->outstanding_tag_count--; + GPR_SWAP(void*, cq->outstanding_tags[i], + cq->outstanding_tags[cq->outstanding_tag_count]); + found = 1; + break; + } + } + + if (lock_cq) { + gpr_mu_unlock(cq->mu); + } + + GPR_ASSERT(found); +} +#else +static void cq_check_tag(grpc_completion_queue* cq, void* tag, bool lock_cq) {} +#endif + +/* Atomically increments a counter only if the counter is not zero. Returns + * true if the increment was successful; false if the counter is zero */ +static bool atm_inc_if_nonzero(gpr_atm* counter) { + while (true) { + gpr_atm count = gpr_atm_acq_load(counter); + /* If zero, we are done. If not, we must to a CAS (instead of an atomic + * increment) to maintain the contract: do not increment the counter if it + * is zero. */ + if (count == 0) { + return false; + } else if (gpr_atm_full_cas(counter, count, count + 1)) { + break; + } + } + + return true; +} + +static bool cq_begin_op_for_next(grpc_completion_queue* cq, void* tag) { + cq_next_data* cqd = static_cast DATA_FROM_CQ(cq); + return atm_inc_if_nonzero(&cqd->pending_events); +} + +static bool cq_begin_op_for_pluck(grpc_completion_queue* cq, void* tag) { + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + return atm_inc_if_nonzero(&cqd->pending_events); +} + +static bool cq_begin_op_for_callback(grpc_completion_queue* cq, void* tag) { + cq_callback_data* cqd = static_cast DATA_FROM_CQ(cq); + return atm_inc_if_nonzero(&cqd->pending_events); +} + +bool grpc_cq_begin_op(grpc_completion_queue* cq, void* tag) { +#ifndef NDEBUG + gpr_mu_lock(cq->mu); + if (cq->outstanding_tag_count == cq->outstanding_tag_capacity) { + cq->outstanding_tag_capacity = GPR_MAX(4, 2 * cq->outstanding_tag_capacity); + cq->outstanding_tags = static_cast(gpr_realloc( + cq->outstanding_tags, + sizeof(*cq->outstanding_tags) * cq->outstanding_tag_capacity)); + } + cq->outstanding_tags[cq->outstanding_tag_count++] = tag; + gpr_mu_unlock(cq->mu); +#endif + return cq->vtable->begin_op(cq, tag); +} + +/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a + * completion + * type of GRPC_CQ_NEXT) */ +static void cq_end_op_for_next(grpc_completion_queue* cq, void* tag, + grpc_error* error, + void (*done)(void* done_arg, + grpc_cq_completion* storage), + void* done_arg, grpc_cq_completion* storage) { + GPR_TIMER_SCOPE("cq_end_op_for_next", 0); + + if (grpc_api_trace.enabled() || + (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) { + const char* errmsg = grpc_error_string(error); + GRPC_API_TRACE( + "cq_end_op_for_next(cq=%p, tag=%p, error=%s, " + "done=%p, done_arg=%p, storage=%p)", + 6, (cq, tag, errmsg, done, done_arg, storage)); + if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); + } + } + cq_next_data* cqd = static_cast DATA_FROM_CQ(cq); + int is_success = (error == GRPC_ERROR_NONE); + + storage->tag = tag; + storage->done = done; + storage->done_arg = done_arg; + storage->next = static_cast(is_success); + + cq_check_tag(cq, tag, true); /* Used in debug builds only */ + + if ((grpc_completion_queue*)gpr_tls_get(&g_cached_cq) == cq && + (grpc_cq_completion*)gpr_tls_get(&g_cached_event) == nullptr) { + gpr_tls_set(&g_cached_event, (intptr_t)storage); + } else { + /* Add the completion to the queue */ + bool is_first = cq_event_queue_push(&cqd->queue, storage); + gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); + + /* Since we do not hold the cq lock here, it is important to do an 'acquire' + load here (instead of a 'no_barrier' load) to match with the release + store + (done via gpr_atm_full_fetch_add(pending_events, -1)) in cq_shutdown_next + */ + bool will_definitely_shutdown = gpr_atm_acq_load(&cqd->pending_events) == 1; + + if (!will_definitely_shutdown) { + /* Only kick if this is the first item queued */ + if (is_first) { + gpr_mu_lock(cq->mu); + grpc_error* kick_error = + cq->poller_vtable->kick(POLLSET_FROM_CQ(cq), nullptr); + gpr_mu_unlock(cq->mu); + + if (kick_error != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(kick_error); + gpr_log(GPR_ERROR, "Kick failed: %s", msg); + GRPC_ERROR_UNREF(kick_error); + } + } + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); + gpr_mu_lock(cq->mu); + cq_finish_shutdown_next(cq); + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down"); + } + } else { + GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); + gpr_atm_rel_store(&cqd->pending_events, 0); + gpr_mu_lock(cq->mu); + cq_finish_shutdown_next(cq); + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down"); + } + } + + GRPC_ERROR_UNREF(error); +} + +/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a + * completion + * type of GRPC_CQ_PLUCK) */ +static void cq_end_op_for_pluck(grpc_completion_queue* cq, void* tag, + grpc_error* error, + void (*done)(void* done_arg, + grpc_cq_completion* storage), + void* done_arg, grpc_cq_completion* storage) { + GPR_TIMER_SCOPE("cq_end_op_for_pluck", 0); + + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + int is_success = (error == GRPC_ERROR_NONE); + + if (grpc_api_trace.enabled() || + (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) { + const char* errmsg = grpc_error_string(error); + GRPC_API_TRACE( + "cq_end_op_for_pluck(cq=%p, tag=%p, error=%s, " + "done=%p, done_arg=%p, storage=%p)", + 6, (cq, tag, errmsg, done, done_arg, storage)); + if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); + } + } + + storage->tag = tag; + storage->done = done; + storage->done_arg = done_arg; + storage->next = + ((uintptr_t)&cqd->completed_head) | (static_cast(is_success)); + + gpr_mu_lock(cq->mu); + cq_check_tag(cq, tag, false); /* Used in debug builds only */ + + /* Add to the list of completions */ + gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); + cqd->completed_tail->next = + ((uintptr_t)storage) | (1u & cqd->completed_tail->next); + cqd->completed_tail = storage; + + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + cq_finish_shutdown_pluck(cq); + gpr_mu_unlock(cq->mu); + } else { + grpc_pollset_worker* pluck_worker = nullptr; + for (int i = 0; i < cqd->num_pluckers; i++) { + if (cqd->pluckers[i].tag == tag) { + pluck_worker = *cqd->pluckers[i].worker; + break; + } + } + + grpc_error* kick_error = + cq->poller_vtable->kick(POLLSET_FROM_CQ(cq), pluck_worker); + + gpr_mu_unlock(cq->mu); + + if (kick_error != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(kick_error); + gpr_log(GPR_ERROR, "Kick failed: %s", msg); + + GRPC_ERROR_UNREF(kick_error); + } + } + + GRPC_ERROR_UNREF(error); +} + +/* Complete an event on a completion queue of type GRPC_CQ_CALLBACK */ +static void cq_end_op_for_callback( + grpc_completion_queue* cq, void* tag, grpc_error* error, + void (*done)(void* done_arg, grpc_cq_completion* storage), void* done_arg, + grpc_cq_completion* storage) { + GPR_TIMER_SCOPE("cq_end_op_for_callback", 0); + + cq_callback_data* cqd = static_cast DATA_FROM_CQ(cq); + bool is_success = (error == GRPC_ERROR_NONE); + + if (grpc_api_trace.enabled() || + (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE)) { + const char* errmsg = grpc_error_string(error); + GRPC_API_TRACE( + "cq_end_op_for_callback(cq=%p, tag=%p, error=%s, " + "done=%p, done_arg=%p, storage=%p)", + 6, (cq, tag, errmsg, done, done_arg, storage)); + if (grpc_trace_operation_failures.enabled() && error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); + } + } + + // The callback-based CQ isn't really a queue at all and thus has no need + // for reserved storage. Invoke the done callback right away to release it. + done(done_arg, storage); + + gpr_mu_lock(cq->mu); + cq_check_tag(cq, tag, false); /* Used in debug builds only */ + + gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + gpr_mu_unlock(cq->mu); + cq_finish_shutdown_callback(cq); + } else { + gpr_mu_unlock(cq->mu); + } + + GRPC_ERROR_UNREF(error); + + auto* functor = static_cast(tag); + (*functor->functor_run)(functor, is_success); +} + +void grpc_cq_end_op(grpc_completion_queue* cq, void* tag, grpc_error* error, + void (*done)(void* done_arg, grpc_cq_completion* storage), + void* done_arg, grpc_cq_completion* storage) { + cq->vtable->end_op(cq, tag, error, done, done_arg, storage); +} + +typedef struct { + gpr_atm last_seen_things_queued_ever; + grpc_completion_queue* cq; + grpc_millis deadline; + grpc_cq_completion* stolen_completion; + void* tag; /* for pluck */ + bool first_loop; +} cq_is_finished_arg; + +class ExecCtxNext : public grpc_core::ExecCtx { + public: + ExecCtxNext(void* arg) : ExecCtx(0), check_ready_to_finish_arg_(arg) {} + + bool CheckReadyToFinish() override { + cq_is_finished_arg* a = + static_cast(check_ready_to_finish_arg_); + grpc_completion_queue* cq = a->cq; + cq_next_data* cqd = static_cast DATA_FROM_CQ(cq); + GPR_ASSERT(a->stolen_completion == nullptr); + + gpr_atm current_last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + + if (current_last_seen_things_queued_ever != + a->last_seen_things_queued_ever) { + a->last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + + /* Pop a cq_completion from the queue. Returns NULL if the queue is empty + * might return NULL in some cases even if the queue is not empty; but + * that + * is ok and doesn't affect correctness. Might effect the tail latencies a + * bit) */ + a->stolen_completion = cq_event_queue_pop(&cqd->queue); + if (a->stolen_completion != nullptr) { + return true; + } + } + return !a->first_loop && a->deadline < grpc_core::ExecCtx::Get()->Now(); + } + + private: + void* check_ready_to_finish_arg_; +}; + +#ifndef NDEBUG +static void dump_pending_tags(grpc_completion_queue* cq) { + if (!grpc_trace_pending_tags.enabled()) return; + + gpr_strvec v; + gpr_strvec_init(&v); + gpr_strvec_add(&v, gpr_strdup("PENDING TAGS:")); + gpr_mu_lock(cq->mu); + for (size_t i = 0; i < cq->outstanding_tag_count; i++) { + char* s; + gpr_asprintf(&s, " %p", cq->outstanding_tags[i]); + gpr_strvec_add(&v, s); + } + gpr_mu_unlock(cq->mu); + char* out = gpr_strvec_flatten(&v, nullptr); + gpr_strvec_destroy(&v); + gpr_log(GPR_DEBUG, "%s", out); + gpr_free(out); +} +#else +static void dump_pending_tags(grpc_completion_queue* cq) {} +#endif + +static grpc_event cq_next(grpc_completion_queue* cq, gpr_timespec deadline, + void* reserved) { + GPR_TIMER_SCOPE("grpc_completion_queue_next", 0); + + grpc_event ret; + cq_next_data* cqd = static_cast DATA_FROM_CQ(cq); + + GRPC_API_TRACE( + "grpc_completion_queue_next(" + "cq=%p, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 5, + (cq, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, + reserved)); + GPR_ASSERT(!reserved); + + dump_pending_tags(cq); + + GRPC_CQ_INTERNAL_REF(cq, "next"); + + grpc_millis deadline_millis = grpc_timespec_to_millis_round_up(deadline); + cq_is_finished_arg is_finished_arg = { + gpr_atm_no_barrier_load(&cqd->things_queued_ever), + cq, + deadline_millis, + nullptr, + nullptr, + true}; + ExecCtxNext exec_ctx(&is_finished_arg); + for (;;) { + grpc_millis iteration_deadline = deadline_millis; + + if (is_finished_arg.stolen_completion != nullptr) { + grpc_cq_completion* c = is_finished_arg.stolen_completion; + is_finished_arg.stolen_completion = nullptr; + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(c->done_arg, c); + break; + } + + grpc_cq_completion* c = cq_event_queue_pop(&cqd->queue); + + if (c != nullptr) { + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(c->done_arg, c); + break; + } else { + /* If c == NULL it means either the queue is empty OR in an transient + inconsistent state. If it is the latter, we shold do a 0-timeout poll + so that the thread comes back quickly from poll to make a second + attempt at popping. Not doing this can potentially deadlock this + thread forever (if the deadline is infinity) */ + if (cq_event_queue_num_items(&cqd->queue) > 0) { + iteration_deadline = 0; + } + } + + if (gpr_atm_acq_load(&cqd->pending_events) == 0) { + /* Before returning, check if the queue has any items left over (since + gpr_mpscq_pop() can sometimes return NULL even if the queue is not + empty. If so, keep retrying but do not return GRPC_QUEUE_SHUTDOWN */ + if (cq_event_queue_num_items(&cqd->queue) > 0) { + /* Go to the beginning of the loop. No point doing a poll because + (cq->shutdown == true) is only possible when there is no pending + work (i.e cq->pending_events == 0) and any outstanding completion + events should have already been queued on this cq */ + continue; + } + + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; + break; + } + + if (!is_finished_arg.first_loop && + grpc_core::ExecCtx::Get()->Now() >= deadline_millis) { + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + + /* The main polling work happens in grpc_pollset_work */ + gpr_mu_lock(cq->mu); + cq->num_polls++; + grpc_error* err = cq->poller_vtable->work(POLLSET_FROM_CQ(cq), nullptr, + iteration_deadline); + gpr_mu_unlock(cq->mu); + + if (err != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg); + + GRPC_ERROR_UNREF(err); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + is_finished_arg.first_loop = false; + } + + if (cq_event_queue_num_items(&cqd->queue) > 0 && + gpr_atm_acq_load(&cqd->pending_events) > 0) { + gpr_mu_lock(cq->mu); + cq->poller_vtable->kick(POLLSET_FROM_CQ(cq), nullptr); + gpr_mu_unlock(cq->mu); + } + + GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret); + GRPC_CQ_INTERNAL_UNREF(cq, "next"); + + GPR_ASSERT(is_finished_arg.stolen_completion == nullptr); + + return ret; +} + +/* Finishes the completion queue shutdown. This means that there are no more + completion events / tags expected from the completion queue + - Must be called under completion queue lock + - Must be called only once in completion queue's lifetime + - grpc_completion_queue_shutdown() MUST have been called before calling + this function */ +static void cq_finish_shutdown_next(grpc_completion_queue* cq) { + cq_next_data* cqd = static_cast DATA_FROM_CQ(cq); + + GPR_ASSERT(cqd->shutdown_called); + GPR_ASSERT(gpr_atm_no_barrier_load(&cqd->pending_events) == 0); + + cq->poller_vtable->shutdown(POLLSET_FROM_CQ(cq), &cq->pollset_shutdown_done); +} + +static void cq_shutdown_next(grpc_completion_queue* cq) { + cq_next_data* cqd = static_cast DATA_FROM_CQ(cq); + + /* Need an extra ref for cq here because: + * We call cq_finish_shutdown_next() below, that would call pollset shutdown. + * Pollset shutdown decrements the cq ref count which can potentially destroy + * the cq (if that happens to be the last ref). + * Creating an extra ref here prevents the cq from getting destroyed while + * this function is still active */ + GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); + gpr_mu_lock(cq->mu); + if (cqd->shutdown_called) { + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down"); + return; + } + cqd->shutdown_called = true; + /* Doing a full_fetch_add (i.e acq/release) here to match with + * cq_begin_op_for_next and and cq_end_op_for_next functions which read/write + * on this counter without necessarily holding a lock on cq */ + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + cq_finish_shutdown_next(cq); + } + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down"); +} + +grpc_event grpc_completion_queue_next(grpc_completion_queue* cq, + gpr_timespec deadline, void* reserved) { + return cq->vtable->next(cq, deadline, reserved); +} + +static int add_plucker(grpc_completion_queue* cq, void* tag, + grpc_pollset_worker** worker) { + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + if (cqd->num_pluckers == GRPC_MAX_COMPLETION_QUEUE_PLUCKERS) { + return 0; + } + cqd->pluckers[cqd->num_pluckers].tag = tag; + cqd->pluckers[cqd->num_pluckers].worker = worker; + cqd->num_pluckers++; + return 1; +} + +static void del_plucker(grpc_completion_queue* cq, void* tag, + grpc_pollset_worker** worker) { + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + for (int i = 0; i < cqd->num_pluckers; i++) { + if (cqd->pluckers[i].tag == tag && cqd->pluckers[i].worker == worker) { + cqd->num_pluckers--; + GPR_SWAP(plucker, cqd->pluckers[i], cqd->pluckers[cqd->num_pluckers]); + return; + } + } + GPR_UNREACHABLE_CODE(return ); +} + +class ExecCtxPluck : public grpc_core::ExecCtx { + public: + ExecCtxPluck(void* arg) : ExecCtx(0), check_ready_to_finish_arg_(arg) {} + + bool CheckReadyToFinish() override { + cq_is_finished_arg* a = + static_cast(check_ready_to_finish_arg_); + grpc_completion_queue* cq = a->cq; + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + + GPR_ASSERT(a->stolen_completion == nullptr); + gpr_atm current_last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + if (current_last_seen_things_queued_ever != + a->last_seen_things_queued_ever) { + gpr_mu_lock(cq->mu); + a->last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + grpc_cq_completion* c; + grpc_cq_completion* prev = &cqd->completed_head; + while ((c = (grpc_cq_completion*)(prev->next & + ~static_cast(1))) != + &cqd->completed_head) { + if (c->tag == a->tag) { + prev->next = (prev->next & static_cast(1)) | + (c->next & ~static_cast(1)); + if (c == cqd->completed_tail) { + cqd->completed_tail = prev; + } + gpr_mu_unlock(cq->mu); + a->stolen_completion = c; + return true; + } + prev = c; + } + gpr_mu_unlock(cq->mu); + } + return !a->first_loop && a->deadline < grpc_core::ExecCtx::Get()->Now(); + } + + private: + void* check_ready_to_finish_arg_; +}; + +static grpc_event cq_pluck(grpc_completion_queue* cq, void* tag, + gpr_timespec deadline, void* reserved) { + GPR_TIMER_SCOPE("grpc_completion_queue_pluck", 0); + + grpc_event ret; + grpc_cq_completion* c; + grpc_cq_completion* prev; + grpc_pollset_worker* worker = nullptr; + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + + if (grpc_cq_pluck_trace.enabled()) { + GRPC_API_TRACE( + "grpc_completion_queue_pluck(" + "cq=%p, tag=%p, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 6, + (cq, tag, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, + reserved)); + } + GPR_ASSERT(!reserved); + + dump_pending_tags(cq); + + GRPC_CQ_INTERNAL_REF(cq, "pluck"); + gpr_mu_lock(cq->mu); + grpc_millis deadline_millis = grpc_timespec_to_millis_round_up(deadline); + cq_is_finished_arg is_finished_arg = { + gpr_atm_no_barrier_load(&cqd->things_queued_ever), + cq, + deadline_millis, + nullptr, + tag, + true}; + ExecCtxPluck exec_ctx(&is_finished_arg); + for (;;) { + if (is_finished_arg.stolen_completion != nullptr) { + gpr_mu_unlock(cq->mu); + c = is_finished_arg.stolen_completion; + is_finished_arg.stolen_completion = nullptr; + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(c->done_arg, c); + break; + } + prev = &cqd->completed_head; + while ( + (c = (grpc_cq_completion*)(prev->next & ~static_cast(1))) != + &cqd->completed_head) { + if (c->tag == tag) { + prev->next = (prev->next & static_cast(1)) | + (c->next & ~static_cast(1)); + if (c == cqd->completed_tail) { + cqd->completed_tail = prev; + } + gpr_mu_unlock(cq->mu); + 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 (gpr_atm_no_barrier_load(&cqd->shutdown)) { + gpr_mu_unlock(cq->mu); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; + break; + } + if (!add_plucker(cq, 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(cq->mu); + memset(&ret, 0, sizeof(ret)); + /* TODO(ctiller): should we use a different result here */ + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + if (!is_finished_arg.first_loop && + grpc_core::ExecCtx::Get()->Now() >= deadline_millis) { + del_plucker(cq, tag, &worker); + gpr_mu_unlock(cq->mu); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + cq->num_polls++; + grpc_error* err = + cq->poller_vtable->work(POLLSET_FROM_CQ(cq), &worker, deadline_millis); + if (err != GRPC_ERROR_NONE) { + del_plucker(cq, tag, &worker); + gpr_mu_unlock(cq->mu); + const char* msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "Completion queue pluck failed: %s", msg); + + GRPC_ERROR_UNREF(err); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + is_finished_arg.first_loop = false; + del_plucker(cq, tag, &worker); + } +done: + GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret); + GRPC_CQ_INTERNAL_UNREF(cq, "pluck"); + + GPR_ASSERT(is_finished_arg.stolen_completion == nullptr); + + return ret; +} + +grpc_event grpc_completion_queue_pluck(grpc_completion_queue* cq, void* tag, + gpr_timespec deadline, void* reserved) { + return cq->vtable->pluck(cq, tag, deadline, reserved); +} + +static void cq_finish_shutdown_pluck(grpc_completion_queue* cq) { + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + + GPR_ASSERT(cqd->shutdown_called); + GPR_ASSERT(!gpr_atm_no_barrier_load(&cqd->shutdown)); + gpr_atm_no_barrier_store(&cqd->shutdown, 1); + + cq->poller_vtable->shutdown(POLLSET_FROM_CQ(cq), &cq->pollset_shutdown_done); +} + +/* NOTE: This function is almost exactly identical to cq_shutdown_next() but + * merging them is a bit tricky and probably not worth it */ +static void cq_shutdown_pluck(grpc_completion_queue* cq) { + cq_pluck_data* cqd = static_cast DATA_FROM_CQ(cq); + + /* Need an extra ref for cq here because: + * We call cq_finish_shutdown_pluck() below, that would call pollset shutdown. + * Pollset shutdown decrements the cq ref count which can potentially destroy + * the cq (if that happens to be the last ref). + * Creating an extra ref here prevents the cq from getting destroyed while + * this function is still active */ + GRPC_CQ_INTERNAL_REF(cq, "shutting_down (pluck cq)"); + gpr_mu_lock(cq->mu); + if (cqd->shutdown_called) { + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (pluck cq)"); + return; + } + cqd->shutdown_called = true; + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + cq_finish_shutdown_pluck(cq); + } + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (pluck cq)"); +} + +static void cq_finish_shutdown_callback(grpc_completion_queue* cq) { + cq_callback_data* cqd = static_cast DATA_FROM_CQ(cq); + auto* callback = cqd->shutdown_callback; + + GPR_ASSERT(cqd->shutdown_called); + + cq->poller_vtable->shutdown(POLLSET_FROM_CQ(cq), &cq->pollset_shutdown_done); + (*callback->functor_run)(callback, true); +} + +static void cq_shutdown_callback(grpc_completion_queue* cq) { + cq_callback_data* cqd = static_cast DATA_FROM_CQ(cq); + + /* Need an extra ref for cq here because: + * We call cq_finish_shutdown_callback() below, which calls pollset shutdown. + * Pollset shutdown decrements the cq ref count which can potentially destroy + * the cq (if that happens to be the last ref). + * Creating an extra ref here prevents the cq from getting destroyed while + * this function is still active */ + GRPC_CQ_INTERNAL_REF(cq, "shutting_down (callback cq)"); + gpr_mu_lock(cq->mu); + if (cqd->shutdown_called) { + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (callback cq)"); + return; + } + cqd->shutdown_called = true; + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + gpr_mu_unlock(cq->mu); + cq_finish_shutdown_callback(cq); + } else { + gpr_mu_unlock(cq->mu); + } + GRPC_CQ_INTERNAL_UNREF(cq, "shutting_down (callback cq)"); +} + +/* 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* cq) { + GPR_TIMER_SCOPE("grpc_completion_queue_shutdown", 0); + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE("grpc_completion_queue_shutdown(cq=%p)", 1, (cq)); + cq->vtable->shutdown(cq); +} + +void grpc_completion_queue_destroy(grpc_completion_queue* cq) { + GPR_TIMER_SCOPE("grpc_completion_queue_destroy", 0); + GRPC_API_TRACE("grpc_completion_queue_destroy(cq=%p)", 1, (cq)); + grpc_completion_queue_shutdown(cq); + + grpc_core::ExecCtx exec_ctx; + GRPC_CQ_INTERNAL_UNREF(cq, "destroy"); +} + +grpc_pollset* grpc_cq_pollset(grpc_completion_queue* cq) { + return cq->poller_vtable->can_get_pollset ? POLLSET_FROM_CQ(cq) : nullptr; +} + +bool grpc_cq_can_listen(grpc_completion_queue* cq) { + return cq->poller_vtable->can_listen; +} diff --git a/third_party/grpc/src/core/lib/surface/completion_queue.h b/third_party/grpc/src/core/lib/surface/completion_queue.h new file mode 100644 index 00000000000000..d60fe6d6efe3f4 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/completion_queue.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H +#define GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H + +/* Internal API for completion queues */ + +#include + +#include +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/iomgr/pollset.h" + +/* These trace flags default to 1. The corresponding lines are only traced + if grpc_api_trace is also truthy */ +extern grpc_core::TraceFlag grpc_cq_pluck_trace; +extern grpc_core::TraceFlag grpc_trace_operation_failures; +extern grpc_core::DebugOnlyTraceFlag grpc_trace_pending_tags; +extern grpc_core::DebugOnlyTraceFlag grpc_trace_cq_refcount; + +typedef struct grpc_cq_completion { + gpr_mpscq_node node; + + /** 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 */ + uintptr_t next; +} grpc_cq_completion; + +#ifndef NDEBUG +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 + +/* Initializes global variables used by completion queues */ +void grpc_cq_global_init(); + +/* Flag that an operation is beginning: the completion channel will not finish + shutdown until a corrensponding grpc_cq_end_* call is made. + \a tag is currently used only in debug builds. Return true on success, and + false if completion_queue has been shutdown. */ +bool grpc_cq_begin_op(grpc_completion_queue* cc, void* tag); + +/* Queue a GRPC_OP_COMPLETED operation; tag must correspond to the tag passed to + grpc_cq_begin_op */ +void grpc_cq_end_op(grpc_completion_queue* cc, void* tag, grpc_error* error, + 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); + +bool grpc_cq_can_listen(grpc_completion_queue* cc); + +grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue* cc); + +int grpc_get_cq_poll_num(grpc_completion_queue* cc); + +grpc_completion_queue* grpc_completion_queue_create_internal( + grpc_cq_completion_type completion_type, grpc_cq_polling_type polling_type, + grpc_experimental_completion_queue_functor* shutdown_callback); + +#endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H */ diff --git a/third_party/grpc/src/core/lib/surface/completion_queue_factory.cc b/third_party/grpc/src/core/lib/surface/completion_queue_factory.cc new file mode 100644 index 00000000000000..2616c156e45c2a --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/completion_queue_factory.cc @@ -0,0 +1,88 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/completion_queue_factory.h" + +#include + +/* + * == Default completion queue factory implementation == + */ + +static grpc_completion_queue* default_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attr) { + return grpc_completion_queue_create_internal( + attr->cq_completion_type, attr->cq_polling_type, attr->cq_shutdown_cb); +} + +static grpc_completion_queue_factory_vtable default_vtable = {default_create}; + +static const grpc_completion_queue_factory g_default_cq_factory = { + "Default Factory", nullptr, &default_vtable}; + +/* + * == Completion queue factory APIs + */ + +const grpc_completion_queue_factory* grpc_completion_queue_factory_lookup( + const grpc_completion_queue_attributes* attributes) { + GPR_ASSERT(attributes->version >= 1 && + attributes->version <= GRPC_CQ_CURRENT_VERSION); + + /* The default factory can handle version 1 of the attributes structure. We + may have to change this as more fields are added to the structure */ + return &g_default_cq_factory; +} + +/* + * == Completion queue creation APIs == + */ + +grpc_completion_queue* grpc_completion_queue_create_for_next(void* reserved) { + GPR_ASSERT(!reserved); + grpc_completion_queue_attributes attr = {1, GRPC_CQ_NEXT, + GRPC_CQ_DEFAULT_POLLING, nullptr}; + return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr); +} + +grpc_completion_queue* grpc_completion_queue_create_for_pluck(void* reserved) { + GPR_ASSERT(!reserved); + grpc_completion_queue_attributes attr = {1, GRPC_CQ_PLUCK, + GRPC_CQ_DEFAULT_POLLING, nullptr}; + return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr); +} + +grpc_completion_queue* grpc_completion_queue_create_for_callback( + grpc_experimental_completion_queue_functor* shutdown_callback, + void* reserved) { + GPR_ASSERT(!reserved); + grpc_completion_queue_attributes attr = { + 2, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING, shutdown_callback}; + return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr); +} + +grpc_completion_queue* grpc_completion_queue_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attr, void* reserved) { + GPR_ASSERT(!reserved); + return factory->vtable->create(factory, attr); +} diff --git a/third_party/grpc/src/core/lib/surface/completion_queue_factory.h b/third_party/grpc/src/core/lib/surface/completion_queue_factory.h new file mode 100644 index 00000000000000..d2b30a9ce194b1 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/completion_queue_factory.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H +#define GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H + +#include + +#include +#include "src/core/lib/surface/completion_queue.h" + +typedef struct grpc_completion_queue_factory_vtable { + grpc_completion_queue* (*create)(const grpc_completion_queue_factory*, + const grpc_completion_queue_attributes*); +} grpc_completion_queue_factory_vtable; + +struct grpc_completion_queue_factory { + const char* name; + void* data; /* Factory specific data */ + grpc_completion_queue_factory_vtable* vtable; +}; + +#endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H */ diff --git a/third_party/grpc/src/core/lib/surface/event_string.cc b/third_party/grpc/src/core/lib/surface/event_string.cc new file mode 100644 index 00000000000000..d639baec4559a8 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/event_string.cc @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/event_string.h" + +#include + +#include +#include +#include "src/core/lib/gpr/string.h" + +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 == nullptr) 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, nullptr); + gpr_strvec_destroy(&buf); + return out; +} diff --git a/third_party/grpc/src/core/lib/surface/event_string.h b/third_party/grpc/src/core/lib/surface/event_string.h new file mode 100644 index 00000000000000..e6095705e9bf60 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/event_string.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_EVENT_STRING_H +#define GRPC_CORE_LIB_SURFACE_EVENT_STRING_H + +#include + +#include + +/* Returns a string describing an event. Must be later freed with gpr_free() */ +char* grpc_event_string(grpc_event* ev); + +#endif /* GRPC_CORE_LIB_SURFACE_EVENT_STRING_H */ diff --git a/third_party/grpc/src/core/lib/surface/init.cc b/third_party/grpc/src/core/lib/surface/init.cc new file mode 100644 index 00000000000000..67cf5d89bff5d2 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/init.cc @@ -0,0 +1,197 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/channel/channelz_registry.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gprpp/fork.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/call_combiner.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/timer_manager.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/init.h" +#include "src/core/lib/surface/lame_client.h" +#include "src/core/lib/surface/server.h" +#include "src/core/lib/transport/bdp_estimator.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/transport_impl.h" + +/* (generated) built in registry of plugins */ +extern void grpc_register_built_in_plugins(void); + +#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_log_verbosity_init(); + gpr_mu_init(&g_init_mu); + grpc_register_built_in_plugins(); + grpc_cq_global_init(); + g_initializations = 0; +} + +static bool append_filter(grpc_channel_stack_builder* builder, void* arg) { + return grpc_channel_stack_builder_append_filter( + builder, static_cast(arg), nullptr, nullptr); +} + +static bool prepend_filter(grpc_channel_stack_builder* builder, void* arg) { + return grpc_channel_stack_builder_prepend_filter( + builder, static_cast(arg), nullptr, nullptr); +} + +static void register_builtin_channel_init() { + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + grpc_add_connected_filter, nullptr); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + grpc_add_connected_filter, nullptr); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + grpc_add_connected_filter, nullptr); + grpc_channel_init_register_stage(GRPC_CLIENT_LAME_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + append_filter, (void*)&grpc_lame_filter); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, prepend_filter, + (void*)&grpc_server_top_filter); +} + +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)) { + GRPC_API_TRACE("grpc_register_plugin(init=%p, destroy=%p)", 2, + ((void*)(intptr_t)init, (void*)(intptr_t)destroy)); + 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) { + grpc_core::Fork::GlobalInit(); + grpc_fork_handlers_auto_register(); + gpr_time_init(); + gpr_arena_init(); + grpc_stats_init(); + grpc_slice_intern_init(); + grpc_mdctx_global_init(); + grpc_channel_init_init(); + grpc_core::channelz::ChannelzRegistry::Init(); + grpc_security_pre_init(); + grpc_core::ExecCtx::GlobalInit(); + grpc_iomgr_init(); + gpr_timers_global_init(); + grpc_handshaker_factory_registry_init(); + grpc_security_init(); + for (i = 0; i < g_number_of_plugins; i++) { + if (g_all_of_the_plugins[i].init != nullptr) { + g_all_of_the_plugins[i].init(); + } + } + /* register channel finalization AFTER all plugins, to ensure that it's run + * at the appropriate time */ + grpc_register_security_filters(); + register_builtin_channel_init(); + grpc_tracer_init("GRPC_TRACE"); + /* no more changes to channel init pipelines */ + grpc_channel_init_finalize(); + grpc_iomgr_start(); + } + gpr_mu_unlock(&g_init_mu); + + GRPC_API_TRACE("grpc_init(void)", 0, ()); +} + +void grpc_shutdown(void) { + int i; + GRPC_API_TRACE("grpc_shutdown(void)", 0, ()); + gpr_mu_lock(&g_init_mu); + if (--g_initializations == 0) { + { + grpc_core::ExecCtx exec_ctx(0); + grpc_iomgr_shutdown_background_closure(); + { + grpc_timer_manager_set_threading( + false); // shutdown timer_manager thread + grpc_executor_shutdown(); + for (i = g_number_of_plugins; i >= 0; i--) { + if (g_all_of_the_plugins[i].destroy != nullptr) { + g_all_of_the_plugins[i].destroy(); + } + } + } + grpc_iomgr_shutdown(); + gpr_timers_global_destroy(); + grpc_tracer_shutdown(); + grpc_mdctx_global_shutdown(); + grpc_handshaker_factory_registry_shutdown(); + grpc_slice_intern_shutdown(); + grpc_core::channelz::ChannelzRegistry::Shutdown(); + grpc_stats_shutdown(); + grpc_core::Fork::GlobalShutdown(); + } + grpc_core::ExecCtx::GlobalShutdown(); + } + 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/third_party/grpc/src/core/lib/surface/init.h b/third_party/grpc/src/core/lib/surface/init.h new file mode 100644 index 00000000000000..193f51447d999d --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/init.h @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_INIT_H +#define GRPC_CORE_LIB_SURFACE_INIT_H + +void grpc_register_security_filters(void); +void grpc_security_pre_init(void); +void grpc_security_init(void); + +#endif /* GRPC_CORE_LIB_SURFACE_INIT_H */ diff --git a/third_party/grpc/src/core/lib/surface/init_secure.cc b/third_party/grpc/src/core/lib/surface/init_secure.cc new file mode 100644 index 00000000000000..765350cced0250 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/init_secure.cc @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/init.h" + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/plugin/plugin_credentials.h" +#include "src/core/lib/security/security_connector/security_connector.h" +#include "src/core/lib/security/transport/auth_filters.h" +#include "src/core/lib/security/transport/secure_endpoint.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/tsi/transport_security_interface.h" + +void grpc_security_pre_init(void) {} + +static bool maybe_prepend_client_auth_filter( + grpc_channel_stack_builder* builder, void* arg) { + const grpc_channel_args* args = + grpc_channel_stack_builder_get_channel_arguments(builder); + if (args) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(GRPC_ARG_SECURITY_CONNECTOR, args->args[i].key)) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_client_auth_filter, nullptr, nullptr); + } + } + } + return true; +} + +static bool maybe_prepend_server_auth_filter( + grpc_channel_stack_builder* builder, void* arg) { + const grpc_channel_args* args = + grpc_channel_stack_builder_get_channel_arguments(builder); + if (args) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(GRPC_SERVER_CREDENTIALS_ARG, args->args[i].key)) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_server_auth_filter, nullptr, nullptr); + } + } + } + return true; +} + +void grpc_register_security_filters(void) { + // Register the auth client with a priority < INT_MAX to allow the authority + // filter -on which the auth filter depends- to be higher on the channel + // stack. + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX - 1, + maybe_prepend_client_auth_filter, nullptr); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX - 1, + maybe_prepend_client_auth_filter, nullptr); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX - 1, + maybe_prepend_server_auth_filter, nullptr); +} + +void grpc_security_init() { grpc_security_register_handshaker_factories(); } diff --git a/third_party/grpc/src/core/lib/surface/init_unsecure.cc b/third_party/grpc/src/core/lib/surface/init_unsecure.cc new file mode 100644 index 00000000000000..2b3bc643820ffd --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/init_unsecure.cc @@ -0,0 +1,27 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/init.h" + +void grpc_security_pre_init(void) {} + +void grpc_register_security_filters(void) {} + +void grpc_security_init(void) {} diff --git a/third_party/grpc/src/core/lib/surface/lame_client.cc b/third_party/grpc/src/core/lib/surface/lame_client.cc new file mode 100644 index 00000000000000..5a84428b0ee010 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/lame_client.cc @@ -0,0 +1,180 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include +#include + +#include "src/core/lib/gprpp/atomic.h" + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/lame_client.h" +#include "src/core/lib/transport/static_metadata.h" + +namespace grpc_core { + +namespace { + +struct CallData { + grpc_call_combiner* call_combiner; + grpc_linked_mdelem status; + grpc_linked_mdelem details; + grpc_core::atomic filled_metadata; +}; + +struct ChannelData { + grpc_status_code error_code; + const char* error_message; +}; + +static void fill_metadata(grpc_call_element* elem, grpc_metadata_batch* mdb) { + CallData* calld = static_cast(elem->call_data); + bool expected = false; + if (!calld->filled_metadata.compare_exchange_strong( + expected, true, grpc_core::memory_order_relaxed, + grpc_core::memory_order_relaxed)) { + return; + } + ChannelData* chand = static_cast(elem->channel_data); + char tmp[GPR_LTOA_MIN_BUFSIZE]; + gpr_ltoa(chand->error_code, tmp); + calld->status.md = grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_STATUS, grpc_slice_from_copied_string(tmp)); + calld->details.md = grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_MESSAGE, + grpc_slice_from_copied_string(chand->error_message)); + calld->status.prev = calld->details.next = nullptr; + calld->status.next = &calld->details; + calld->details.prev = &calld->status; + mdb->list.head = &calld->status; + mdb->list.tail = &calld->details; + mdb->list.count = 2; + mdb->deadline = GRPC_MILLIS_INF_FUTURE; +} + +static void lame_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + CallData* calld = static_cast(elem->call_data); + if (op->recv_initial_metadata) { + fill_metadata(elem, + op->payload->recv_initial_metadata.recv_initial_metadata); + } else if (op->recv_trailing_metadata) { + fill_metadata(elem, + op->payload->recv_trailing_metadata.recv_trailing_metadata); + } + grpc_transport_stream_op_batch_finish_with_failure( + op, GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel"), + calld->call_combiner); +} + +static void lame_get_channel_info(grpc_channel_element* elem, + const grpc_channel_info* channel_info) {} + +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_SHUTDOWN); + *op->connectivity_state = GRPC_CHANNEL_SHUTDOWN; + GRPC_CLOSURE_SCHED(op->on_connectivity_state_change, GRPC_ERROR_NONE); + } + if (op->send_ping.on_initiate != nullptr) { + GRPC_CLOSURE_SCHED( + op->send_ping.on_initiate, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel")); + } + if (op->send_ping.on_ack != nullptr) { + GRPC_CLOSURE_SCHED( + op->send_ping.on_ack, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("lame client channel")); + } + GRPC_ERROR_UNREF(op->disconnect_with_error); + if (op->on_consumed != nullptr) { + GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE); + } +} + +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + CallData* calld = static_cast(elem->call_data); + calld->call_combiner = args->call_combiner; + return GRPC_ERROR_NONE; +} + +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_schedule_closure) { + GRPC_CLOSURE_SCHED(then_schedule_closure, GRPC_ERROR_NONE); +} + +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + GPR_ASSERT(args->is_first); + GPR_ASSERT(args->is_last); + return GRPC_ERROR_NONE; +} + +static void destroy_channel_elem(grpc_channel_element* elem) {} + +} // namespace + +} // namespace grpc_core + +const grpc_channel_filter grpc_lame_filter = { + grpc_core::lame_start_transport_stream_op_batch, + grpc_core::lame_start_transport_op, + sizeof(grpc_core::CallData), + grpc_core::init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + grpc_core::destroy_call_elem, + sizeof(grpc_core::ChannelData), + grpc_core::init_channel_elem, + grpc_core::destroy_channel_elem, + grpc_core::lame_get_channel_info, + "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_core::ExecCtx exec_ctx; + grpc_channel_element* elem; + grpc_channel* channel = + grpc_channel_create(target, nullptr, GRPC_CLIENT_LAME_CHANNEL, nullptr); + elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + GRPC_API_TRACE( + "grpc_lame_client_channel_create(target=%s, error_code=%d, " + "error_message=%s)", + 3, (target, (int)error_code, error_message)); + GPR_ASSERT(elem->filter == &grpc_lame_filter); + auto chand = static_cast(elem->channel_data); + chand->error_code = error_code; + chand->error_message = error_message; + + return channel; +} diff --git a/third_party/grpc/src/core/lib/surface/lame_client.h b/third_party/grpc/src/core/lib/surface/lame_client.h new file mode 100644 index 00000000000000..aefa67c24a6d97 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/lame_client.h @@ -0,0 +1,28 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H +#define GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H + +#include + +#include "src/core/lib/channel/channel_stack.h" + +extern const grpc_channel_filter grpc_lame_filter; + +#endif /* GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H */ diff --git a/third_party/grpc/src/core/lib/surface/metadata_array.cc b/third_party/grpc/src/core/lib/surface/metadata_array.cc new file mode 100644 index 00000000000000..f794a2bb952d73 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/metadata_array.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +#include "src/core/lib/surface/api_trace.h" + +void grpc_metadata_array_init(grpc_metadata_array* array) { + GRPC_API_TRACE("grpc_metadata_array_init(array=%p)", 1, (array)); + memset(array, 0, sizeof(*array)); +} + +void grpc_metadata_array_destroy(grpc_metadata_array* array) { + GRPC_API_TRACE("grpc_metadata_array_destroy(array=%p)", 1, (array)); + gpr_free(array->metadata); +} diff --git a/third_party/grpc/src/core/lib/surface/server.cc b/third_party/grpc/src/core/lib/surface/server.cc new file mode 100644 index 00000000000000..7ae6e51a5fb96c --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/server.cc @@ -0,0 +1,1601 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/server.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/gpr/mpscq.h" +#include "src/core/lib/gpr/spinlock.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/init.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/static_metadata.h" + +grpc_core::TraceFlag grpc_server_channel_trace(false, "server_channel"); + +static void server_on_recv_initial_metadata(void* ptr, grpc_error* error); +static void server_recv_trailing_metadata_ready(void* user_data, + grpc_error* error); + +namespace { +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, grpc_closure* closure); + struct listener* next; + intptr_t socket_uuid; + grpc_closure destroy_done; +}; + +enum requested_call_type { BATCH_CALL, REGISTERED_CALL }; + +struct registered_method; + +struct requested_call { + gpr_mpscq_node request_link; /* must be first */ + requested_call_type type; + size_t cq_idx; + void* tag; + grpc_server* server; + grpc_completion_queue* cq_bound_to_call; + grpc_call** call; + grpc_cq_completion completion; + grpc_metadata_array* initial_metadata; + union { + struct { + grpc_call_details* details; + } batch; + struct { + registered_method* method; + gpr_timespec* deadline; + grpc_byte_buffer** optional_payload; + } registered; + } data; +}; + +struct channel_registered_method { + registered_method* server_registered_method; + uint32_t flags; + bool has_host; + grpc_slice method; + grpc_slice host; +}; + +struct channel_data { + grpc_server* server; + grpc_connectivity_state connectivity_state; + grpc_channel* channel; + size_t cq_idx; + /* linked list of all channels on a server */ + channel_data* next; + channel_data* prev; + channel_registered_method* registered_methods; + uint32_t registered_method_slots; + uint32_t registered_method_max_probes; + grpc_closure finish_destroy_channel_closure; + grpc_closure channel_connectivity_changed; + grpc_core::RefCountedPtr socket_node; +}; + +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 { + call_data(grpc_call_element* elem, const grpc_call_element_args& args) + : call(grpc_call_from_top_element(elem)), + call_combiner(args.call_combiner) { + GRPC_CLOSURE_INIT(&server_on_recv_initial_metadata, + ::server_on_recv_initial_metadata, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready, + server_recv_trailing_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + } + ~call_data() { + GPR_ASSERT(state != PENDING); + GRPC_ERROR_UNREF(recv_initial_metadata_error); + if (host_set) { + grpc_slice_unref_internal(host); + } + if (path_set) { + grpc_slice_unref_internal(path); + } + grpc_metadata_array_destroy(&initial_metadata); + grpc_byte_buffer_destroy(payload); + } + + grpc_call* call; + + gpr_atm state = NOT_STARTED; + + bool path_set = false; + bool host_set = false; + grpc_slice path; + grpc_slice host; + grpc_millis deadline = GRPC_MILLIS_INF_FUTURE; + + grpc_completion_queue* cq_new = nullptr; + + grpc_metadata_batch* recv_initial_metadata = nullptr; + uint32_t recv_initial_metadata_flags = 0; + grpc_metadata_array initial_metadata = + grpc_metadata_array(); // Zero-initialize the C struct. + + request_matcher* matcher = nullptr; + grpc_byte_buffer* payload = nullptr; + + grpc_closure got_initial_metadata; + grpc_closure server_on_recv_initial_metadata; + grpc_closure kill_zombie_closure; + grpc_closure* on_done_recv_initial_metadata; + grpc_closure recv_trailing_metadata_ready; + grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE; + grpc_closure* original_recv_trailing_metadata_ready; + grpc_error* recv_trailing_metadata_error = GRPC_ERROR_NONE; + bool seen_recv_trailing_metadata_ready = false; + + grpc_closure publish; + + call_data* pending_next = nullptr; + grpc_call_combiner* call_combiner; +}; + +struct request_matcher { + grpc_server* server; + call_data* pending_head; + call_data* pending_tail; + gpr_locked_mpscq* requests_per_cq; +}; + +struct registered_method { + char* method; + char* host; + grpc_server_register_method_payload_handling payload_handling; + uint32_t flags; + /* one request matcher per method */ + request_matcher matcher; + registered_method* next; +}; + +typedef struct { + grpc_channel** channels; + size_t num_channels; +} channel_broadcaster; +} // namespace + +struct grpc_server { + grpc_channel_args* channel_args; + + grpc_resource_user* default_resource_user; + + grpc_completion_queue** cqs; + grpc_pollset** pollsets; + size_t cq_count; + size_t pollset_count; + bool started; + + /* 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 */ + + /* startup synchronization: flag is protected by mu_global, signals whether + we are doing the listener start routine or not */ + bool starting; + gpr_cv starting_cv; + + registered_method* registered_methods; + /** one request matcher for unregistered methods */ + request_matcher unregistered_request_matcher; + + gpr_atm shutdown_flag; + uint8_t 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; + + grpc_core::RefCountedPtr channelz_server; +}; + +#define SERVER_FROM_CALL_ELEM(elem) \ + (((channel_data*)(elem)->channel_data)->server) + +static void publish_new_rpc(void* calld, grpc_error* error); +static void fail_call(grpc_server* server, size_t cq_idx, requested_call* rc, + grpc_error* error); +/* 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 = static_cast( + 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_closure closure; + grpc_slice slice; +}; + +static void shutdown_cleanup(void* arg, grpc_error* error) { + struct shutdown_cleanup_args* a = + static_cast(arg); + grpc_slice_unref_internal(a->slice); + gpr_free(a); +} + +static void send_shutdown(grpc_channel* channel, bool send_goaway, + grpc_error* send_disconnect) { + struct shutdown_cleanup_args* sc = + static_cast(gpr_malloc(sizeof(*sc))); + GRPC_CLOSURE_INIT(&sc->closure, shutdown_cleanup, sc, + grpc_schedule_on_exec_ctx); + grpc_transport_op* op = grpc_make_transport_op(&sc->closure); + grpc_channel_element* elem; + + op->goaway_error = + send_goaway ? grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK) + : GRPC_ERROR_NONE; + op->set_accept_stream = true; + sc->slice = grpc_slice_from_copied_string("Server shutdown"); + op->disconnect_with_error = send_disconnect; + + 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, + bool send_goaway, + grpc_error* force_disconnect) { + size_t i; + + for (i = 0; i < cb->num_channels; i++) { + send_shutdown(cb->channels[i], send_goaway, + GRPC_ERROR_REF(force_disconnect)); + GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast"); + } + gpr_free(cb->channels); + GRPC_ERROR_UNREF(force_disconnect); +} + +/* + * request_matcher + */ + +static void request_matcher_init(request_matcher* rm, grpc_server* server) { + memset(rm, 0, sizeof(*rm)); + rm->server = server; + rm->requests_per_cq = static_cast( + gpr_malloc(sizeof(*rm->requests_per_cq) * server->cq_count)); + for (size_t i = 0; i < server->cq_count; i++) { + gpr_locked_mpscq_init(&rm->requests_per_cq[i]); + } +} + +static void request_matcher_destroy(request_matcher* rm) { + for (size_t i = 0; i < rm->server->cq_count; i++) { + GPR_ASSERT(gpr_locked_mpscq_pop(&rm->requests_per_cq[i]) == nullptr); + gpr_locked_mpscq_destroy(&rm->requests_per_cq[i]); + } + gpr_free(rm->requests_per_cq); +} + +static void kill_zombie(void* elem, grpc_error* error) { + grpc_call_unref( + grpc_call_from_top_element(static_cast(elem))); +} + +static void request_matcher_zombify_all_pending_calls(request_matcher* rm) { + while (rm->pending_head) { + call_data* calld = rm->pending_head; + rm->pending_head = calld->pending_next; + gpr_atm_no_barrier_store(&calld->state, ZOMBIED); + GRPC_CLOSURE_INIT( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE); + } +} + +static void request_matcher_kill_requests(grpc_server* server, + request_matcher* rm, + grpc_error* error) { + requested_call* rc; + for (size_t i = 0; i < server->cq_count; i++) { + while ((rc = reinterpret_cast( + gpr_locked_mpscq_pop(&rm->requests_per_cq[i]))) != nullptr) { + fail_call(server, i, rc, GRPC_ERROR_REF(error)); + } + } + GRPC_ERROR_UNREF(error); +} + +/* + * 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; + server->channelz_server.reset(); + grpc_channel_args_destroy(server->channel_args); + gpr_mu_destroy(&server->mu_global); + gpr_mu_destroy(&server->mu_call); + gpr_cv_destroy(&server->starting_cv); + while ((rm = server->registered_methods) != nullptr) { + server->registered_methods = rm->next; + if (server->started) { + request_matcher_destroy(&rm->matcher); + } + gpr_free(rm->method); + gpr_free(rm->host); + gpr_free(rm); + } + if (server->started) { + request_matcher_destroy(&server->unregistered_request_matcher); + } + for (i = 0; i < server->cq_count; i++) { + GRPC_CQ_INTERNAL_UNREF(server->cqs[i], "server"); + } + gpr_free(server->cqs); + gpr_free(server->pollsets); + gpr_free(server->shutdown_tags); + 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, grpc_error* error) { + channel_data* chand = static_cast(cd); + grpc_server* server = chand->server; + GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "server"); + server_unref(server); +} + +static void destroy_channel(channel_data* chand, grpc_error* error) { + if (is_channel_orphaned(chand)) return; + GPR_ASSERT(chand->server != nullptr); + orphan_channel(chand); + server_ref(chand->server); + maybe_finish_shutdown(chand->server); + GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure, + finish_destroy_channel, chand, grpc_schedule_on_exec_ctx); + + if (grpc_server_channel_trace.enabled() && error != GRPC_ERROR_NONE) { + const char* msg = grpc_error_string(error); + gpr_log(GPR_INFO, "Disconnected client: %s", msg); + } + GRPC_ERROR_UNREF(error); + + grpc_transport_op* op = + grpc_make_transport_op(&chand->finish_destroy_channel_closure); + op->set_accept_stream = true; + grpc_channel_next_op(grpc_channel_stack_element( + grpc_channel_get_channel_stack(chand->channel), 0), + op); +} + +static void done_request_event(void* req, grpc_cq_completion* c) { + gpr_free(req); +} + +static void publish_call(grpc_server* server, call_data* calld, size_t cq_idx, + requested_call* rc) { + grpc_call_set_completion_queue(calld->call, rc->cq_bound_to_call); + grpc_call* call = calld->call; + *rc->call = call; + calld->cq_new = server->cqs[cq_idx]; + GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, calld->initial_metadata); + switch (rc->type) { + case BATCH_CALL: + GPR_ASSERT(calld->host_set); + GPR_ASSERT(calld->path_set); + rc->data.batch.details->host = grpc_slice_ref_internal(calld->host); + rc->data.batch.details->method = grpc_slice_ref_internal(calld->path); + rc->data.batch.details->deadline = + grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC); + rc->data.batch.details->flags = calld->recv_initial_metadata_flags; + break; + case REGISTERED_CALL: + *rc->data.registered.deadline = + grpc_millis_to_timespec(calld->deadline, GPR_CLOCK_MONOTONIC); + if (rc->data.registered.optional_payload) { + *rc->data.registered.optional_payload = calld->payload; + calld->payload = nullptr; + } + break; + default: + GPR_UNREACHABLE_CODE(return ); + } + + grpc_cq_end_op(calld->cq_new, rc->tag, GRPC_ERROR_NONE, done_request_event, + rc, &rc->completion); +} + +static void publish_new_rpc(void* arg, grpc_error* error) { + grpc_call_element* call_elem = static_cast(arg); + call_data* calld = static_cast(call_elem->call_data); + channel_data* chand = static_cast(call_elem->channel_data); + request_matcher* rm = calld->matcher; + grpc_server* server = rm->server; + + if (error != GRPC_ERROR_NONE || gpr_atm_acq_load(&server->shutdown_flag)) { + gpr_atm_no_barrier_store(&calld->state, ZOMBIED); + GRPC_CLOSURE_INIT( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_REF(error)); + return; + } + + for (size_t i = 0; i < server->cq_count; i++) { + size_t cq_idx = (chand->cq_idx + i) % server->cq_count; + requested_call* rc = reinterpret_cast( + gpr_locked_mpscq_try_pop(&rm->requests_per_cq[cq_idx])); + if (rc == nullptr) { + continue; + } else { + GRPC_STATS_INC_SERVER_CQS_CHECKED(i); + gpr_atm_no_barrier_store(&calld->state, ACTIVATED); + publish_call(server, calld, cq_idx, rc); + return; /* early out */ + } + } + + /* no cq to take the request found: queue it on the slow list */ + GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED(); + gpr_mu_lock(&server->mu_call); + + // We need to ensure that all the queues are empty. We do this under + // the server mu_call lock to ensure that if something is added to + // an empty request queue, it will block until the call is actually + // added to the pending list. + for (size_t i = 0; i < server->cq_count; i++) { + size_t cq_idx = (chand->cq_idx + i) % server->cq_count; + requested_call* rc = reinterpret_cast( + gpr_locked_mpscq_pop(&rm->requests_per_cq[cq_idx])); + if (rc == nullptr) { + continue; + } else { + gpr_mu_unlock(&server->mu_call); + GRPC_STATS_INC_SERVER_CQS_CHECKED(i + server->cq_count); + gpr_atm_no_barrier_store(&calld->state, ACTIVATED); + publish_call(server, calld, cq_idx, rc); + return; /* early out */ + } + } + + gpr_atm_no_barrier_store(&calld->state, PENDING); + if (rm->pending_head == nullptr) { + rm->pending_tail = rm->pending_head = calld; + } else { + rm->pending_tail->pending_next = calld; + rm->pending_tail = calld; + } + calld->pending_next = nullptr; + gpr_mu_unlock(&server->mu_call); +} + +static void finish_start_new_rpc( + grpc_server* server, grpc_call_element* elem, request_matcher* rm, + grpc_server_register_method_payload_handling payload_handling) { + call_data* calld = static_cast(elem->call_data); + + if (gpr_atm_acq_load(&server->shutdown_flag)) { + gpr_atm_no_barrier_store(&calld->state, ZOMBIED); + GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE); + return; + } + + calld->matcher = rm; + + switch (payload_handling) { + case GRPC_SRM_PAYLOAD_NONE: + publish_new_rpc(elem, GRPC_ERROR_NONE); + break; + case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_MESSAGE; + op.data.recv_message.recv_message = &calld->payload; + GRPC_CLOSURE_INIT(&calld->publish, publish_new_rpc, elem, + grpc_schedule_on_exec_ctx); + grpc_call_start_batch_and_execute(calld->call, &op, 1, &calld->publish); + break; + } + } +} + +static void start_new_rpc(grpc_call_element* elem) { + channel_data* chand = static_cast(elem->channel_data); + call_data* calld = static_cast(elem->call_data); + grpc_server* server = chand->server; + uint32_t i; + uint32_t hash; + channel_registered_method* rm; + + if (chand->registered_methods && calld->path_set && calld->host_set) { + /* TODO(ctiller): unify these two searches */ + /* check for an exact match with host */ + hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(calld->host), + grpc_slice_hash(calld->path)); + 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->has_host) continue; + if (!grpc_slice_eq(rm->host, calld->host)) continue; + if (!grpc_slice_eq(rm->method, calld->path)) continue; + if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && + 0 == (calld->recv_initial_metadata_flags & + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { + continue; + } + finish_start_new_rpc(server, elem, &rm->server_registered_method->matcher, + rm->server_registered_method->payload_handling); + return; + } + /* check for a wildcard method definition (no host set) */ + hash = GRPC_MDSTR_KV_HASH(0, grpc_slice_hash(calld->path)); + 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->has_host) continue; + if (!grpc_slice_eq(rm->method, calld->path)) continue; + if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && + 0 == (calld->recv_initial_metadata_flags & + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { + continue; + } + finish_start_new_rpc(server, elem, &rm->server_registered_method->matcher, + rm->server_registered_method->payload_handling); + return; + } + } + finish_start_new_rpc(server, elem, &server->unregistered_request_matcher, + GRPC_SRM_PAYLOAD_NONE); +} + +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(static_cast(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, grpc_error* error) { + if (server->started) { + request_matcher_kill_requests(server, &server->unregistered_request_matcher, + GRPC_ERROR_REF(error)); + request_matcher_zombify_all_pending_calls( + &server->unregistered_request_matcher); + for (registered_method* rm = server->registered_methods; rm; + rm = rm->next) { + request_matcher_kill_requests(server, &rm->matcher, + GRPC_ERROR_REF(error)); + request_matcher_zombify_all_pending_calls(&rm->matcher); + } + } + GRPC_ERROR_UNREF(error); +} + +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, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); + + 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, + GRPC_ERROR_NONE, done_shutdown_event, server, + &server->shutdown_tags[i].completion); + } +} + +static void server_on_recv_initial_metadata(void* ptr, grpc_error* error) { + grpc_call_element* elem = static_cast(ptr); + call_data* calld = static_cast(elem->call_data); + grpc_millis op_deadline; + + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(calld->recv_initial_metadata->idx.named.path != nullptr); + GPR_ASSERT(calld->recv_initial_metadata->idx.named.authority != nullptr); + calld->path = grpc_slice_ref_internal( + GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md)); + calld->host = grpc_slice_ref_internal( + GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.authority->md)); + calld->path_set = true; + calld->host_set = true; + grpc_metadata_batch_remove(calld->recv_initial_metadata, + calld->recv_initial_metadata->idx.named.path); + grpc_metadata_batch_remove( + calld->recv_initial_metadata, + calld->recv_initial_metadata->idx.named.authority); + } else { + GRPC_ERROR_REF(error); + } + op_deadline = calld->recv_initial_metadata->deadline; + if (op_deadline != GRPC_MILLIS_INF_FUTURE) { + calld->deadline = op_deadline; + } + if (calld->host_set && calld->path_set) { + /* do nothing */ + } else { + /* Pass the error reference to calld->recv_initial_metadata_error */ + grpc_error* src_error = error; + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Missing :authority or :path", &src_error, 1); + GRPC_ERROR_UNREF(src_error); + calld->recv_initial_metadata_error = GRPC_ERROR_REF(error); + } + grpc_closure* closure = calld->on_done_recv_initial_metadata; + calld->on_done_recv_initial_metadata = nullptr; + if (calld->seen_recv_trailing_metadata_ready) { + GRPC_CALL_COMBINER_START(calld->call_combiner, + &calld->recv_trailing_metadata_ready, + calld->recv_trailing_metadata_error, + "continue server_recv_trailing_metadata_ready"); + } + GRPC_CLOSURE_RUN(closure, error); +} + +static void server_recv_trailing_metadata_ready(void* user_data, + grpc_error* error) { + grpc_call_element* elem = static_cast(user_data); + call_data* calld = static_cast(elem->call_data); + if (calld->on_done_recv_initial_metadata != nullptr) { + calld->recv_trailing_metadata_error = GRPC_ERROR_REF(error); + calld->seen_recv_trailing_metadata_ready = true; + GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_ready, + server_recv_trailing_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_STOP(calld->call_combiner, + "deferring server_recv_trailing_metadata_ready " + "until after server_on_recv_initial_metadata"); + return; + } + error = + grpc_error_add_child(GRPC_ERROR_REF(error), + GRPC_ERROR_REF(calld->recv_initial_metadata_error)); + GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, error); +} + +static void server_mutate_op(grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + call_data* calld = static_cast(elem->call_data); + + if (op->recv_initial_metadata) { + GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags == nullptr); + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + calld->on_done_recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->server_on_recv_initial_metadata; + op->payload->recv_initial_metadata.recv_flags = + &calld->recv_initial_metadata_flags; + } + if (op->recv_trailing_metadata) { + calld->original_recv_trailing_metadata_ready = + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + op->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &calld->recv_trailing_metadata_ready; + } +} + +static void server_start_transport_stream_op_batch( + grpc_call_element* elem, grpc_transport_stream_op_batch* op) { + server_mutate_op(elem, op); + grpc_call_next_op(elem, op); +} + +static void got_initial_metadata(void* ptr, grpc_error* error) { + grpc_call_element* elem = static_cast(ptr); + call_data* calld = static_cast(elem->call_data); + if (error == GRPC_ERROR_NONE) { + start_new_rpc(elem); + } else { + if (gpr_atm_full_cas(&calld->state, NOT_STARTED, ZOMBIED)) { + GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE); + } else if (gpr_atm_full_cas(&calld->state, PENDING, ZOMBIED)) { + /* zombied call will be destroyed when it's removed from the pending + queue... later */ + } + } +} + +static void accept_stream(void* cd, grpc_transport* transport, + const void* transport_server_data) { + channel_data* chand = static_cast(cd); + /* create a call */ + grpc_call_create_args args; + args.channel = chand->channel; + args.server = chand->server; + args.parent = nullptr; + args.propagation_mask = 0; + args.cq = nullptr; + args.pollset_set_alternative = nullptr; + args.server_transport_data = transport_server_data; + args.add_initial_metadata = nullptr; + args.add_initial_metadata_count = 0; + args.send_deadline = GRPC_MILLIS_INF_FUTURE; + grpc_call* call; + grpc_error* error = grpc_call_create(&args, &call); + grpc_call_element* elem = + grpc_call_stack_element(grpc_call_get_call_stack(call), 0); + if (error != GRPC_ERROR_NONE) { + got_initial_metadata(elem, error); + GRPC_ERROR_UNREF(error); + return; + } + call_data* calld = static_cast(elem->call_data); + grpc_op op; + op.op = GRPC_OP_RECV_INITIAL_METADATA; + op.flags = 0; + op.reserved = nullptr; + op.data.recv_initial_metadata.recv_initial_metadata = + &calld->initial_metadata; + GRPC_CLOSURE_INIT(&calld->got_initial_metadata, got_initial_metadata, elem, + grpc_schedule_on_exec_ctx); + grpc_call_start_batch_and_execute(call, &op, 1, &calld->got_initial_metadata); +} + +static void channel_connectivity_changed(void* cd, grpc_error* error) { + channel_data* chand = static_cast(cd); + grpc_server* server = chand->server; + if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { + grpc_transport_op* op = grpc_make_transport_op(nullptr); + 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, GRPC_ERROR_REF(error)); + gpr_mu_unlock(&server->mu_global); + GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "connectivity"); + } +} + +static grpc_error* init_call_elem(grpc_call_element* elem, + const grpc_call_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + server_ref(chand->server); + new (elem->call_data) call_data(elem, *args); + return GRPC_ERROR_NONE; +} + +static void destroy_call_elem(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + call_data* calld = static_cast(elem->call_data); + calld->~call_data(); + channel_data* chand = static_cast(elem->channel_data); + server_unref(chand->server); +} + +static grpc_error* init_channel_elem(grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* chand = static_cast(elem->channel_data); + GPR_ASSERT(args->is_first); + GPR_ASSERT(!args->is_last); + chand->server = nullptr; + chand->channel = nullptr; + chand->next = chand->prev = chand; + chand->registered_methods = nullptr; + chand->connectivity_state = GRPC_CHANNEL_IDLE; + GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed, + channel_connectivity_changed, chand, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +static void destroy_channel_elem(grpc_channel_element* elem) { + size_t i; + channel_data* chand = static_cast(elem->channel_data); + chand->socket_node.reset(); + if (chand->registered_methods) { + for (i = 0; i < chand->registered_method_slots; i++) { + grpc_slice_unref_internal(chand->registered_methods[i].method); + if (chand->registered_methods[i].has_host) { + grpc_slice_unref_internal(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); + server_unref(chand->server); + } +} + +const grpc_channel_filter grpc_server_top_filter = { + server_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "server", +}; + +static void 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"); + n = server->cq_count++; + server->cqs = static_cast(gpr_realloc( + server->cqs, server->cq_count * sizeof(grpc_completion_queue*))); + server->cqs[n] = cq; +} + +void grpc_server_register_completion_queue(grpc_server* server, + grpc_completion_queue* cq, + void* reserved) { + GRPC_API_TRACE( + "grpc_server_register_completion_queue(server=%p, cq=%p, reserved=%p)", 3, + (server, cq, reserved)); + + if (grpc_get_cq_completion_type(cq) != GRPC_CQ_NEXT) { + gpr_log(GPR_INFO, + "Completion queue which is not of type GRPC_CQ_NEXT is being " + "registered as a server-completion-queue"); + /* Ideally we should log an error and abort but ruby-wrapped-language API + calls grpc_completion_queue_pluck() on server completion queues */ + } + + register_completion_queue(server, cq, reserved); +} + +grpc_server* grpc_server_create(const grpc_channel_args* args, void* reserved) { + grpc_core::ExecCtx exec_ctx; + GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved)); + + grpc_server* server = + static_cast(gpr_zalloc(sizeof(grpc_server))); + + gpr_mu_init(&server->mu_global); + gpr_mu_init(&server->mu_call); + gpr_cv_init(&server->starting_cv); + + /* 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; + + server->channel_args = grpc_channel_args_copy(args); + + const grpc_arg* arg = grpc_channel_args_find(args, GRPC_ARG_ENABLE_CHANNELZ); + if (grpc_channel_arg_get_bool(arg, GRPC_ENABLE_CHANNELZ_DEFAULT)) { + arg = grpc_channel_args_find( + args, GRPC_ARG_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE); + size_t channel_tracer_max_memory = grpc_channel_arg_get_integer( + arg, + {GRPC_MAX_CHANNEL_TRACE_EVENT_MEMORY_PER_NODE_DEFAULT, 0, INT_MAX}); + server->channelz_server = + grpc_core::MakeRefCounted( + server, channel_tracer_max_memory); + server->channelz_server->AddTraceEvent( + grpc_core::channelz::ChannelTrace::Severity::Info, + grpc_slice_from_static_string("Server created")); + } + + if (args != nullptr) { + grpc_resource_quota* resource_quota = + grpc_resource_quota_from_channel_args(args, false /* create */); + if (resource_quota != nullptr) { + server->default_resource_user = + grpc_resource_user_create(resource_quota, "default"); + } + } + + return server; +} + +static int streq(const char* a, const char* b) { + if (a == nullptr && b == nullptr) return 1; + if (a == nullptr) return 0; + if (b == nullptr) return 0; + return 0 == strcmp(a, b); +} + +void* grpc_server_register_method( + grpc_server* server, const char* method, const char* host, + grpc_server_register_method_payload_handling payload_handling, + uint32_t flags) { + registered_method* m; + GRPC_API_TRACE( + "grpc_server_register_method(server=%p, method=%s, host=%s, " + "flags=0x%08x)", + 4, (server, method, host, flags)); + if (!method) { + gpr_log(GPR_ERROR, + "grpc_server_register_method method string cannot be NULL"); + return nullptr; + } + 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 nullptr; + } + } + if ((flags & ~GRPC_INITIAL_METADATA_USED_MASK) != 0) { + gpr_log(GPR_ERROR, "grpc_server_register_method invalid flags 0x%08x", + flags); + return nullptr; + } + m = static_cast(gpr_zalloc(sizeof(registered_method))); + m->method = gpr_strdup(method); + m->host = gpr_strdup(host); + m->next = server->registered_methods; + m->payload_handling = payload_handling; + m->flags = flags; + server->registered_methods = m; + return m; +} + +static void start_listeners(void* s, grpc_error* error) { + grpc_server* server = static_cast(s); + for (listener* l = server->listeners; l; l = l->next) { + l->start(server, l->arg, server->pollsets, server->pollset_count); + } + + gpr_mu_lock(&server->mu_global); + server->starting = false; + gpr_cv_signal(&server->starting_cv); + gpr_mu_unlock(&server->mu_global); + + server_unref(server); +} + +void grpc_server_start(grpc_server* server) { + size_t i; + grpc_core::ExecCtx exec_ctx; + + GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server)); + + server->started = true; + server->pollset_count = 0; + server->pollsets = static_cast( + gpr_malloc(sizeof(grpc_pollset*) * server->cq_count)); + for (i = 0; i < server->cq_count; i++) { + if (grpc_cq_can_listen(server->cqs[i])) { + server->pollsets[server->pollset_count++] = + grpc_cq_pollset(server->cqs[i]); + } + } + request_matcher_init(&server->unregistered_request_matcher, server); + for (registered_method* rm = server->registered_methods; rm; rm = rm->next) { + request_matcher_init(&rm->matcher, server); + } + + server_ref(server); + server->starting = true; + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_CREATE(start_listeners, server, + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)), + GRPC_ERROR_NONE); +} + +void grpc_server_get_pollsets(grpc_server* server, grpc_pollset*** pollsets, + size_t* pollset_count) { + *pollset_count = server->pollset_count; + *pollsets = server->pollsets; +} + +void grpc_server_setup_transport( + grpc_server* s, grpc_transport* transport, grpc_pollset* accepting_pollset, + const grpc_channel_args* args, + grpc_core::RefCountedPtr socket_node, + grpc_resource_user* resource_user) { + size_t num_registered_methods; + size_t alloc; + registered_method* rm; + channel_registered_method* crm; + grpc_channel* channel; + channel_data* chand; + uint32_t hash; + size_t slots; + uint32_t probes; + uint32_t max_probes = 0; + grpc_transport_op* op = nullptr; + + channel = grpc_channel_create(nullptr, args, GRPC_SERVER_CHANNEL, transport, + resource_user); + chand = static_cast( + grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0) + ->channel_data); + chand->server = s; + server_ref(s); + chand->channel = channel; + chand->socket_node = std::move(socket_node); + + size_t cq_idx; + for (cq_idx = 0; cq_idx < s->cq_count; cq_idx++) { + if (grpc_cq_pollset(s->cqs[cq_idx]) == accepting_pollset) break; + } + if (cq_idx == s->cq_count) { + /* completion queue not found: pick a random one to publish new calls to */ + cq_idx = static_cast(rand()) % s->cq_count; + } + chand->cq_idx = cq_idx; + + 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 = + static_cast(gpr_zalloc(alloc)); + for (rm = s->registered_methods; rm; rm = rm->next) { + grpc_slice host; + bool has_host; + grpc_slice method; + if (rm->host != nullptr) { + host = grpc_slice_intern(grpc_slice_from_static_string(rm->host)); + has_host = true; + } else { + has_host = false; + } + method = grpc_slice_intern(grpc_slice_from_static_string(rm->method)); + hash = GRPC_MDSTR_KV_HASH(has_host ? grpc_slice_hash(host) : 0, + grpc_slice_hash(method)); + for (probes = 0; chand->registered_methods[(hash + probes) % slots] + .server_registered_method != nullptr; + probes++) + ; + if (probes > max_probes) max_probes = probes; + crm = &chand->registered_methods[(hash + probes) % slots]; + crm->server_registered_method = rm; + crm->flags = rm->flags; + crm->has_host = has_host; + if (has_host) { + crm->host = host; + } + crm->method = method; + } + GPR_ASSERT(slots <= UINT32_MAX); + chand->registered_method_slots = static_cast(slots); + chand->registered_method_max_probes = max_probes; + } + + 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); + + GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity"); + op = grpc_make_transport_op(nullptr); + op->set_accept_stream = true; + op->set_accept_stream_fn = accept_stream; + op->set_accept_stream_user_data = chand; + op->on_connectivity_state_change = &chand->channel_connectivity_changed; + op->connectivity_state = &chand->connectivity_state; + if (gpr_atm_acq_load(&s->shutdown_flag) != 0) { + op->disconnect_with_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"); + } + grpc_transport_perform_op(transport, op); +} + +void grpc_server_populate_server_sockets( + grpc_server* s, grpc_core::channelz::ChildSocketsList* server_sockets, + intptr_t start_idx) { + gpr_mu_lock(&s->mu_global); + channel_data* c = nullptr; + for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { + if (c->socket_node != nullptr && c->socket_node->uuid() >= start_idx) { + server_sockets->push_back(c->socket_node.get()); + } + } + gpr_mu_unlock(&s->mu_global); +} + +void grpc_server_populate_listen_sockets( + grpc_server* server, grpc_core::channelz::ChildRefsList* listen_sockets) { + gpr_mu_lock(&server->mu_global); + for (listener* l = server->listeners; l != nullptr; l = l->next) { + listen_sockets->push_back(l->socket_uuid); + } + gpr_mu_unlock(&server->mu_global); +} + +void done_published_shutdown(void* done_arg, grpc_cq_completion* storage) { + (void)done_arg; + gpr_free(storage); +} + +static void listener_destroy_done(void* s, grpc_error* error) { + grpc_server* server = static_cast(s); + gpr_mu_lock(&server->mu_global); + server->listeners_destroyed++; + maybe_finish_shutdown(server); + gpr_mu_unlock(&server->mu_global); +} + +/* + - Kills all pending requests-for-incoming-RPC-calls (i.e the requests made via + grpc_server_request_call and grpc_server_request_registered call will now be + cancelled). See 'kill_pending_work_locked()' + + - Shuts down the listeners (i.e the server will no longer listen on the port + for new incoming channels). + + - Iterates through all channels on the server and sends shutdown msg (see + 'channel_broadcaster_shutdown()' for details) to the clients via the + transport layer. The transport layer then guarantees the following: + -- Sends shutdown to the client (for eg: HTTP2 transport sends GOAWAY) + -- If the server has outstanding calls that are in the process, the + connection is NOT closed until the server is done with all those calls + -- Once, there are no more calls in progress, the channel is closed + */ +void grpc_server_shutdown_and_notify(grpc_server* server, + grpc_completion_queue* cq, void* tag) { + listener* l; + shutdown_tag* sdt; + channel_broadcaster broadcaster; + grpc_core::ExecCtx exec_ctx; + + GRPC_API_TRACE("grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", 3, + (server, cq, tag)); + + /* wait for startup to be finished: locks mu_global */ + gpr_mu_lock(&server->mu_global); + while (server->starting) { + gpr_cv_wait(&server->starting_cv, &server->mu_global, + gpr_inf_future(GPR_CLOCK_MONOTONIC)); + } + + /* stay locked, and gather up some stuff to do */ + GPR_ASSERT(grpc_cq_begin_op(cq, tag)); + if (server->shutdown_published) { + grpc_cq_end_op(cq, tag, GRPC_ERROR_NONE, done_published_shutdown, nullptr, + static_cast( + gpr_malloc(sizeof(grpc_cq_completion)))); + gpr_mu_unlock(&server->mu_global); + return; + } + server->shutdown_tags = static_cast( + 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); + + gpr_atm_rel_store(&server->shutdown_flag, 1); + + /* collect all unregistered then registered calls */ + gpr_mu_lock(&server->mu_call); + kill_pending_work_locked( + server, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); + gpr_mu_unlock(&server->mu_call); + + maybe_finish_shutdown(server); + gpr_mu_unlock(&server->mu_global); + + /* Shutdown listeners */ + for (l = server->listeners; l; l = l->next) { + GRPC_CLOSURE_INIT(&l->destroy_done, listener_destroy_done, server, + grpc_schedule_on_exec_ctx); + l->destroy(server, l->arg, &l->destroy_done); + } + + channel_broadcaster_shutdown(&broadcaster, true /* send_goaway */, + GRPC_ERROR_NONE); + + if (server->default_resource_user != nullptr) { + grpc_resource_quota_unref( + grpc_resource_user_quota(server->default_resource_user)); + grpc_resource_user_shutdown(server->default_resource_user); + grpc_resource_user_unref(server->default_resource_user); + } +} + +void grpc_server_cancel_all_calls(grpc_server* server) { + channel_broadcaster broadcaster; + grpc_core::ExecCtx exec_ctx; + + GRPC_API_TRACE("grpc_server_cancel_all_calls(server=%p)", 1, (server)); + + gpr_mu_lock(&server->mu_global); + channel_broadcaster_init(server, &broadcaster); + gpr_mu_unlock(&server->mu_global); + + channel_broadcaster_shutdown( + &broadcaster, false /* send_goaway */, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Cancelling all calls")); +} + +void grpc_server_destroy(grpc_server* server) { + listener* l; + grpc_core::ExecCtx exec_ctx; + + GRPC_API_TRACE("grpc_server_destroy(server=%p)", 1, (server)); + + 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, + grpc_closure* on_done), + intptr_t socket_uuid) { + listener* l = static_cast(gpr_malloc(sizeof(listener))); + l->arg = arg; + l->start = start; + l->destroy = destroy; + l->socket_uuid = socket_uuid; + l->next = server->listeners; + server->listeners = l; +} + +static grpc_call_error queue_call_request(grpc_server* server, size_t cq_idx, + requested_call* rc) { + call_data* calld = nullptr; + request_matcher* rm = nullptr; + if (gpr_atm_acq_load(&server->shutdown_flag)) { + fail_call(server, cq_idx, rc, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); + return GRPC_CALL_OK; + } + switch (rc->type) { + case BATCH_CALL: + rm = &server->unregistered_request_matcher; + break; + case REGISTERED_CALL: + rm = &rc->data.registered.method->matcher; + break; + } + if (gpr_locked_mpscq_push(&rm->requests_per_cq[cq_idx], &rc->request_link)) { + /* this was the first queued request: we need to lock and start + matching calls */ + gpr_mu_lock(&server->mu_call); + while ((calld = rm->pending_head) != nullptr) { + rc = reinterpret_cast( + gpr_locked_mpscq_pop(&rm->requests_per_cq[cq_idx])); + if (rc == nullptr) break; + rm->pending_head = calld->pending_next; + gpr_mu_unlock(&server->mu_call); + if (!gpr_atm_full_cas(&calld->state, PENDING, ACTIVATED)) { + // Zombied Call + GRPC_CLOSURE_INIT( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&calld->kill_zombie_closure, GRPC_ERROR_NONE); + } else { + publish_call(server, calld, cq_idx, rc); + } + 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) { + grpc_call_error error; + grpc_core::ExecCtx exec_ctx; + requested_call* rc = static_cast(gpr_malloc(sizeof(*rc))); + GRPC_STATS_INC_SERVER_REQUESTED_CALLS(); + GRPC_API_TRACE( + "grpc_server_request_call(" + "server=%p, call=%p, details=%p, initial_metadata=%p, " + "cq_bound_to_call=%p, cq_for_notification=%p, tag=%p)", + 7, + (server, call, details, initial_metadata, cq_bound_to_call, + cq_for_notification, tag)); + size_t cq_idx; + for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) { + if (server->cqs[cq_idx] == cq_for_notification) { + break; + } + } + if (cq_idx == server->cq_count) { + gpr_free(rc); + error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + goto done; + } + if (grpc_cq_begin_op(cq_for_notification, tag) == false) { + gpr_free(rc); + error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN; + goto done; + } + details->reserved = nullptr; + rc->cq_idx = cq_idx; + rc->type = BATCH_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->call = call; + rc->data.batch.details = details; + rc->initial_metadata = initial_metadata; + error = queue_call_request(server, cq_idx, rc); +done: + + return error; +} + +grpc_call_error grpc_server_request_registered_call( + grpc_server* server, void* rmp, 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) { + grpc_call_error error; + grpc_core::ExecCtx exec_ctx; + requested_call* rc = static_cast(gpr_malloc(sizeof(*rc))); + registered_method* rm = static_cast(rmp); + GRPC_STATS_INC_SERVER_REQUESTED_CALLS(); + GRPC_API_TRACE( + "grpc_server_request_registered_call(" + "server=%p, rmp=%p, call=%p, deadline=%p, initial_metadata=%p, " + "optional_payload=%p, cq_bound_to_call=%p, cq_for_notification=%p, " + "tag=%p)", + 9, + (server, rmp, call, deadline, initial_metadata, optional_payload, + cq_bound_to_call, cq_for_notification, tag)); + + size_t cq_idx; + for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) { + if (server->cqs[cq_idx] == cq_for_notification) { + break; + } + } + if (cq_idx == server->cq_count) { + gpr_free(rc); + error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + goto done; + } + if ((optional_payload == nullptr) != + (rm->payload_handling == GRPC_SRM_PAYLOAD_NONE)) { + gpr_free(rc); + error = GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH; + goto done; + } + if (grpc_cq_begin_op(cq_for_notification, tag) == false) { + gpr_free(rc); + error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN; + goto done; + } + rc->cq_idx = cq_idx; + rc->type = REGISTERED_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->call = call; + rc->data.registered.method = rm; + rc->data.registered.deadline = deadline; + rc->initial_metadata = initial_metadata; + rc->data.registered.optional_payload = optional_payload; + error = queue_call_request(server, cq_idx, rc); +done: + + return error; +} + +static void fail_call(grpc_server* server, size_t cq_idx, requested_call* rc, + grpc_error* error) { + *rc->call = nullptr; + rc->initial_metadata->count = 0; + GPR_ASSERT(error != GRPC_ERROR_NONE); + + grpc_cq_end_op(server->cqs[cq_idx], rc->tag, error, done_request_event, rc, + &rc->completion); +} + +const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server) { + return server->channel_args; +} + +grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server) { + return server->default_resource_user; +} + +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; +} + +grpc_core::channelz::ServerNode* grpc_server_get_channelz_node( + grpc_server* server) { + if (server == nullptr) { + return nullptr; + } + return server->channelz_server.get(); +} diff --git a/third_party/grpc/src/core/lib/surface/server.h b/third_party/grpc/src/core/lib/surface/server.h new file mode 100644 index 00000000000000..393bb2421482fc --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/server.h @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_SERVER_H +#define GRPC_CORE_LIB_SURFACE_SERVER_H + +#include + +#include +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/channel/channelz.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/transport/transport.h" + +extern const grpc_channel_filter grpc_server_top_filter; + +/** Lightweight tracing of server channel state */ +extern grpc_core::TraceFlag grpc_server_channel_trace; + +/* 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, + grpc_closure* on_done), + intptr_t socket_uuid); + +/* 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_pollset* accepting_pollset, const grpc_channel_args* args, + grpc_core::RefCountedPtr socket_node, + grpc_resource_user* resource_user = nullptr); + +/* fills in the uuids of all sockets used for connections on this server */ +void grpc_server_populate_server_sockets( + grpc_server* server, grpc_core::channelz::ChildSocketsList* server_sockets, + intptr_t start_idx); + +/* fills in the uuids of all listen sockets on this server */ +void grpc_server_populate_listen_sockets( + grpc_server* server, grpc_core::channelz::ChildRefsList* listen_sockets); + +grpc_core::channelz::ServerNode* grpc_server_get_channelz_node( + grpc_server* server); + +const grpc_channel_args* grpc_server_get_channel_args(grpc_server* server); + +grpc_resource_user* grpc_server_get_default_resource_user(grpc_server* server); + +int grpc_server_has_open_connections(grpc_server* server); + +/* Do not call this before grpc_server_start. Returns the pollsets and the + * number of pollsets via 'pollsets' and 'pollset_count'. */ +void grpc_server_get_pollsets(grpc_server* server, grpc_pollset*** pollsets, + size_t* pollset_count); + +#endif /* GRPC_CORE_LIB_SURFACE_SERVER_H */ diff --git a/third_party/grpc/src/core/lib/surface/validate_metadata.cc b/third_party/grpc/src/core/lib/surface/validate_metadata.cc new file mode 100644 index 00000000000000..2dd18f3dd3fb06 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/validate_metadata.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/validate_metadata.h" + +static grpc_error* conforms_to(grpc_slice slice, const uint8_t* legal_bits, + const char* err_desc) { + const uint8_t* p = GRPC_SLICE_START_PTR(slice); + const uint8_t* e = GRPC_SLICE_END_PTR(slice); + for (; p != e; p++) { + int idx = *p; + int byte = idx / 8; + int bit = idx % 8; + if ((legal_bits[byte] & (1 << bit)) == 0) { + char* dump = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); + grpc_error* error = grpc_error_set_str( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_desc), + GRPC_ERROR_INT_OFFSET, + p - GRPC_SLICE_START_PTR(slice)), + GRPC_ERROR_STR_RAW_BYTES, grpc_slice_from_copied_string(dump)); + gpr_free(dump); + return error; + } + } + return GRPC_ERROR_NONE; +} + +static int error2int(grpc_error* error) { + int r = (error == GRPC_ERROR_NONE); + GRPC_ERROR_UNREF(error); + return r; +} + +grpc_error* grpc_validate_header_key_is_legal(grpc_slice slice) { + static const uint8_t legal_header_bits[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 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}; + if (GRPC_SLICE_LENGTH(slice) == 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Metadata keys cannot be zero length"); + } + if (GRPC_SLICE_START_PTR(slice)[0] == ':') { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Metadata keys cannot start with :"); + } + return conforms_to(slice, legal_header_bits, "Illegal header key"); +} + +int grpc_header_key_is_legal(grpc_slice slice) { + return error2int(grpc_validate_header_key_is_legal(slice)); +} + +grpc_error* grpc_validate_header_nonbin_value_is_legal(grpc_slice slice) { + static const uint8_t legal_header_bits[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 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(slice, legal_header_bits, "Illegal header value"); +} + +int grpc_header_nonbin_value_is_legal(grpc_slice slice) { + return error2int(grpc_validate_header_nonbin_value_is_legal(slice)); +} + +int grpc_is_binary_header(grpc_slice slice) { + if (GRPC_SLICE_LENGTH(slice) < 5) return 0; + return 0 == memcmp(GRPC_SLICE_END_PTR(slice) - 4, "-bin", 4); +} diff --git a/third_party/grpc/src/core/lib/surface/validate_metadata.h b/third_party/grpc/src/core/lib/surface/validate_metadata.h new file mode 100644 index 00000000000000..e87fb7beedcf81 --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/validate_metadata.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H +#define GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H + +#include + +#include +#include "src/core/lib/iomgr/error.h" + +grpc_error* grpc_validate_header_key_is_legal(grpc_slice slice); +grpc_error* grpc_validate_header_nonbin_value_is_legal(grpc_slice slice); + +#endif /* GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H */ diff --git a/third_party/grpc/src/core/lib/surface/version.cc b/third_party/grpc/src/core/lib/surface/version.cc new file mode 100644 index 00000000000000..fac938992d3d8b --- /dev/null +++ b/third_party/grpc/src/core/lib/surface/version.cc @@ -0,0 +1,28 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* This file is autogenerated from: + templates/src/core/surface/version.c.template */ + +#include + +#include + +const char* grpc_version_string(void) { return "7.0.0"; } + +const char* grpc_g_stands_for(void) { return "goose"; } diff --git a/third_party/grpc/src/core/lib/transport/README.md b/third_party/grpc/src/core/lib/transport/README.md new file mode 100644 index 00000000000000..e7e135edeb134d --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/README.md @@ -0,0 +1,7 @@ +# Transport + +Common implementation details for gRPC Transports. + +Transports multiplex messages across some single connection. In ext/ there are +implementations atop [a custom http2 implementation](/src/core/ext/transport/chttp2/README.md) +and atop [cronet](/src/core/ext/transport/cronet/README.md). diff --git a/third_party/grpc/src/core/lib/transport/bdp_estimator.cc b/third_party/grpc/src/core/lib/transport/bdp_estimator.cc new file mode 100644 index 00000000000000..8e71f86989474f --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/bdp_estimator.cc @@ -0,0 +1,87 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/bdp_estimator.h" + +#include +#include + +#include "src/core/lib/gpr/useful.h" + +grpc_core::TraceFlag grpc_bdp_estimator_trace(false, "bdp_estimator"); + +namespace grpc_core { + +BdpEstimator::BdpEstimator(const char* name) + : ping_state_(PingState::UNSCHEDULED), + accumulator_(0), + estimate_(65536), + ping_start_time_(gpr_time_0(GPR_CLOCK_MONOTONIC)), + inter_ping_delay_(100.0), // start at 100ms + stable_estimate_count_(0), + bw_est_(0), + name_(name) {} + +grpc_millis BdpEstimator::CompletePing() { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec dt_ts = gpr_time_sub(now, ping_start_time_); + double dt = static_cast(dt_ts.tv_sec) + + 1e-9 * static_cast(dt_ts.tv_nsec); + double bw = dt > 0 ? (static_cast(accumulator_) / dt) : 0; + int start_inter_ping_delay = inter_ping_delay_; + if (grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, + "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64 + " dt=%lf bw=%lfMbs bw_est=%lfMbs", + name_, accumulator_, estimate_, dt, bw / 125000.0, + bw_est_ / 125000.0); + } + GPR_ASSERT(ping_state_ == PingState::STARTED); + if (accumulator_ > 2 * estimate_ / 3 && bw > bw_est_) { + estimate_ = GPR_MAX(accumulator_, estimate_ * 2); + bw_est_ = bw; + if (grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, "bdp[%s]: estimate increased to %" PRId64, name_, + estimate_); + } + inter_ping_delay_ /= 2; // if the ping estimate changes, + // exponentially get faster at probing + } else if (inter_ping_delay_ < 10000) { + stable_estimate_count_++; + if (stable_estimate_count_ >= 2) { + inter_ping_delay_ += + 100 + static_cast(rand() * 100.0 / + RAND_MAX); // if the ping estimate is steady, + // slowly ramp down the probe time + } + } + if (start_inter_ping_delay != inter_ping_delay_) { + stable_estimate_count_ = 0; + if (grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, "bdp[%s]:update_inter_time to %dms", name_, + inter_ping_delay_); + } + } + ping_state_ = PingState::UNSCHEDULED; + accumulator_ = 0; + return grpc_core::ExecCtx::Get()->Now() + inter_ping_delay_; +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/transport/bdp_estimator.h b/third_party/grpc/src/core/lib/transport/bdp_estimator.h new file mode 100644 index 00000000000000..ab13ae4be4cb96 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/bdp_estimator.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H +#define GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H + +#include + +#include +#include +#include + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +extern grpc_core::TraceFlag grpc_bdp_estimator_trace; + +namespace grpc_core { + +class BdpEstimator { + public: + explicit BdpEstimator(const char* name); + ~BdpEstimator() {} + + int64_t EstimateBdp() const { return estimate_; } + double EstimateBandwidth() const { return bw_est_; } + + void AddIncomingBytes(int64_t num_bytes) { accumulator_ += num_bytes; } + + // Schedule a ping: call in response to receiving a true from + // grpc_bdp_estimator_add_incoming_bytes once a ping has been scheduled by a + // transport (but not necessarily started) + void SchedulePing() { + if (grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, "bdp[%s]:sched acc=%" PRId64 " est=%" PRId64, name_, + accumulator_, estimate_); + } + GPR_ASSERT(ping_state_ == PingState::UNSCHEDULED); + ping_state_ = PingState::SCHEDULED; + accumulator_ = 0; + } + + // Start a ping: call after calling grpc_bdp_estimator_schedule_ping and + // once + // the ping is on the wire + void StartPing() { + if (grpc_bdp_estimator_trace.enabled()) { + gpr_log(GPR_INFO, "bdp[%s]:start acc=%" PRId64 " est=%" PRId64, name_, + accumulator_, estimate_); + } + GPR_ASSERT(ping_state_ == PingState::SCHEDULED); + ping_state_ = PingState::STARTED; + accumulator_ = 0; + ping_start_time_ = gpr_now(GPR_CLOCK_MONOTONIC); + } + + // Completes a previously started ping, returns when to schedule the next one + grpc_millis CompletePing(); + + private: + enum class PingState { UNSCHEDULED, SCHEDULED, STARTED }; + + PingState ping_state_; + int64_t accumulator_; + int64_t estimate_; + // when was the current ping started? + gpr_timespec ping_start_time_; + int inter_ping_delay_; + int stable_estimate_count_; + double bw_est_; + const char* name_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H */ diff --git a/third_party/grpc/src/core/lib/transport/byte_stream.cc b/third_party/grpc/src/core/lib/transport/byte_stream.cc new file mode 100644 index 00000000000000..16b85ca0db0e49 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/byte_stream.cc @@ -0,0 +1,160 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/byte_stream.h" + +#include +#include + +#include + +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/slice/slice_internal.h" + +namespace grpc_core { + +// +// SliceBufferByteStream +// + +SliceBufferByteStream::SliceBufferByteStream(grpc_slice_buffer* slice_buffer, + uint32_t flags) + : ByteStream(static_cast(slice_buffer->length), flags) { + GPR_ASSERT(slice_buffer->length <= UINT32_MAX); + grpc_slice_buffer_init(&backing_buffer_); + grpc_slice_buffer_swap(slice_buffer, &backing_buffer_); +} + +SliceBufferByteStream::~SliceBufferByteStream() {} + +void SliceBufferByteStream::Orphan() { + grpc_slice_buffer_destroy_internal(&backing_buffer_); + GRPC_ERROR_UNREF(shutdown_error_); + // Note: We do not actually delete the object here, since + // SliceBufferByteStream is usually allocated as part of a larger + // object and has an OrphanablePtr of itself passed down through the + // filter stack. +} + +bool SliceBufferByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { + GPR_ASSERT(cursor_ < backing_buffer_.count); + return true; +} + +grpc_error* SliceBufferByteStream::Pull(grpc_slice* slice) { + if (shutdown_error_ != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(shutdown_error_); + } + GPR_ASSERT(cursor_ < backing_buffer_.count); + *slice = grpc_slice_ref_internal(backing_buffer_.slices[cursor_]); + ++cursor_; + return GRPC_ERROR_NONE; +} + +void SliceBufferByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(shutdown_error_); + shutdown_error_ = error; +} + +// +// ByteStreamCache +// + +ByteStreamCache::ByteStreamCache(OrphanablePtr underlying_stream) + : underlying_stream_(std::move(underlying_stream)), + length_(underlying_stream_->length()), + flags_(underlying_stream_->flags()) { + grpc_slice_buffer_init(&cache_buffer_); +} + +ByteStreamCache::~ByteStreamCache() { Destroy(); } + +void ByteStreamCache::Destroy() { + underlying_stream_.reset(); + if (cache_buffer_.length > 0) { + grpc_slice_buffer_destroy_internal(&cache_buffer_); + } +} + +// +// ByteStreamCache::CachingByteStream +// + +ByteStreamCache::CachingByteStream::CachingByteStream(ByteStreamCache* cache) + : ByteStream(cache->length_, cache->flags_), cache_(cache) {} + +ByteStreamCache::CachingByteStream::~CachingByteStream() {} + +void ByteStreamCache::CachingByteStream::Orphan() { + GRPC_ERROR_UNREF(shutdown_error_); + // Note: We do not actually delete the object here, since + // CachingByteStream is usually allocated as part of a larger + // object and has an OrphanablePtr of itself passed down through the + // filter stack. +} + +bool ByteStreamCache::CachingByteStream::Next(size_t max_size_hint, + grpc_closure* on_complete) { + if (shutdown_error_ != GRPC_ERROR_NONE) return true; + if (cursor_ < cache_->cache_buffer_.count) return true; + GPR_ASSERT(cache_->underlying_stream_ != nullptr); + return cache_->underlying_stream_->Next(max_size_hint, on_complete); +} + +grpc_error* ByteStreamCache::CachingByteStream::Pull(grpc_slice* slice) { + if (shutdown_error_ != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(shutdown_error_); + } + if (cursor_ < cache_->cache_buffer_.count) { + *slice = grpc_slice_ref_internal(cache_->cache_buffer_.slices[cursor_]); + ++cursor_; + offset_ += GRPC_SLICE_LENGTH(*slice); + return GRPC_ERROR_NONE; + } + GPR_ASSERT(cache_->underlying_stream_ != nullptr); + grpc_error* error = cache_->underlying_stream_->Pull(slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&cache_->cache_buffer_, + grpc_slice_ref_internal(*slice)); + ++cursor_; + offset_ += GRPC_SLICE_LENGTH(*slice); + // Orphan the underlying stream if it's been drained. + if (offset_ == cache_->underlying_stream_->length()) { + cache_->underlying_stream_.reset(); + } + } + return error; +} + +void ByteStreamCache::CachingByteStream::Shutdown(grpc_error* error) { + GRPC_ERROR_UNREF(shutdown_error_); + shutdown_error_ = GRPC_ERROR_REF(error); + if (cache_->underlying_stream_ != nullptr) { + cache_->underlying_stream_->Shutdown(error); + } +} + +void ByteStreamCache::CachingByteStream::Reset() { + cursor_ = 0; + offset_ = 0; +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/transport/byte_stream.h b/third_party/grpc/src/core/lib/transport/byte_stream.h new file mode 100644 index 00000000000000..eff832515dace0 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/byte_stream.h @@ -0,0 +1,164 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H +#define GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H + +#include + +#include +#include "src/core/lib/gprpp/abstract.h" +#include "src/core/lib/gprpp/orphanable.h" +#include "src/core/lib/iomgr/closure.h" + +/** 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) + +namespace grpc_core { + +class ByteStream : public Orphanable { + public: + virtual ~ByteStream() {} + + // Returns true if the bytes are available immediately (in which case + // on_complete will not be called), or false if the bytes will be available + // asynchronously (in which case on_complete will be called when they + // are available). + // + // max_size_hint can be set as a hint as to the maximum number + // of bytes that would be acceptable to read. + virtual bool Next(size_t max_size_hint, + grpc_closure* on_complete) GRPC_ABSTRACT; + + // Returns the next slice in the byte stream when it is available, as + // indicated by Next(). + // + // Once a slice is returned into *slice, it is owned by the caller. + virtual grpc_error* Pull(grpc_slice* slice) GRPC_ABSTRACT; + + // Shuts down the byte stream. + // + // If there is a pending call to on_complete from Next(), it will be + // invoked with the error passed to Shutdown(). + // + // The next call to Pull() (if any) will return the error passed to + // Shutdown(). + virtual void Shutdown(grpc_error* error) GRPC_ABSTRACT; + + uint32_t length() const { return length_; } + uint32_t flags() const { return flags_; } + + void set_flags(uint32_t flags) { flags_ = flags; } + + GRPC_ABSTRACT_BASE_CLASS + + protected: + ByteStream(uint32_t length, uint32_t flags) + : length_(length), flags_(flags) {} + + private: + const uint32_t length_; + uint32_t flags_; +}; + +// +// SliceBufferByteStream +// +// A ByteStream that wraps a slice buffer. +// + +class SliceBufferByteStream : public ByteStream { + public: + // Removes all slices in slice_buffer, leaving it empty. + SliceBufferByteStream(grpc_slice_buffer* slice_buffer, uint32_t flags); + + ~SliceBufferByteStream(); + + void Orphan() override; + + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; + + private: + grpc_slice_buffer backing_buffer_; + size_t cursor_ = 0; + grpc_error* shutdown_error_ = GRPC_ERROR_NONE; +}; + +// +// CachingByteStream +// +// A ByteStream that that wraps an underlying byte stream but caches +// the resulting slices in a slice buffer. If an initial attempt fails +// without fully draining the underlying stream, a new caching stream +// can be created from the same underlying cache, in which case it will +// return whatever is in the backing buffer before continuing to read the +// underlying stream. +// +// NOTE: No synchronization is done, so it is not safe to have multiple +// CachingByteStreams simultaneously drawing from the same underlying +// ByteStreamCache at the same time. +// + +class ByteStreamCache { + public: + class CachingByteStream : public ByteStream { + public: + explicit CachingByteStream(ByteStreamCache* cache); + + ~CachingByteStream(); + + void Orphan() override; + + bool Next(size_t max_size_hint, grpc_closure* on_complete) override; + grpc_error* Pull(grpc_slice* slice) override; + void Shutdown(grpc_error* error) override; + + // Resets the byte stream to the start of the underlying stream. + void Reset(); + + private: + ByteStreamCache* cache_; + size_t cursor_ = 0; + size_t offset_ = 0; + grpc_error* shutdown_error_ = GRPC_ERROR_NONE; + }; + + explicit ByteStreamCache(OrphanablePtr underlying_stream); + + ~ByteStreamCache(); + + // Must not be destroyed while still in use by a CachingByteStream. + void Destroy(); + + grpc_slice_buffer* cache_buffer() { return &cache_buffer_; } + + private: + OrphanablePtr underlying_stream_; + uint32_t length_; + uint32_t flags_; + grpc_slice_buffer cache_buffer_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H */ diff --git a/third_party/grpc/src/core/lib/transport/connectivity_state.cc b/third_party/grpc/src/core/lib/transport/connectivity_state.cc new file mode 100644 index 00000000000000..db6b6c04440e7d --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/connectivity_state.cc @@ -0,0 +1,195 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/connectivity_state.h" + +#include + +#include +#include +#include + +grpc_core::TraceFlag grpc_connectivity_state_trace(false, "connectivity_state"); + +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_SHUTDOWN: + return "SHUTDOWN"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +void grpc_connectivity_state_init(grpc_connectivity_state_tracker* tracker, + grpc_connectivity_state init_state, + const char* name) { + gpr_atm_no_barrier_store(&tracker->current_state_atm, init_state); + tracker->current_error = GRPC_ERROR_NONE; + tracker->watchers = nullptr; + tracker->name = gpr_strdup(name); +} + +void grpc_connectivity_state_destroy(grpc_connectivity_state_tracker* tracker) { + grpc_error* error; + grpc_connectivity_state_watcher* w; + while ((w = tracker->watchers)) { + tracker->watchers = w->next; + + if (GRPC_CHANNEL_SHUTDOWN != *w->current) { + *w->current = GRPC_CHANNEL_SHUTDOWN; + error = GRPC_ERROR_NONE; + } else { + error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutdown connectivity owner"); + } + GRPC_CLOSURE_SCHED(w->notify, error); + gpr_free(w); + } + GRPC_ERROR_UNREF(tracker->current_error); + gpr_free(tracker->name); +} + +grpc_connectivity_state grpc_connectivity_state_check( + grpc_connectivity_state_tracker* tracker) { + grpc_connectivity_state cur = static_cast( + gpr_atm_no_barrier_load(&tracker->current_state_atm)); + if (grpc_connectivity_state_trace.enabled()) { + gpr_log(GPR_INFO, "CONWATCH: %p %s: get %s", tracker, tracker->name, + grpc_connectivity_state_name(cur)); + } + return cur; +} + +grpc_connectivity_state grpc_connectivity_state_get( + grpc_connectivity_state_tracker* tracker, grpc_error** error) { + grpc_connectivity_state cur = static_cast( + gpr_atm_no_barrier_load(&tracker->current_state_atm)); + if (grpc_connectivity_state_trace.enabled()) { + gpr_log(GPR_INFO, "CONWATCH: %p %s: get %s", tracker, tracker->name, + grpc_connectivity_state_name(cur)); + } + if (error != nullptr) { + *error = GRPC_ERROR_REF(tracker->current_error); + } + return cur; +} + +bool grpc_connectivity_state_has_watchers( + grpc_connectivity_state_tracker* connectivity_state) { + return connectivity_state->watchers != nullptr; +} + +bool grpc_connectivity_state_notify_on_state_change( + grpc_connectivity_state_tracker* tracker, grpc_connectivity_state* current, + grpc_closure* notify) { + grpc_connectivity_state cur = static_cast( + gpr_atm_no_barrier_load(&tracker->current_state_atm)); + if (grpc_connectivity_state_trace.enabled()) { + if (current == nullptr) { + gpr_log(GPR_INFO, "CONWATCH: %p %s: unsubscribe notify=%p", tracker, + tracker->name, notify); + } else { + gpr_log(GPR_INFO, "CONWATCH: %p %s: from %s [cur=%s] notify=%p", tracker, + tracker->name, grpc_connectivity_state_name(*current), + grpc_connectivity_state_name(cur), notify); + } + } + if (current == nullptr) { + grpc_connectivity_state_watcher* w = tracker->watchers; + if (w != nullptr && w->notify == notify) { + GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_CANCELLED); + tracker->watchers = w->next; + gpr_free(w); + return false; + } + while (w != nullptr) { + grpc_connectivity_state_watcher* rm_candidate = w->next; + if (rm_candidate != nullptr && rm_candidate->notify == notify) { + GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_CANCELLED); + w->next = w->next->next; + gpr_free(rm_candidate); + return false; + } + w = w->next; + } + return false; + } else { + if (cur != *current) { + *current = cur; + GRPC_CLOSURE_SCHED(notify, GRPC_ERROR_REF(tracker->current_error)); + } else { + grpc_connectivity_state_watcher* w = + static_cast(gpr_malloc(sizeof(*w))); + w->current = current; + w->notify = notify; + w->next = tracker->watchers; + tracker->watchers = w; + } + return cur == GRPC_CHANNEL_IDLE; + } +} + +void grpc_connectivity_state_set(grpc_connectivity_state_tracker* tracker, + grpc_connectivity_state state, + grpc_error* error, const char* reason) { + grpc_connectivity_state cur = static_cast( + gpr_atm_no_barrier_load(&tracker->current_state_atm)); + grpc_connectivity_state_watcher* w; + if (grpc_connectivity_state_trace.enabled()) { + const char* error_string = grpc_error_string(error); + gpr_log(GPR_INFO, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker, + tracker->name, grpc_connectivity_state_name(cur), + grpc_connectivity_state_name(state), reason, error, error_string); + } + switch (state) { + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_IDLE: + case GRPC_CHANNEL_READY: + GPR_ASSERT(error == GRPC_ERROR_NONE); + break; + case GRPC_CHANNEL_SHUTDOWN: + case GRPC_CHANNEL_TRANSIENT_FAILURE: + GPR_ASSERT(error != GRPC_ERROR_NONE); + break; + } + GRPC_ERROR_UNREF(tracker->current_error); + tracker->current_error = error; + if (cur == state) { + return; + } + GPR_ASSERT(cur != GRPC_CHANNEL_SHUTDOWN); + gpr_atm_no_barrier_store(&tracker->current_state_atm, state); + while ((w = tracker->watchers) != nullptr) { + *w->current = state; + tracker->watchers = w->next; + if (grpc_connectivity_state_trace.enabled()) { + gpr_log(GPR_INFO, "NOTIFY: %p %s: %p", tracker, tracker->name, w->notify); + } + GRPC_CLOSURE_SCHED(w->notify, GRPC_ERROR_REF(tracker->current_error)); + gpr_free(w); + } +} diff --git a/third_party/grpc/src/core/lib/transport/connectivity_state.h b/third_party/grpc/src/core/lib/transport/connectivity_state.h new file mode 100644 index 00000000000000..ecb083cfc2ff30 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/connectivity_state.h @@ -0,0 +1,87 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H +#define GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H + +#include + +#include +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/closure.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_closure* notify; + /** the current state as believed by the watcher */ + grpc_connectivity_state* current; +} grpc_connectivity_state_watcher; + +typedef struct { + /** current grpc_connectivity_state */ + gpr_atm current_state_atm; + /** error associated with state */ + grpc_error* current_error; + /** all our watchers */ + grpc_connectivity_state_watcher* watchers; + /** a name to help debugging */ + char* name; +} grpc_connectivity_state_tracker; + +extern grpc_core::TraceFlag grpc_connectivity_state_trace; + +/** enum --> string conversion */ +const char* grpc_connectivity_state_name(grpc_connectivity_state state); + +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); + +/** Set connectivity state; not thread safe; access must be serialized with an + * external lock */ +void grpc_connectivity_state_set(grpc_connectivity_state_tracker* tracker, + grpc_connectivity_state state, + grpc_error* associated_error, + const char* reason); + +/** Return true if this connectivity state has watchers. + Access must be serialized with an external lock. */ +bool grpc_connectivity_state_has_watchers( + grpc_connectivity_state_tracker* tracker); + +/** Return the last seen connectivity state. No need to synchronize access. */ +grpc_connectivity_state grpc_connectivity_state_check( + grpc_connectivity_state_tracker* tracker); + +/** Return the last seen connectivity state, and the associated error. + Access must be serialized with an external lock. */ +grpc_connectivity_state grpc_connectivity_state_get( + grpc_connectivity_state_tracker* tracker, grpc_error** error); + +/** Return 1 if the channel should start connecting, 0 otherwise. + If current==NULL cancel notify if it is already queued (success==0 in that + case). + Access must be serialized with an external lock. */ +bool grpc_connectivity_state_notify_on_state_change( + grpc_connectivity_state_tracker* tracker, grpc_connectivity_state* current, + grpc_closure* notify); + +#endif /* GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H */ diff --git a/third_party/grpc/src/core/lib/transport/error_utils.cc b/third_party/grpc/src/core/lib/transport/error_utils.cc new file mode 100644 index 00000000000000..558f1d494cd1f2 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/error_utils.cc @@ -0,0 +1,120 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/error_utils.h" + +#include +#include "src/core/lib/iomgr/error_internal.h" +#include "src/core/lib/transport/status_conversion.h" + +static grpc_error* recursively_find_error_with_field(grpc_error* error, + grpc_error_ints which) { + intptr_t unused; + // If the error itself has a status code, return it. + if (grpc_error_get_int(error, which, &unused)) { + return error; + } + if (grpc_error_is_special(error)) return nullptr; + // Otherwise, search through its children. + uint8_t slot = error->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error* lerr = + reinterpret_cast(error->arena + slot); + grpc_error* result = recursively_find_error_with_field(lerr->err, which); + if (result) return result; + slot = lerr->next; + } + return nullptr; +} + +void grpc_error_get_status(grpc_error* error, grpc_millis deadline, + grpc_status_code* code, grpc_slice* slice, + grpc_http2_error_code* http_error, + const char** error_string) { + // Start with the parent error and recurse through the tree of children + // until we find the first one that has a status code. + grpc_error* found_error = + recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS); + if (found_error == nullptr) { + /// If no grpc-status exists, retry through the tree to find a http2 error + /// code + found_error = + recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR); + } + + // If we found an error with a status code above, use that; otherwise, + // fall back to using the parent error. + if (found_error == nullptr) found_error = error; + + grpc_status_code status = GRPC_STATUS_UNKNOWN; + intptr_t integer; + if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) { + status = static_cast(integer); + } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, + &integer)) { + status = grpc_http2_error_to_grpc_status( + static_cast(integer), deadline); + } + if (code != nullptr) *code = status; + + if (error_string != nullptr && status != GRPC_STATUS_OK) { + *error_string = gpr_strdup(grpc_error_string(error)); + } + + if (http_error != nullptr) { + if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) { + *http_error = static_cast(integer); + } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, + &integer)) { + *http_error = + grpc_status_to_http2_error(static_cast(integer)); + } else { + *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR + : GRPC_HTTP2_INTERNAL_ERROR; + } + } + + // If the error has a status message, use it. Otherwise, fall back to + // the error description. + if (slice != nullptr) { + if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE, slice)) { + if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION, slice)) { + *slice = grpc_slice_from_static_string("unknown error"); + } + } + } +} + +bool grpc_error_has_clear_grpc_status(grpc_error* error) { + intptr_t unused; + if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, &unused)) { + return true; + } + uint8_t slot = error->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error* lerr = + reinterpret_cast(error->arena + slot); + if (grpc_error_has_clear_grpc_status(lerr->err)) { + return true; + } + slot = lerr->next; + } + return false; +} diff --git a/third_party/grpc/src/core/lib/transport/error_utils.h b/third_party/grpc/src/core/lib/transport/error_utils.h new file mode 100644 index 00000000000000..9a46267f38ae3f --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/error_utils.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H +#define GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H + +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/transport/http2_errors.h" + +/// A utility function to get the status code and message to be returned +/// to the application. If not set in the top-level message, looks +/// through child errors until it finds the first one with these attributes. +/// All attributes are pulled from the same child error. error_string will +/// be populated with the entire error string. If any of the attributes (code, +/// msg, http_status, error_string) are unneeded, they can be passed as +/// NULL. +void grpc_error_get_status(grpc_error* error, grpc_millis deadline, + grpc_status_code* code, grpc_slice* slice, + grpc_http2_error_code* http_status, + const char** error_string); + +/// A utility function to check whether there is a clear status code that +/// doesn't need to be guessed in \a error. This means that \a error or some +/// child has GRPC_ERROR_INT_GRPC_STATUS set, or that it is GRPC_ERROR_NONE or +/// GRPC_ERROR_CANCELLED +bool grpc_error_has_clear_grpc_status(grpc_error* error); + +#endif /* GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H */ diff --git a/third_party/grpc/src/core/lib/transport/http2_errors.h b/third_party/grpc/src/core/lib/transport/http2_errors.h new file mode 100644 index 00000000000000..d412c5dffc7399 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/http2_errors.h @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_HTTP2_ERRORS_H +#define GRPC_CORE_LIB_TRANSPORT_HTTP2_ERRORS_H + +/* error codes for RST_STREAM from http2 draft 14 section 7 */ +typedef enum { + GRPC_HTTP2_NO_ERROR = 0x0, + GRPC_HTTP2_PROTOCOL_ERROR = 0x1, + GRPC_HTTP2_INTERNAL_ERROR = 0x2, + GRPC_HTTP2_FLOW_CONTROL_ERROR = 0x3, + GRPC_HTTP2_SETTINGS_TIMEOUT = 0x4, + GRPC_HTTP2_STREAM_CLOSED = 0x5, + GRPC_HTTP2_FRAME_SIZE_ERROR = 0x6, + GRPC_HTTP2_REFUSED_STREAM = 0x7, + GRPC_HTTP2_CANCEL = 0x8, + GRPC_HTTP2_COMPRESSION_ERROR = 0x9, + GRPC_HTTP2_CONNECT_ERROR = 0xa, + GRPC_HTTP2_ENHANCE_YOUR_CALM = 0xb, + GRPC_HTTP2_INADEQUATE_SECURITY = 0xc, + /* force use of a default clause */ + GRPC_HTTP2__ERROR_DO_NOT_USE = -1 +} grpc_http2_error_code; + +#endif /* GRPC_CORE_LIB_TRANSPORT_HTTP2_ERRORS_H */ diff --git a/third_party/grpc/src/core/lib/transport/metadata.cc b/third_party/grpc/src/core/lib/transport/metadata.cc new file mode 100644 index 00000000000000..30482a1b3b1e8c --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/metadata.cc @@ -0,0 +1,523 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/metadata.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/murmur_hash.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" + +/* There are two kinds of mdelem and mdstr instances. + * Static instances are declared in static_metadata.{h,c} and + * are initialized by grpc_mdctx_global_init(). + * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed + * by internal_string and internal_element structures. + * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are + * used to determine which kind of element a pointer refers to. + */ + +grpc_core::DebugOnlyTraceFlag grpc_trace_metadata(false, "metadata"); + +#ifndef NDEBUG +#define DEBUG_ARGS , const char *file, int line +#define FWD_DEBUG_ARGS , file, line +#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__) +#else +#define DEBUG_ARGS +#define FWD_DEBUG_ARGS +#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s)) +#endif + +#define INITIAL_SHARD_CAPACITY 8 +#define LOG2_SHARD_COUNT 4 +#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT)) + +#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity)) +#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1)) + +typedef void (*destroy_user_data_func)(void* user_data); + +/* Shadow structure for grpc_mdelem_data for interned elements */ +typedef struct interned_metadata { + /* must be byte compatible with grpc_mdelem_data */ + grpc_slice key; + grpc_slice value; + + /* private only data */ + gpr_atm refcnt; + + gpr_mu mu_user_data; + gpr_atm destroy_user_data; + gpr_atm user_data; + + struct interned_metadata* bucket_next; +} interned_metadata; + +/* Shadow structure for grpc_mdelem_data for allocated elements */ +typedef struct allocated_metadata { + /* must be byte compatible with grpc_mdelem_data */ + grpc_slice key; + grpc_slice value; + + /* private only data */ + gpr_atm refcnt; +} allocated_metadata; + +typedef struct mdtab_shard { + gpr_mu mu; + interned_metadata** elems; + size_t count; + size_t capacity; + /** Estimate of the number of unreferenced mdelems in the hash table. + This will eventually converge to the exact number, but it's instantaneous + accuracy is not guaranteed */ + gpr_atm free_estimate; +} mdtab_shard; + +static mdtab_shard g_shards[SHARD_COUNT]; + +static void gc_mdtab(mdtab_shard* shard); + +void grpc_mdctx_global_init(void) { + /* initialize shards */ + for (size_t i = 0; i < SHARD_COUNT; i++) { + mdtab_shard* shard = &g_shards[i]; + gpr_mu_init(&shard->mu); + shard->count = 0; + gpr_atm_no_barrier_store(&shard->free_estimate, 0); + shard->capacity = INITIAL_SHARD_CAPACITY; + shard->elems = static_cast( + gpr_zalloc(sizeof(*shard->elems) * shard->capacity)); + } +} + +void grpc_mdctx_global_shutdown() { + for (size_t i = 0; i < SHARD_COUNT; i++) { + mdtab_shard* shard = &g_shards[i]; + gpr_mu_destroy(&shard->mu); + gc_mdtab(shard); + /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ + if (shard->count != 0) { + gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked", + shard->count); + if (grpc_iomgr_abort_on_leaks()) { + abort(); + } + } + gpr_free(shard->elems); + } +} + +static int is_mdelem_static(grpc_mdelem e) { + return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] && + GRPC_MDELEM_DATA(e) < + &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; +} + +static void ref_md_locked(mdtab_shard* shard, + interned_metadata* md DEBUG_ARGS) { +#ifndef NDEBUG + if (grpc_trace_metadata.enabled()) { + char* key_str = grpc_slice_to_c_string(md->key); + char* value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void*)md, + gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) { + gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1); + } +} + +static void gc_mdtab(mdtab_shard* shard) { + GPR_TIMER_SCOPE("gc_mdtab", 0); + + size_t i; + interned_metadata** prev_next; + interned_metadata *md, *next; + gpr_atm num_freed = 0; + + for (i = 0; i < shard->capacity; i++) { + prev_next = &shard->elems[i]; + for (md = shard->elems[i]; md; md = next) { + void* user_data = (void*)gpr_atm_no_barrier_load(&md->user_data); + next = md->bucket_next; + if (gpr_atm_acq_load(&md->refcnt) == 0) { + grpc_slice_unref_internal(md->key); + grpc_slice_unref_internal(md->value); + if (md->user_data) { + ((destroy_user_data_func)gpr_atm_no_barrier_load( + &md->destroy_user_data))(user_data); + } + gpr_mu_destroy(&md->mu_user_data); + gpr_free(md); + *prev_next = next; + num_freed++; + shard->count--; + } else { + prev_next = &md->bucket_next; + } + } + } + gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed); +} + +static void grow_mdtab(mdtab_shard* shard) { + GPR_TIMER_SCOPE("grow_mdtab", 0); + + size_t capacity = shard->capacity * 2; + size_t i; + interned_metadata** mdtab; + interned_metadata *md, *next; + uint32_t hash; + + mdtab = static_cast( + gpr_zalloc(sizeof(interned_metadata*) * capacity)); + + for (i = 0; i < shard->capacity; i++) { + for (md = shard->elems[i]; md; md = next) { + size_t idx; + hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key), + grpc_slice_hash(md->value)); + next = md->bucket_next; + idx = TABLE_IDX(hash, capacity); + md->bucket_next = mdtab[idx]; + mdtab[idx] = md; + } + } + gpr_free(shard->elems); + shard->elems = mdtab; + shard->capacity = capacity; +} + +static void rehash_mdtab(mdtab_shard* shard) { + if (gpr_atm_no_barrier_load(&shard->free_estimate) > + static_cast(shard->capacity / 4)) { + gc_mdtab(shard); + } else { + grow_mdtab(shard); + } +} + +grpc_mdelem grpc_mdelem_create( + const grpc_slice& key, const grpc_slice& value, + grpc_mdelem_data* compatible_external_backing_store) { + if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) { + if (compatible_external_backing_store != nullptr) { + return GRPC_MAKE_MDELEM(compatible_external_backing_store, + GRPC_MDELEM_STORAGE_EXTERNAL); + } + + allocated_metadata* allocated = + static_cast(gpr_malloc(sizeof(*allocated))); + allocated->key = grpc_slice_ref_internal(key); + allocated->value = grpc_slice_ref_internal(value); + gpr_atm_rel_store(&allocated->refcnt, 1); +#ifndef NDEBUG + if (grpc_trace_metadata.enabled()) { + char* key_str = grpc_slice_to_c_string(allocated->key); + char* value_str = grpc_slice_to_c_string(allocated->value); + gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'", + (void*)allocated, gpr_atm_no_barrier_load(&allocated->refcnt), + key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED); + } + + if (GRPC_IS_STATIC_METADATA_STRING(key) && + GRPC_IS_STATIC_METADATA_STRING(value)) { + grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings( + GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value)); + if (!GRPC_MDISNULL(static_elem)) { + return static_elem; + } + } + + uint32_t hash = + GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value)); + interned_metadata* md; + mdtab_shard* shard = &g_shards[SHARD_IDX(hash)]; + size_t idx; + + GPR_TIMER_SCOPE("grpc_mdelem_from_metadata_strings", 0); + + gpr_mu_lock(&shard->mu); + + idx = TABLE_IDX(hash, shard->capacity); + /* search for an existing pair */ + for (md = shard->elems[idx]; md; md = md->bucket_next) { + if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) { + REF_MD_LOCKED(shard, md); + gpr_mu_unlock(&shard->mu); + return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED); + } + } + + /* not found: create a new pair */ + md = static_cast(gpr_malloc(sizeof(interned_metadata))); + gpr_atm_rel_store(&md->refcnt, 1); + md->key = grpc_slice_ref_internal(key); + md->value = grpc_slice_ref_internal(value); + md->user_data = 0; + md->destroy_user_data = 0; + md->bucket_next = shard->elems[idx]; + shard->elems[idx] = md; + gpr_mu_init(&md->mu_user_data); +#ifndef NDEBUG + if (grpc_trace_metadata.enabled()) { + char* key_str = grpc_slice_to_c_string(md->key); + char* value_str = grpc_slice_to_c_string(md->value); + gpr_log(GPR_DEBUG, "ELM NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void*)md, + gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + shard->count++; + + if (shard->count > shard->capacity * 2) { + rehash_mdtab(shard); + } + + gpr_mu_unlock(&shard->mu); + + return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED); +} + +grpc_mdelem grpc_mdelem_from_slices(const grpc_slice& key, + const grpc_slice& value) { + grpc_mdelem out = grpc_mdelem_create(key, value, nullptr); + grpc_slice_unref_internal(key); + grpc_slice_unref_internal(value); + return out; +} + +grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata) { + bool changed = false; + grpc_slice key_slice = + grpc_slice_maybe_static_intern(metadata->key, &changed); + grpc_slice value_slice = + grpc_slice_maybe_static_intern(metadata->value, &changed); + return grpc_mdelem_create( + key_slice, value_slice, + changed ? nullptr : reinterpret_cast(metadata)); +} + +grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) { + switch (GRPC_MDELEM_STORAGE(gmd)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_STATIC: + break; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata* md = + reinterpret_cast GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (grpc_trace_metadata.enabled()) { + char* key_str = grpc_slice_to_c_string(md->key); + char* value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void*)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#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 */ + GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1); + gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); + break; + } + case GRPC_MDELEM_STORAGE_ALLOCATED: { + allocated_metadata* md = + reinterpret_cast GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (grpc_trace_metadata.enabled()) { + char* key_str = grpc_slice_to_c_string(md->key); + char* value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void*)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#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 */ + gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); + break; + } + } + return gmd; +} + +void grpc_mdelem_unref(grpc_mdelem gmd DEBUG_ARGS) { + switch (GRPC_MDELEM_STORAGE(gmd)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_STATIC: + break; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata* md = + reinterpret_cast GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (grpc_trace_metadata.enabled()) { + char* key_str = grpc_slice_to_c_string(md->key); + char* value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void*)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key), + grpc_slice_hash(md->value)); + const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1); + GPR_ASSERT(prev_refcount >= 1); + if (1 == prev_refcount) { + /* once the refcount hits zero, some other thread can come along and + free md at any time: it's unsafe from this point on to access it */ + mdtab_shard* shard = &g_shards[SHARD_IDX(hash)]; + gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1); + } + break; + } + case GRPC_MDELEM_STORAGE_ALLOCATED: { + allocated_metadata* md = + reinterpret_cast GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (grpc_trace_metadata.enabled()) { + char* key_str = grpc_slice_to_c_string(md->key); + char* value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void*)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1); + GPR_ASSERT(prev_refcount >= 1); + if (1 == prev_refcount) { + grpc_slice_unref_internal(md->key); + grpc_slice_unref_internal(md->value); + gpr_free(md); + } + break; + } + } +} + +void* grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void*)) { + switch (GRPC_MDELEM_STORAGE(md)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_ALLOCATED: + return nullptr; + case GRPC_MDELEM_STORAGE_STATIC: + return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) - + grpc_static_mdelem_table]; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata* im = + reinterpret_cast GRPC_MDELEM_DATA(md); + void* result; + if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) { + return (void*)gpr_atm_no_barrier_load(&im->user_data); + } else { + return nullptr; + } + return result; + } + } + GPR_UNREACHABLE_CODE(return nullptr); +} + +void* grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void*), + void* user_data) { + switch (GRPC_MDELEM_STORAGE(md)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_ALLOCATED: + destroy_func(user_data); + return nullptr; + case GRPC_MDELEM_STORAGE_STATIC: + destroy_func(user_data); + return (void*)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) - + grpc_static_mdelem_table]; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata* im = + reinterpret_cast GRPC_MDELEM_DATA(md); + GPR_ASSERT(!is_mdelem_static(md)); + GPR_ASSERT((user_data == nullptr) == (destroy_func == nullptr)); + gpr_mu_lock(&im->mu_user_data); + if (gpr_atm_no_barrier_load(&im->destroy_user_data)) { + /* user data can only be set once */ + gpr_mu_unlock(&im->mu_user_data); + if (destroy_func != nullptr) { + destroy_func(user_data); + } + return (void*)gpr_atm_no_barrier_load(&im->user_data); + } + gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data); + gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func); + gpr_mu_unlock(&im->mu_user_data); + return user_data; + } + } + GPR_UNREACHABLE_CODE(return nullptr); +} + +bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) { + if (a.payload == b.payload) return true; + if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false; + if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false; + return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) && + grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b)); +} diff --git a/third_party/grpc/src/core/lib/transport/metadata.h b/third_party/grpc/src/core/lib/transport/metadata.h new file mode 100644 index 00000000000000..989c7544c1f237 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/metadata.h @@ -0,0 +1,163 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_H +#define GRPC_CORE_LIB_TRANSPORT_METADATA_H + +#include + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/gpr/useful.h" + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_metadata; + +/* 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. + + STATIC METADATA: in static_metadata.h we declare a set of static metadata. + These mdelems and mdstrs are available via pre-declared code generated macros + and are available to code anywhere between grpc_init() and grpc_shutdown(). + They are not refcounted, but can be passed to _ref and _unref functions + declared here - in which case those functions are effectively no-ops. */ + +/* Forward declarations */ +typedef struct grpc_mdelem grpc_mdelem; + +/* if changing this, make identical changes in: + - interned_metadata, allocated_metadata in metadata.c + - grpc_metadata in grpc_types.h */ +typedef struct grpc_mdelem_data { + const grpc_slice key; + const grpc_slice value; + /* there is a private part to this in metadata.c */ +} grpc_mdelem_data; + +/* GRPC_MDELEM_STORAGE_* enum values that can be treated as interned always have + this bit set in their integer value */ +#define GRPC_MDELEM_STORAGE_INTERNED_BIT 1 + +typedef enum { + /* memory pointed to by grpc_mdelem::payload is owned by an external system */ + GRPC_MDELEM_STORAGE_EXTERNAL = 0, + /* memory pointed to by grpc_mdelem::payload is interned by the metadata + system */ + GRPC_MDELEM_STORAGE_INTERNED = GRPC_MDELEM_STORAGE_INTERNED_BIT, + /* memory pointed to by grpc_mdelem::payload is allocated by the metadata + system */ + GRPC_MDELEM_STORAGE_ALLOCATED = 2, + /* memory is in the static metadata table */ + GRPC_MDELEM_STORAGE_STATIC = 2 | GRPC_MDELEM_STORAGE_INTERNED_BIT, +} grpc_mdelem_data_storage; + +struct grpc_mdelem { + /* a grpc_mdelem_data* generally, with the two lower bits signalling memory + ownership as per grpc_mdelem_data_storage */ + uintptr_t payload; +}; + +#define GRPC_MDELEM_DATA(md) ((grpc_mdelem_data*)((md).payload & ~(uintptr_t)3)) +#define GRPC_MDELEM_STORAGE(md) \ + ((grpc_mdelem_data_storage)((md).payload & (uintptr_t)3)) +#ifdef __cplusplus +#define GRPC_MAKE_MDELEM(data, storage) \ + (grpc_mdelem{((uintptr_t)(data)) | ((uintptr_t)storage)}) +#else +#define GRPC_MAKE_MDELEM(data, storage) \ + ((grpc_mdelem){((uintptr_t)(data)) | ((uintptr_t)storage)}) +#endif +#define GRPC_MDELEM_IS_INTERNED(md) \ + ((grpc_mdelem_data_storage)((md).payload & \ + (uintptr_t)GRPC_MDELEM_STORAGE_INTERNED_BIT)) + +/* Unrefs the slices. */ +grpc_mdelem grpc_mdelem_from_slices(const grpc_slice& key, + const grpc_slice& value); + +/* Cheaply convert a grpc_metadata to a grpc_mdelem; may use the grpc_metadata + object as backing storage (so lifetimes should align) */ +grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_metadata* metadata); + +/* Does not unref the slices; if a new non-interned mdelem is needed, allocates + one if compatible_external_backing_store is NULL, or uses + compatible_external_backing_store if it is non-NULL (in which case it's the + users responsibility to ensure that it outlives usage) */ +grpc_mdelem grpc_mdelem_create( + const grpc_slice& key, const grpc_slice& value, + grpc_mdelem_data* compatible_external_backing_store); + +bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b); + +/* 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); + +#ifndef NDEBUG +#define GRPC_MDELEM_REF(s) grpc_mdelem_ref((s), __FILE__, __LINE__) +#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s), __FILE__, __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_MDELEM_REF(s) grpc_mdelem_ref((s)) +#define GRPC_MDELEM_UNREF(s) grpc_mdelem_unref((s)) +grpc_mdelem grpc_mdelem_ref(grpc_mdelem md); +void grpc_mdelem_unref(grpc_mdelem md); +#endif + +#define GRPC_MDKEY(md) (GRPC_MDELEM_DATA(md)->key) +#define GRPC_MDVALUE(md) (GRPC_MDELEM_DATA(md)->value) + +#define GRPC_MDNULL GRPC_MAKE_MDELEM(NULL, GRPC_MDELEM_STORAGE_EXTERNAL) +#define GRPC_MDISNULL(md) (GRPC_MDELEM_DATA(md) == NULL) + +/* We add 32 bytes of padding as per RFC-7540 section 6.5.2. */ +#define GRPC_MDELEM_LENGTH(e) \ + (GRPC_SLICE_LENGTH(GRPC_MDKEY((e))) + GRPC_SLICE_LENGTH(GRPC_MDVALUE((e))) + \ + 32) + +#define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash)) + +void grpc_mdctx_global_init(void); +void grpc_mdctx_global_shutdown(); + +#endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_H */ diff --git a/third_party/grpc/src/core/lib/transport/metadata_batch.cc b/third_party/grpc/src/core/lib/transport/metadata_batch.cc new file mode 100644 index 00000000000000..928ed73cdad861 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/metadata_batch.cc @@ -0,0 +1,330 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/metadata_batch.h" + +#include +#include + +#include +#include + +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +static void assert_valid_list(grpc_mdelem_list* list) { +#ifndef NDEBUG + grpc_linked_mdelem* l; + + GPR_ASSERT((list->head == nullptr) == (list->tail == nullptr)); + if (!list->head) return; + GPR_ASSERT(list->head->prev == nullptr); + GPR_ASSERT(list->tail->next == nullptr); + GPR_ASSERT((list->head == list->tail) == (list->head->next == nullptr)); + + size_t verified_count = 0; + for (l = list->head; l; l = l->next) { + GPR_ASSERT(!GRPC_MDISNULL(l->md)); + GPR_ASSERT((l->prev == nullptr) == (l == list->head)); + GPR_ASSERT((l->next == nullptr) == (l == list->tail)); + if (l->next) GPR_ASSERT(l->next->prev == l); + if (l->prev) GPR_ASSERT(l->prev->next == l); + verified_count++; + } + GPR_ASSERT(list->count == verified_count); +#endif /* NDEBUG */ +} + +static void assert_valid_callouts(grpc_metadata_batch* batch) { +#ifndef NDEBUG + for (grpc_linked_mdelem* l = batch->list.head; l != nullptr; l = l->next) { + grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md)); + grpc_metadata_batch_callouts_index callout_idx = + GRPC_BATCH_INDEX_OF(key_interned); + if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) { + GPR_ASSERT(batch->idx.array[callout_idx] == l); + } + grpc_slice_unref_internal(key_interned); + } +#endif +} + +#ifndef NDEBUG +void grpc_metadata_batch_assert_ok(grpc_metadata_batch* batch) { + assert_valid_list(&batch->list); +} +#endif /* NDEBUG */ + +void grpc_metadata_batch_init(grpc_metadata_batch* batch) { + memset(batch, 0, sizeof(*batch)); + batch->deadline = GRPC_MILLIS_INF_FUTURE; +} + +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); + } +} + +grpc_error* grpc_attach_md_to_error(grpc_error* src, grpc_mdelem md) { + grpc_error* out = grpc_error_set_str( + grpc_error_set_str(src, GRPC_ERROR_STR_KEY, + grpc_slice_ref_internal(GRPC_MDKEY(md))), + GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md))); + return out; +} + +static grpc_error* maybe_link_callout(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) + GRPC_MUST_USE_RESULT; + +static grpc_error* maybe_link_callout(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) { + grpc_metadata_batch_callouts_index idx = + GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); + if (idx == GRPC_BATCH_CALLOUTS_COUNT) { + return GRPC_ERROR_NONE; + } + if (batch->idx.array[idx] == nullptr) { + ++batch->list.default_count; + batch->idx.array[idx] = storage; + return GRPC_ERROR_NONE; + } + return grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"), + storage->md); +} + +static void maybe_unlink_callout(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) { + grpc_metadata_batch_callouts_index idx = + GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); + if (idx == GRPC_BATCH_CALLOUTS_COUNT) { + return; + } + --batch->list.default_count; + GPR_ASSERT(batch->idx.array[idx] != nullptr); + batch->idx.array[idx] = nullptr; +} + +grpc_error* grpc_metadata_batch_add_head(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage, + grpc_mdelem elem_to_add) { + GPR_ASSERT(!GRPC_MDISNULL(elem_to_add)); + storage->md = elem_to_add; + return 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(!GRPC_MDISNULL(storage->md)); + storage->prev = nullptr; + storage->next = list->head; + storage->reserved = nullptr; + if (list->head != nullptr) { + list->head->prev = storage; + } else { + list->tail = storage; + } + list->head = storage; + list->count++; + assert_valid_list(list); +} + +grpc_error* grpc_metadata_batch_link_head(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) { + assert_valid_callouts(batch); + grpc_error* err = maybe_link_callout(batch, storage); + if (err != GRPC_ERROR_NONE) { + assert_valid_callouts(batch); + return err; + } + link_head(&batch->list, storage); + assert_valid_callouts(batch); + return GRPC_ERROR_NONE; +} + +grpc_error* grpc_metadata_batch_add_tail(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage, + grpc_mdelem elem_to_add) { + GPR_ASSERT(!GRPC_MDISNULL(elem_to_add)); + storage->md = elem_to_add; + return 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(!GRPC_MDISNULL(storage->md)); + storage->prev = list->tail; + storage->next = nullptr; + storage->reserved = nullptr; + if (list->tail != nullptr) { + list->tail->next = storage; + } else { + list->head = storage; + } + list->tail = storage; + list->count++; + assert_valid_list(list); +} + +grpc_error* grpc_metadata_batch_link_tail(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) { + assert_valid_callouts(batch); + grpc_error* err = maybe_link_callout(batch, storage); + if (err != GRPC_ERROR_NONE) { + assert_valid_callouts(batch); + return err; + } + link_tail(&batch->list, storage); + assert_valid_callouts(batch); + return GRPC_ERROR_NONE; +} + +static void unlink_storage(grpc_mdelem_list* list, + grpc_linked_mdelem* storage) { + assert_valid_list(list); + if (storage->prev != nullptr) { + storage->prev->next = storage->next; + } else { + list->head = storage->next; + } + if (storage->next != nullptr) { + storage->next->prev = storage->prev; + } else { + list->tail = storage->prev; + } + list->count--; + assert_valid_list(list); +} + +void grpc_metadata_batch_remove(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) { + assert_valid_callouts(batch); + maybe_unlink_callout(batch, storage); + unlink_storage(&batch->list, storage); + GRPC_MDELEM_UNREF(storage->md); + assert_valid_callouts(batch); +} + +void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage, + grpc_slice value) { + grpc_mdelem old_mdelem = storage->md; + grpc_mdelem new_mdelem = grpc_mdelem_from_slices( + grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value); + storage->md = new_mdelem; + GRPC_MDELEM_UNREF(old_mdelem); +} + +grpc_error* grpc_metadata_batch_substitute(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage, + grpc_mdelem new_mdelem) { + assert_valid_callouts(batch); + grpc_error* error = GRPC_ERROR_NONE; + grpc_mdelem old_mdelem = storage->md; + if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) { + maybe_unlink_callout(batch, storage); + storage->md = new_mdelem; + error = maybe_link_callout(batch, storage); + if (error != GRPC_ERROR_NONE) { + unlink_storage(&batch->list, storage); + GRPC_MDELEM_UNREF(storage->md); + } + } else { + storage->md = new_mdelem; + } + GRPC_MDELEM_UNREF(old_mdelem); + assert_valid_callouts(batch); + return error; +} + +void grpc_metadata_batch_clear(grpc_metadata_batch* batch) { + grpc_metadata_batch_destroy(batch); + grpc_metadata_batch_init(batch); +} + +bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch) { + return batch->list.head == nullptr && + batch->deadline == GRPC_MILLIS_INF_FUTURE; +} + +size_t grpc_metadata_batch_size(grpc_metadata_batch* batch) { + size_t size = 0; + for (grpc_linked_mdelem* elem = batch->list.head; elem != nullptr; + elem = elem->next) { + size += GRPC_MDELEM_LENGTH(elem->md); + } + return size; +} + +static void add_error(grpc_error** composite, grpc_error* error, + const char* composite_error_string) { + if (error == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string); + } + *composite = grpc_error_add_child(*composite, error); +} + +grpc_error* grpc_metadata_batch_filter(grpc_metadata_batch* batch, + grpc_metadata_batch_filter_func func, + void* user_data, + const char* composite_error_string) { + grpc_linked_mdelem* l = batch->list.head; + grpc_error* error = GRPC_ERROR_NONE; + while (l) { + grpc_linked_mdelem* next = l->next; + grpc_filtered_mdelem new_mdelem = func(user_data, l->md); + add_error(&error, new_mdelem.error, composite_error_string); + if (GRPC_MDISNULL(new_mdelem.md)) { + grpc_metadata_batch_remove(batch, l); + } else if (new_mdelem.md.payload != l->md.payload) { + grpc_metadata_batch_substitute(batch, l, new_mdelem.md); + } + l = next; + } + return error; +} + +void grpc_metadata_batch_copy(grpc_metadata_batch* src, + grpc_metadata_batch* dst, + grpc_linked_mdelem* storage) { + grpc_metadata_batch_init(dst); + dst->deadline = src->deadline; + size_t i = 0; + for (grpc_linked_mdelem* elem = src->list.head; elem != nullptr; + elem = elem->next) { + grpc_error* error = grpc_metadata_batch_add_tail(dst, &storage[i++], + GRPC_MDELEM_REF(elem->md)); + // The only way that grpc_metadata_batch_add_tail() can fail is if + // there's a duplicate entry for a callout. However, that can't be + // the case here, because we would not have been allowed to create + // a source batch that had that kind of conflict. + GPR_ASSERT(error == GRPC_ERROR_NONE); + } +} + +void grpc_metadata_batch_move(grpc_metadata_batch* src, + grpc_metadata_batch* dst) { + *dst = *src; + grpc_metadata_batch_init(src); +} diff --git a/third_party/grpc/src/core/lib/transport/metadata_batch.h b/third_party/grpc/src/core/lib/transport/metadata_batch.h new file mode 100644 index 00000000000000..f6e8bbf2052e20 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/metadata_batch.h @@ -0,0 +1,154 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H +#define GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H + +#include + +#include + +#include +#include +#include +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/static_metadata.h" + +typedef struct grpc_linked_mdelem { + grpc_linked_mdelem() {} + + grpc_mdelem md; + struct grpc_linked_mdelem* next = nullptr; + struct grpc_linked_mdelem* prev = nullptr; + void* reserved; +} grpc_linked_mdelem; + +typedef struct grpc_mdelem_list { + size_t count; + size_t default_count; // Number of default keys. + 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; + grpc_metadata_batch_callouts idx; + /** Used to calculate grpc-timeout at the point of sending, + or GRPC_MILLIS_INF_FUTURE if this batch does not need to send a + grpc-timeout */ + grpc_millis 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_clear(grpc_metadata_batch* batch); +bool grpc_metadata_batch_is_empty(grpc_metadata_batch* batch); + +/* Returns the transport size of the batch. */ +size_t grpc_metadata_batch_size(grpc_metadata_batch* batch); + +/** Remove \a storage from the batch, unreffing the mdelem contained */ +void grpc_metadata_batch_remove(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage); + +/** Substitute a new mdelem for an old value */ +grpc_error* grpc_metadata_batch_substitute(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage, + grpc_mdelem new_value); + +void grpc_metadata_batch_set_value(grpc_linked_mdelem* storage, + grpc_slice value); + +/** 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. */ +grpc_error* grpc_metadata_batch_link_head(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) + GRPC_MUST_USE_RESULT; + +/** 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. */ +grpc_error* grpc_metadata_batch_link_tail(grpc_metadata_batch* batch, + grpc_linked_mdelem* storage) + GRPC_MUST_USE_RESULT; + +/** 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 */ +grpc_error* grpc_metadata_batch_add_head( + grpc_metadata_batch* batch, grpc_linked_mdelem* storage, + grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT; + +/** 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 */ +grpc_error* grpc_metadata_batch_add_tail( + grpc_metadata_batch* batch, grpc_linked_mdelem* storage, + grpc_mdelem elem_to_add) GRPC_MUST_USE_RESULT; + +grpc_error* grpc_attach_md_to_error(grpc_error* src, grpc_mdelem md); + +typedef struct { + grpc_error* error; + grpc_mdelem md; +} grpc_filtered_mdelem; + +#define GRPC_FILTERED_ERROR(error) \ + { (error), GRPC_MDNULL } +#define GRPC_FILTERED_MDELEM(md) \ + { GRPC_ERROR_NONE, (md) } +#define GRPC_FILTERED_REMOVE() \ + { GRPC_ERROR_NONE, GRPC_MDNULL } + +typedef grpc_filtered_mdelem (*grpc_metadata_batch_filter_func)( + void* user_data, grpc_mdelem elem); +grpc_error* grpc_metadata_batch_filter( + grpc_metadata_batch* batch, grpc_metadata_batch_filter_func func, + void* user_data, const char* composite_error_string) GRPC_MUST_USE_RESULT; + +#ifndef NDEBUG +void grpc_metadata_batch_assert_ok(grpc_metadata_batch* comd); +#else +#define grpc_metadata_batch_assert_ok(comd) \ + do { \ + } while (0) +#endif + +/// Copies \a src to \a dst. \a storage must point to an array of +/// \a grpc_linked_mdelem structs of at least the same size as \a src. +void grpc_metadata_batch_copy(grpc_metadata_batch* src, + grpc_metadata_batch* dst, + grpc_linked_mdelem* storage); + +void grpc_metadata_batch_move(grpc_metadata_batch* src, + grpc_metadata_batch* dst); + +#endif /* GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H */ diff --git a/third_party/grpc/src/core/lib/transport/pid_controller.cc b/third_party/grpc/src/core/lib/transport/pid_controller.cc new file mode 100644 index 00000000000000..dbc98f4917f7b6 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/pid_controller.cc @@ -0,0 +1,51 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/pid_controller.h" + +#include "src/core/lib/gpr/useful.h" + +namespace grpc_core { + +PidController::PidController(const Args& args) + : last_control_value_(args.initial_control_value()), args_(args) {} + +double PidController::Update(double error, double dt) { + if (dt <= 0) return last_control_value_; + /* integrate error using the trapezoid rule */ + error_integral_ += dt * (last_error_ + error) * 0.5; + error_integral_ = GPR_CLAMP(error_integral_, -args_.integral_range(), + args_.integral_range()); + double diff_error = (error - last_error_) / dt; + /* calculate derivative of control value vs time */ + double dc_dt = args_.gain_p() * error + args_.gain_i() * error_integral_ + + args_.gain_d() * diff_error; + /* and perform trapezoidal integration */ + double new_control_value = + last_control_value_ + dt * (last_dc_dt_ + dc_dt) * 0.5; + new_control_value = GPR_CLAMP(new_control_value, args_.min_control_value(), + args_.max_control_value()); + last_error_ = error; + last_dc_dt_ = dc_dt; + last_control_value_ = new_control_value; + return new_control_value; +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/transport/pid_controller.h b/third_party/grpc/src/core/lib/transport/pid_controller.h new file mode 100644 index 00000000000000..e26205bf20d456 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/pid_controller.h @@ -0,0 +1,116 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H +#define GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H + +#include + +#include + +/* \file Simple PID controller. + Implements a proportional-integral-derivative controller. + Used when we want to iteratively control a variable to converge some other + observed value to a 'set-point'. + Gains can be set to adjust sensitivity to current error (p), the integral + of error (i), and the derivative of error (d). */ + +namespace grpc_core { + +class PidController { + public: + class Args { + public: + double gain_p() const { return gain_p_; } + double gain_i() const { return gain_i_; } + double gain_d() const { return gain_d_; } + double initial_control_value() const { return initial_control_value_; } + double min_control_value() const { return min_control_value_; } + double max_control_value() const { return max_control_value_; } + double integral_range() const { return integral_range_; } + + Args& set_gain_p(double gain_p) { + gain_p_ = gain_p; + return *this; + } + Args& set_gain_i(double gain_i) { + gain_i_ = gain_i; + return *this; + } + Args& set_gain_d(double gain_d) { + gain_d_ = gain_d; + return *this; + } + Args& set_initial_control_value(double initial_control_value) { + initial_control_value_ = initial_control_value; + return *this; + } + Args& set_min_control_value(double min_control_value) { + min_control_value_ = min_control_value; + return *this; + } + Args& set_max_control_value(double max_control_value) { + max_control_value_ = max_control_value; + return *this; + } + Args& set_integral_range(double integral_range) { + integral_range_ = integral_range; + return *this; + } + + private: + double gain_p_ = 0.0; + double gain_i_ = 0.0; + double gain_d_ = 0.0; + double initial_control_value_ = 0.0; + double min_control_value_ = std::numeric_limits::min(); + double max_control_value_ = std::numeric_limits::max(); + double integral_range_ = std::numeric_limits::max(); + }; + + explicit PidController(const Args& args); + + /// Reset the controller internal state: useful when the environment has + /// changed significantly + void Reset() { + last_error_ = 0.0; + last_dc_dt_ = 0.0; + error_integral_ = 0.0; + } + + /// Update the controller: given a current error estimate, and the time since + /// the last update, returns a new control value + double Update(double error, double dt); + + /// Returns the last control value calculated + double last_control_value() const { return last_control_value_; } + + /// Returns the current error integral (mostly for testing) + double error_integral() const { return error_integral_; } + + private: + double last_error_ = 0.0; + double error_integral_ = 0.0; + double last_control_value_; + double last_dc_dt_ = 0.0; + const Args args_; +}; + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H */ diff --git a/third_party/grpc/src/core/lib/transport/service_config.cc b/third_party/grpc/src/core/lib/transport/service_config.cc new file mode 100644 index 00000000000000..405e3360287273 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/service_config.cc @@ -0,0 +1,106 @@ +// +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/lib/transport/service_config.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +namespace grpc_core { + +UniquePtr ServiceConfig::Create(const char* json) { + UniquePtr json_string(gpr_strdup(json)); + grpc_json* json_tree = grpc_json_parse_string(json_string.get()); + if (json_tree == nullptr) { + gpr_log(GPR_INFO, "failed to parse JSON for service config"); + return nullptr; + } + return MakeUnique(std::move(json_string), json_tree); +} + +ServiceConfig::ServiceConfig(UniquePtr json_string, grpc_json* json_tree) + : json_string_(std::move(json_string)), json_tree_(json_tree) {} + +ServiceConfig::~ServiceConfig() { grpc_json_destroy(json_tree_); } + +const char* ServiceConfig::GetLoadBalancingPolicyName() const { + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return nullptr; + } + const char* lb_policy_name = nullptr; + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { + if (field->key == nullptr) return nullptr; + if (strcmp(field->key, "loadBalancingPolicy") == 0) { + if (lb_policy_name != nullptr) return nullptr; // Duplicate. + if (field->type != GRPC_JSON_STRING) return nullptr; + lb_policy_name = field->value; + } + } + return lb_policy_name; +} + +int ServiceConfig::CountNamesInMethodConfig(grpc_json* json) { + int num_names = 0; + for (grpc_json* field = json->child; field != nullptr; field = field->next) { + if (field->key != nullptr && strcmp(field->key, "name") == 0) { + if (field->type != GRPC_JSON_ARRAY) return -1; + for (grpc_json* name = field->child; name != nullptr; name = name->next) { + if (name->type != GRPC_JSON_OBJECT) return -1; + ++num_names; + } + } + } + return num_names; +} + +UniquePtr ServiceConfig::ParseJsonMethodName(grpc_json* json) { + if (json->type != GRPC_JSON_OBJECT) return nullptr; + const char* service_name = nullptr; + const char* method_name = nullptr; + for (grpc_json* child = json->child; child != nullptr; child = child->next) { + if (child->key == nullptr) return nullptr; + if (child->type != GRPC_JSON_STRING) return nullptr; + if (strcmp(child->key, "service") == 0) { + if (service_name != nullptr) return nullptr; // Duplicate. + if (child->value == nullptr) return nullptr; + service_name = child->value; + } else if (strcmp(child->key, "method") == 0) { + if (method_name != nullptr) return nullptr; // Duplicate. + if (child->value == nullptr) return nullptr; + method_name = child->value; + } + } + if (service_name == nullptr) return nullptr; // Required field. + char* path; + gpr_asprintf(&path, "/%s/%s", service_name, + method_name == nullptr ? "*" : method_name); + return UniquePtr(path); +} + +} // namespace grpc_core diff --git a/third_party/grpc/src/core/lib/transport/service_config.h b/third_party/grpc/src/core/lib/transport/service_config.h new file mode 100644 index 00000000000000..2c0dd758453347 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/service_config.h @@ -0,0 +1,249 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H +#define GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H + +#include + +#include +#include + +#include "src/core/lib/gprpp/inlined_vector.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/slice/slice_hash_table.h" + +// The main purpose of the code here is to parse the service config in +// JSON form, which will look like this: +// +// { +// "loadBalancingPolicy": "string", // optional +// "methodConfig": [ // array of one or more method_config objects +// { +// "name": [ // array of one or more name objects +// { +// "service": "string", // required +// "method": "string", // optional +// } +// ], +// // remaining fields are optional. +// // see +// https://developers.google.com/protocol-buffers/docs/proto3#json +// // for format details. +// "waitForReady": bool, +// "timeout": "duration_string", +// "maxRequestMessageBytes": "int64_string", +// "maxResponseMessageBytes": "int64_string", +// } +// ] +// } + +namespace grpc_core { + +class ServiceConfig { + public: + /// Creates a new service config from parsing \a json_string. + /// Returns null on parse error. + static UniquePtr Create(const char* json); + + ~ServiceConfig(); + + /// Invokes \a process_json() for each global parameter in the service + /// config. \a arg is passed as the second argument to \a process_json(). + template + using ProcessJson = void (*)(const grpc_json*, T*); + template + void ParseGlobalParams(ProcessJson process_json, T* arg) const; + + /// Gets the LB policy name from \a service_config. + /// Returns NULL if no LB policy name was specified. + /// Caller does NOT take ownership. + const char* GetLoadBalancingPolicyName() const; + + /// Creates a method config table based on the data in \a json. + /// The table's keys are request paths. The table's value type is + /// returned by \a create_value(), based on data parsed from the JSON tree. + /// Returns null on error. + template + using CreateValue = RefCountedPtr (*)(const grpc_json* method_config_json); + template + RefCountedPtr>> CreateMethodConfigTable( + CreateValue create_value); + + /// A helper function for looking up values in the table returned by + /// \a CreateMethodConfigTable(). + /// Gets the method config for the specified \a path, which should be of + /// the form "/service/method". + /// Returns null if the method has no config. + /// Caller does NOT own a reference to the result. + template + static RefCountedPtr MethodConfigTableLookup( + const SliceHashTable>& table, grpc_slice path); + + private: + // So New() can call our private ctor. + template + friend T* New(Args&&... args); + + // Takes ownership of \a json_tree. + ServiceConfig(UniquePtr json_string, grpc_json* json_tree); + + // Returns the number of names specified in the method config \a json. + static int CountNamesInMethodConfig(grpc_json* json); + + // Returns a path string for the JSON name object specified by \a json. + // Returns null on error. + static UniquePtr ParseJsonMethodName(grpc_json* json); + + // Parses the method config from \a json. Adds an entry to \a entries for + // each name found, incrementing \a idx for each entry added. + // Returns false on error. + template + static bool ParseJsonMethodConfig( + grpc_json* json, CreateValue create_value, + typename SliceHashTable>::Entry* entries, size_t* idx); + + UniquePtr json_string_; // Underlying storage for json_tree. + grpc_json* json_tree_; +}; + +// +// implementation -- no user-serviceable parts below +// + +template +void ServiceConfig::ParseGlobalParams(ProcessJson process_json, + T* arg) const { + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return; + } + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { + if (field->key == nullptr) return; + if (strcmp(field->key, "methodConfig") == 0) continue; + process_json(field, arg); + } +} + +template +bool ServiceConfig::ParseJsonMethodConfig( + grpc_json* json, CreateValue create_value, + typename SliceHashTable>::Entry* entries, size_t* idx) { + // Construct value. + RefCountedPtr method_config = create_value(json); + if (method_config == nullptr) return false; + // Construct list of paths. + InlinedVector, 10> paths; + for (grpc_json* child = json->child; child != nullptr; child = child->next) { + if (child->key == nullptr) continue; + if (strcmp(child->key, "name") == 0) { + if (child->type != GRPC_JSON_ARRAY) return false; + for (grpc_json* name = child->child; name != nullptr; name = name->next) { + UniquePtr path = ParseJsonMethodName(name); + if (path == nullptr) return false; + paths.push_back(std::move(path)); + } + } + } + if (paths.size() == 0) return false; // No names specified. + // Add entry for each path. + for (size_t i = 0; i < paths.size(); ++i) { + entries[*idx].key = grpc_slice_from_copied_string(paths[i].get()); + entries[*idx].value = method_config; // Takes a new ref. + ++*idx; + } + // Success. + return true; +} + +template +RefCountedPtr>> +ServiceConfig::CreateMethodConfigTable(CreateValue create_value) { + // Traverse parsed JSON tree. + if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) { + return nullptr; + } + size_t num_entries = 0; + typename SliceHashTable>::Entry* entries = nullptr; + for (grpc_json* field = json_tree_->child; field != nullptr; + field = field->next) { + if (field->key == nullptr) return nullptr; + if (strcmp(field->key, "methodConfig") == 0) { + if (entries != nullptr) return nullptr; // Duplicate. + if (field->type != GRPC_JSON_ARRAY) return nullptr; + // Find number of entries. + for (grpc_json* method = field->child; method != nullptr; + method = method->next) { + int count = CountNamesInMethodConfig(method); + if (count <= 0) return nullptr; + num_entries += static_cast(count); + } + // Populate method config table entries. + entries = static_cast>::Entry*>( + gpr_zalloc(num_entries * + sizeof(typename SliceHashTable>::Entry))); + size_t idx = 0; + for (grpc_json* method = field->child; method != nullptr; + method = method->next) { + if (!ParseJsonMethodConfig(method, create_value, entries, &idx)) { + for (size_t i = 0; i < idx; ++i) { + grpc_slice_unref_internal(entries[i].key); + entries[i].value.reset(); + } + gpr_free(entries); + return nullptr; + } + } + GPR_ASSERT(idx == num_entries); + } + } + // Instantiate method config table. + RefCountedPtr>> method_config_table; + if (entries != nullptr) { + method_config_table = + SliceHashTable>::Create(num_entries, entries, nullptr); + gpr_free(entries); + } + return method_config_table; +} + +template +RefCountedPtr ServiceConfig::MethodConfigTableLookup( + const SliceHashTable>& table, grpc_slice path) { + const RefCountedPtr* value = table.Get(path); + // If we didn't find a match for the path, try looking for a wildcard + // entry (i.e., change "/service/method" to "/service/*"). + if (value == nullptr) { + char* path_str = grpc_slice_to_c_string(path); + const char* sep = strrchr(path_str, '/') + 1; + const size_t len = (size_t)(sep - path_str); + char* buf = (char*)gpr_malloc(len + 2); // '*' and NUL + memcpy(buf, path_str, len); + buf[len] = '*'; + buf[len + 1] = '\0'; + grpc_slice wildcard_path = grpc_slice_from_copied_string(buf); + gpr_free(buf); + value = table.Get(wildcard_path); + grpc_slice_unref_internal(wildcard_path); + gpr_free(path_str); + } + return RefCountedPtr(*value); +} + +} // namespace grpc_core + +#endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */ diff --git a/third_party/grpc/src/core/lib/transport/static_metadata.cc b/third_party/grpc/src/core/lib/transport/static_metadata.cc new file mode 100644 index 00000000000000..3dfaaaad5c82e4 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/static_metadata.cc @@ -0,0 +1,583 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * WARNING: Auto-generated code. + * + * To make changes to this file, change + * tools/codegen/core/gen_static_metadata.py, and then re-run it. + * + * See metadata.h for an explanation of the interface here, and metadata.cc for + * an explanation of what's going on. + */ + +#include + +#include "src/core/lib/transport/static_metadata.h" + +#include "src/core/lib/slice/slice_internal.h" + +static uint8_t g_bytes[] = { + 58, 112, 97, 116, 104, 58, 109, 101, 116, 104, 111, 100, 58, 115, 116, + 97, 116, 117, 115, 58, 97, 117, 116, 104, 111, 114, 105, 116, 121, 58, + 115, 99, 104, 101, 109, 101, 116, 101, 103, 114, 112, 99, 45, 109, 101, + 115, 115, 97, 103, 101, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, + 115, 103, 114, 112, 99, 45, 112, 97, 121, 108, 111, 97, 100, 45, 98, + 105, 110, 103, 114, 112, 99, 45, 101, 110, 99, 111, 100, 105, 110, 103, + 103, 114, 112, 99, 45, 97, 99, 99, 101, 112, 116, 45, 101, 110, 99, + 111, 100, 105, 110, 103, 103, 114, 112, 99, 45, 115, 101, 114, 118, 101, + 114, 45, 115, 116, 97, 116, 115, 45, 98, 105, 110, 103, 114, 112, 99, + 45, 116, 97, 103, 115, 45, 98, 105, 110, 103, 114, 112, 99, 45, 116, + 114, 97, 99, 101, 45, 98, 105, 110, 99, 111, 110, 116, 101, 110, 116, + 45, 116, 121, 112, 101, 99, 111, 110, 116, 101, 110, 116, 45, 101, 110, + 99, 111, 100, 105, 110, 103, 97, 99, 99, 101, 112, 116, 45, 101, 110, + 99, 111, 100, 105, 110, 103, 103, 114, 112, 99, 45, 105, 110, 116, 101, + 114, 110, 97, 108, 45, 101, 110, 99, 111, 100, 105, 110, 103, 45, 114, + 101, 113, 117, 101, 115, 116, 103, 114, 112, 99, 45, 105, 110, 116, 101, + 114, 110, 97, 108, 45, 115, 116, 114, 101, 97, 109, 45, 101, 110, 99, + 111, 100, 105, 110, 103, 45, 114, 101, 113, 117, 101, 115, 116, 117, 115, + 101, 114, 45, 97, 103, 101, 110, 116, 104, 111, 115, 116, 108, 98, 45, + 116, 111, 107, 101, 110, 103, 114, 112, 99, 45, 112, 114, 101, 118, 105, + 111, 117, 115, 45, 114, 112, 99, 45, 97, 116, 116, 101, 109, 112, 116, + 115, 103, 114, 112, 99, 45, 114, 101, 116, 114, 121, 45, 112, 117, 115, + 104, 98, 97, 99, 107, 45, 109, 115, 103, 114, 112, 99, 45, 116, 105, + 109, 101, 111, 117, 116, 49, 50, 51, 52, 103, 114, 112, 99, 46, 119, + 97, 105, 116, 95, 102, 111, 114, 95, 114, 101, 97, 100, 121, 103, 114, + 112, 99, 46, 116, 105, 109, 101, 111, 117, 116, 103, 114, 112, 99, 46, + 109, 97, 120, 95, 114, 101, 113, 117, 101, 115, 116, 95, 109, 101, 115, + 115, 97, 103, 101, 95, 98, 121, 116, 101, 115, 103, 114, 112, 99, 46, + 109, 97, 120, 95, 114, 101, 115, 112, 111, 110, 115, 101, 95, 109, 101, + 115, 115, 97, 103, 101, 95, 98, 121, 116, 101, 115, 47, 103, 114, 112, + 99, 46, 108, 98, 46, 118, 49, 46, 76, 111, 97, 100, 66, 97, 108, + 97, 110, 99, 101, 114, 47, 66, 97, 108, 97, 110, 99, 101, 76, 111, + 97, 100, 47, 103, 114, 112, 99, 46, 104, 101, 97, 108, 116, 104, 46, + 118, 49, 46, 72, 101, 97, 108, 116, 104, 47, 87, 97, 116, 99, 104, + 47, 101, 110, 118, 111, 121, 46, 115, 101, 114, 118, 105, 99, 101, 46, + 100, 105, 115, 99, 111, 118, 101, 114, 121, 46, 118, 50, 46, 65, 103, + 103, 114, 101, 103, 97, 116, 101, 100, 68, 105, 115, 99, 111, 118, 101, + 114, 121, 83, 101, 114, 118, 105, 99, 101, 47, 83, 116, 114, 101, 97, + 109, 65, 103, 103, 114, 101, 103, 97, 116, 101, 100, 82, 101, 115, 111, + 117, 114, 99, 101, 115, 100, 101, 102, 108, 97, 116, 101, 103, 122, 105, + 112, 115, 116, 114, 101, 97, 109, 47, 103, 122, 105, 112, 71, 69, 84, + 80, 79, 83, 84, 47, 47, 105, 110, 100, 101, 120, 46, 104, 116, 109, + 108, 104, 116, 116, 112, 104, 116, 116, 112, 115, 50, 48, 48, 50, 48, + 52, 50, 48, 54, 51, 48, 52, 52, 48, 48, 52, 48, 52, 53, 48, + 48, 97, 99, 99, 101, 112, 116, 45, 99, 104, 97, 114, 115, 101, 116, + 103, 122, 105, 112, 44, 32, 100, 101, 102, 108, 97, 116, 101, 97, 99, + 99, 101, 112, 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, 97, 99, + 99, 101, 112, 116, 45, 114, 97, 110, 103, 101, 115, 97, 99, 99, 101, + 112, 116, 97, 99, 99, 101, 115, 115, 45, 99, 111, 110, 116, 114, 111, + 108, 45, 97, 108, 108, 111, 119, 45, 111, 114, 105, 103, 105, 110, 97, + 103, 101, 97, 108, 108, 111, 119, 97, 117, 116, 104, 111, 114, 105, 122, + 97, 116, 105, 111, 110, 99, 97, 99, 104, 101, 45, 99, 111, 110, 116, + 114, 111, 108, 99, 111, 110, 116, 101, 110, 116, 45, 100, 105, 115, 112, + 111, 115, 105, 116, 105, 111, 110, 99, 111, 110, 116, 101, 110, 116, 45, + 108, 97, 110, 103, 117, 97, 103, 101, 99, 111, 110, 116, 101, 110, 116, + 45, 108, 101, 110, 103, 116, 104, 99, 111, 110, 116, 101, 110, 116, 45, + 108, 111, 99, 97, 116, 105, 111, 110, 99, 111, 110, 116, 101, 110, 116, + 45, 114, 97, 110, 103, 101, 99, 111, 111, 107, 105, 101, 100, 97, 116, + 101, 101, 116, 97, 103, 101, 120, 112, 101, 99, 116, 101, 120, 112, 105, + 114, 101, 115, 102, 114, 111, 109, 105, 102, 45, 109, 97, 116, 99, 104, + 105, 102, 45, 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, 105, 110, + 99, 101, 105, 102, 45, 110, 111, 110, 101, 45, 109, 97, 116, 99, 104, + 105, 102, 45, 114, 97, 110, 103, 101, 105, 102, 45, 117, 110, 109, 111, + 100, 105, 102, 105, 101, 100, 45, 115, 105, 110, 99, 101, 108, 97, 115, + 116, 45, 109, 111, 100, 105, 102, 105, 101, 100, 108, 105, 110, 107, 108, + 111, 99, 97, 116, 105, 111, 110, 109, 97, 120, 45, 102, 111, 114, 119, + 97, 114, 100, 115, 112, 114, 111, 120, 121, 45, 97, 117, 116, 104, 101, + 110, 116, 105, 99, 97, 116, 101, 112, 114, 111, 120, 121, 45, 97, 117, + 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 114, 97, 110, 103, + 101, 114, 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, 101, 115, 104, + 114, 101, 116, 114, 121, 45, 97, 102, 116, 101, 114, 115, 101, 114, 118, + 101, 114, 115, 101, 116, 45, 99, 111, 111, 107, 105, 101, 115, 116, 114, + 105, 99, 116, 45, 116, 114, 97, 110, 115, 112, 111, 114, 116, 45, 115, + 101, 99, 117, 114, 105, 116, 121, 116, 114, 97, 110, 115, 102, 101, 114, + 45, 101, 110, 99, 111, 100, 105, 110, 103, 118, 97, 114, 121, 118, 105, + 97, 119, 119, 119, 45, 97, 117, 116, 104, 101, 110, 116, 105, 99, 97, + 116, 101, 48, 105, 100, 101, 110, 116, 105, 116, 121, 116, 114, 97, 105, + 108, 101, 114, 115, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, + 47, 103, 114, 112, 99, 103, 114, 112, 99, 80, 85, 84, 108, 98, 45, + 99, 111, 115, 116, 45, 98, 105, 110, 105, 100, 101, 110, 116, 105, 116, + 121, 44, 100, 101, 102, 108, 97, 116, 101, 105, 100, 101, 110, 116, 105, + 116, 121, 44, 103, 122, 105, 112, 100, 101, 102, 108, 97, 116, 101, 44, + 103, 122, 105, 112, 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, + 102, 108, 97, 116, 101, 44, 103, 122, 105, 112}; + +static void static_ref(void* unused) {} +static void static_unref(void* unused) {} +static const grpc_slice_refcount_vtable static_sub_vtable = { + static_ref, static_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; +const grpc_slice_refcount_vtable grpc_static_metadata_vtable = { + static_ref, static_unref, grpc_static_slice_eq, grpc_static_slice_hash}; +static grpc_slice_refcount static_sub_refcnt = {&static_sub_vtable, + &static_sub_refcnt}; +grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = { + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, +}; + +const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = { + {&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, + {&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, + {&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, + {&grpc_static_metadata_refcounts[6], {{g_bytes + 38, 12}}}, + {&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[8], {{g_bytes + 61, 16}}}, + {&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[11], {{g_bytes + 110, 21}}}, + {&grpc_static_metadata_refcounts[12], {{g_bytes + 131, 13}}}, + {&grpc_static_metadata_refcounts[13], {{g_bytes + 144, 14}}}, + {&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, + {&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[17], {{g_bytes + 201, 30}}}, + {&grpc_static_metadata_refcounts[18], {{g_bytes + 231, 37}}}, + {&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, + {&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, + {&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, + {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 26}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 316, 22}}}, + {&grpc_static_metadata_refcounts[24], {{g_bytes + 338, 12}}}, + {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}, + {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}, + {&grpc_static_metadata_refcounts[27], {{g_bytes + 352, 1}}}, + {&grpc_static_metadata_refcounts[28], {{g_bytes + 353, 1}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}, + {&grpc_static_metadata_refcounts[30], {{g_bytes + 354, 19}}}, + {&grpc_static_metadata_refcounts[31], {{g_bytes + 373, 12}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 385, 30}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 415, 31}}}, + {&grpc_static_metadata_refcounts[34], {{g_bytes + 446, 36}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 482, 28}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 510, 80}}}, + {&grpc_static_metadata_refcounts[37], {{g_bytes + 590, 7}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 601, 11}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 612, 3}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 615, 4}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 619, 1}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 620, 11}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 631, 4}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 635, 5}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 640, 3}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 643, 3}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 646, 3}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 649, 3}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 652, 3}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 655, 3}}}, + {&grpc_static_metadata_refcounts[52], {{g_bytes + 658, 3}}}, + {&grpc_static_metadata_refcounts[53], {{g_bytes + 661, 14}}}, + {&grpc_static_metadata_refcounts[54], {{g_bytes + 675, 13}}}, + {&grpc_static_metadata_refcounts[55], {{g_bytes + 688, 15}}}, + {&grpc_static_metadata_refcounts[56], {{g_bytes + 703, 13}}}, + {&grpc_static_metadata_refcounts[57], {{g_bytes + 716, 6}}}, + {&grpc_static_metadata_refcounts[58], {{g_bytes + 722, 27}}}, + {&grpc_static_metadata_refcounts[59], {{g_bytes + 749, 3}}}, + {&grpc_static_metadata_refcounts[60], {{g_bytes + 752, 5}}}, + {&grpc_static_metadata_refcounts[61], {{g_bytes + 757, 13}}}, + {&grpc_static_metadata_refcounts[62], {{g_bytes + 770, 13}}}, + {&grpc_static_metadata_refcounts[63], {{g_bytes + 783, 19}}}, + {&grpc_static_metadata_refcounts[64], {{g_bytes + 802, 16}}}, + {&grpc_static_metadata_refcounts[65], {{g_bytes + 818, 14}}}, + {&grpc_static_metadata_refcounts[66], {{g_bytes + 832, 16}}}, + {&grpc_static_metadata_refcounts[67], {{g_bytes + 848, 13}}}, + {&grpc_static_metadata_refcounts[68], {{g_bytes + 861, 6}}}, + {&grpc_static_metadata_refcounts[69], {{g_bytes + 867, 4}}}, + {&grpc_static_metadata_refcounts[70], {{g_bytes + 871, 4}}}, + {&grpc_static_metadata_refcounts[71], {{g_bytes + 875, 6}}}, + {&grpc_static_metadata_refcounts[72], {{g_bytes + 881, 7}}}, + {&grpc_static_metadata_refcounts[73], {{g_bytes + 888, 4}}}, + {&grpc_static_metadata_refcounts[74], {{g_bytes + 892, 8}}}, + {&grpc_static_metadata_refcounts[75], {{g_bytes + 900, 17}}}, + {&grpc_static_metadata_refcounts[76], {{g_bytes + 917, 13}}}, + {&grpc_static_metadata_refcounts[77], {{g_bytes + 930, 8}}}, + {&grpc_static_metadata_refcounts[78], {{g_bytes + 938, 19}}}, + {&grpc_static_metadata_refcounts[79], {{g_bytes + 957, 13}}}, + {&grpc_static_metadata_refcounts[80], {{g_bytes + 970, 4}}}, + {&grpc_static_metadata_refcounts[81], {{g_bytes + 974, 8}}}, + {&grpc_static_metadata_refcounts[82], {{g_bytes + 982, 12}}}, + {&grpc_static_metadata_refcounts[83], {{g_bytes + 994, 18}}}, + {&grpc_static_metadata_refcounts[84], {{g_bytes + 1012, 19}}}, + {&grpc_static_metadata_refcounts[85], {{g_bytes + 1031, 5}}}, + {&grpc_static_metadata_refcounts[86], {{g_bytes + 1036, 7}}}, + {&grpc_static_metadata_refcounts[87], {{g_bytes + 1043, 7}}}, + {&grpc_static_metadata_refcounts[88], {{g_bytes + 1050, 11}}}, + {&grpc_static_metadata_refcounts[89], {{g_bytes + 1061, 6}}}, + {&grpc_static_metadata_refcounts[90], {{g_bytes + 1067, 10}}}, + {&grpc_static_metadata_refcounts[91], {{g_bytes + 1077, 25}}}, + {&grpc_static_metadata_refcounts[92], {{g_bytes + 1102, 17}}}, + {&grpc_static_metadata_refcounts[93], {{g_bytes + 1119, 4}}}, + {&grpc_static_metadata_refcounts[94], {{g_bytes + 1123, 3}}}, + {&grpc_static_metadata_refcounts[95], {{g_bytes + 1126, 16}}}, + {&grpc_static_metadata_refcounts[96], {{g_bytes + 1142, 1}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}, + {&grpc_static_metadata_refcounts[98], {{g_bytes + 1151, 8}}}, + {&grpc_static_metadata_refcounts[99], {{g_bytes + 1159, 16}}}, + {&grpc_static_metadata_refcounts[100], {{g_bytes + 1175, 4}}}, + {&grpc_static_metadata_refcounts[101], {{g_bytes + 1179, 3}}}, + {&grpc_static_metadata_refcounts[102], {{g_bytes + 1182, 11}}}, + {&grpc_static_metadata_refcounts[103], {{g_bytes + 1193, 16}}}, + {&grpc_static_metadata_refcounts[104], {{g_bytes + 1209, 13}}}, + {&grpc_static_metadata_refcounts[105], {{g_bytes + 1222, 12}}}, + {&grpc_static_metadata_refcounts[106], {{g_bytes + 1234, 21}}}, +}; + +uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { + 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, 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, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 6, 6, 8, 8, 2, 4, 4}; + +static const int8_t elems_r[] = { + 15, 10, -8, 0, 2, -42, -81, -43, 0, 6, -8, 0, 0, 0, 2, + -3, -10, 0, 0, 1, 0, -1, 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, -64, 0, -67, -68, -69, -70, 0, + 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, + 5, 4, 5, 4, 4, 8, 8, 0, 0, 0, 0, 0, 0, -5, 0}; +static uint32_t elems_phash(uint32_t i) { + i -= 42; + uint32_t x = i % 105; + uint32_t y = i / 105; + uint32_t h = x; + if (y < GPR_ARRAY_SIZE(elems_r)) { + uint32_t delta = (uint32_t)elems_r[y]; + h += delta; + } + return h; +} + +static const uint16_t elem_keys[] = { + 260, 261, 262, 263, 264, 265, 266, 1107, 1108, 1741, 147, 148, + 472, 473, 1634, 42, 43, 1527, 1750, 1000, 1001, 774, 775, 1643, + 633, 845, 2062, 2169, 2276, 5700, 5914, 6021, 6128, 6235, 1766, 6342, + 6449, 6556, 6663, 6770, 6877, 6984, 7091, 7198, 7305, 7412, 7519, 7626, + 7733, 7840, 7947, 8054, 8161, 8268, 8375, 8482, 8589, 8696, 8803, 8910, + 9017, 9124, 9231, 9338, 9445, 9552, 9659, 1167, 528, 9766, 9873, 208, + 9980, 1173, 1174, 1175, 1176, 1809, 10087, 1060, 10194, 10943, 1702, 0, + 1816, 0, 0, 1597, 0, 0, 350, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0}; +static const uint8_t elem_idxs[] = { + 7, 8, 9, 10, 11, 12, 13, 77, 79, 71, 1, 2, 5, 6, 25, 3, + 4, 30, 84, 66, 65, 62, 63, 73, 67, 61, 57, 37, 74, 14, 16, 17, + 18, 19, 15, 20, 21, 22, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34, + 35, 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 76, 69, 55, 56, 70, 58, 78, 80, 81, 82, 83, 59, 64, + 60, 75, 72, 255, 85, 255, 255, 68, 255, 255, 0}; + +grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) { + if (a == -1 || b == -1) return GRPC_MDNULL; + uint32_t k = (uint32_t)(a * 107 + b); + uint32_t h = elems_phash(k); + return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k && + elem_idxs[h] != 255 + ? GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[elem_idxs[h]], + GRPC_MDELEM_STORAGE_STATIC) + : GRPC_MDNULL; +} + +grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = { + {{&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 612, 3}}}}, + {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 615, 4}}}}, + {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 619, 1}}}}, + {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 620, 11}}}}, + {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 631, 4}}}}, + {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 635, 5}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 640, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 643, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 646, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 649, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 652, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 655, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[52], {{g_bytes + 658, 3}}}}, + {{&grpc_static_metadata_refcounts[53], {{g_bytes + 661, 14}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[54], {{g_bytes + 675, 13}}}}, + {{&grpc_static_metadata_refcounts[55], {{g_bytes + 688, 15}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[56], {{g_bytes + 703, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[57], {{g_bytes + 716, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[58], {{g_bytes + 722, 27}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[59], {{g_bytes + 749, 3}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[60], {{g_bytes + 752, 5}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[61], {{g_bytes + 757, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[62], {{g_bytes + 770, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[63], {{g_bytes + 783, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[64], {{g_bytes + 802, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[65], {{g_bytes + 818, 14}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[66], {{g_bytes + 832, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[67], {{g_bytes + 848, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[68], {{g_bytes + 861, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[69], {{g_bytes + 867, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[70], {{g_bytes + 871, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[71], {{g_bytes + 875, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[72], {{g_bytes + 881, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[73], {{g_bytes + 888, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[74], {{g_bytes + 892, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[75], {{g_bytes + 900, 17}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[76], {{g_bytes + 917, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[77], {{g_bytes + 930, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[78], {{g_bytes + 938, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[79], {{g_bytes + 957, 13}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[80], {{g_bytes + 970, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[81], {{g_bytes + 974, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[82], {{g_bytes + 982, 12}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[83], {{g_bytes + 994, 18}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[84], {{g_bytes + 1012, 19}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[85], {{g_bytes + 1031, 5}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[86], {{g_bytes + 1036, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[87], {{g_bytes + 1043, 7}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[88], {{g_bytes + 1050, 11}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[89], {{g_bytes + 1061, 6}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[90], {{g_bytes + 1067, 10}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[91], {{g_bytes + 1077, 25}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[92], {{g_bytes + 1102, 17}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[93], {{g_bytes + 1119, 4}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1123, 3}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1126, 16}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[96], {{g_bytes + 1142, 1}}}}, + {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[25], {{g_bytes + 350, 1}}}}, + {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[26], {{g_bytes + 351, 1}}}}, + {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}}, + {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}}, + {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[37], {{g_bytes + 590, 7}}}}, + {{&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, + {&grpc_static_metadata_refcounts[98], {{g_bytes + 1151, 8}}}}, + {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, + {&grpc_static_metadata_refcounts[99], {{g_bytes + 1159, 16}}}}, + {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[100], {{g_bytes + 1175, 4}}}}, + {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[101], {{g_bytes + 1179, 3}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}}, + {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}}, + {{&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[102], {{g_bytes + 1182, 11}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 354, 0}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[37], {{g_bytes + 590, 7}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[103], {{g_bytes + 1193, 16}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[104], {{g_bytes + 1209, 13}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[105], {{g_bytes + 1222, 12}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[106], {{g_bytes + 1234, 21}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1143, 8}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 597, 4}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[104], {{g_bytes + 1209, 13}}}}, +}; +const uint8_t grpc_static_accept_encoding_metadata[8] = {0, 76, 77, 78, + 79, 80, 81, 82}; + +const uint8_t grpc_static_accept_stream_encoding_metadata[4] = {0, 83, 84, 85}; diff --git a/third_party/grpc/src/core/lib/transport/static_metadata.h b/third_party/grpc/src/core/lib/transport/static_metadata.h new file mode 100644 index 00000000000000..4f9670232c3830 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/static_metadata.h @@ -0,0 +1,608 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * WARNING: Auto-generated code. + * + * To make changes to this file, change + * tools/codegen/core/gen_static_metadata.py, and then re-run it. + * + * See metadata.h for an explanation of the interface here, and metadata.cc for + * an explanation of what's going on. + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H +#define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H + +#include + +#include "src/core/lib/transport/metadata.h" + +#define GRPC_STATIC_MDSTR_COUNT 107 +extern const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT]; +/* ":path" */ +#define GRPC_MDSTR_PATH (grpc_static_slice_table[0]) +/* ":method" */ +#define GRPC_MDSTR_METHOD (grpc_static_slice_table[1]) +/* ":status" */ +#define GRPC_MDSTR_STATUS (grpc_static_slice_table[2]) +/* ":authority" */ +#define GRPC_MDSTR_AUTHORITY (grpc_static_slice_table[3]) +/* ":scheme" */ +#define GRPC_MDSTR_SCHEME (grpc_static_slice_table[4]) +/* "te" */ +#define GRPC_MDSTR_TE (grpc_static_slice_table[5]) +/* "grpc-message" */ +#define GRPC_MDSTR_GRPC_MESSAGE (grpc_static_slice_table[6]) +/* "grpc-status" */ +#define GRPC_MDSTR_GRPC_STATUS (grpc_static_slice_table[7]) +/* "grpc-payload-bin" */ +#define GRPC_MDSTR_GRPC_PAYLOAD_BIN (grpc_static_slice_table[8]) +/* "grpc-encoding" */ +#define GRPC_MDSTR_GRPC_ENCODING (grpc_static_slice_table[9]) +/* "grpc-accept-encoding" */ +#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (grpc_static_slice_table[10]) +/* "grpc-server-stats-bin" */ +#define GRPC_MDSTR_GRPC_SERVER_STATS_BIN (grpc_static_slice_table[11]) +/* "grpc-tags-bin" */ +#define GRPC_MDSTR_GRPC_TAGS_BIN (grpc_static_slice_table[12]) +/* "grpc-trace-bin" */ +#define GRPC_MDSTR_GRPC_TRACE_BIN (grpc_static_slice_table[13]) +/* "content-type" */ +#define GRPC_MDSTR_CONTENT_TYPE (grpc_static_slice_table[14]) +/* "content-encoding" */ +#define GRPC_MDSTR_CONTENT_ENCODING (grpc_static_slice_table[15]) +/* "accept-encoding" */ +#define GRPC_MDSTR_ACCEPT_ENCODING (grpc_static_slice_table[16]) +/* "grpc-internal-encoding-request" */ +#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (grpc_static_slice_table[17]) +/* "grpc-internal-stream-encoding-request" */ +#define GRPC_MDSTR_GRPC_INTERNAL_STREAM_ENCODING_REQUEST \ + (grpc_static_slice_table[18]) +/* "user-agent" */ +#define GRPC_MDSTR_USER_AGENT (grpc_static_slice_table[19]) +/* "host" */ +#define GRPC_MDSTR_HOST (grpc_static_slice_table[20]) +/* "lb-token" */ +#define GRPC_MDSTR_LB_TOKEN (grpc_static_slice_table[21]) +/* "grpc-previous-rpc-attempts" */ +#define GRPC_MDSTR_GRPC_PREVIOUS_RPC_ATTEMPTS (grpc_static_slice_table[22]) +/* "grpc-retry-pushback-ms" */ +#define GRPC_MDSTR_GRPC_RETRY_PUSHBACK_MS (grpc_static_slice_table[23]) +/* "grpc-timeout" */ +#define GRPC_MDSTR_GRPC_TIMEOUT (grpc_static_slice_table[24]) +/* "1" */ +#define GRPC_MDSTR_1 (grpc_static_slice_table[25]) +/* "2" */ +#define GRPC_MDSTR_2 (grpc_static_slice_table[26]) +/* "3" */ +#define GRPC_MDSTR_3 (grpc_static_slice_table[27]) +/* "4" */ +#define GRPC_MDSTR_4 (grpc_static_slice_table[28]) +/* "" */ +#define GRPC_MDSTR_EMPTY (grpc_static_slice_table[29]) +/* "grpc.wait_for_ready" */ +#define GRPC_MDSTR_GRPC_DOT_WAIT_FOR_READY (grpc_static_slice_table[30]) +/* "grpc.timeout" */ +#define GRPC_MDSTR_GRPC_DOT_TIMEOUT (grpc_static_slice_table[31]) +/* "grpc.max_request_message_bytes" */ +#define GRPC_MDSTR_GRPC_DOT_MAX_REQUEST_MESSAGE_BYTES \ + (grpc_static_slice_table[32]) +/* "grpc.max_response_message_bytes" */ +#define GRPC_MDSTR_GRPC_DOT_MAX_RESPONSE_MESSAGE_BYTES \ + (grpc_static_slice_table[33]) +/* "/grpc.lb.v1.LoadBalancer/BalanceLoad" */ +#define GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD \ + (grpc_static_slice_table[34]) +/* "/grpc.health.v1.Health/Watch" */ +#define GRPC_MDSTR_SLASH_GRPC_DOT_HEALTH_DOT_V1_DOT_HEALTH_SLASH_WATCH \ + (grpc_static_slice_table[35]) +/* "/envoy.service.discovery.v2.AggregatedDiscoveryService/StreamAggregatedResources" + */ +#define GRPC_MDSTR_SLASH_ENVOY_DOT_SERVICE_DOT_DISCOVERY_DOT_V2_DOT_AGGREGATEDDISCOVERYSERVICE_SLASH_STREAMAGGREGATEDRESOURCES \ + (grpc_static_slice_table[36]) +/* "deflate" */ +#define GRPC_MDSTR_DEFLATE (grpc_static_slice_table[37]) +/* "gzip" */ +#define GRPC_MDSTR_GZIP (grpc_static_slice_table[38]) +/* "stream/gzip" */ +#define GRPC_MDSTR_STREAM_SLASH_GZIP (grpc_static_slice_table[39]) +/* "GET" */ +#define GRPC_MDSTR_GET (grpc_static_slice_table[40]) +/* "POST" */ +#define GRPC_MDSTR_POST (grpc_static_slice_table[41]) +/* "/" */ +#define GRPC_MDSTR_SLASH (grpc_static_slice_table[42]) +/* "/index.html" */ +#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (grpc_static_slice_table[43]) +/* "http" */ +#define GRPC_MDSTR_HTTP (grpc_static_slice_table[44]) +/* "https" */ +#define GRPC_MDSTR_HTTPS (grpc_static_slice_table[45]) +/* "200" */ +#define GRPC_MDSTR_200 (grpc_static_slice_table[46]) +/* "204" */ +#define GRPC_MDSTR_204 (grpc_static_slice_table[47]) +/* "206" */ +#define GRPC_MDSTR_206 (grpc_static_slice_table[48]) +/* "304" */ +#define GRPC_MDSTR_304 (grpc_static_slice_table[49]) +/* "400" */ +#define GRPC_MDSTR_400 (grpc_static_slice_table[50]) +/* "404" */ +#define GRPC_MDSTR_404 (grpc_static_slice_table[51]) +/* "500" */ +#define GRPC_MDSTR_500 (grpc_static_slice_table[52]) +/* "accept-charset" */ +#define GRPC_MDSTR_ACCEPT_CHARSET (grpc_static_slice_table[53]) +/* "gzip, deflate" */ +#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (grpc_static_slice_table[54]) +/* "accept-language" */ +#define GRPC_MDSTR_ACCEPT_LANGUAGE (grpc_static_slice_table[55]) +/* "accept-ranges" */ +#define GRPC_MDSTR_ACCEPT_RANGES (grpc_static_slice_table[56]) +/* "accept" */ +#define GRPC_MDSTR_ACCEPT (grpc_static_slice_table[57]) +/* "access-control-allow-origin" */ +#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (grpc_static_slice_table[58]) +/* "age" */ +#define GRPC_MDSTR_AGE (grpc_static_slice_table[59]) +/* "allow" */ +#define GRPC_MDSTR_ALLOW (grpc_static_slice_table[60]) +/* "authorization" */ +#define GRPC_MDSTR_AUTHORIZATION (grpc_static_slice_table[61]) +/* "cache-control" */ +#define GRPC_MDSTR_CACHE_CONTROL (grpc_static_slice_table[62]) +/* "content-disposition" */ +#define GRPC_MDSTR_CONTENT_DISPOSITION (grpc_static_slice_table[63]) +/* "content-language" */ +#define GRPC_MDSTR_CONTENT_LANGUAGE (grpc_static_slice_table[64]) +/* "content-length" */ +#define GRPC_MDSTR_CONTENT_LENGTH (grpc_static_slice_table[65]) +/* "content-location" */ +#define GRPC_MDSTR_CONTENT_LOCATION (grpc_static_slice_table[66]) +/* "content-range" */ +#define GRPC_MDSTR_CONTENT_RANGE (grpc_static_slice_table[67]) +/* "cookie" */ +#define GRPC_MDSTR_COOKIE (grpc_static_slice_table[68]) +/* "date" */ +#define GRPC_MDSTR_DATE (grpc_static_slice_table[69]) +/* "etag" */ +#define GRPC_MDSTR_ETAG (grpc_static_slice_table[70]) +/* "expect" */ +#define GRPC_MDSTR_EXPECT (grpc_static_slice_table[71]) +/* "expires" */ +#define GRPC_MDSTR_EXPIRES (grpc_static_slice_table[72]) +/* "from" */ +#define GRPC_MDSTR_FROM (grpc_static_slice_table[73]) +/* "if-match" */ +#define GRPC_MDSTR_IF_MATCH (grpc_static_slice_table[74]) +/* "if-modified-since" */ +#define GRPC_MDSTR_IF_MODIFIED_SINCE (grpc_static_slice_table[75]) +/* "if-none-match" */ +#define GRPC_MDSTR_IF_NONE_MATCH (grpc_static_slice_table[76]) +/* "if-range" */ +#define GRPC_MDSTR_IF_RANGE (grpc_static_slice_table[77]) +/* "if-unmodified-since" */ +#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (grpc_static_slice_table[78]) +/* "last-modified" */ +#define GRPC_MDSTR_LAST_MODIFIED (grpc_static_slice_table[79]) +/* "link" */ +#define GRPC_MDSTR_LINK (grpc_static_slice_table[80]) +/* "location" */ +#define GRPC_MDSTR_LOCATION (grpc_static_slice_table[81]) +/* "max-forwards" */ +#define GRPC_MDSTR_MAX_FORWARDS (grpc_static_slice_table[82]) +/* "proxy-authenticate" */ +#define GRPC_MDSTR_PROXY_AUTHENTICATE (grpc_static_slice_table[83]) +/* "proxy-authorization" */ +#define GRPC_MDSTR_PROXY_AUTHORIZATION (grpc_static_slice_table[84]) +/* "range" */ +#define GRPC_MDSTR_RANGE (grpc_static_slice_table[85]) +/* "referer" */ +#define GRPC_MDSTR_REFERER (grpc_static_slice_table[86]) +/* "refresh" */ +#define GRPC_MDSTR_REFRESH (grpc_static_slice_table[87]) +/* "retry-after" */ +#define GRPC_MDSTR_RETRY_AFTER (grpc_static_slice_table[88]) +/* "server" */ +#define GRPC_MDSTR_SERVER (grpc_static_slice_table[89]) +/* "set-cookie" */ +#define GRPC_MDSTR_SET_COOKIE (grpc_static_slice_table[90]) +/* "strict-transport-security" */ +#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (grpc_static_slice_table[91]) +/* "transfer-encoding" */ +#define GRPC_MDSTR_TRANSFER_ENCODING (grpc_static_slice_table[92]) +/* "vary" */ +#define GRPC_MDSTR_VARY (grpc_static_slice_table[93]) +/* "via" */ +#define GRPC_MDSTR_VIA (grpc_static_slice_table[94]) +/* "www-authenticate" */ +#define GRPC_MDSTR_WWW_AUTHENTICATE (grpc_static_slice_table[95]) +/* "0" */ +#define GRPC_MDSTR_0 (grpc_static_slice_table[96]) +/* "identity" */ +#define GRPC_MDSTR_IDENTITY (grpc_static_slice_table[97]) +/* "trailers" */ +#define GRPC_MDSTR_TRAILERS (grpc_static_slice_table[98]) +/* "application/grpc" */ +#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (grpc_static_slice_table[99]) +/* "grpc" */ +#define GRPC_MDSTR_GRPC (grpc_static_slice_table[100]) +/* "PUT" */ +#define GRPC_MDSTR_PUT (grpc_static_slice_table[101]) +/* "lb-cost-bin" */ +#define GRPC_MDSTR_LB_COST_BIN (grpc_static_slice_table[102]) +/* "identity,deflate" */ +#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (grpc_static_slice_table[103]) +/* "identity,gzip" */ +#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (grpc_static_slice_table[104]) +/* "deflate,gzip" */ +#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (grpc_static_slice_table[105]) +/* "identity,deflate,gzip" */ +#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \ + (grpc_static_slice_table[106]) + +extern const grpc_slice_refcount_vtable grpc_static_metadata_vtable; +extern grpc_slice_refcount + grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT]; +#define GRPC_IS_STATIC_METADATA_STRING(slice) \ + ((slice).refcount != NULL && \ + (slice).refcount->vtable == &grpc_static_metadata_vtable) + +#define GRPC_STATIC_METADATA_INDEX(static_slice) \ + ((int)((static_slice).refcount - grpc_static_metadata_refcounts)) + +#define GRPC_STATIC_MDELEM_COUNT 86 +extern grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; +extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT]; +/* ":authority": "" */ +#define GRPC_MDELEM_AUTHORITY_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[0], GRPC_MDELEM_STORAGE_STATIC)) +/* ":method": "GET" */ +#define GRPC_MDELEM_METHOD_GET \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[1], GRPC_MDELEM_STORAGE_STATIC)) +/* ":method": "POST" */ +#define GRPC_MDELEM_METHOD_POST \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[2], GRPC_MDELEM_STORAGE_STATIC)) +/* ":path": "/" */ +#define GRPC_MDELEM_PATH_SLASH \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[3], GRPC_MDELEM_STORAGE_STATIC)) +/* ":path": "/index.html" */ +#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[4], GRPC_MDELEM_STORAGE_STATIC)) +/* ":scheme": "http" */ +#define GRPC_MDELEM_SCHEME_HTTP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[5], GRPC_MDELEM_STORAGE_STATIC)) +/* ":scheme": "https" */ +#define GRPC_MDELEM_SCHEME_HTTPS \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[6], GRPC_MDELEM_STORAGE_STATIC)) +/* ":status": "200" */ +#define GRPC_MDELEM_STATUS_200 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[7], GRPC_MDELEM_STORAGE_STATIC)) +/* ":status": "204" */ +#define GRPC_MDELEM_STATUS_204 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[8], GRPC_MDELEM_STORAGE_STATIC)) +/* ":status": "206" */ +#define GRPC_MDELEM_STATUS_206 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[9], GRPC_MDELEM_STORAGE_STATIC)) +/* ":status": "304" */ +#define GRPC_MDELEM_STATUS_304 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[10], GRPC_MDELEM_STORAGE_STATIC)) +/* ":status": "400" */ +#define GRPC_MDELEM_STATUS_400 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[11], GRPC_MDELEM_STORAGE_STATIC)) +/* ":status": "404" */ +#define GRPC_MDELEM_STATUS_404 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[12], GRPC_MDELEM_STORAGE_STATIC)) +/* ":status": "500" */ +#define GRPC_MDELEM_STATUS_500 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[13], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-charset": "" */ +#define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[14], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-encoding": "gzip, deflate" */ +#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP_COMMA_DEFLATE \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[15], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-language": "" */ +#define GRPC_MDELEM_ACCEPT_LANGUAGE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[16], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-ranges": "" */ +#define GRPC_MDELEM_ACCEPT_RANGES_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[17], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept": "" */ +#define GRPC_MDELEM_ACCEPT_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[18], GRPC_MDELEM_STORAGE_STATIC)) +/* "access-control-allow-origin": "" */ +#define GRPC_MDELEM_ACCESS_CONTROL_ALLOW_ORIGIN_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[19], GRPC_MDELEM_STORAGE_STATIC)) +/* "age": "" */ +#define GRPC_MDELEM_AGE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[20], GRPC_MDELEM_STORAGE_STATIC)) +/* "allow": "" */ +#define GRPC_MDELEM_ALLOW_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[21], GRPC_MDELEM_STORAGE_STATIC)) +/* "authorization": "" */ +#define GRPC_MDELEM_AUTHORIZATION_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[22], GRPC_MDELEM_STORAGE_STATIC)) +/* "cache-control": "" */ +#define GRPC_MDELEM_CACHE_CONTROL_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[23], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-disposition": "" */ +#define GRPC_MDELEM_CONTENT_DISPOSITION_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[24], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-encoding": "" */ +#define GRPC_MDELEM_CONTENT_ENCODING_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[25], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-language": "" */ +#define GRPC_MDELEM_CONTENT_LANGUAGE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[26], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-length": "" */ +#define GRPC_MDELEM_CONTENT_LENGTH_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[27], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-location": "" */ +#define GRPC_MDELEM_CONTENT_LOCATION_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[28], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-range": "" */ +#define GRPC_MDELEM_CONTENT_RANGE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[29], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-type": "" */ +#define GRPC_MDELEM_CONTENT_TYPE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[30], GRPC_MDELEM_STORAGE_STATIC)) +/* "cookie": "" */ +#define GRPC_MDELEM_COOKIE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[31], GRPC_MDELEM_STORAGE_STATIC)) +/* "date": "" */ +#define GRPC_MDELEM_DATE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[32], GRPC_MDELEM_STORAGE_STATIC)) +/* "etag": "" */ +#define GRPC_MDELEM_ETAG_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[33], GRPC_MDELEM_STORAGE_STATIC)) +/* "expect": "" */ +#define GRPC_MDELEM_EXPECT_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[34], GRPC_MDELEM_STORAGE_STATIC)) +/* "expires": "" */ +#define GRPC_MDELEM_EXPIRES_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[35], GRPC_MDELEM_STORAGE_STATIC)) +/* "from": "" */ +#define GRPC_MDELEM_FROM_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[36], GRPC_MDELEM_STORAGE_STATIC)) +/* "host": "" */ +#define GRPC_MDELEM_HOST_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[37], GRPC_MDELEM_STORAGE_STATIC)) +/* "if-match": "" */ +#define GRPC_MDELEM_IF_MATCH_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[38], GRPC_MDELEM_STORAGE_STATIC)) +/* "if-modified-since": "" */ +#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[39], GRPC_MDELEM_STORAGE_STATIC)) +/* "if-none-match": "" */ +#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[40], GRPC_MDELEM_STORAGE_STATIC)) +/* "if-range": "" */ +#define GRPC_MDELEM_IF_RANGE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[41], GRPC_MDELEM_STORAGE_STATIC)) +/* "if-unmodified-since": "" */ +#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[42], GRPC_MDELEM_STORAGE_STATIC)) +/* "last-modified": "" */ +#define GRPC_MDELEM_LAST_MODIFIED_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[43], GRPC_MDELEM_STORAGE_STATIC)) +/* "link": "" */ +#define GRPC_MDELEM_LINK_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[44], GRPC_MDELEM_STORAGE_STATIC)) +/* "location": "" */ +#define GRPC_MDELEM_LOCATION_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[45], GRPC_MDELEM_STORAGE_STATIC)) +/* "max-forwards": "" */ +#define GRPC_MDELEM_MAX_FORWARDS_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[46], GRPC_MDELEM_STORAGE_STATIC)) +/* "proxy-authenticate": "" */ +#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[47], GRPC_MDELEM_STORAGE_STATIC)) +/* "proxy-authorization": "" */ +#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[48], GRPC_MDELEM_STORAGE_STATIC)) +/* "range": "" */ +#define GRPC_MDELEM_RANGE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[49], GRPC_MDELEM_STORAGE_STATIC)) +/* "referer": "" */ +#define GRPC_MDELEM_REFERER_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[50], GRPC_MDELEM_STORAGE_STATIC)) +/* "refresh": "" */ +#define GRPC_MDELEM_REFRESH_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[51], GRPC_MDELEM_STORAGE_STATIC)) +/* "retry-after": "" */ +#define GRPC_MDELEM_RETRY_AFTER_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[52], GRPC_MDELEM_STORAGE_STATIC)) +/* "server": "" */ +#define GRPC_MDELEM_SERVER_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[53], GRPC_MDELEM_STORAGE_STATIC)) +/* "set-cookie": "" */ +#define GRPC_MDELEM_SET_COOKIE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[54], GRPC_MDELEM_STORAGE_STATIC)) +/* "strict-transport-security": "" */ +#define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[55], GRPC_MDELEM_STORAGE_STATIC)) +/* "transfer-encoding": "" */ +#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[56], GRPC_MDELEM_STORAGE_STATIC)) +/* "user-agent": "" */ +#define GRPC_MDELEM_USER_AGENT_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[57], GRPC_MDELEM_STORAGE_STATIC)) +/* "vary": "" */ +#define GRPC_MDELEM_VARY_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[58], GRPC_MDELEM_STORAGE_STATIC)) +/* "via": "" */ +#define GRPC_MDELEM_VIA_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[59], GRPC_MDELEM_STORAGE_STATIC)) +/* "www-authenticate": "" */ +#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[60], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-status": "0" */ +#define GRPC_MDELEM_GRPC_STATUS_0 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[61], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-status": "1" */ +#define GRPC_MDELEM_GRPC_STATUS_1 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[62], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-status": "2" */ +#define GRPC_MDELEM_GRPC_STATUS_2 \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[63], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-encoding": "identity" */ +#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[64], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-encoding": "gzip" */ +#define GRPC_MDELEM_GRPC_ENCODING_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[65], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-encoding": "deflate" */ +#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[66], GRPC_MDELEM_STORAGE_STATIC)) +/* "te": "trailers" */ +#define GRPC_MDELEM_TE_TRAILERS \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[67], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-type": "application/grpc" */ +#define GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[68], GRPC_MDELEM_STORAGE_STATIC)) +/* ":scheme": "grpc" */ +#define GRPC_MDELEM_SCHEME_GRPC \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[69], GRPC_MDELEM_STORAGE_STATIC)) +/* ":method": "PUT" */ +#define GRPC_MDELEM_METHOD_PUT \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[70], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-encoding": "" */ +#define GRPC_MDELEM_ACCEPT_ENCODING_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[71], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-encoding": "identity" */ +#define GRPC_MDELEM_CONTENT_ENCODING_IDENTITY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[72], GRPC_MDELEM_STORAGE_STATIC)) +/* "content-encoding": "gzip" */ +#define GRPC_MDELEM_CONTENT_ENCODING_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[73], GRPC_MDELEM_STORAGE_STATIC)) +/* "lb-token": "" */ +#define GRPC_MDELEM_LB_TOKEN_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[74], GRPC_MDELEM_STORAGE_STATIC)) +/* "lb-cost-bin": "" */ +#define GRPC_MDELEM_LB_COST_BIN_EMPTY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[75], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-accept-encoding": "identity" */ +#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[76], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-accept-encoding": "deflate" */ +#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[77], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-accept-encoding": "identity,deflate" */ +#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[78], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-accept-encoding": "gzip" */ +#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[79], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-accept-encoding": "identity,gzip" */ +#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[80], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-accept-encoding": "deflate,gzip" */ +#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE_COMMA_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[81], GRPC_MDELEM_STORAGE_STATIC)) +/* "grpc-accept-encoding": "identity,deflate,gzip" */ +#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[82], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-encoding": "identity" */ +#define GRPC_MDELEM_ACCEPT_ENCODING_IDENTITY \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[83], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-encoding": "gzip" */ +#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[84], GRPC_MDELEM_STORAGE_STATIC)) +/* "accept-encoding": "identity,gzip" */ +#define GRPC_MDELEM_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[85], GRPC_MDELEM_STORAGE_STATIC)) + +grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b); +typedef enum { + GRPC_BATCH_PATH, + GRPC_BATCH_METHOD, + GRPC_BATCH_STATUS, + GRPC_BATCH_AUTHORITY, + GRPC_BATCH_SCHEME, + GRPC_BATCH_TE, + GRPC_BATCH_GRPC_MESSAGE, + GRPC_BATCH_GRPC_STATUS, + GRPC_BATCH_GRPC_PAYLOAD_BIN, + GRPC_BATCH_GRPC_ENCODING, + GRPC_BATCH_GRPC_ACCEPT_ENCODING, + GRPC_BATCH_GRPC_SERVER_STATS_BIN, + GRPC_BATCH_GRPC_TAGS_BIN, + GRPC_BATCH_GRPC_TRACE_BIN, + GRPC_BATCH_CONTENT_TYPE, + GRPC_BATCH_CONTENT_ENCODING, + GRPC_BATCH_ACCEPT_ENCODING, + GRPC_BATCH_GRPC_INTERNAL_ENCODING_REQUEST, + GRPC_BATCH_GRPC_INTERNAL_STREAM_ENCODING_REQUEST, + GRPC_BATCH_USER_AGENT, + GRPC_BATCH_HOST, + GRPC_BATCH_LB_TOKEN, + GRPC_BATCH_GRPC_PREVIOUS_RPC_ATTEMPTS, + GRPC_BATCH_GRPC_RETRY_PUSHBACK_MS, + GRPC_BATCH_CALLOUTS_COUNT +} grpc_metadata_batch_callouts_index; + +typedef union { + struct grpc_linked_mdelem* array[GRPC_BATCH_CALLOUTS_COUNT]; + struct { + struct grpc_linked_mdelem* path; + struct grpc_linked_mdelem* method; + struct grpc_linked_mdelem* status; + struct grpc_linked_mdelem* authority; + struct grpc_linked_mdelem* scheme; + struct grpc_linked_mdelem* te; + struct grpc_linked_mdelem* grpc_message; + struct grpc_linked_mdelem* grpc_status; + struct grpc_linked_mdelem* grpc_payload_bin; + struct grpc_linked_mdelem* grpc_encoding; + struct grpc_linked_mdelem* grpc_accept_encoding; + struct grpc_linked_mdelem* grpc_server_stats_bin; + struct grpc_linked_mdelem* grpc_tags_bin; + struct grpc_linked_mdelem* grpc_trace_bin; + struct grpc_linked_mdelem* content_type; + struct grpc_linked_mdelem* content_encoding; + struct grpc_linked_mdelem* accept_encoding; + struct grpc_linked_mdelem* grpc_internal_encoding_request; + struct grpc_linked_mdelem* grpc_internal_stream_encoding_request; + struct grpc_linked_mdelem* user_agent; + struct grpc_linked_mdelem* host; + struct grpc_linked_mdelem* lb_token; + struct grpc_linked_mdelem* grpc_previous_rpc_attempts; + struct grpc_linked_mdelem* grpc_retry_pushback_ms; + } named; +} grpc_metadata_batch_callouts; + +#define GRPC_BATCH_INDEX_OF(slice) \ + (GRPC_IS_STATIC_METADATA_STRING((slice)) \ + ? (grpc_metadata_batch_callouts_index)GPR_CLAMP( \ + GRPC_STATIC_METADATA_INDEX((slice)), 0, \ + GRPC_BATCH_CALLOUTS_COUNT) \ + : GRPC_BATCH_CALLOUTS_COUNT) + +extern const uint8_t grpc_static_accept_encoding_metadata[8]; +#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) \ + (GRPC_MAKE_MDELEM( \ + &grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]], \ + GRPC_MDELEM_STORAGE_STATIC)) + +extern const uint8_t grpc_static_accept_stream_encoding_metadata[4]; +#define GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(algs) \ + (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table \ + [grpc_static_accept_stream_encoding_metadata[(algs)]], \ + GRPC_MDELEM_STORAGE_STATIC)) +#endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */ diff --git a/third_party/grpc/src/core/lib/transport/status_conversion.cc b/third_party/grpc/src/core/lib/transport/status_conversion.cc new file mode 100644 index 00000000000000..e58bef5ba985db --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/status_conversion.cc @@ -0,0 +1,100 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/status_conversion.h" + +grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status) { + switch (status) { + case GRPC_STATUS_OK: + return GRPC_HTTP2_NO_ERROR; + case GRPC_STATUS_CANCELLED: + return GRPC_HTTP2_CANCEL; + case GRPC_STATUS_DEADLINE_EXCEEDED: + return GRPC_HTTP2_CANCEL; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return GRPC_HTTP2_ENHANCE_YOUR_CALM; + case GRPC_STATUS_PERMISSION_DENIED: + return GRPC_HTTP2_INADEQUATE_SECURITY; + case GRPC_STATUS_UNAVAILABLE: + return GRPC_HTTP2_REFUSED_STREAM; + default: + return GRPC_HTTP2_INTERNAL_ERROR; + } +} + +grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error, + grpc_millis deadline) { + switch (error) { + case GRPC_HTTP2_NO_ERROR: + /* should never be received */ + return GRPC_STATUS_INTERNAL; + case GRPC_HTTP2_CANCEL: + /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been + * exceeded */ + return grpc_core::ExecCtx::Get()->Now() > deadline + ? GRPC_STATUS_DEADLINE_EXCEEDED + : GRPC_STATUS_CANCELLED; + case GRPC_HTTP2_ENHANCE_YOUR_CALM: + return GRPC_STATUS_RESOURCE_EXHAUSTED; + case GRPC_HTTP2_INADEQUATE_SECURITY: + return GRPC_STATUS_PERMISSION_DENIED; + case GRPC_HTTP2_REFUSED_STREAM: + return GRPC_STATUS_UNAVAILABLE; + default: + return GRPC_STATUS_INTERNAL; + } +} + +grpc_status_code grpc_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_status_to_http2_status(grpc_status_code status) { return 200; } diff --git a/third_party/grpc/src/core/lib/transport/status_conversion.h b/third_party/grpc/src/core/lib/transport/status_conversion.h new file mode 100644 index 00000000000000..487f00c08b15b1 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/status_conversion.h @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H +#define GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H + +#include + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/transport/http2_errors.h" + +/* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */ +grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status); +grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error, + grpc_millis deadline); + +/* Conversion of HTTP status codes (:status) to grpc status codes */ +grpc_status_code grpc_http2_status_to_grpc_status(int status); +int grpc_status_to_http2_status(grpc_status_code status); + +#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H */ diff --git a/third_party/grpc/src/core/lib/transport/status_metadata.cc b/third_party/grpc/src/core/lib/transport/status_metadata.cc new file mode 100644 index 00000000000000..f896053e4daed6 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/status_metadata.cc @@ -0,0 +1,54 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/status_metadata.h" + +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" + +/* 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) {} + +grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md) { + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + return GRPC_STATUS_OK; + } + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) { + return GRPC_STATUS_CANCELLED; + } + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) { + return GRPC_STATUS_UNKNOWN; + } + void* user_data = grpc_mdelem_get_user_data(md, destroy_status); + if (user_data != nullptr) { + return static_cast((intptr_t)user_data - STATUS_OFFSET); + } + uint32_t status; + if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) { + status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ + } + grpc_mdelem_set_user_data( + md, destroy_status, (void*)static_cast(status + STATUS_OFFSET)); + return static_cast(status); +} diff --git a/third_party/grpc/src/core/lib/transport/status_metadata.h b/third_party/grpc/src/core/lib/transport/status_metadata.h new file mode 100644 index 00000000000000..aed9c7ac201ff6 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/status_metadata.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H +#define GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H + +#include + +#include + +#include "src/core/lib/transport/metadata.h" + +grpc_status_code grpc_get_status_code_from_metadata(grpc_mdelem md); + +#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_METADATA_H */ diff --git a/third_party/grpc/src/core/lib/transport/timeout_encoding.cc b/third_party/grpc/src/core/lib/transport/timeout_encoding.cc new file mode 100644 index 00000000000000..c37249920bde82 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/timeout_encoding.cc @@ -0,0 +1,144 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/timeout_encoding.h" + +#include +#include + +#include "src/core/lib/gpr/string.h" + +static int64_t round_up(int64_t x, int64_t divisor) { + return (x / divisor + (x % divisor != 0)) * divisor; +} + +/* round an integer up to the next value with three significant figures */ +static int64_t round_up_to_three_sig_figs(int64_t 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, int64_t value, char ext) { + int n = int64_ttoa(value, buffer); + buffer[n] = ext; + buffer[n + 1] = 0; +} + +static void enc_seconds(char* buffer, int64_t 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_millis(char* buffer, int64_t x) { + x = round_up_to_three_sig_figs(x); + if (x < GPR_MS_PER_SEC) { + enc_ext(buffer, x, 'm'); + } else { + if (x % GPR_MS_PER_SEC == 0) { + enc_seconds(buffer, x / GPR_MS_PER_SEC); + } else { + enc_ext(buffer, x, 'm'); + } + } +} + +void grpc_http2_encode_timeout(grpc_millis timeout, char* buffer) { + if (timeout <= 0) { + enc_tiny(buffer); + } else if (timeout < 1000 * GPR_MS_PER_SEC) { + enc_millis(buffer, timeout); + } else { + enc_seconds(buffer, + timeout / GPR_MS_PER_SEC + (timeout % GPR_MS_PER_SEC != 0)); + } +} + +static int is_all_whitespace(const char* p, const char* end) { + while (p != end && *p == ' ') p++; + return p == end; +} + +int grpc_http2_decode_timeout(grpc_slice text, grpc_millis* timeout) { + grpc_millis x = 0; + const uint8_t* p = GRPC_SLICE_START_PTR(text); + const uint8_t* end = GRPC_SLICE_END_PTR(text); + int have_digit = 0; + /* skip whitespace */ + for (; p != end && *p == ' '; p++) + ; + /* decode numeric part */ + for (; p != end && *p >= '0' && *p <= '9'; p++) { + int32_t digit = static_cast(*p - static_cast('0')); + have_digit = 1; + /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */ + if (x >= (100 * 1000 * 1000)) { + if (x != (100 * 1000 * 1000) || digit != 0) { + *timeout = GRPC_MILLIS_INF_FUTURE; + return 1; + } + } + x = x * 10 + digit; + } + if (!have_digit) return 0; + /* skip whitespace */ + for (; p != end && *p == ' '; p++) + ; + if (p == end) return 0; + /* decode unit specifier */ + switch (*p) { + case 'n': + *timeout = x / GPR_NS_PER_MS + (x % GPR_NS_PER_MS != 0); + break; + case 'u': + *timeout = x / GPR_US_PER_MS + (x % GPR_US_PER_MS != 0); + break; + case 'm': + *timeout = x; + break; + case 'S': + *timeout = x * GPR_MS_PER_SEC; + break; + case 'M': + *timeout = x * 60 * GPR_MS_PER_SEC; + break; + case 'H': + *timeout = x * 60 * 60 * GPR_MS_PER_SEC; + break; + default: + return 0; + } + p++; + return is_all_whitespace(reinterpret_cast(p), + reinterpret_cast(end)); +} diff --git a/third_party/grpc/src/core/lib/transport/timeout_encoding.h b/third_party/grpc/src/core/lib/transport/timeout_encoding.h new file mode 100644 index 00000000000000..8505e32ff098c9 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/timeout_encoding.h @@ -0,0 +1,37 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H +#define GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H + +#include + +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +#define GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1) + +/* Encode/decode timeouts to the GRPC over HTTP/2 format; + encoding may round up arbitrarily */ +void grpc_http2_encode_timeout(grpc_millis timeout, char* buffer); +int grpc_http2_decode_timeout(grpc_slice text, grpc_millis* timeout); + +#endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */ diff --git a/third_party/grpc/src/core/lib/transport/transport.cc b/third_party/grpc/src/core/lib/transport/transport.cc new file mode 100644 index 00000000000000..b32f9c6ec1ab9a --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/transport.cc @@ -0,0 +1,291 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/transport/transport.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/alloc.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/transport_impl.h" + +grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount(false, + "stream_refcount"); + +#ifndef NDEBUG +void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason) { + if (grpc_trace_stream_refcount.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); + gpr_log(GPR_DEBUG, "%s %p:%p REF %" PRIdPTR "->%" PRIdPTR " %s", + refcount->object_type, refcount, refcount->destroy.cb_arg, val, + val + 1, reason); + } +#else +void grpc_stream_ref(grpc_stream_refcount* refcount) { +#endif + gpr_ref_non_zero(&refcount->refs); +} + +#ifndef NDEBUG +void grpc_stream_unref(grpc_stream_refcount* refcount, const char* reason) { + if (grpc_trace_stream_refcount.enabled()) { + gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); + gpr_log(GPR_DEBUG, "%s %p:%p UNREF %" PRIdPTR "->%" PRIdPTR " %s", + refcount->object_type, refcount, refcount->destroy.cb_arg, val, + val - 1, reason); + } +#else +void grpc_stream_unref(grpc_stream_refcount* refcount) { +#endif + if (gpr_unref(&refcount->refs)) { + if (grpc_core::ExecCtx::Get()->flags() & + GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP) { + /* Ick. + The thread we're running on MAY be owned (indirectly) by a call-stack. + If that's the case, destroying the call-stack MAY try to destroy the + thread, which is a tangled mess that we just don't want to ever have to + cope with. + Throw this over to the executor (on a core-owned thread) and process it + there. */ + refcount->destroy.scheduler = + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + } + GRPC_CLOSURE_SCHED(&refcount->destroy, GRPC_ERROR_NONE); + } +} + +#define STREAM_REF_FROM_SLICE_REF(p) \ + ((grpc_stream_refcount*)(((uint8_t*)p) - \ + offsetof(grpc_stream_refcount, slice_refcount))) + +static void slice_stream_ref(void* p) { +#ifndef NDEBUG + grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p), "slice"); +#else + grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p)); +#endif +} + +static void slice_stream_unref(void* p) { +#ifndef NDEBUG + grpc_stream_unref(STREAM_REF_FROM_SLICE_REF(p), "slice"); +#else + grpc_stream_unref(STREAM_REF_FROM_SLICE_REF(p)); +#endif +} + +grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount, + void* buffer, size_t length) { + slice_stream_ref(&refcount->slice_refcount); + grpc_slice res; + res.refcount = &refcount->slice_refcount; + res.data.refcounted.bytes = static_cast(buffer); + res.data.refcounted.length = length; + return res; +} + +static const grpc_slice_refcount_vtable stream_ref_slice_vtable = { + slice_stream_ref, /* ref */ + slice_stream_unref, /* unref */ + grpc_slice_default_eq_impl, /* eq */ + grpc_slice_default_hash_impl /* hash */ +}; + +#ifndef NDEBUG +void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs, + grpc_iomgr_cb_func cb, void* cb_arg, + const char* object_type) { + refcount->object_type = object_type; +#else +void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs, + grpc_iomgr_cb_func cb, void* cb_arg) { +#endif + gpr_ref_init(&refcount->refs, initial_refs); + GRPC_CLOSURE_INIT(&refcount->destroy, cb, cb_arg, grpc_schedule_on_exec_ctx); + refcount->slice_refcount.vtable = &stream_ref_slice_vtable; + refcount->slice_refcount.sub_refcount = &refcount->slice_refcount; +} + +static void move64(uint64_t* from, uint64_t* to) { + *to += *from; + *from = 0; +} + +void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats* from, + grpc_transport_one_way_stats* to) { + move64(&from->framing_bytes, &to->framing_bytes); + move64(&from->data_bytes, &to->data_bytes); + move64(&from->header_bytes, &to->header_bytes); +} + +void grpc_transport_move_stats(grpc_transport_stream_stats* from, + grpc_transport_stream_stats* to) { + grpc_transport_move_one_way_stats(&from->incoming, &to->incoming); + grpc_transport_move_one_way_stats(&from->outgoing, &to->outgoing); +} + +size_t grpc_transport_stream_size(grpc_transport* transport) { + return GPR_ROUND_UP_TO_ALIGNMENT_SIZE(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, + grpc_stream_refcount* refcount, + const void* server_data, gpr_arena* arena) { + return transport->vtable->init_stream(transport, stream, refcount, + server_data, arena); +} + +void grpc_transport_perform_stream_op(grpc_transport* transport, + grpc_stream* stream, + grpc_transport_stream_op_batch* 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_set_pops(grpc_transport* transport, grpc_stream* stream, + grpc_polling_entity* pollent) { + grpc_pollset* pollset; + grpc_pollset_set* pollset_set; + if ((pollset = grpc_polling_entity_pollset(pollent)) != nullptr) { + transport->vtable->set_pollset(transport, stream, pollset); + } else if ((pollset_set = grpc_polling_entity_pollset_set(pollent)) != + nullptr) { + transport->vtable->set_pollset_set(transport, stream, pollset_set); + } else { + // No-op for empty pollset. Empty pollset is possible when using + // non-fd-based event engines such as CFStream. + } +} + +void grpc_transport_destroy_stream(grpc_transport* transport, + grpc_stream* stream, + grpc_closure* then_schedule_closure) { + transport->vtable->destroy_stream(transport, stream, then_schedule_closure); +} + +grpc_endpoint* grpc_transport_get_endpoint(grpc_transport* transport) { + return transport->vtable->get_endpoint(transport); +} + +// This comment should be sung to the tune of +// "Supercalifragilisticexpialidocious": +// +// grpc_transport_stream_op_batch_finish_with_failure +// is a function that must always unref cancel_error +// though it lives in lib, it handles transport stream ops sure +// it's grpc_transport_stream_op_batch_finish_with_failure +void grpc_transport_stream_op_batch_finish_with_failure( + grpc_transport_stream_op_batch* batch, grpc_error* error, + grpc_call_combiner* call_combiner) { + if (batch->send_message) { + batch->payload->send_message.send_message.reset(); + } + if (batch->cancel_stream) { + GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error); + } + // Construct a list of closures to execute. + grpc_core::CallCombinerClosureList closures; + if (batch->recv_initial_metadata) { + closures.Add( + batch->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_REF(error), "failing recv_initial_metadata_ready"); + } + if (batch->recv_message) { + closures.Add(batch->payload->recv_message.recv_message_ready, + GRPC_ERROR_REF(error), "failing recv_message_ready"); + } + if (batch->recv_trailing_metadata) { + closures.Add( + batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready, + GRPC_ERROR_REF(error), "failing recv_trailing_metadata_ready"); + } + if (batch->on_complete != nullptr) { + closures.Add(batch->on_complete, GRPC_ERROR_REF(error), + "failing on_complete"); + } + // Execute closures. + closures.RunClosures(call_combiner); + GRPC_ERROR_UNREF(error); +} + +typedef struct { + grpc_closure outer_on_complete; + grpc_closure* inner_on_complete; + grpc_transport_op op; +} made_transport_op; + +static void destroy_made_transport_op(void* arg, grpc_error* error) { + made_transport_op* op = static_cast(arg); + GRPC_CLOSURE_SCHED(op->inner_on_complete, GRPC_ERROR_REF(error)); + gpr_free(op); +} + +grpc_transport_op* grpc_make_transport_op(grpc_closure* on_complete) { + made_transport_op* op = + static_cast(gpr_malloc(sizeof(*op))); + GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_op, op, + grpc_schedule_on_exec_ctx); + op->inner_on_complete = on_complete; + memset(&op->op, 0, sizeof(op->op)); + op->op.on_consumed = &op->outer_on_complete; + return &op->op; +} + +typedef struct { + grpc_closure outer_on_complete; + grpc_closure* inner_on_complete; + grpc_transport_stream_op_batch op; + grpc_transport_stream_op_batch_payload payload; +} made_transport_stream_op; + +static void destroy_made_transport_stream_op(void* arg, grpc_error* error) { + made_transport_stream_op* op = static_cast(arg); + grpc_closure* c = op->inner_on_complete; + gpr_free(op); + GRPC_CLOSURE_RUN(c, GRPC_ERROR_REF(error)); +} + +grpc_transport_stream_op_batch* grpc_make_transport_stream_op( + grpc_closure* on_complete) { + made_transport_stream_op* op = + static_cast(gpr_zalloc(sizeof(*op))); + op->op.payload = &op->payload; + GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_stream_op, + op, grpc_schedule_on_exec_ctx); + op->inner_on_complete = on_complete; + op->op.on_complete = &op->outer_on_complete; + return &op->op; +} diff --git a/third_party/grpc/src/core/lib/transport/transport.h b/third_party/grpc/src/core/lib/transport/transport.h new file mode 100644 index 00000000000000..5ce568834e9aef --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/transport.h @@ -0,0 +1,400 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H +#define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H + +#include + +#include + +#include "src/core/lib/channel/context.h" +#include "src/core/lib/gpr/arena.h" +#include "src/core/lib/iomgr/call_combiner.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/transport/byte_stream.h" +#include "src/core/lib/transport/metadata_batch.h" + +/* Minimum and maximum protocol accepted versions. */ +#define GRPC_PROTOCOL_VERSION_MAX_MAJOR 2 +#define GRPC_PROTOCOL_VERSION_MAX_MINOR 1 +#define GRPC_PROTOCOL_VERSION_MIN_MAJOR 2 +#define GRPC_PROTOCOL_VERSION_MIN_MINOR 1 + +/* 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; + +extern grpc_core::DebugOnlyTraceFlag grpc_trace_stream_refcount; + +typedef struct grpc_stream_refcount { + gpr_refcount refs; + grpc_closure destroy; +#ifndef NDEBUG + const char* object_type; +#endif + grpc_slice_refcount slice_refcount; +} grpc_stream_refcount; + +#ifndef NDEBUG +void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs, + grpc_iomgr_cb_func cb, void* cb_arg, + const char* object_type); +void grpc_stream_ref(grpc_stream_refcount* refcount, const char* reason); +void grpc_stream_unref(grpc_stream_refcount* refcount, const char* reason); +#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \ + grpc_stream_ref_init(rc, ir, cb, cb_arg, objtype) +#else +void grpc_stream_ref_init(grpc_stream_refcount* refcount, int initial_refs, + grpc_iomgr_cb_func cb, void* cb_arg); +void grpc_stream_ref(grpc_stream_refcount* refcount); +void grpc_stream_unref(grpc_stream_refcount* refcount); +#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \ + grpc_stream_ref_init(rc, ir, cb, cb_arg) +#endif + +/* Wrap a buffer that is owned by some stream object into a slice that shares + the same refcount */ +grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount* refcount, + void* buffer, size_t length); + +struct grpc_transport_one_way_stats { + uint64_t framing_bytes = 0; + uint64_t data_bytes = 0; + uint64_t header_bytes = 0; +}; + +struct grpc_transport_stream_stats { + grpc_transport_one_way_stats incoming; + grpc_transport_one_way_stats outgoing; +}; + +void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats* from, + grpc_transport_one_way_stats* to); + +void grpc_transport_move_stats(grpc_transport_stream_stats* from, + grpc_transport_stream_stats* to); + +// This struct (which is present in both grpc_transport_stream_op_batch +// and grpc_transport_op_batch) is a convenience to allow filters or +// transports to schedule a closure related to a particular batch without +// having to allocate memory. The general pattern is to initialize the +// closure with the callback arg set to the batch and extra_arg set to +// whatever state is associated with the handler (e.g., the call element +// or the transport stream object). +// +// Note that this can only be used by the current handler of a given +// batch on the way down the stack (i.e., whichever filter or transport is +// currently handling the batch). Once a filter or transport passes control +// of the batch to the next handler, it cannot depend on the contents of +// this struct anymore, because the next handler may reuse it. +typedef struct { + void* extra_arg; + grpc_closure closure; +} grpc_handler_private_op_data; + +typedef struct grpc_transport_stream_op_batch_payload + grpc_transport_stream_op_batch_payload; + +/* Transport stream op: a set of operations to perform on a transport + against a single stream */ +struct grpc_transport_stream_op_batch { + grpc_transport_stream_op_batch() + : send_initial_metadata(false), + send_trailing_metadata(false), + send_message(false), + recv_initial_metadata(false), + recv_message(false), + recv_trailing_metadata(false), + cancel_stream(false), + is_traced(false) {} + + /** Should be scheduled when all of the non-recv operations in the batch + are complete. + + The recv ops (recv_initial_metadata, recv_message, and + recv_trailing_metadata) each have their own callbacks. If a batch + contains both recv ops and non-recv ops, on_complete should be + scheduled as soon as the non-recv ops are complete, regardless of + whether or not the recv ops are complete. If a batch contains + only recv ops, on_complete can be null. */ + grpc_closure* on_complete = nullptr; + + /** Values for the stream op (fields set are determined by flags above) */ + grpc_transport_stream_op_batch_payload* payload = nullptr; + + /** Send initial metadata to the peer, from the provided metadata batch. */ + bool send_initial_metadata : 1; + + /** Send trailing metadata to the peer, from the provided metadata batch. */ + bool send_trailing_metadata : 1; + + /** Send message data to the peer, from the provided byte stream. */ + bool send_message : 1; + + /** Receive initial metadata from the stream, into provided metadata batch. */ + bool recv_initial_metadata : 1; + + /** Receive message data from the stream, into provided byte stream. */ + bool recv_message : 1; + + /** Receive trailing metadata from the stream, into provided metadata batch. + */ + bool recv_trailing_metadata : 1; + + /** Cancel this stream with the provided error */ + bool cancel_stream : 1; + + /** Is this stream traced */ + bool is_traced : 1; + + /*************************************************************************** + * remaining fields are initialized and used at the discretion of the + * current handler of the op */ + + grpc_handler_private_op_data handler_private; +}; + +struct grpc_transport_stream_op_batch_payload { + explicit grpc_transport_stream_op_batch_payload( + grpc_call_context_element* context) + : context(context) {} + ~grpc_transport_stream_op_batch_payload() { + // We don't really own `send_message`, so release ownership and let the + // owner clean the data. + send_message.send_message.release(); + } + + struct { + grpc_metadata_batch* send_initial_metadata = nullptr; + /** Iff send_initial_metadata != NULL, flags associated with + send_initial_metadata: a bitfield of GRPC_INITIAL_METADATA_xxx */ + uint32_t send_initial_metadata_flags = 0; + // If non-NULL, will be set by the transport to the peer string (a char*). + // The transport retains ownership of the string. + // Note: This pointer may be used by the transport after the + // send_initial_metadata op is completed. It must remain valid + // until the call is destroyed. + gpr_atm* peer_string = nullptr; + } send_initial_metadata; + + struct { + grpc_metadata_batch* send_trailing_metadata = nullptr; + } send_trailing_metadata; + + struct { + // The transport (or a filter that decides to return a failure before + // the op gets down to the transport) takes ownership. + // The batch's on_complete will not be called until after the byte + // stream is orphaned. + grpc_core::OrphanablePtr send_message; + } send_message; + + struct { + grpc_metadata_batch* recv_initial_metadata = nullptr; + // Flags are used only on the server side. If non-null, will be set to + // a bitfield of the GRPC_INITIAL_METADATA_xxx macros (e.g., to + // indicate if the call is idempotent). + uint32_t* recv_flags = nullptr; + /** Should be enqueued when initial metadata is ready to be processed. */ + grpc_closure* recv_initial_metadata_ready = nullptr; + // If not NULL, will be set to true if trailing metadata is + // immediately available. This may be a signal that we received a + // Trailers-Only response. + bool* trailing_metadata_available = nullptr; + // If non-NULL, will be set by the transport to the peer string (a char*). + // The transport retains ownership of the string. + // Note: This pointer may be used by the transport after the + // recv_initial_metadata op is completed. It must remain valid + // until the call is destroyed. + gpr_atm* peer_string = nullptr; + } recv_initial_metadata; + + struct { + // Will be set by the transport to point to the byte stream + // containing a received message. + // Will be NULL if trailing metadata is received instead of a message. + grpc_core::OrphanablePtr* recv_message = nullptr; + /** Should be enqueued when one message is ready to be processed. */ + grpc_closure* recv_message_ready = nullptr; + } recv_message; + + struct { + grpc_metadata_batch* recv_trailing_metadata = nullptr; + grpc_transport_stream_stats* collect_stats = nullptr; + /** Should be enqueued when initial metadata is ready to be processed. */ + grpc_closure* recv_trailing_metadata_ready = nullptr; + } recv_trailing_metadata; + + /** Forcefully close this stream. + The HTTP2 semantics should be: + - server side: if cancel_error has GRPC_ERROR_INT_GRPC_STATUS, and + trailing metadata has not been sent, send trailing metadata with status + and message from cancel_error (use grpc_error_get_status) followed by + a RST_STREAM with error=GRPC_CHTTP2_NO_ERROR to force a full close + - at all other times: use grpc_error_get_status to get a status code, and + convert to a HTTP2 error code using + grpc_chttp2_grpc_status_to_http2_error. Send a RST_STREAM with this + error. */ + struct { + // Error contract: the transport that gets this op must cause cancel_error + // to be unref'ed after processing it + grpc_error* cancel_error = GRPC_ERROR_NONE; + } cancel_stream; + + /* Indexes correspond to grpc_context_index enum values */ + grpc_call_context_element* context; +}; + +/** 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_closure* on_consumed; + /** connectivity monitoring - set connectivity_state to NULL to unsubscribe */ + grpc_closure* on_connectivity_state_change; + grpc_connectivity_state* connectivity_state; + /** should the transport be disconnected + * Error contract: the transport that gets this op must cause + * disconnect_with_error to be unref'ed after processing it */ + grpc_error* disconnect_with_error; + /** what should the goaway contain? + * Error contract: the transport that gets this op must cause + * goaway_error to be unref'ed after processing it */ + grpc_error* goaway_error; + /** set the callback for accepting new streams; + this is a permanent callback, unlike the other one-shot closures. + If true, the callback is set to set_accept_stream_fn, with its + user_data argument set to set_accept_stream_user_data */ + bool set_accept_stream; + void (*set_accept_stream_fn)(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, if either on_initiate or on_ack is not NULL */ + struct { + /** Ping may be delayed by the transport, on_initiate callback will be + called when the ping is actually being sent. */ + grpc_closure* on_initiate; + /** Called when the ping ack is received */ + grpc_closure* on_ack; + } send_ping; + // If true, will reset the channel's connection backoff. + bool reset_connect_backoff; + + /*************************************************************************** + * remaining fields are initialized and used at the discretion of the + * transport implementation */ + + grpc_handler_private_op_data handler_private; +} 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. + May assume that stream contains all-zeros. + + 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, + grpc_stream_refcount* refcount, + const void* server_data, gpr_arena* arena); + +void grpc_transport_set_pops(grpc_transport* transport, grpc_stream* stream, + grpc_polling_entity* pollent); + +/* 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, + grpc_closure* then_schedule_closure); + +void grpc_transport_stream_op_batch_finish_with_failure( + grpc_transport_stream_op_batch* op, grpc_error* error, + grpc_call_combiner* call_combiner); + +char* grpc_transport_stream_op_batch_string(grpc_transport_stream_op_batch* op); +char* grpc_transport_op_string(grpc_transport_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_batch specifying the op to perform + */ +void grpc_transport_perform_stream_op(grpc_transport* transport, + grpc_stream* stream, + grpc_transport_stream_op_batch* 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_closure* cb); + +/* Advise peer of pending connection termination. */ +void grpc_transport_goaway(grpc_transport* transport, grpc_status_code status, + grpc_slice debug_data); + +/* Destroy the transport */ +void grpc_transport_destroy(grpc_transport* transport); + +/* Get the endpoint used by \a transport */ +grpc_endpoint* grpc_transport_get_endpoint(grpc_transport* transport); + +/* Allocate a grpc_transport_op, and preconfigure the on_consumed closure to + \a on_consumed and then delete the returned transport op */ +grpc_transport_op* grpc_make_transport_op(grpc_closure* on_consumed); +/* Allocate a grpc_transport_stream_op_batch, and preconfigure the on_consumed + closure + to \a on_consumed and then delete the returned transport op */ +grpc_transport_stream_op_batch* grpc_make_transport_stream_op( + grpc_closure* on_consumed); + +#endif /* GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H */ diff --git a/third_party/grpc/src/core/lib/transport/transport_impl.h b/third_party/grpc/src/core/lib/transport/transport_impl.h new file mode 100644 index 00000000000000..ba5e05df0af628 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/transport_impl.h @@ -0,0 +1,71 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H +#define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H + +#include + +#include "src/core/lib/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) */ + + /* name of this transport implementation */ + const char* name; + + /* implementation of grpc_transport_init_stream */ + int (*init_stream)(grpc_transport* self, grpc_stream* stream, + grpc_stream_refcount* refcount, const void* server_data, + gpr_arena* arena); + + /* implementation of grpc_transport_set_pollset */ + void (*set_pollset)(grpc_transport* self, grpc_stream* stream, + grpc_pollset* pollset); + + /* implementation of grpc_transport_set_pollset */ + void (*set_pollset_set)(grpc_transport* self, grpc_stream* stream, + grpc_pollset_set* pollset_set); + + /* implementation of grpc_transport_perform_stream_op */ + void (*perform_stream_op)(grpc_transport* self, grpc_stream* stream, + grpc_transport_stream_op_batch* 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, + grpc_closure* then_schedule_closure); + + /* implementation of grpc_transport_destroy */ + void (*destroy)(grpc_transport* self); + + /* implementation of grpc_transport_get_endpoint */ + grpc_endpoint* (*get_endpoint)(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_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H */ diff --git a/third_party/grpc/src/core/lib/transport/transport_op_string.cc b/third_party/grpc/src/core/lib/transport/transport_op_string.cc new file mode 100644 index 00000000000000..8c7db642a54659 --- /dev/null +++ b/third_party/grpc/src/core/lib/transport/transport_op_string.cc @@ -0,0 +1,207 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channel_stack.h" + +#include +#include +#include +#include + +#include +#include +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/connectivity_state.h" + +/* 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, grpc_dump_slice(GRPC_MDKEY(md), GPR_DUMP_HEX | GPR_DUMP_ASCII)); + + gpr_strvec_add(b, gpr_strdup(" value=")); + gpr_strvec_add( + b, grpc_dump_slice(GRPC_MDVALUE(md), 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 != nullptr; m = m->next) { + if (m != md.list.head) gpr_strvec_add(b, gpr_strdup(", ")); + put_metadata(b, m->md); + } + if (md.deadline != GRPC_MILLIS_INF_FUTURE) { + char* tmp; + gpr_asprintf(&tmp, " deadline=%" PRId64, md.deadline); + gpr_strvec_add(b, tmp); + } +} + +char* grpc_transport_stream_op_batch_string( + grpc_transport_stream_op_batch* op) { + char* tmp; + char* out; + + gpr_strvec b; + gpr_strvec_init(&b); + + if (op->send_initial_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA{")); + put_metadata_list( + &b, *op->payload->send_initial_metadata.send_initial_metadata); + gpr_strvec_add(&b, gpr_strdup("}")); + } + + if (op->send_message) { + gpr_strvec_add(&b, gpr_strdup(" ")); + if (op->payload->send_message.send_message != nullptr) { + gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", + op->payload->send_message.send_message->flags(), + op->payload->send_message.send_message->length()); + } else { + // This can happen when we check a batch after the transport has + // processed and cleared the send_message op. + tmp = + gpr_strdup("SEND_MESSAGE(flag and length unknown, already orphaned)"); + } + gpr_strvec_add(&b, tmp); + } + + if (op->send_trailing_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("SEND_TRAILING_METADATA{")); + put_metadata_list( + &b, *op->payload->send_trailing_metadata.send_trailing_metadata); + gpr_strvec_add(&b, gpr_strdup("}")); + } + + if (op->recv_initial_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("RECV_INITIAL_METADATA")); + } + + if (op->recv_message) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE")); + } + + if (op->recv_trailing_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA")); + } + + if (op->cancel_stream) { + gpr_strvec_add(&b, gpr_strdup(" ")); + const char* msg = + grpc_error_string(op->payload->cancel_stream.cancel_error); + gpr_asprintf(&tmp, "CANCEL:%s", msg); + + gpr_strvec_add(&b, tmp); + } + + out = gpr_strvec_flatten(&b, nullptr); + gpr_strvec_destroy(&b); + + return out; +} + +char* grpc_transport_op_string(grpc_transport_op* op) { + char* tmp; + char* out; + bool first = true; + + gpr_strvec b; + gpr_strvec_init(&b); + + if (op->on_connectivity_state_change != nullptr) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + if (op->connectivity_state != nullptr) { + gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:from=%s", + op->on_connectivity_state_change, + grpc_connectivity_state_name(*op->connectivity_state)); + gpr_strvec_add(&b, tmp); + } else { + gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:unsubscribe", + op->on_connectivity_state_change); + gpr_strvec_add(&b, tmp); + } + } + + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + const char* err = grpc_error_string(op->disconnect_with_error); + gpr_asprintf(&tmp, "DISCONNECT:%s", err); + gpr_strvec_add(&b, tmp); + } + + if (op->goaway_error) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + const char* msg = grpc_error_string(op->goaway_error); + gpr_asprintf(&tmp, "SEND_GOAWAY:%s", msg); + + gpr_strvec_add(&b, tmp); + } + + if (op->set_accept_stream) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + gpr_asprintf(&tmp, "SET_ACCEPT_STREAM:%p(%p,...)", op->set_accept_stream_fn, + op->set_accept_stream_user_data); + gpr_strvec_add(&b, tmp); + } + + if (op->bind_pollset != nullptr) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET")); + } + + if (op->bind_pollset_set != nullptr) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET_SET")); + } + + if (op->send_ping.on_initiate != nullptr || op->send_ping.on_ack != nullptr) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + // first = false; + gpr_strvec_add(&b, gpr_strdup("SEND_PING")); + } + + out = gpr_strvec_flatten(&b, nullptr); + gpr_strvec_destroy(&b); + + return out; +} + +void grpc_call_log_op(const char* file, int line, gpr_log_severity severity, + grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + char* str = grpc_transport_stream_op_batch_string(op); + gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str); + gpr_free(str); +} diff --git a/third_party/grpc/src/core/lib/uri/uri_parser.cc b/third_party/grpc/src/core/lib/uri/uri_parser.cc new file mode 100644 index 00000000000000..f212c7d2c01a1f --- /dev/null +++ b/third_party/grpc/src/core/lib/uri/uri_parser.cc @@ -0,0 +1,314 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/uri/uri_parser.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/slice/percent_encoding.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +/** a size_t default value... maps to all 1's */ +#define NOT_SET (~(size_t)0) + +static grpc_uri* bad_uri(const char* uri_text, size_t pos, const char* section, + bool suppress_errors) { + char* line_prefix; + size_t 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 = static_cast(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 nullptr; +} + +/** Returns a copy of percent decoded \a src[begin, end) */ +static char* decode_and_copy_component(const char* src, size_t begin, + size_t end) { + grpc_slice component = + (begin == NOT_SET || end == NOT_SET) + ? grpc_empty_slice() + : grpc_slice_from_copied_buffer(src + begin, end - begin); + grpc_slice decoded_component = + grpc_permissive_percent_decode_slice(component); + char* out = grpc_dump_slice(decoded_component, GPR_DUMP_ASCII); + grpc_slice_unref_internal(component); + grpc_slice_unref_internal(decoded_component); + return out; +} + +static bool valid_hex(char c) { + return ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) || + ((c >= '0') && (c <= '9')); +} + +/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar + * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent + * sign not followed by two hex digits), NOT_SET is returned. */ +static size_t parse_pchar(const char* uri_text, size_t i) { + /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" */ + char c = uri_text[i]; + switch (c) { + default: + if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9'))) { + return 1; + } + break; + case ':': + case '@': + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + return 1; + case '%': /* pct-encoded */ + if (valid_hex(uri_text[i + 1]) && valid_hex(uri_text[i + 2])) { + return 2; + } + return NOT_SET; + } + return 0; +} + +/* *( pchar / "?" / "/" ) */ +static int parse_fragment_or_query(const char* uri_text, size_t* i) { + char c; + while ((c = uri_text[*i]) != 0) { + const size_t advance = parse_pchar(uri_text, *i); /* pchar */ + switch (advance) { + case 0: /* uri_text[i] isn't in pchar */ + /* maybe it's ? or / */ + if (uri_text[*i] == '?' || uri_text[*i] == '/') { + (*i)++; + break; + } else { + return 1; + } + GPR_UNREACHABLE_CODE(return 0); + default: + (*i) += advance; + break; + case NOT_SET: /* uri_text[i] introduces an invalid URI */ + return 0; + } + } + /* *i is the first uri_text position past the \a query production, maybe \0 */ + return 1; +} + +static void parse_query_parts(grpc_uri* uri) { + static const char* QUERY_PARTS_SEPARATOR = "&"; + static const char* QUERY_PARTS_VALUE_SEPARATOR = "="; + GPR_ASSERT(uri->query != nullptr); + if (uri->query[0] == '\0') { + uri->query_parts = nullptr; + uri->query_parts_values = nullptr; + uri->num_query_parts = 0; + return; + } + + gpr_string_split(uri->query, QUERY_PARTS_SEPARATOR, &uri->query_parts, + &uri->num_query_parts); + uri->query_parts_values = + static_cast(gpr_malloc(uri->num_query_parts * sizeof(char**))); + for (size_t i = 0; i < uri->num_query_parts; i++) { + char** query_param_parts; + size_t num_query_param_parts; + char* full = uri->query_parts[i]; + gpr_string_split(full, QUERY_PARTS_VALUE_SEPARATOR, &query_param_parts, + &num_query_param_parts); + GPR_ASSERT(num_query_param_parts > 0); + uri->query_parts[i] = query_param_parts[0]; + if (num_query_param_parts > 1) { + /* TODO(dgq): only the first value after the separator is considered. + * Perhaps all chars after the first separator for the query part should + * be included, even if they include the separator. */ + uri->query_parts_values[i] = query_param_parts[1]; + } else { + uri->query_parts_values[i] = nullptr; + } + for (size_t j = 2; j < num_query_param_parts; j++) { + gpr_free(query_param_parts[j]); + } + gpr_free(query_param_parts); + gpr_free(full); + } +} + +grpc_uri* grpc_uri_parse(const char* uri_text, bool suppress_errors) { + grpc_uri* uri; + size_t scheme_begin = 0; + size_t scheme_end = NOT_SET; + size_t authority_begin = NOT_SET; + size_t authority_end = NOT_SET; + size_t path_begin = NOT_SET; + size_t path_end = NOT_SET; + size_t query_begin = NOT_SET; + size_t query_end = NOT_SET; + size_t fragment_begin = NOT_SET; + size_t fragment_end = NOT_SET; + size_t 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 == NOT_SET) { + 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 == NOT_SET; + i++) { + if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') { + authority_end = i; + } + } + if (authority_end == NOT_SET && uri_text[i] == 0) { + authority_end = i; + } + if (authority_end == NOT_SET) { + 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] == '?' || uri_text[i] == '#') { + path_end = i; + break; + } + } + if (path_end == NOT_SET && uri_text[i] == 0) { + path_end = i; + } + if (path_end == NOT_SET) { + return bad_uri(uri_text, i, "path", suppress_errors); + } + + if (uri_text[i] == '?') { + query_begin = ++i; + if (!parse_fragment_or_query(uri_text, &i)) { + return bad_uri(uri_text, i, "query", suppress_errors); + } else if (uri_text[i] != 0 && uri_text[i] != '#') { + /* We must be at the end or at the beginning of a fragment */ + return bad_uri(uri_text, i, "query", suppress_errors); + } + query_end = i; + } + if (uri_text[i] == '#') { + fragment_begin = ++i; + if (!parse_fragment_or_query(uri_text, &i)) { + return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors); + } else if (uri_text[i] != 0) { + /* We must be at the end */ + return bad_uri(uri_text, i, "fragment", suppress_errors); + } + fragment_end = i; + } + + uri = static_cast(gpr_zalloc(sizeof(*uri))); + uri->scheme = decode_and_copy_component(uri_text, scheme_begin, scheme_end); + uri->authority = + decode_and_copy_component(uri_text, authority_begin, authority_end); + uri->path = decode_and_copy_component(uri_text, path_begin, path_end); + uri->query = decode_and_copy_component(uri_text, query_begin, query_end); + uri->fragment = + decode_and_copy_component(uri_text, fragment_begin, fragment_end); + parse_query_parts(uri); + + return uri; +} + +const char* grpc_uri_get_query_arg(const grpc_uri* uri, const char* key) { + GPR_ASSERT(key != nullptr); + if (key[0] == '\0') return nullptr; + + for (size_t i = 0; i < uri->num_query_parts; ++i) { + if (0 == strcmp(key, uri->query_parts[i])) { + return uri->query_parts_values[i]; + } + } + return nullptr; +} + +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->query); + for (size_t i = 0; i < uri->num_query_parts; ++i) { + gpr_free(uri->query_parts[i]); + gpr_free(uri->query_parts_values[i]); + } + gpr_free(uri->query_parts); + gpr_free(uri->query_parts_values); + gpr_free(uri->fragment); + gpr_free(uri); +} diff --git a/third_party/grpc/src/core/lib/uri/uri_parser.h b/third_party/grpc/src/core/lib/uri/uri_parser.h new file mode 100644 index 00000000000000..b6771bbde3f9b8 --- /dev/null +++ b/third_party/grpc/src/core/lib/uri/uri_parser.h @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_LIB_URI_URI_PARSER_H +#define GRPC_CORE_LIB_URI_URI_PARSER_H + +#include + +#include + +typedef struct { + char* scheme; + char* authority; + char* path; + char* query; + /** Query substrings separated by '&' */ + char** query_parts; + /** Number of elements in \a query_parts and \a query_parts_values */ + size_t num_query_parts; + /** Split each query part by '='. NULL if not present. */ + char** query_parts_values; + char* fragment; +} grpc_uri; + +/** parse a uri, return NULL on failure */ +grpc_uri* grpc_uri_parse(const char* uri_text, bool suppress_errors); + +/** return the part of a query string after the '=' in "?key=xxx&...", or NULL + * if key is not present */ +const char* grpc_uri_get_query_arg(const grpc_uri* uri, const char* key); + +/** destroy a uri */ +void grpc_uri_destroy(grpc_uri* uri); + +#endif /* GRPC_CORE_LIB_URI_URI_PARSER_H */ diff --git a/third_party/grpc/src/core/plugin_registry/grpc_cronet_plugin_registry.cc b/third_party/grpc/src/core/plugin_registry/grpc_cronet_plugin_registry.cc new file mode 100644 index 00000000000000..92085d31d70a1e --- /dev/null +++ b/third_party/grpc/src/core/plugin_registry/grpc_cronet_plugin_registry.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +void grpc_http_filters_init(void); +void grpc_http_filters_shutdown(void); +void grpc_chttp2_plugin_init(void); +void grpc_chttp2_plugin_shutdown(void); +void grpc_deadline_filter_init(void); +void grpc_deadline_filter_shutdown(void); +void grpc_client_channel_init(void); +void grpc_client_channel_shutdown(void); + +void grpc_register_built_in_plugins(void) { + grpc_register_plugin(grpc_http_filters_init, + grpc_http_filters_shutdown); + grpc_register_plugin(grpc_chttp2_plugin_init, + grpc_chttp2_plugin_shutdown); + grpc_register_plugin(grpc_deadline_filter_init, + grpc_deadline_filter_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); +} diff --git a/third_party/grpc/src/core/plugin_registry/grpc_plugin_registry.cc b/third_party/grpc/src/core/plugin_registry/grpc_plugin_registry.cc new file mode 100644 index 00000000000000..cde40ef65cf860 --- /dev/null +++ b/third_party/grpc/src/core/plugin_registry/grpc_plugin_registry.cc @@ -0,0 +1,93 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +void grpc_http_filters_init(void); +void grpc_http_filters_shutdown(void); +void grpc_chttp2_plugin_init(void); +void grpc_chttp2_plugin_shutdown(void); +void grpc_deadline_filter_init(void); +void grpc_deadline_filter_shutdown(void); +void grpc_client_channel_init(void); +void grpc_client_channel_shutdown(void); +void grpc_inproc_plugin_init(void); +void grpc_inproc_plugin_shutdown(void); +void grpc_resolver_fake_init(void); +void grpc_resolver_fake_shutdown(void); +void grpc_lb_policy_grpclb_init(void); +void grpc_lb_policy_grpclb_shutdown(void); +void grpc_lb_policy_xds_init(void); +void grpc_lb_policy_xds_shutdown(void); +void grpc_lb_policy_pick_first_init(void); +void grpc_lb_policy_pick_first_shutdown(void); +void grpc_lb_policy_round_robin_init(void); +void grpc_lb_policy_round_robin_shutdown(void); +void grpc_resolver_dns_ares_init(void); +void grpc_resolver_dns_ares_shutdown(void); +void grpc_resolver_dns_native_init(void); +void grpc_resolver_dns_native_shutdown(void); +void grpc_resolver_sockaddr_init(void); +void grpc_resolver_sockaddr_shutdown(void); +void grpc_max_age_filter_init(void); +void grpc_max_age_filter_shutdown(void); +void grpc_message_size_filter_init(void); +void grpc_message_size_filter_shutdown(void); +void grpc_client_authority_filter_init(void); +void grpc_client_authority_filter_shutdown(void); +void grpc_workaround_cronet_compression_filter_init(void); +void grpc_workaround_cronet_compression_filter_shutdown(void); + +void grpc_register_built_in_plugins(void) { + grpc_register_plugin(grpc_http_filters_init, + grpc_http_filters_shutdown); + grpc_register_plugin(grpc_chttp2_plugin_init, + grpc_chttp2_plugin_shutdown); + grpc_register_plugin(grpc_deadline_filter_init, + grpc_deadline_filter_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); + grpc_register_plugin(grpc_inproc_plugin_init, + grpc_inproc_plugin_shutdown); + grpc_register_plugin(grpc_resolver_fake_init, + grpc_resolver_fake_shutdown); + grpc_register_plugin(grpc_lb_policy_grpclb_init, + grpc_lb_policy_grpclb_shutdown); + grpc_register_plugin(grpc_lb_policy_xds_init, + grpc_lb_policy_xds_shutdown); + grpc_register_plugin(grpc_lb_policy_pick_first_init, + grpc_lb_policy_pick_first_shutdown); + grpc_register_plugin(grpc_lb_policy_round_robin_init, + grpc_lb_policy_round_robin_shutdown); + grpc_register_plugin(grpc_resolver_dns_ares_init, + grpc_resolver_dns_ares_shutdown); + grpc_register_plugin(grpc_resolver_dns_native_init, + grpc_resolver_dns_native_shutdown); + grpc_register_plugin(grpc_resolver_sockaddr_init, + grpc_resolver_sockaddr_shutdown); + grpc_register_plugin(grpc_max_age_filter_init, + grpc_max_age_filter_shutdown); + grpc_register_plugin(grpc_message_size_filter_init, + grpc_message_size_filter_shutdown); + grpc_register_plugin(grpc_client_authority_filter_init, + grpc_client_authority_filter_shutdown); + grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, + grpc_workaround_cronet_compression_filter_shutdown); +} diff --git a/third_party/grpc/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc b/third_party/grpc/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc new file mode 100644 index 00000000000000..5749ab6b954d62 --- /dev/null +++ b/third_party/grpc/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc @@ -0,0 +1,93 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +void grpc_http_filters_init(void); +void grpc_http_filters_shutdown(void); +void grpc_chttp2_plugin_init(void); +void grpc_chttp2_plugin_shutdown(void); +void grpc_deadline_filter_init(void); +void grpc_deadline_filter_shutdown(void); +void grpc_client_channel_init(void); +void grpc_client_channel_shutdown(void); +void grpc_inproc_plugin_init(void); +void grpc_inproc_plugin_shutdown(void); +void grpc_resolver_dns_ares_init(void); +void grpc_resolver_dns_ares_shutdown(void); +void grpc_resolver_dns_native_init(void); +void grpc_resolver_dns_native_shutdown(void); +void grpc_resolver_sockaddr_init(void); +void grpc_resolver_sockaddr_shutdown(void); +void grpc_resolver_fake_init(void); +void grpc_resolver_fake_shutdown(void); +void grpc_lb_policy_grpclb_init(void); +void grpc_lb_policy_grpclb_shutdown(void); +void grpc_lb_policy_xds_init(void); +void grpc_lb_policy_xds_shutdown(void); +void grpc_lb_policy_pick_first_init(void); +void grpc_lb_policy_pick_first_shutdown(void); +void grpc_lb_policy_round_robin_init(void); +void grpc_lb_policy_round_robin_shutdown(void); +void grpc_max_age_filter_init(void); +void grpc_max_age_filter_shutdown(void); +void grpc_message_size_filter_init(void); +void grpc_message_size_filter_shutdown(void); +void grpc_client_authority_filter_init(void); +void grpc_client_authority_filter_shutdown(void); +void grpc_workaround_cronet_compression_filter_init(void); +void grpc_workaround_cronet_compression_filter_shutdown(void); + +void grpc_register_built_in_plugins(void) { + grpc_register_plugin(grpc_http_filters_init, + grpc_http_filters_shutdown); + grpc_register_plugin(grpc_chttp2_plugin_init, + grpc_chttp2_plugin_shutdown); + grpc_register_plugin(grpc_deadline_filter_init, + grpc_deadline_filter_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); + grpc_register_plugin(grpc_inproc_plugin_init, + grpc_inproc_plugin_shutdown); + grpc_register_plugin(grpc_resolver_dns_ares_init, + grpc_resolver_dns_ares_shutdown); + grpc_register_plugin(grpc_resolver_dns_native_init, + grpc_resolver_dns_native_shutdown); + grpc_register_plugin(grpc_resolver_sockaddr_init, + grpc_resolver_sockaddr_shutdown); + grpc_register_plugin(grpc_resolver_fake_init, + grpc_resolver_fake_shutdown); + grpc_register_plugin(grpc_lb_policy_grpclb_init, + grpc_lb_policy_grpclb_shutdown); + grpc_register_plugin(grpc_lb_policy_xds_init, + grpc_lb_policy_xds_shutdown); + grpc_register_plugin(grpc_lb_policy_pick_first_init, + grpc_lb_policy_pick_first_shutdown); + grpc_register_plugin(grpc_lb_policy_round_robin_init, + grpc_lb_policy_round_robin_shutdown); + grpc_register_plugin(grpc_max_age_filter_init, + grpc_max_age_filter_shutdown); + grpc_register_plugin(grpc_message_size_filter_init, + grpc_message_size_filter_shutdown); + grpc_register_plugin(grpc_client_authority_filter_init, + grpc_client_authority_filter_shutdown); + grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, + grpc_workaround_cronet_compression_filter_shutdown); +} diff --git a/third_party/grpc/src/core/profiling/stap_timers.c b/third_party/grpc/src/core/profiling/stap_timers.c deleted file mode 100644 index efcd1af4a1c8a6..00000000000000 --- a/third_party/grpc/src/core/profiling/stap_timers.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 gpr_timer_add_mark(int tag, const char *tagstr, void *id, const char *file, - int line) { - _STAP_ADD_MARK(tag); -} - -void gpr_timer_add_important_mark(int tag, const char *tagstr, void *id, - const char *file, int line) { - _STAP_ADD_IMPORTANT_MARK(tag); -} - -void gpr_timer_begin(int tag, const char *tagstr, void *id, const char *file, - int line) { - _STAP_TIMING_NS_BEGIN(tag); -} - -void gpr_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/third_party/grpc/src/core/profiling/timers.h b/third_party/grpc/src/core/profiling/timers.h deleted file mode 100644 index 6a188dc56648bd..00000000000000 --- a/third_party/grpc/src/core/profiling/timers.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 gpr_timers_global_init(void); -void gpr_timers_global_destroy(void); - -void gpr_timer_add_mark(const char *tagstr, int important, const char *file, - int line); -void gpr_timer_begin(const char *tagstr, int important, const char *file, - int line); -void gpr_timer_end(const char *tagstr, int important, const char *file, - int line); - -void gpr_timers_set_log_filename(const char *filename); - -#if !(defined(GRPC_STAP_PROFILER) + defined(GRPC_BASIC_PROFILER)) -/* No profiling. No-op all the things. */ -#define GPR_TIMER_MARK(tag, important) \ - do { \ - } while (0) - -#define GPR_TIMER_BEGIN(tag, important) \ - do { \ - } while (0) - -#define GPR_TIMER_END(tag, important) \ - 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 GPR_TIMER_MARK(tag, important) \ - gpr_timer_add_mark(tag, important, __FILE__, __LINE__); - -#define GPR_TIMER_BEGIN(tag, important) \ - gpr_timer_begin(tag, important, __FILE__, __LINE__); - -#define GPR_TIMER_END(tag, important) \ - gpr_timer_end(tag, important, __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 -} - -#if (defined(GRPC_STAP_PROFILER) + defined(GRPC_BASIC_PROFILER)) -namespace grpc { -class ProfileScope { - public: - ProfileScope(const char *desc, bool important) : desc_(desc) { - GPR_TIMER_BEGIN(desc_, important ? 1 : 0); - } - ~ProfileScope() { GPR_TIMER_END(desc_, 0); } - - private: - const char *const desc_; -}; -} - -#define GPR_TIMER_SCOPE(tag, important) \ - ::grpc::ProfileScope _profile_scope_##__LINE__((tag), (important)) -#else -#define GPR_TIMER_SCOPE(tag, important) \ - do { \ - } while (false) -#endif -#endif - -#endif /* GRPC_CORE_PROFILING_TIMERS_H */ diff --git a/third_party/grpc/src/core/security/auth_filters.h b/third_party/grpc/src/core/security/auth_filters.h deleted file mode 100644 index c179b54bec28af..00000000000000 --- a/third_party/grpc/src/core/security/auth_filters.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/security/base64.h b/third_party/grpc/src/core/security/base64.h deleted file mode 100644 index 31ae982691affe..00000000000000 --- a/third_party/grpc/src/core/security/base64.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/security/client_auth_filter.c b/third_party/grpc/src/core/security/client_auth_filter.c deleted file mode 100644 index 57b367d00fffee..00000000000000 --- a/third_party/grpc/src/core/security/client_auth_filter.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/security/auth_filters.h" - -#include - -#include -#include -#include - -#include "src/core/channel/channel_stack.h" -#include "src/core/security/credentials.h" -#include "src/core/security/security_connector.h" -#include "src/core/security/security_context.h" -#include "src/core/support/string.h" -#include "src/core/surface/call.h" -#include "src/core/transport/static_metadata.h" - -#define MAX_CREDENTIALS_METADATA_COUNT 4 - -/* We can have a per-call credentials. */ -typedef struct { - grpc_call_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; - uint8_t security_context_set; - grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; - grpc_auth_metadata_context auth_md_context; -} call_data; - -/* We can have a per-channel credentials. */ -typedef struct { - grpc_channel_security_connector *security_connector; - grpc_auth_context *auth_context; -} channel_data; - -static void reset_auth_metadata_context( - grpc_auth_metadata_context *auth_md_context) { - if (auth_md_context->service_url != NULL) { - gpr_free((char *)auth_md_context->service_url); - auth_md_context->service_url = NULL; - } - if (auth_md_context->method_name != NULL) { - gpr_free((char *)auth_md_context->method_name); - auth_md_context->method_name = NULL; - } - GRPC_AUTH_CONTEXT_UNREF( - (grpc_auth_context *)auth_md_context->channel_auth_context, - "grpc_auth_metadata_context"); - auth_md_context->channel_auth_context = NULL; -} - -static void bubble_up_error(grpc_exec_ctx *exec_ctx, 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(exec_ctx, elem, &calld->op); -} - -static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, 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; - grpc_transport_stream_op *op = &calld->op; - grpc_metadata_batch *mdb; - size_t i; - reset_auth_metadata_context(&calld->auth_md_context); - if (status != GRPC_CREDENTIALS_OK) { - bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED, - "Credentials failed to get metadata."); - return; - } - GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT); - GPR_ASSERT(op->send_initial_metadata != NULL); - mdb = op->send_initial_metadata; - for (i = 0; i < num_md; i++) { - grpc_metadata_batch_add_tail( - mdb, &calld->md_links[i], - grpc_mdelem_from_slices(gpr_slice_ref(md_elems[i].key), - gpr_slice_ref(md_elems[i].value))); - } - grpc_call_next_op(exec_ctx, elem, op); -} - -void build_auth_metadata_context(grpc_security_connector *sc, - grpc_auth_context *auth_context, - call_data *calld) { - char *service = gpr_strdup(grpc_mdstr_as_c_string(calld->method)); - char *last_slash = strrchr(service, '/'); - char *method_name = NULL; - char *service_url = NULL; - reset_auth_metadata_context(&calld->auth_md_context); - 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'; - method_name = gpr_strdup(last_slash + 1); - } - if (method_name == NULL) method_name = gpr_strdup(""); - gpr_asprintf(&service_url, "%s://%s%s", - sc->url_scheme == NULL ? "" : sc->url_scheme, - grpc_mdstr_as_c_string(calld->host), service); - calld->auth_md_context.service_url = service_url; - calld->auth_md_context.method_name = method_name; - calld->auth_md_context.channel_auth_context = - GRPC_AUTH_CONTEXT_REF(auth_context, "grpc_auth_metadata_context"); - gpr_free(service); -} - -static void send_security_metadata(grpc_exec_ctx *exec_ctx, - 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; - grpc_call_credentials *channel_call_creds = - chand->security_connector->request_metadata_creds; - int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL); - - if (channel_call_creds == NULL && !call_creds_has_md) { - /* Skip sending metadata altogether. */ - grpc_call_next_op(exec_ctx, elem, op); - return; - } - - if (channel_call_creds != NULL && call_creds_has_md) { - calld->creds = grpc_composite_call_credentials_create(channel_call_creds, - ctx->creds, NULL); - if (calld->creds == NULL) { - bubble_up_error(exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, - "Incompatible credentials set on channel and call."); - return; - } - } else { - calld->creds = grpc_call_credentials_ref( - call_creds_has_md ? ctx->creds : channel_call_creds); - } - - build_auth_metadata_context(&chand->security_connector->base, - chand->auth_context, calld); - calld->op = *op; /* Copy op (originates from the caller's stack). */ - GPR_ASSERT(calld->pollset); - grpc_call_credentials_get_request_metadata( - exec_ctx, calld->creds, calld->pollset, calld->auth_md_context, - on_credentials_metadata, elem); -} - -static void on_host_checked(grpc_exec_ctx *exec_ctx, 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(exec_ctx, 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(exec_ctx, 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_exec_ctx *exec_ctx, - 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; - 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->auth_context, "client_auth_filter"); - } - - if (op->send_initial_metadata != NULL) { - for (l = op->send_initial_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 == GRPC_MDSTR_AUTHORITY) { - if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host); - calld->host = GRPC_MDSTR_REF(md->value); - } else if (md->key == GRPC_MDSTR_PATH) { - if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method); - calld->method = GRPC_MDSTR_REF(md->value); - } - } - if (calld->host != NULL) { - const char *call_host = grpc_mdstr_as_c_string(calld->host); - calld->op = *op; /* Copy op (originates from the caller's stack). */ - grpc_channel_security_connector_check_call_host( - exec_ctx, chand->security_connector, call_host, chand->auth_context, - on_host_checked, elem); - return; /* early exit */ - } - } - - /* pass control down the stack */ - grpc_call_next_op(exec_ctx, elem, op); -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - call_data *calld = elem->call_data; - memset(calld, 0, sizeof(*calld)); -} - -static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_pollset *pollset) { - call_data *calld = elem->call_data; - calld->pollset = pollset; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - call_data *calld = elem->call_data; - grpc_call_credentials_unref(calld->creds); - if (calld->host != NULL) { - GRPC_MDSTR_UNREF(calld->host); - } - if (calld->method != NULL) { - GRPC_MDSTR_UNREF(calld->method); - } - reset_auth_metadata_context(&calld->auth_md_context); -} - -/* Constructor for channel_data */ -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - grpc_security_connector *sc = - grpc_find_security_connector_in_args(args->channel_args); - grpc_auth_context *auth_context = - grpc_find_auth_context_in_args(args->channel_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(!args->is_last); - GPR_ASSERT(sc != NULL); - GPR_ASSERT(auth_context != 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->auth_context = - GRPC_AUTH_CONTEXT_REF(auth_context, "client_auth_filter"); -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - /* grab pointers to our data from the channel element */ - channel_data *chand = elem->channel_data; - grpc_channel_security_connector *sc = chand->security_connector; - if (sc != NULL) { - GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "client_auth_filter"); - } - GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "client_auth_filter"); -} - -const grpc_channel_filter grpc_client_auth_filter = { - auth_start_transport_op, grpc_channel_next_op, sizeof(call_data), - init_call_elem, set_pollset, destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer, - "client-auth"}; diff --git a/third_party/grpc/src/core/security/credentials.c b/third_party/grpc/src/core/security/credentials.c deleted file mode 100644 index afba0079f5617a..00000000000000 --- a/third_party/grpc/src/core/security/credentials.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/httpcli/httpcli.h" -#include "src/core/iomgr/executor.h" -#include "src/core/json/json.h" -#include "src/core/support/string.h" -#include "src/core/surface/api_trace.h" - -#include -#include -#include -#include -#include - -/* -- Common. -- */ - -struct grpc_credentials_metadata_request { - grpc_call_credentials *creds; - grpc_credentials_metadata_cb cb; - void *user_data; -}; - -static grpc_credentials_metadata_request * -grpc_credentials_metadata_request_create(grpc_call_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_call_credentials_ref(creds); - r->cb = cb; - r->user_data = user_data; - return r; -} - -static void grpc_credentials_metadata_request_destroy( - grpc_credentials_metadata_request *r) { - grpc_call_credentials_unref(r->creds); - gpr_free(r); -} - -grpc_channel_credentials *grpc_channel_credentials_ref( - grpc_channel_credentials *creds) { - if (creds == NULL) return NULL; - gpr_ref(&creds->refcount); - return creds; -} - -void grpc_channel_credentials_unref(grpc_channel_credentials *creds) { - if (creds == NULL) return; - if (gpr_unref(&creds->refcount)) { - if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds); - gpr_free(creds); - } -} - -void grpc_channel_credentials_release(grpc_channel_credentials *creds) { - GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds)); - grpc_channel_credentials_unref(creds); -} - -grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds) { - if (creds == NULL) return NULL; - gpr_ref(&creds->refcount); - return creds; -} - -void grpc_call_credentials_unref(grpc_call_credentials *creds) { - if (creds == NULL) return; - if (gpr_unref(&creds->refcount)) { - if (creds->vtable->destruct != NULL) creds->vtable->destruct(creds); - gpr_free(creds); - } -} - -void grpc_call_credentials_release(grpc_call_credentials *creds) { - GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds)); - grpc_call_credentials_unref(creds); -} - -void grpc_call_credentials_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_pollset *pollset, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data) { - if (creds == NULL || creds->vtable->get_request_metadata == NULL) { - if (cb != NULL) { - cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK); - } - return; - } - creds->vtable->get_request_metadata(exec_ctx, creds, pollset, context, cb, - user_data); -} - -grpc_security_status grpc_channel_credentials_create_security_connector( - grpc_channel_credentials *channel_creds, const char *target, - const grpc_channel_args *args, grpc_channel_security_connector **sc, - grpc_channel_args **new_args) { - *new_args = NULL; - if (channel_creds == NULL) { - return GRPC_SECURITY_ERROR; - } - GPR_ASSERT(channel_creds->vtable->create_security_connector != NULL); - return channel_creds->vtable->create_security_connector( - channel_creds, NULL, target, args, 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)) { - if (creds->vtable->destruct != NULL) 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_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (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) { - GRPC_API_TRACE( - "grpc_server_credentials_set_auth_metadata_processor(" - "creds=%p, " - "processor=grpc_auth_metadata_processor { process: %p, state: %p })", - 3, (creds, (void *)(intptr_t)processor.process, processor.state)); - if (creds == NULL) return; - if (creds->processor.destroy != NULL && creds->processor.state != NULL) { - creds->processor.destroy(creds->processor.state); - } - creds->processor = processor; -} - -static void server_credentials_pointer_arg_destroy(void *p) { - grpc_server_credentials_unref(p); -} - -static void *server_credentials_pointer_arg_copy(void *p) { - return grpc_server_credentials_ref(p); -} - -grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) { - grpc_arg arg; - memset(&arg, 0, sizeof(grpc_arg)); - arg.type = GRPC_ARG_POINTER; - arg.key = GRPC_SERVER_CREDENTIALS_ARG; - arg.value.pointer.p = p; - arg.value.pointer.copy = server_credentials_pointer_arg_copy; - arg.value.pointer.destroy = server_credentials_pointer_arg_destroy; - return arg; -} - -grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg) { - if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return NULL; - if (arg->type != GRPC_ARG_POINTER) { - gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, - GRPC_SERVER_CREDENTIALS_ARG); - return NULL; - } - return arg->value.pointer.p; -} - -grpc_server_credentials *grpc_find_server_credentials_in_args( - const grpc_channel_args *args) { - size_t i; - if (args == NULL) return NULL; - for (i = 0; i < args->num_args; i++) { - grpc_server_credentials *p = - grpc_server_credentials_from_arg(&args->args[i]); - if (p != NULL) return p; - } - return NULL; -} - -/* -- Ssl credentials. -- */ - -static void ssl_destruct(grpc_channel_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 grpc_security_status ssl_create_security_connector( - grpc_channel_credentials *creds, grpc_call_credentials *call_creds, - const char *target, const grpc_channel_args *args, - 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 new_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( - call_creds, &c->config, target, overridden_target_name, sc); - if (status != GRPC_SECURITY_OK) { - return status; - } - new_arg.type = GRPC_ARG_STRING; - new_arg.key = GRPC_ARG_HTTP2_SCHEME; - new_arg.value.string = "https"; - *new_args = grpc_channel_args_copy_and_add(args, &new_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_channel_credentials_vtable ssl_vtable = { - ssl_destruct, 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_channel_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)); - GRPC_API_TRACE( - "grpc_ssl_credentials_create(pem_root_certs=%s, " - "pem_key_cert_pair=%p, " - "reserved=%p)", - 3, (pem_root_certs, pem_key_cert_pair, reserved)); - GPR_ASSERT(reserved == NULL); - memset(c, 0, sizeof(grpc_ssl_credentials)); - c->base.type = GRPC_CHANNEL_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)); - GRPC_API_TRACE( - "grpc_ssl_server_credentials_create(" - "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, " - "force_client_auth=%d, reserved=%p)", - 5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs, - force_client_auth, reserved)); - GPR_ASSERT(reserved == NULL); - memset(c, 0, sizeof(grpc_ssl_server_credentials)); - c->base.type = GRPC_CHANNEL_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_call_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 void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds, - grpc_pollset *pollset, - grpc_auth_metadata_context context, - 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, context.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, context.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(context.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(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries, - GRPC_CREDENTIALS_OK); - grpc_credentials_md_store_unref(jwt_md); - } else { - cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR); - } -} - -static grpc_call_credentials_vtable jwt_vtable = {jwt_destruct, - jwt_get_request_metadata}; - -grpc_call_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_CALL_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_call_credentials *grpc_service_account_jwt_access_credentials_create( - const char *json_key, gpr_timespec token_lifetime, void *reserved) { - GRPC_API_TRACE( - "grpc_service_account_jwt_access_credentials_create(" - "json_key=%s, " - "token_lifetime=" - "gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 5, - (json_key, (long long)token_lifetime.tv_sec, (int)token_lifetime.tv_nsec, - (int)token_lifetime.clock_type, 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_call_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); -} - -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( - grpc_exec_ctx *exec_ctx, 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(exec_ctx, 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(exec_ctx, 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_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_pollset *pollset, grpc_auth_metadata_context context, - 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(exec_ctx, 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( - exec_ctx, - 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_CALL_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_call_credentials_vtable compute_engine_vtable = { - oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata}; - -static void compute_engine_fetch_oauth2( - grpc_exec_ctx *exec_ctx, 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(exec_ctx, httpcli_context, pollset, &request, deadline, - response_cb, metadata_req); -} - -grpc_call_credentials *grpc_google_compute_engine_credentials_create( - void *reserved) { - grpc_oauth2_token_fetcher_credentials *c = - gpr_malloc(sizeof(grpc_oauth2_token_fetcher_credentials)); - GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1, - (reserved)); - 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_call_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_call_credentials_vtable refresh_token_vtable = { - refresh_token_destruct, oauth2_token_fetcher_get_request_metadata}; - -static void refresh_token_fetch_oauth2( - grpc_exec_ctx *exec_ctx, 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(exec_ctx, httpcli_context, pollset, &request, body, - strlen(body), deadline, response_cb, metadata_req); - gpr_free(body); -} - -grpc_call_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_call_credentials *grpc_google_refresh_token_credentials_create( - const char *json_refresh_token, void *reserved) { - GRPC_API_TRACE( - "grpc_refresh_token_credentials_create(json_refresh_token=%s, " - "reserved=%p)", - 2, (json_refresh_token, 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_call_credentials *creds) { - grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; - grpc_credentials_md_store_unref(c->md_store); -} - -static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx, - void *user_data, bool 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; - r->cb(exec_ctx, 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_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_pollset *pollset, grpc_auth_metadata_context context, - 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_executor_enqueue( - grpc_closure_create(on_simulated_token_fetch_done, cb_arg), true); - } else { - cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK); - } -} - -static grpc_call_credentials_vtable md_only_test_vtable = { - md_only_test_destruct, md_only_test_get_request_metadata}; - -grpc_call_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_CALL_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_call_credentials *creds) { - grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; - grpc_credentials_md_store_unref(c->access_token_md); -} - -static void access_token_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_pollset *pollset, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data) { - grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; - cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK); -} - -static grpc_call_credentials_vtable access_token_vtable = { - access_token_destruct, access_token_get_request_metadata}; - -grpc_call_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; - GRPC_API_TRACE( - "grpc_access_token_credentials_create(access_token=%s, " - "reserved=%p)", - 2, (access_token, reserved)); - GPR_ASSERT(reserved == NULL); - memset(c, 0, sizeof(grpc_access_token_credentials)); - c->base.type = GRPC_CALL_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 grpc_security_status fake_transport_security_create_security_connector( - grpc_channel_credentials *c, grpc_call_credentials *call_creds, - const char *target, const grpc_channel_args *args, - grpc_channel_security_connector **sc, grpc_channel_args **new_args) { - *sc = grpc_fake_channel_security_connector_create(call_creds); - 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_channel_credentials_vtable - fake_transport_security_credentials_vtable = { - NULL, fake_transport_security_create_security_connector}; - -static grpc_server_credentials_vtable - fake_transport_security_server_credentials_vtable = { - NULL, fake_transport_security_server_create_security_connector}; - -grpc_channel_credentials *grpc_fake_transport_security_credentials_create( - void) { - grpc_channel_credentials *c = gpr_malloc(sizeof(grpc_channel_credentials)); - memset(c, 0, sizeof(grpc_channel_credentials)); - c->type = GRPC_CHANNEL_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_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY; - gpr_ref_init(&c->refcount, 1); - c->vtable = &fake_transport_security_server_credentials_vtable; - return c; -} - -/* -- Composite call credentials. -- */ - -typedef struct { - grpc_composite_call_credentials *composite_creds; - size_t creds_index; - grpc_credentials_md_store *md_elems; - grpc_auth_metadata_context auth_md_context; - void *user_data; - grpc_pollset *pollset; - grpc_credentials_metadata_cb cb; -} grpc_composite_call_credentials_metadata_context; - -static void composite_call_destruct(grpc_call_credentials *creds) { - grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; - size_t i; - for (i = 0; i < c->inner.num_creds; i++) { - grpc_call_credentials_unref(c->inner.creds_array[i]); - } - gpr_free(c->inner.creds_array); -} - -static void composite_call_md_context_destroy( - grpc_composite_call_credentials_metadata_context *ctx) { - grpc_credentials_md_store_unref(ctx->md_elems); - gpr_free(ctx); -} - -static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_credentials_md *md_elems, - size_t num_md, - grpc_credentials_status status) { - grpc_composite_call_credentials_metadata_context *ctx = - (grpc_composite_call_credentials_metadata_context *)user_data; - if (status != GRPC_CREDENTIALS_OK) { - ctx->cb(exec_ctx, 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. */ - if (ctx->creds_index < ctx->composite_creds->inner.num_creds) { - grpc_call_credentials *inner_creds = - ctx->composite_creds->inner.creds_array[ctx->creds_index++]; - grpc_call_credentials_get_request_metadata( - exec_ctx, inner_creds, ctx->pollset, ctx->auth_md_context, - composite_call_metadata_cb, ctx); - return; - } - - /* We're done!. */ - ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries, - ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK); - composite_call_md_context_destroy(ctx); -} - -static void composite_call_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_pollset *pollset, grpc_auth_metadata_context auth_md_context, - grpc_credentials_metadata_cb cb, void *user_data) { - grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; - grpc_composite_call_credentials_metadata_context *ctx; - - ctx = gpr_malloc(sizeof(grpc_composite_call_credentials_metadata_context)); - memset(ctx, 0, sizeof(grpc_composite_call_credentials_metadata_context)); - ctx->auth_md_context = auth_md_context; - 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); - grpc_call_credentials_get_request_metadata( - exec_ctx, c->inner.creds_array[ctx->creds_index++], pollset, - auth_md_context, composite_call_metadata_cb, ctx); -} - -static grpc_call_credentials_vtable composite_call_credentials_vtable = { - composite_call_destruct, composite_call_get_request_metadata}; - -static grpc_call_credentials_array get_creds_array( - grpc_call_credentials **creds_addr) { - grpc_call_credentials_array result; - grpc_call_credentials *creds = *creds_addr; - result.creds_array = creds_addr; - result.num_creds = 1; - if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) { - result = *grpc_composite_call_credentials_get_credentials(creds); - } - return result; -} - -grpc_call_credentials *grpc_composite_call_credentials_create( - grpc_call_credentials *creds1, grpc_call_credentials *creds2, - void *reserved) { - size_t i; - size_t creds_array_byte_size; - grpc_call_credentials_array creds1_array; - grpc_call_credentials_array creds2_array; - grpc_composite_call_credentials *c; - GRPC_API_TRACE( - "grpc_composite_call_credentials_create(creds1=%p, creds2=%p, " - "reserved=%p)", - 3, (creds1, creds2, reserved)); - GPR_ASSERT(reserved == NULL); - GPR_ASSERT(creds1 != NULL); - GPR_ASSERT(creds2 != NULL); - c = gpr_malloc(sizeof(grpc_composite_call_credentials)); - memset(c, 0, sizeof(grpc_composite_call_credentials)); - c->base.type = GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE; - c->base.vtable = &composite_call_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_call_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_call_credentials *cur_creds = creds1_array.creds_array[i]; - c->inner.creds_array[i] = grpc_call_credentials_ref(cur_creds); - } - for (i = 0; i < creds2_array.num_creds; i++) { - grpc_call_credentials *cur_creds = creds2_array.creds_array[i]; - c->inner.creds_array[i + creds1_array.num_creds] = - grpc_call_credentials_ref(cur_creds); - } - return &c->base; -} - -const grpc_call_credentials_array * -grpc_composite_call_credentials_get_credentials(grpc_call_credentials *creds) { - const grpc_composite_call_credentials *c = - (const grpc_composite_call_credentials *)creds; - GPR_ASSERT(strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0); - return &c->inner; -} - -grpc_call_credentials *grpc_credentials_contains_type( - grpc_call_credentials *creds, const char *type, - grpc_call_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_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) { - const grpc_call_credentials_array *inner_creds_array = - grpc_composite_call_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_call_credentials *creds) { - grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; - grpc_credentials_md_store_unref(c->iam_md); -} - -static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds, - grpc_pollset *pollset, - grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, - void *user_data) { - grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; - cb(exec_ctx, user_data, c->iam_md->entries, c->iam_md->num_entries, - GRPC_CREDENTIALS_OK); -} - -static grpc_call_credentials_vtable iam_vtable = {iam_destruct, - iam_get_request_metadata}; - -grpc_call_credentials *grpc_google_iam_credentials_create( - const char *token, const char *authority_selector, void *reserved) { - grpc_google_iam_credentials *c; - GRPC_API_TRACE( - "grpc_iam_credentials_create(token=%s, authority_selector=%s, " - "reserved=%p)", - 3, (token, authority_selector, reserved)); - 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_CALL_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; -} - -/* -- Plugin credentials. -- */ - -typedef struct { - void *user_data; - grpc_credentials_metadata_cb cb; -} grpc_metadata_plugin_request; - -static void plugin_destruct(grpc_call_credentials *creds) { - grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; - if (c->plugin.state != NULL && c->plugin.destroy != NULL) { - c->plugin.destroy(c->plugin.state); - } -} - -static void plugin_md_request_metadata_ready(void *request, - const grpc_metadata *md, - size_t num_md, - grpc_status_code status, - const char *error_details) { - /* called from application code */ - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_metadata_plugin_request *r = (grpc_metadata_plugin_request *)request; - if (status != GRPC_STATUS_OK) { - if (error_details != NULL) { - gpr_log(GPR_ERROR, "Getting metadata from plugin failed with error: %s", - error_details); - } - r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR); - } else { - size_t i; - grpc_credentials_md *md_array = NULL; - if (num_md > 0) { - md_array = gpr_malloc(num_md * sizeof(grpc_credentials_md)); - for (i = 0; i < num_md; i++) { - md_array[i].key = gpr_slice_from_copied_string(md[i].key); - md_array[i].value = - gpr_slice_from_copied_buffer(md[i].value, md[i].value_length); - } - } - r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK); - if (md_array != NULL) { - for (i = 0; i < num_md; i++) { - gpr_slice_unref(md_array[i].key); - gpr_slice_unref(md_array[i].value); - } - gpr_free(md_array); - } - } - gpr_free(r); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds, - grpc_pollset *pollset, - grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, - void *user_data) { - grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; - if (c->plugin.get_metadata != NULL) { - grpc_metadata_plugin_request *request = gpr_malloc(sizeof(*request)); - memset(request, 0, sizeof(*request)); - request->user_data = user_data; - request->cb = cb; - c->plugin.get_metadata(c->plugin.state, context, - plugin_md_request_metadata_ready, request); - } else { - cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK); - } -} - -static grpc_call_credentials_vtable plugin_vtable = { - plugin_destruct, plugin_get_request_metadata}; - -grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( - grpc_metadata_credentials_plugin plugin, void *reserved) { - grpc_plugin_credentials *c = gpr_malloc(sizeof(*c)); - GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1, - (reserved)); - GPR_ASSERT(reserved == NULL); - memset(c, 0, sizeof(*c)); - c->base.type = plugin.type; - c->base.vtable = &plugin_vtable; - gpr_ref_init(&c->base.refcount, 1); - c->plugin = plugin; - return &c->base; -} - -/* -- Composite channel credentials. -- */ - -static void composite_channel_destruct(grpc_channel_credentials *creds) { - grpc_composite_channel_credentials *c = - (grpc_composite_channel_credentials *)creds; - grpc_channel_credentials_unref(c->inner_creds); - grpc_call_credentials_unref(c->call_creds); -} - -static grpc_security_status composite_channel_create_security_connector( - grpc_channel_credentials *creds, grpc_call_credentials *call_creds, - const char *target, const grpc_channel_args *args, - grpc_channel_security_connector **sc, grpc_channel_args **new_args) { - grpc_composite_channel_credentials *c = - (grpc_composite_channel_credentials *)creds; - grpc_security_status status = GRPC_SECURITY_ERROR; - - GPR_ASSERT(c->inner_creds != NULL && c->call_creds != NULL && - c->inner_creds->vtable != NULL && - c->inner_creds->vtable->create_security_connector != NULL); - /* If we are passed a call_creds, create a call composite to pass it - downstream. */ - if (call_creds != NULL) { - grpc_call_credentials *composite_call_creds = - grpc_composite_call_credentials_create(c->call_creds, call_creds, NULL); - status = c->inner_creds->vtable->create_security_connector( - c->inner_creds, composite_call_creds, target, args, sc, new_args); - grpc_call_credentials_unref(composite_call_creds); - } else { - status = c->inner_creds->vtable->create_security_connector( - c->inner_creds, c->call_creds, target, args, sc, new_args); - } - return status; -} - -static grpc_channel_credentials_vtable composite_channel_credentials_vtable = { - composite_channel_destruct, composite_channel_create_security_connector}; - -grpc_channel_credentials *grpc_composite_channel_credentials_create( - grpc_channel_credentials *channel_creds, grpc_call_credentials *call_creds, - void *reserved) { - grpc_composite_channel_credentials *c = gpr_malloc(sizeof(*c)); - memset(c, 0, sizeof(*c)); - GPR_ASSERT(channel_creds != NULL && call_creds != NULL && reserved == NULL); - GRPC_API_TRACE( - "grpc_composite_channel_credentials_create(channel_creds=%p, " - "call_creds=%p, reserved=%p)", - 3, (channel_creds, call_creds, reserved)); - c->base.type = channel_creds->type; - c->base.vtable = &composite_channel_credentials_vtable; - gpr_ref_init(&c->base.refcount, 1); - c->inner_creds = grpc_channel_credentials_ref(channel_creds); - c->call_creds = grpc_call_credentials_ref(call_creds); - return &c->base; -} diff --git a/third_party/grpc/src/core/security/credentials.h b/third_party/grpc/src/core/security/credentials.h deleted file mode 100644 index 3cd652cd574db5..00000000000000 --- a/third_party/grpc/src/core/security/credentials.h +++ /dev/null @@ -1,376 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/metadata_batch.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_CHANNEL_CREDENTIALS_TYPE_SSL "Ssl" -#define GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY \ - "FakeTransportSecurity" - -#define GRPC_CALL_CREDENTIALS_TYPE_OAUTH2 "Oauth2" -#define GRPC_CALL_CREDENTIALS_TYPE_JWT "Jwt" -#define GRPC_CALL_CREDENTIALS_TYPE_IAM "Iam" -#define GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE "Composite" - -#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" - -/* --- Google utils --- */ - -/* It is the caller's responsibility to gpr_free the result if not NULL. */ -char *grpc_get_well_known_google_credentials_file_path(void); - -/* Implementation function for the different platforms. */ -char *grpc_get_well_known_google_credentials_file_path_impl(void); - -/* Override for testing only. Not thread-safe */ -typedef char *(*grpc_well_known_credentials_path_getter)(void); -void grpc_override_well_known_credentials_path_getter( - grpc_well_known_credentials_path_getter getter); - -/* --- grpc_channel_credentials. --- */ - -typedef struct { - void (*destruct)(grpc_channel_credentials *c); - - grpc_security_status (*create_security_connector)( - grpc_channel_credentials *c, grpc_call_credentials *call_creds, - const char *target, const grpc_channel_args *args, - grpc_channel_security_connector **sc, grpc_channel_args **new_args); -} grpc_channel_credentials_vtable; - -struct grpc_channel_credentials { - const grpc_channel_credentials_vtable *vtable; - const char *type; - gpr_refcount refcount; -}; - -grpc_channel_credentials *grpc_channel_credentials_ref( - grpc_channel_credentials *creds); -void grpc_channel_credentials_unref(grpc_channel_credentials *creds); - -/* 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_channel_credentials_create_security_connector( - grpc_channel_credentials *creds, const char *target, - const grpc_channel_args *args, grpc_channel_security_connector **sc, - grpc_channel_args **new_args); - -/* --- 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_call_credentials. --- */ - -typedef void (*grpc_credentials_metadata_cb)(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_credentials_md *md_elems, - size_t num_md, - grpc_credentials_status status); - -typedef struct { - void (*destruct)(grpc_call_credentials *c); - void (*get_request_metadata)(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *c, grpc_pollset *pollset, - grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, - void *user_data); -} grpc_call_credentials_vtable; - -struct grpc_call_credentials { - const grpc_call_credentials_vtable *vtable; - const char *type; - gpr_refcount refcount; -}; - -grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds); -void grpc_call_credentials_unref(grpc_call_credentials *creds); -void grpc_call_credentials_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_pollset *pollset, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data); - -typedef struct { - grpc_call_credentials **creds_array; - size_t num_creds; -} grpc_call_credentials_array; - -const grpc_call_credentials_array * -grpc_composite_call_credentials_get_credentials( - grpc_call_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_call_credentials *grpc_credentials_contains_type( - grpc_call_credentials *creds, const char *type, - grpc_call_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_call_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_call_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_call_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; - -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); - -#define GRPC_SERVER_CREDENTIALS_ARG "grpc.server_credentials" - -grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *c); -grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg); -grpc_server_credentials *grpc_find_server_credentials_in_args( - const grpc_channel_args *args); - -/* -- Fake transport security credentials. -- */ - -/* Creates a fake transport security credentials object for testing. */ -grpc_channel_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); - -/* -- Ssl credentials. -- */ - -typedef struct { - grpc_channel_credentials base; - grpc_ssl_config config; -} grpc_ssl_credentials; - -typedef struct { - grpc_server_credentials base; - grpc_ssl_server_config config; -} grpc_ssl_server_credentials; - -/* -- Channel composite credentials. -- */ - -typedef struct { - grpc_channel_credentials base; - grpc_channel_credentials *inner_creds; - grpc_call_credentials *call_creds; -} grpc_composite_channel_credentials; - -/* -- Jwt credentials -- */ - -typedef struct { - grpc_call_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_exec_ctx *exec_ctx, - 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_call_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_call_credentials base; - grpc_credentials_md_store *access_token_md; -} grpc_access_token_credentials; - -/* -- Metadata-only Test credentials. -- */ - -typedef struct { - grpc_call_credentials base; - grpc_credentials_md_store *md_store; - int is_async; -} grpc_md_only_test_credentials; - -/* -- GoogleIAM credentials. -- */ - -typedef struct { - grpc_call_credentials base; - grpc_credentials_md_store *iam_md; -} grpc_google_iam_credentials; - -/* -- Composite credentials. -- */ - -typedef struct { - grpc_call_credentials base; - grpc_call_credentials_array inner; -} grpc_composite_call_credentials; - -/* -- Plugin credentials. -- */ - -typedef struct { - grpc_call_credentials base; - grpc_metadata_credentials_plugin plugin; - grpc_credentials_md_store *plugin_md; -} grpc_plugin_credentials; - -#endif /* GRPC_INTERNAL_CORE_SECURITY_CREDENTIALS_H */ diff --git a/third_party/grpc/src/core/security/credentials_metadata.c b/third_party/grpc/src/core/security/credentials_metadata.c deleted file mode 100644 index b8a132f1eaf39d..00000000000000 --- a/third_party/grpc/src/core/security/credentials_metadata.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/security/credentials_posix.c b/third_party/grpc/src/core/security/credentials_posix.c deleted file mode 100644 index 0c92bd4a96bcfa..00000000000000 --- a/third_party/grpc/src/core/security/credentials_posix.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_impl(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/third_party/grpc/src/core/security/credentials_win32.c b/third_party/grpc/src/core/security/credentials_win32.c deleted file mode 100644 index 8ee9f706a1f2d9..00000000000000 --- a/third_party/grpc/src/core/security/credentials_win32.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_impl(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/third_party/grpc/src/core/security/google_default_credentials.c b/third_party/grpc/src/core/security/google_default_credentials.c deleted file mode 100644 index f3ac14568a6366..00000000000000 --- a/third_party/grpc/src/core/security/google_default_credentials.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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" -#include "src/core/surface/api_trace.h" - -/* -- Constants. -- */ - -#define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal" - -/* -- Default credentials. -- */ - -static grpc_channel_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( - grpc_exec_ctx *exec_ctx, 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(grpc_exec_ctx *exec_ctx, void *p, bool s) { - 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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_closure destroy_closure; - - /* 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( - &exec_ctx, &context, &detector.pollset, &request, - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay), - on_compute_engine_detection_http_response, &detector); - - grpc_exec_ctx_finish(&exec_ctx); - - /* 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(&exec_ctx, &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_closure_init(&destroy_closure, destroy_pollset, &detector.pollset); - grpc_pollset_shutdown(&exec_ctx, &detector.pollset, &destroy_closure); - grpc_exec_ctx_finish(&exec_ctx); - - return detector.success; -} - -/* Takes ownership of creds_path if not NULL. */ -static grpc_call_credentials *create_default_creds_from_path(char *creds_path) { - grpc_json *json = NULL; - grpc_auth_json_key key; - grpc_auth_refresh_token token; - grpc_call_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_channel_credentials *grpc_google_default_credentials_create(void) { - grpc_channel_credentials *result = NULL; - grpc_call_credentials *call_creds = NULL; - - GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ()); - - gpr_once_init(&g_once, init_default_credentials); - - gpr_mu_lock(&g_mu); - - if (default_credentials != NULL) { - result = grpc_channel_credentials_ref(default_credentials); - goto end; - } - - /* First, try the environment variable. */ - call_creds = create_default_creds_from_path( - gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR)); - if (call_creds != NULL) goto end; - - /* Then the well-known file. */ - call_creds = create_default_creds_from_path( - grpc_get_well_known_google_credentials_file_path()); - if (call_creds != 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) { - call_creds = grpc_google_compute_engine_credentials_create(NULL); - } - } - -end: - if (result == NULL) { - if (call_creds != NULL) { - /* Blend with default ssl credentials and add a global reference so that - it - can be cached and re-served. */ - grpc_channel_credentials *ssl_creds = - grpc_ssl_credentials_create(NULL, NULL, NULL); - default_credentials = grpc_channel_credentials_ref( - grpc_composite_channel_credentials_create(ssl_creds, call_creds, - NULL)); - GPR_ASSERT(default_credentials != NULL); - grpc_channel_credentials_unref(ssl_creds); - grpc_call_credentials_unref(call_creds); - result = default_credentials; - } else { - gpr_log(GPR_ERROR, "Could not create google 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_channel_credentials_unref(default_credentials); - default_credentials = NULL; - } - compute_engine_detection_done = 0; - gpr_mu_unlock(&g_mu); -} - -/* -- Well known credentials path. -- */ - -static grpc_well_known_credentials_path_getter creds_path_getter = NULL; - -char *grpc_get_well_known_google_credentials_file_path(void) { - if (creds_path_getter != NULL) return creds_path_getter(); - return grpc_get_well_known_google_credentials_file_path_impl(); -} - -void grpc_override_well_known_credentials_path_getter( - grpc_well_known_credentials_path_getter getter) { - creds_path_getter = getter; -} diff --git a/third_party/grpc/src/core/security/handshake.c b/third_party/grpc/src/core/security/handshake.c deleted file mode 100644 index a8b2fef6297035..00000000000000 --- a/third_party/grpc/src/core/security/handshake.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/security/handshake.h" - -#include - -#include "src/core/security/security_context.h" -#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_security_handshake_done_cb cb; - void *user_data; - grpc_closure on_handshake_data_sent_to_peer; - grpc_closure on_handshake_data_received_from_peer; - grpc_auth_context *auth_context; -} grpc_security_handshake; - -static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, - void *setup, bool success); - -static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup, - bool success); - -static void security_connector_remove_handshake(grpc_security_handshake *h) { - grpc_security_connector_handshake_list *node; - grpc_security_connector_handshake_list *tmp; - grpc_security_connector *sc = h->connector; - gpr_mu_lock(&sc->mu); - node = sc->handshaking_handshakes; - if (node && node->handshake == h) { - sc->handshaking_handshakes = node->next; - gpr_free(node); - gpr_mu_unlock(&sc->mu); - return; - } - while (node) { - if (node->next->handshake == h) { - tmp = node->next; - node->next = node->next->next; - gpr_free(tmp); - gpr_mu_unlock(&sc->mu); - return; - } - node = node->next; - } - gpr_mu_unlock(&sc->mu); -} - -static void security_handshake_done(grpc_exec_ctx *exec_ctx, - grpc_security_handshake *h, - int is_success) { - if (!h->connector->is_client_side) { - security_connector_remove_handshake(h); - } - if (is_success) { - h->cb(exec_ctx, h->user_data, GRPC_SECURITY_OK, h->secure_endpoint, - h->auth_context); - } else { - if (h->secure_endpoint != NULL) { - grpc_endpoint_shutdown(exec_ctx, h->secure_endpoint); - grpc_endpoint_destroy(exec_ctx, h->secure_endpoint); - } else { - grpc_endpoint_destroy(exec_ctx, h->wrapped_endpoint); - } - h->cb(exec_ctx, h->user_data, GRPC_SECURITY_ERROR, NULL, NULL); - } - if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker); - if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer); - gpr_slice_buffer_destroy(&h->left_overs); - gpr_slice_buffer_destroy(&h->outgoing); - gpr_slice_buffer_destroy(&h->incoming); - GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake"); - GRPC_SECURITY_CONNECTOR_UNREF(h->connector, "handshake"); - gpr_free(h); -} - -static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_security_status status, - grpc_auth_context *auth_context) { - grpc_security_handshake *h = user_data; - tsi_frame_protector *protector; - tsi_result result; - if (status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, "Error checking peer."); - security_handshake_done(exec_ctx, h, 0); - return; - } - h->auth_context = GRPC_AUTH_CONTEXT_REF(auth_context, "handshake"); - result = - tsi_handshaker_create_frame_protector(h->handshaker, NULL, &protector); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.", - tsi_result_to_string(result)); - security_handshake_done(exec_ctx, h, 0); - return; - } - h->secure_endpoint = - grpc_secure_endpoint_create(protector, h->wrapped_endpoint, - h->left_overs.slices, h->left_overs.count); - h->left_overs.count = 0; - h->left_overs.length = 0; - security_handshake_done(exec_ctx, h, 1); - return; -} - -static void check_peer(grpc_exec_ctx *exec_ctx, grpc_security_handshake *h) { - tsi_peer peer; - tsi_result result = tsi_handshaker_extract_peer(h->handshaker, &peer); - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Peer extraction failed with error %s", - tsi_result_to_string(result)); - security_handshake_done(exec_ctx, h, 0); - return; - } - grpc_security_connector_check_peer(exec_ctx, h->connector, peer, - on_peer_checked, h); -} - -static void send_handshake_bytes_to_peer(grpc_exec_ctx *exec_ctx, - grpc_security_handshake *h) { - size_t offset = 0; - tsi_result result = TSI_OK; - gpr_slice to_send; - - do { - size_t to_send_size = h->handshake_buffer_size - offset; - result = tsi_handshaker_get_bytes_to_send_to_peer( - h->handshaker, h->handshake_buffer + offset, &to_send_size); - offset += to_send_size; - if (result == TSI_INCOMPLETE_DATA) { - h->handshake_buffer_size *= 2; - h->handshake_buffer = - gpr_realloc(h->handshake_buffer, h->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)); - security_handshake_done(exec_ctx, h, 0); - return; - } - - to_send = - gpr_slice_from_copied_buffer((const char *)h->handshake_buffer, offset); - gpr_slice_buffer_reset_and_unref(&h->outgoing); - gpr_slice_buffer_add(&h->outgoing, to_send); - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - grpc_endpoint_write(exec_ctx, h->wrapped_endpoint, &h->outgoing, - &h->on_handshake_data_sent_to_peer); -} - -static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx, - void *handshake, - bool success) { - grpc_security_handshake *h = handshake; - 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."); - security_handshake_done(exec_ctx, h, 0); - return; - } - - for (i = 0; i < h->incoming.count; i++) { - consumed_slice_size = GPR_SLICE_LENGTH(h->incoming.slices[i]); - result = tsi_handshaker_process_bytes_from_peer( - h->handshaker, GPR_SLICE_START_PTR(h->incoming.slices[i]), - &consumed_slice_size); - if (!tsi_handshaker_is_in_progress(h->handshaker)) break; - } - - if (tsi_handshaker_is_in_progress(h->handshaker)) { - /* We may need more data. */ - if (result == TSI_INCOMPLETE_DATA) { - grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming, - &h->on_handshake_data_received_from_peer); - return; - } else { - send_handshake_bytes_to_peer(exec_ctx, h); - return; - } - } - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshake failed with error %s", - tsi_result_to_string(result)); - security_handshake_done(exec_ctx, h, 0); - return; - } - - /* Handshake is done and successful this point. */ - has_left_overs_in_current_slice = - (consumed_slice_size < GPR_SLICE_LENGTH(h->incoming.slices[i])); - num_left_overs = - (has_left_overs_in_current_slice ? 1 : 0) + h->incoming.count - i - 1; - if (num_left_overs == 0) { - check_peer(exec_ctx, h); - return; - } - - /* Put the leftovers in our buffer (ownership transfered). */ - if (has_left_overs_in_current_slice) { - gpr_slice_buffer_add( - &h->left_overs, - gpr_slice_split_tail(&h->incoming.slices[i], consumed_slice_size)); - gpr_slice_unref( - h->incoming.slices[i]); /* split_tail above increments refcount. */ - } - gpr_slice_buffer_addn( - &h->left_overs, &h->incoming.slices[i + 1], - num_left_overs - (size_t)has_left_overs_in_current_slice); - check_peer(exec_ctx, h); -} - -/* If handshake is NULL, the handshake is done. */ -static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, - void *handshake, bool success) { - grpc_security_handshake *h = handshake; - - /* Make sure that write is OK. */ - if (!success) { - gpr_log(GPR_ERROR, "Write failed."); - if (handshake != NULL) security_handshake_done(exec_ctx, h, 0); - return; - } - - /* We may be done. */ - if (tsi_handshaker_is_in_progress(h->handshaker)) { - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - grpc_endpoint_read(exec_ctx, h->wrapped_endpoint, &h->incoming, - &h->on_handshake_data_received_from_peer); - } else { - check_peer(exec_ctx, h); - } -} - -void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx, - tsi_handshaker *handshaker, - grpc_security_connector *connector, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data) { - grpc_security_connector_handshake_list *handshake_node; - grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake)); - memset(h, 0, sizeof(grpc_security_handshake)); - h->handshaker = handshaker; - h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake"); - h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; - h->handshake_buffer = gpr_malloc(h->handshake_buffer_size); - h->wrapped_endpoint = nonsecure_endpoint; - h->user_data = user_data; - h->cb = cb; - grpc_closure_init(&h->on_handshake_data_sent_to_peer, - on_handshake_data_sent_to_peer, h); - grpc_closure_init(&h->on_handshake_data_received_from_peer, - on_handshake_data_received_from_peer, h); - gpr_slice_buffer_init(&h->left_overs); - gpr_slice_buffer_init(&h->outgoing); - gpr_slice_buffer_init(&h->incoming); - if (!connector->is_client_side) { - handshake_node = gpr_malloc(sizeof(grpc_security_connector_handshake_list)); - handshake_node->handshake = h; - gpr_mu_lock(&connector->mu); - handshake_node->next = connector->handshaking_handshakes; - connector->handshaking_handshakes = handshake_node; - gpr_mu_unlock(&connector->mu); - } - send_handshake_bytes_to_peer(exec_ctx, h); -} - -void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, - void *handshake) { - grpc_security_handshake *h = handshake; - grpc_endpoint_shutdown(exec_ctx, h->wrapped_endpoint); -} diff --git a/third_party/grpc/src/core/security/handshake.h b/third_party/grpc/src/core/security/handshake.h deleted file mode 100644 index 44215d16ef0e00..00000000000000 --- a/third_party/grpc/src/core/security/handshake.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_HANDSHAKE_H -#define GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H - -#include "src/core/iomgr/endpoint.h" -#include "src/core/security/security_connector.h" - -/* Calls the callback upon completion. Takes owership of handshaker. */ -void grpc_do_security_handshake(grpc_exec_ctx *exec_ctx, - tsi_handshaker *handshaker, - grpc_security_connector *connector, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data); - -void grpc_security_handshake_shutdown(grpc_exec_ctx *exec_ctx, void *handshake); - -#endif /* GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H */ diff --git a/third_party/grpc/src/core/security/json_token.c b/third_party/grpc/src/core/security/json_token.c deleted file mode 100644 index 762f02989acc50..00000000000000 --- a/third_party/grpc/src/core/security/json_token.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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. */ -gpr_timespec grpc_max_auth_token_lifetime() { - gpr_timespec out; - out.tv_sec = 3600; - out.tv_nsec = 0; - out.clock_type = GPR_TIMESPAN; - return out; -} - -#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()); - } - int64_ttoa(now.tv_sec, now_str); - int64_ttoa(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((uintptr_t)(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/third_party/grpc/src/core/security/json_token.h b/third_party/grpc/src/core/security/json_token.h deleted file mode 100644 index 7e06864ff3adb5..00000000000000 --- a/third_party/grpc/src/core/security/json_token.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/security/jwt_verifier.c b/third_party/grpc/src/core/security/jwt_verifier.c deleted file mode 100644 index 042a117f5d7481..00000000000000 --- a/third_party/grpc/src/core/security/jwt_verifier.c +++ /dev/null @@ -1,843 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/security/jwt_verifier.h" - -#include -#include - -#include "src/core/httpcli/httpcli.h" -#include "src/core/security/base64.h" -#include "src/core/tsi/ssl_types.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()); - size_t len = strlen(x509_str); - GPR_ASSERT(len < INT_MAX); - BIO_write(bio, x509_str, (int)len); - 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), - TSI_SIZE_AS_SIZE(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(grpc_exec_ctx *exec_ctx, 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(grpc_exec_ctx *exec_ctx, 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( - exec_ctx, &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(grpc_exec_ctx *exec_ctx, - 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( - exec_ctx, &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_exec_ctx *exec_ctx, - 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, (size_t)(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, (size_t)(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( - exec_ctx, - 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/third_party/grpc/src/core/security/jwt_verifier.h b/third_party/grpc/src/core/security/jwt_verifier.h deleted file mode 100644 index 25613f03a07f14..00000000000000 --- a/third_party/grpc/src/core/security/jwt_verifier.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, - 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/third_party/grpc/src/core/security/secure_endpoint.c b/third_party/grpc/src/core/security/secure_endpoint.c deleted file mode 100644 index d11c43be203e51..00000000000000 --- a/third_party/grpc/src/core/security/secure_endpoint.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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_closure *read_cb; - grpc_closure *write_cb; - grpc_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(grpc_exec_ctx *exec_ctx, secure_endpoint *secure_ep) { - secure_endpoint *ep = secure_ep; - grpc_endpoint_destroy(exec_ctx, 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(exec_ctx, ep, reason) \ - secure_endpoint_unref((exec_ctx), (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, - grpc_closure_list *closure_list, - 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(exec_ctx, 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(exec_ctx, ep, reason) \ - secure_endpoint_unref((exec_ctx), (ep)) -#define SECURE_ENDPOINT_REF(ep, reason) secure_endpoint_ref((ep)) -static void secure_endpoint_unref(grpc_exec_ctx *exec_ctx, - secure_endpoint *ep) { - if (gpr_unref(&ep->ref)) { - destroy(exec_ctx, ep); - } -} - -static void secure_endpoint_ref(secure_endpoint *ep) { gpr_ref(&ep->ref); } -#endif - -static void flush_read_staging_buffer(secure_endpoint *ep, uint8_t **cur, - uint8_t **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(grpc_exec_ctx *exec_ctx, secure_endpoint *ep, - bool 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; - grpc_exec_ctx_enqueue(exec_ctx, ep->read_cb, success, NULL); - SECURE_ENDPOINT_UNREF(exec_ctx, ep, "read"); -} - -static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, bool success) { - unsigned i; - uint8_t keep_looping = 0; - tsi_result result = TSI_OK; - secure_endpoint *ep = (secure_endpoint *)user_data; - uint8_t *cur = GPR_SLICE_START_PTR(ep->read_staging_buffer); - uint8_t *end = GPR_SLICE_END_PTR(ep->read_staging_buffer); - - if (!success) { - gpr_slice_buffer_reset_and_unref(ep->read_buffer); - call_read_cb(exec_ctx, ep, 0); - return; - } - - /* 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]; - uint8_t *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); - call_read_cb(exec_ctx, ep, 0); - return; - } - - call_read_cb(exec_ctx, ep, 1); -} - -static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, - gpr_slice_buffer *slices, grpc_closure *cb) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - ep->read_cb = cb; - ep->read_buffer = slices; - gpr_slice_buffer_reset_and_unref(ep->read_buffer); - - SECURE_ENDPOINT_REF(ep, "read"); - if (ep->leftover_bytes.count) { - gpr_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer); - GPR_ASSERT(ep->leftover_bytes.count == 0); - on_read(exec_ctx, ep, 1); - return; - } - - grpc_endpoint_read(exec_ctx, ep->wrapped_ep, &ep->source_buffer, - &ep->on_read); -} - -static void flush_write_staging_buffer(secure_endpoint *ep, uint8_t **cur, - uint8_t **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 void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, - gpr_slice_buffer *slices, grpc_closure *cb) { - unsigned i; - tsi_result result = TSI_OK; - secure_endpoint *ep = (secure_endpoint *)secure_ep; - uint8_t *cur = GPR_SLICE_START_PTR(ep->write_staging_buffer); - uint8_t *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]; - uint8_t *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); - grpc_exec_ctx_enqueue(exec_ctx, cb, false, NULL); - return; - } - - grpc_endpoint_write(exec_ctx, ep->wrapped_ep, &ep->output_buffer, cb); -} - -static void endpoint_shutdown(grpc_exec_ctx *exec_ctx, - grpc_endpoint *secure_ep) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_shutdown(exec_ctx, ep->wrapped_ep); -} - -static void endpoint_destroy(grpc_exec_ctx *exec_ctx, - grpc_endpoint *secure_ep) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - SECURE_ENDPOINT_UNREF(exec_ctx, ep, "destroy"); -} - -static void endpoint_add_to_pollset(grpc_exec_ctx *exec_ctx, - grpc_endpoint *secure_ep, - grpc_pollset *pollset) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_add_to_pollset(exec_ctx, ep->wrapped_ep, pollset); -} - -static void endpoint_add_to_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_endpoint *secure_ep, - grpc_pollset_set *pollset_set) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_add_to_pollset_set(exec_ctx, 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_closure_init(&ep->on_read, on_read, ep); - gpr_mu_init(&ep->protector_mu); - gpr_ref_init(&ep->ref, 1); - return &ep->base; -} diff --git a/third_party/grpc/src/core/security/secure_endpoint.h b/third_party/grpc/src/core/security/secure_endpoint.h deleted file mode 100644 index c563bdd9c505f4..00000000000000 --- a/third_party/grpc/src/core/security/secure_endpoint.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/security/security_connector.c b/third_party/grpc/src/core/security/security_connector.c deleted file mode 100644 index bdccbabfea3458..00000000000000 --- a/third_party/grpc/src/core/security/security_connector.c +++ /dev/null @@ -1,786 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/security/security_connector.h" - -#include - -#include "src/core/security/credentials.h" -#include "src/core/security/handshake.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 - -/* -- Overridden default roots. -- */ - -static grpc_ssl_roots_override_callback ssl_roots_override_cb = NULL; - -void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) { - ssl_roots_override_cb = cb; -} - -/* -- 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; -} - -void grpc_security_connector_shutdown(grpc_exec_ctx *exec_ctx, - grpc_security_connector *connector) { - grpc_security_connector_handshake_list *tmp; - if (!connector->is_client_side) { - gpr_mu_lock(&connector->mu); - while (connector->handshaking_handshakes) { - tmp = connector->handshaking_handshakes; - grpc_security_handshake_shutdown( - exec_ctx, connector->handshaking_handshakes->handshake); - connector->handshaking_handshakes = tmp->next; - gpr_free(tmp); - } - gpr_mu_unlock(&connector->mu); - } -} - -void grpc_security_connector_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data) { - if (sc == NULL || nonsecure_endpoint == NULL) { - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL, NULL); - } else { - sc->vtable->do_handshake(exec_ctx, sc, nonsecure_endpoint, cb, user_data); - } -} - -void grpc_security_connector_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - tsi_peer peer, - grpc_security_peer_check_cb cb, - void *user_data) { - if (sc == NULL) { - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR, NULL); - tsi_peer_destruct(&peer); - } else { - sc->vtable->check_peer(exec_ctx, sc, peer, cb, user_data); - } -} - -void grpc_channel_security_connector_check_call_host( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, - const char *host, grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, void *user_data) { - if (sc == NULL || sc->check_call_host == NULL) { - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR); - } else { - sc->check_call_host(exec_ctx, sc, host, auth_context, 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; -} - -/* -- Fake implementation. -- */ - -static void fake_channel_destroy(grpc_security_connector *sc) { - grpc_channel_security_connector *c = (grpc_channel_security_connector *)sc; - grpc_call_credentials_unref(c->request_metadata_creds); - gpr_free(sc); -} - -static void fake_server_destroy(grpc_security_connector *sc) { - gpr_mu_destroy(&sc->mu); - gpr_free(sc); -} - -static void fake_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_security_peer_check_cb cb, void *user_data) { - const char *prop_name; - grpc_security_status status = GRPC_SECURITY_OK; - grpc_auth_context *auth_context = NULL; - 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; - } - auth_context = grpc_auth_context_create(NULL); - grpc_auth_context_add_cstring_property( - auth_context, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, - GRPC_FAKE_TRANSPORT_SECURITY_TYPE); - -end: - cb(exec_ctx, user_data, status, auth_context); - grpc_auth_context_unref(auth_context); - tsi_peer_destruct(&peer); -} - -static void fake_channel_check_call_host(grpc_exec_ctx *exec_ctx, - grpc_channel_security_connector *sc, - const char *host, - grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, - void *user_data) { - cb(exec_ctx, user_data, GRPC_SECURITY_OK); -} - -static void fake_channel_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data) { - grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(1), sc, - nonsecure_endpoint, cb, user_data); -} - -static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data) { - grpc_do_security_handshake(exec_ctx, tsi_create_fake_handshaker(0), sc, - nonsecure_endpoint, cb, user_data); -} - -static grpc_security_connector_vtable fake_channel_vtable = { - fake_channel_destroy, fake_channel_do_handshake, fake_check_peer}; - -static grpc_security_connector_vtable fake_server_vtable = { - fake_server_destroy, fake_server_do_handshake, fake_check_peer}; - -grpc_channel_security_connector *grpc_fake_channel_security_connector_create( - grpc_call_credentials *request_metadata_creds) { - grpc_channel_security_connector *c = gpr_malloc(sizeof(*c)); - memset(c, 0, sizeof(*c)); - gpr_ref_init(&c->base.refcount, 1); - c->base.is_client_side = 1; - c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; - c->base.vtable = &fake_channel_vtable; - c->request_metadata_creds = grpc_call_credentials_ref(request_metadata_creds); - c->check_call_host = fake_channel_check_call_host; - return c; -} - -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; - gpr_mu_init(&c->mu); - return c; -} - -/* --- Ssl implementation. --- */ - -typedef struct { - grpc_channel_security_connector base; - tsi_ssl_handshaker_factory *handshaker_factory; - char *target_name; - char *overridden_target_name; -} 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_call_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); - 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); - } - gpr_mu_destroy(&sc->mu); - 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 void ssl_channel_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data) { - grpc_ssl_channel_security_connector *c = - (grpc_ssl_channel_security_connector *)sc; - tsi_handshaker *handshaker; - grpc_security_status status = ssl_create_handshaker( - c->handshaker_factory, 1, - c->overridden_target_name != NULL ? c->overridden_target_name - : c->target_name, - &handshaker); - if (status != GRPC_SECURITY_OK) { - cb(exec_ctx, user_data, status, NULL, NULL); - } else { - grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, - user_data); - } -} - -static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data) { - grpc_ssl_server_security_connector *c = - (grpc_ssl_server_security_connector *)sc; - tsi_handshaker *handshaker; - grpc_security_status status = - ssl_create_handshaker(c->handshaker_factory, 0, NULL, &handshaker); - if (status != GRPC_SECURITY_OK) { - cb(exec_ctx, user_data, status, NULL, NULL); - } else { - grpc_do_security_handshake(exec_ctx, handshaker, sc, nonsecure_endpoint, cb, - user_data); - } -} - -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, - grpc_auth_context **auth_context) { - /* 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; - } - *auth_context = tsi_ssl_peer_to_auth_context(peer); - return GRPC_SECURITY_OK; -} - -static void ssl_channel_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_security_peer_check_cb cb, - void *user_data) { - grpc_ssl_channel_security_connector *c = - (grpc_ssl_channel_security_connector *)sc; - grpc_security_status status; - grpc_auth_context *auth_context = NULL; - status = ssl_check_peer(sc, c->overridden_target_name != NULL - ? c->overridden_target_name - : c->target_name, - &peer, &auth_context); - cb(exec_ctx, user_data, status, auth_context); - grpc_auth_context_unref(auth_context); - tsi_peer_destruct(&peer); -} - -static void ssl_server_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_security_peer_check_cb cb, - void *user_data) { - grpc_auth_context *auth_context = NULL; - grpc_security_status status = ssl_check_peer(sc, NULL, &peer, &auth_context); - tsi_peer_destruct(&peer); - cb(exec_ctx, user_data, status, auth_context); - grpc_auth_context_unref(auth_context); -} - -static void add_shalow_auth_property_to_peer(tsi_peer *peer, - const grpc_auth_property *prop, - const char *tsi_prop_name) { - tsi_peer_property *tsi_prop = &peer->properties[peer->property_count++]; - tsi_prop->name = (char *)tsi_prop_name; - tsi_prop->value.data = prop->value; - tsi_prop->value.length = prop->value_length; -} - -tsi_peer tsi_shallow_peer_from_ssl_auth_context( - const grpc_auth_context *auth_context) { - size_t max_num_props = 0; - grpc_auth_property_iterator it; - const grpc_auth_property *prop; - tsi_peer peer; - memset(&peer, 0, sizeof(peer)); - - it = grpc_auth_context_property_iterator(auth_context); - while (grpc_auth_property_iterator_next(&it) != NULL) max_num_props++; - - if (max_num_props > 0) { - peer.properties = gpr_malloc(max_num_props * sizeof(tsi_peer_property)); - it = grpc_auth_context_property_iterator(auth_context); - while ((prop = grpc_auth_property_iterator_next(&it)) != NULL) { - if (strcmp(prop->name, GRPC_X509_SAN_PROPERTY_NAME) == 0) { - add_shalow_auth_property_to_peer( - &peer, prop, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY); - } else if (strcmp(prop->name, GRPC_X509_CN_PROPERTY_NAME) == 0) { - add_shalow_auth_property_to_peer( - &peer, prop, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); - } - } - } - return peer; -} - -void tsi_shallow_peer_destruct(tsi_peer *peer) { - if (peer->properties != NULL) gpr_free(peer->properties); -} - -static void ssl_channel_check_call_host(grpc_exec_ctx *exec_ctx, - grpc_channel_security_connector *sc, - const char *host, - grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, - void *user_data) { - grpc_ssl_channel_security_connector *c = - (grpc_ssl_channel_security_connector *)sc; - grpc_security_status status = GRPC_SECURITY_ERROR; - tsi_peer peer = tsi_shallow_peer_from_ssl_auth_context(auth_context); - if (ssl_host_matches_name(&peer, host)) status = 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) { - status = GRPC_SECURITY_OK; - } - cb(exec_ctx, user_data, status); - tsi_shallow_peer_destruct(&peer); -} - -static grpc_security_connector_vtable ssl_channel_vtable = { - ssl_channel_destroy, ssl_channel_do_handshake, ssl_channel_check_peer}; - -static grpc_security_connector_vtable ssl_server_vtable = { - ssl_server_destroy, ssl_server_do_handshake, ssl_server_check_peer}; - -static gpr_slice compute_default_pem_root_certs_once(void) { - gpr_slice result = gpr_empty_slice(); - - /* 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) { - result = gpr_load_file(default_root_certs_path, 0, NULL); - gpr_free(default_root_certs_path); - } - - /* Try overridden roots if needed. */ - grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL; - if (GPR_SLICE_IS_EMPTY(result) && ssl_roots_override_cb != NULL) { - char *pem_root_certs = NULL; - ovrd_res = ssl_roots_override_cb(&pem_root_certs); - if (ovrd_res == GRPC_SSL_ROOTS_OVERRIDE_OK) { - GPR_ASSERT(pem_root_certs != NULL); - result = gpr_slice_new(pem_root_certs, strlen(pem_root_certs), gpr_free); - } - } - - /* Fall back to installed certs if needed. */ - if (GPR_SLICE_IS_EMPTY(result) && - ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) { - result = gpr_load_file(installed_roots_path, 0, NULL); - } - return result; -} - -static gpr_slice default_pem_root_certs; - -static void init_default_pem_root_certs(void) { - default_pem_root_certs = compute_default_pem_root_certs_once(); -} - -gpr_slice grpc_get_default_ssl_roots_for_testing(void) { - return compute_default_pem_root_certs_once(); -} - -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_call_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 (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_call_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((void *)alpn_protocol_strings); - gpr_free(alpn_protocol_string_lengths); - return GRPC_SECURITY_OK; - -error: - gpr_free((void *)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; - } - gpr_mu_init(&c->base.mu); - *sc = &c->base; - gpr_free((void *)alpn_protocol_strings); - gpr_free(alpn_protocol_string_lengths); - return GRPC_SECURITY_OK; - -error: - gpr_free((void *)alpn_protocol_strings); - gpr_free(alpn_protocol_string_lengths); - return GRPC_SECURITY_ERROR; -} diff --git a/third_party/grpc/src/core/security/security_connector.h b/third_party/grpc/src/core/security/security_connector.h deleted file mode 100644 index 39df7821f0c72c..00000000000000 --- a/third_party/grpc/src/core/security/security_connector.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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_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_peer_check_cb)(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_security_status status, - grpc_auth_context *auth_context); - -/* Ownership of the secure_endpoint is transfered. */ -typedef void (*grpc_security_handshake_done_cb)( - grpc_exec_ctx *exec_ctx, void *user_data, grpc_security_status status, - grpc_endpoint *secure_endpoint, grpc_auth_context *auth_context); - -typedef struct { - void (*destroy)(grpc_security_connector *sc); - void (*do_handshake)(grpc_exec_ctx *exec_ctx, grpc_security_connector *sc, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, void *user_data); - void (*check_peer)(grpc_exec_ctx *exec_ctx, grpc_security_connector *sc, - tsi_peer peer, grpc_security_peer_check_cb cb, - void *user_data); -} grpc_security_connector_vtable; - -typedef struct grpc_security_connector_handshake_list { - void *handshake; - struct grpc_security_connector_handshake_list *next; -} grpc_security_connector_handshake_list; - -struct grpc_security_connector { - const grpc_security_connector_vtable *vtable; - gpr_refcount refcount; - int is_client_side; - const char *url_scheme; - /* Used on server side only. */ - /* TODO(yangg): Create a grpc_server_security_connector with these. */ - gpr_mu mu; - grpc_security_connector_handshake_list *handshaking_handshakes; - const grpc_channel_args *channel_args; -}; - -/* 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. */ -void grpc_security_connector_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_security_connector *connector, - grpc_endpoint *nonsecure_endpoint, - grpc_security_handshake_done_cb cb, - void *user_data); - -/* Check the peer. Callee takes ownership of the peer object. - The callback will include the resulting auth_context. */ -void grpc_security_connector_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - tsi_peer peer, - grpc_security_peer_check_cb cb, - void *user_data); - -void grpc_security_connector_shutdown(grpc_exec_ctx *exec_ctx, - grpc_security_connector *connector); - -/* 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; - -typedef void (*grpc_security_call_host_check_cb)(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_security_status status); - -struct grpc_channel_security_connector { - grpc_security_connector base; /* requires is_client_side to be non 0. */ - grpc_call_credentials *request_metadata_creds; - void (*check_call_host)(grpc_exec_ctx *exec_ctx, - grpc_channel_security_connector *sc, const char *host, - grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, void *user_data); -}; - -/* Checks that the host that will be set for a call is acceptable. */ -void grpc_channel_security_connector_check_call_host( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, - const char *host, grpc_auth_context *auth_context, - grpc_security_call_host_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_call_credentials *request_metadata_creds); - -/* 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_call_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); - -/* Exposed for TESTING ONLY!. */ -gpr_slice grpc_get_default_ssl_roots_for_testing(void); - -/* 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); -tsi_peer tsi_shallow_peer_from_ssl_auth_context( - const grpc_auth_context *auth_context); -void tsi_shallow_peer_destruct(tsi_peer *peer); - -#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONNECTOR_H */ diff --git a/third_party/grpc/src/core/security/security_context.c b/third_party/grpc/src/core/security/security_context.c deleted file mode 100644 index 2068c97d78c223..00000000000000 --- a/third_party/grpc/src/core/security/security_context.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/api_trace.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_call_credentials *creds) { - grpc_client_security_context *ctx = NULL; - GRPC_API_TRACE("grpc_call_set_credentials(call=%p, creds=%p)", 2, - (call, creds)); - if (!grpc_call_is_client(call)) { - gpr_log(GPR_ERROR, "Method is client-side only."); - return GRPC_CALL_ERROR_NOT_ON_SERVER; - } - 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_call_credentials_ref(creds); - grpc_call_context_set(call, GRPC_CONTEXT_SECURITY, ctx, - grpc_client_security_context_destroy); - } else { - grpc_call_credentials_unref(ctx->creds); - ctx->creds = grpc_call_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); - GRPC_API_TRACE("grpc_call_auth_context(call=%p)", 1, (call)); - 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_API_TRACE("grpc_auth_context_release(context=%p)", 1, (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_call_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) { - GRPC_API_TRACE("grpc_auth_context_peer_identity_property_name(ctx=%p)", 1, - (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); - GRPC_API_TRACE( - "grpc_auth_context_set_peer_identity_property_name(ctx=%p, name=%s)", 2, - (ctx, name)); - 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) { - GRPC_API_TRACE("grpc_auth_context_peer_is_authenticated(ctx=%p)", 1, (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; - GRPC_API_TRACE("grpc_auth_context_property_iterator(ctx=%p)", 1, (ctx)); - if (ctx == NULL) return it; - it.ctx = ctx; - return it; -} - -const grpc_auth_property *grpc_auth_property_iterator_next( - grpc_auth_property_iterator *it) { - GRPC_API_TRACE("grpc_auth_property_iterator_next(it=%p)", 1, (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; - GRPC_API_TRACE("grpc_auth_context_find_properties_by_name(ctx=%p, name=%s)", - 2, (ctx, name)); - 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) { - GRPC_API_TRACE("grpc_auth_context_peer_identity(ctx=%p)", 1, (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; - GRPC_API_TRACE( - "grpc_auth_context_add_property(ctx=%p, name=%s, value=%*.*s, " - "value_length=%lu)", - 6, (ctx, name, (int)value_length, (int)value_length, value, - (unsigned long)value_length)); - 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; - GRPC_API_TRACE( - "grpc_auth_context_add_cstring_property(ctx=%p, name=%s, value=%s)", 3, - (ctx, name, value)); - 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)); -} - -static void auth_context_pointer_arg_destroy(void *p) { - GRPC_AUTH_CONTEXT_UNREF(p, "auth_context_pointer_arg"); -} - -static void *auth_context_pointer_arg_copy(void *p) { - return GRPC_AUTH_CONTEXT_REF(p, "auth_context_pointer_arg"); -} - -grpc_arg grpc_auth_context_to_arg(grpc_auth_context *p) { - grpc_arg arg; - memset(&arg, 0, sizeof(grpc_arg)); - arg.type = GRPC_ARG_POINTER; - arg.key = GRPC_AUTH_CONTEXT_ARG; - arg.value.pointer.p = p; - arg.value.pointer.copy = auth_context_pointer_arg_copy; - arg.value.pointer.destroy = auth_context_pointer_arg_destroy; - return arg; -} - -grpc_auth_context *grpc_auth_context_from_arg(const grpc_arg *arg) { - if (strcmp(arg->key, GRPC_AUTH_CONTEXT_ARG) != 0) return NULL; - if (arg->type != GRPC_ARG_POINTER) { - gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, - GRPC_AUTH_CONTEXT_ARG); - return NULL; - } - return arg->value.pointer.p; -} - -grpc_auth_context *grpc_find_auth_context_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_context *p = grpc_auth_context_from_arg(&args->args[i]); - if (p != NULL) return p; - } - return NULL; -} diff --git a/third_party/grpc/src/core/security/security_context.h b/third_party/grpc/src/core/security/security_context.h deleted file mode 100644 index 794258edbcb147..00000000000000 --- a/third_party/grpc/src/core/security/security_context.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_call_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); - -/* --- Channel args for auth context --- */ -#define GRPC_AUTH_CONTEXT_ARG "grpc.auth_context" - -grpc_arg grpc_auth_context_to_arg(grpc_auth_context *c); -grpc_auth_context *grpc_auth_context_from_arg(const grpc_arg *arg); -grpc_auth_context *grpc_find_auth_context_in_args( - const grpc_channel_args *args); - -#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */ diff --git a/third_party/grpc/src/core/security/server_auth_filter.c b/third_party/grpc/src/core/security/server_auth_filter.c deleted file mode 100644 index 4c78711387e773..00000000000000 --- a/third_party/grpc/src/core/security/server_auth_filter.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include "src/core/security/auth_filters.h" -#include "src/core/security/credentials.h" -#include "src/core/security/security_context.h" - -#include -#include - -typedef struct call_data { - grpc_metadata_batch *recv_initial_metadata; - /* Closure to call when finished with the auth_on_recv hook. */ - grpc_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_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_auth_context *auth_context; -} call_data; - -typedef struct channel_data { - grpc_auth_context *auth_context; - grpc_server_credentials *creds; -} 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; -} - -/* called from application code */ -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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - /* 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->recv_initial_metadata, remove_consumed_md, - elem); - grpc_metadata_array_destroy(&calld->md); - calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 1); - } else { - gpr_slice message; - grpc_transport_stream_op close_op; - memset(&close_op, 0, sizeof(close_op)); - 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); - calld->transport_op.send_initial_metadata = NULL; - if (calld->transport_op.send_message != NULL) { - grpc_byte_stream_destroy(&exec_ctx, calld->transport_op.send_message); - calld->transport_op.send_message = NULL; - } - calld->transport_op.send_trailing_metadata = NULL; - grpc_transport_stream_op_add_close(&close_op, status, &message); - grpc_call_next_op(&exec_ctx, elem, &close_op); - calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 0); - } - - grpc_exec_ctx_finish(&exec_ctx); -} - -static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, - bool success) { - grpc_call_element *elem = user_data; - call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; - if (success) { - if (chand->creds->processor.process != NULL) { - calld->md = metadata_batch_to_md_array(calld->recv_initial_metadata); - chand->creds->processor.process( - chand->creds->processor.state, calld->auth_context, - calld->md.metadata, calld->md.count, on_md_processing_done, elem); - return; - } - } - calld->on_done_recv->cb(exec_ctx, 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_initial_metadata != NULL) { - /* substitute our callback for the higher callback */ - calld->recv_initial_metadata = op->recv_initial_metadata; - calld->on_done_recv = op->on_complete; - op->on_complete = &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_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - set_recv_ops_md_callbacks(elem, op); - grpc_call_next_op(exec_ctx, elem, op); -} - -/* Constructor for call_data */ -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - /* 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_closure_init(&calld->auth_on_recv, auth_on_recv, elem); - - if (args->context[GRPC_CONTEXT_SECURITY].value != NULL) { - args->context[GRPC_CONTEXT_SECURITY].destroy( - args->context[GRPC_CONTEXT_SECURITY].value); - } - - server_ctx = grpc_server_security_context_create(); - server_ctx->auth_context = grpc_auth_context_create(chand->auth_context); - calld->auth_context = server_ctx->auth_context; - - args->context[GRPC_CONTEXT_SECURITY].value = server_ctx; - args->context[GRPC_CONTEXT_SECURITY].destroy = - grpc_server_security_context_destroy; -} - -static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_pollset *pollset) {} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) {} - -/* Constructor for channel_data */ -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - grpc_auth_context *auth_context = - grpc_find_auth_context_in_args(args->channel_args); - grpc_server_credentials *creds = - grpc_find_server_credentials_in_args(args->channel_args); - /* grab pointers to our data from the channel element */ - channel_data *chand = elem->channel_data; - - GPR_ASSERT(!args->is_last); - GPR_ASSERT(auth_context != NULL); - GPR_ASSERT(creds != NULL); - - /* initialize members */ - chand->auth_context = - GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter"); - chand->creds = grpc_server_credentials_ref(creds); -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - /* grab pointers to our data from the channel element */ - channel_data *chand = elem->channel_data; - GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "server_auth_filter"); - grpc_server_credentials_unref(chand->creds); -} - -const grpc_channel_filter grpc_server_auth_filter = { - auth_start_transport_op, grpc_channel_next_op, sizeof(call_data), - init_call_elem, set_pollset, destroy_call_elem, sizeof(channel_data), - init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer, - "server-auth"}; diff --git a/third_party/grpc/src/core/security/server_secure_chttp2.c b/third_party/grpc/src/core/security/server_secure_chttp2.c deleted file mode 100644 index 84a883390c2a19..00000000000000 --- a/third_party/grpc/src/core/security/server_secure_chttp2.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/surface/api_trace.h" -#include "src/core/surface/server.h" -#include "src/core/transport/chttp2_transport.h" -#include -#include -#include -#include - -typedef struct grpc_server_secure_state { - grpc_server *server; - grpc_tcp_server *tcp; - grpc_security_connector *sc; - grpc_server_credentials *creds; - int is_shutdown; - gpr_mu mu; - gpr_refcount refcount; - grpc_closure destroy_closure; - grpc_closure *destroy_callback; -} 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(grpc_exec_ctx *exec_ctx, void *statep, - grpc_transport *transport, - grpc_auth_context *auth_context) { - 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_server_credentials_to_arg(state->creds); - args_to_add[1] = grpc_auth_context_to_arg(auth_context); - 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(exec_ctx, state->server, transport, extra_filters, - GPR_ARRAY_SIZE(extra_filters), args_copy); - grpc_channel_args_destroy(args_copy); -} - -static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *statep, - grpc_security_status status, - grpc_endpoint *secure_endpoint, - grpc_auth_context *auth_context) { - grpc_server_secure_state *state = statep; - grpc_transport *transport; - if (status == GRPC_SECURITY_OK) { - if (secure_endpoint) { - gpr_mu_lock(&state->mu); - if (!state->is_shutdown) { - transport = grpc_create_chttp2_transport( - exec_ctx, grpc_server_get_channel_args(state->server), - secure_endpoint, 0); - setup_transport(exec_ctx, state, transport, auth_context); - grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0); - } else { - /* We need to consume this here, because the server may already have - * gone away. */ - grpc_endpoint_destroy(exec_ctx, secure_endpoint); - } - gpr_mu_unlock(&state->mu); - } - } else { - gpr_log(GPR_ERROR, "Secure transport failed with error %d", status); - } - state_unref(state); -} - -static void on_accept(grpc_exec_ctx *exec_ctx, void *statep, grpc_endpoint *tcp, - grpc_tcp_server_acceptor *acceptor) { - grpc_server_secure_state *state = statep; - state_ref(state); - grpc_security_connector_do_handshake(exec_ctx, state->sc, tcp, - on_secure_handshake_done, state); -} - -/* Server callback: start listening on our ports */ -static void start(grpc_exec_ctx *exec_ctx, grpc_server *server, void *statep, - grpc_pollset **pollsets, size_t pollset_count) { - grpc_server_secure_state *state = statep; - grpc_tcp_server_start(exec_ctx, state->tcp, pollsets, pollset_count, - on_accept, state); -} - -static void destroy_done(grpc_exec_ctx *exec_ctx, void *statep, bool success) { - grpc_server_secure_state *state = statep; - if (state->destroy_callback != NULL) { - state->destroy_callback->cb(exec_ctx, state->destroy_callback->cb_arg, - success); - } - grpc_security_connector_shutdown(exec_ctx, state->sc); - state_unref(state); -} - -/* Server callback: destroy the tcp listener (so we don't generate further - callbacks) */ -static void destroy(grpc_exec_ctx *exec_ctx, grpc_server *server, void *statep, - grpc_closure *callback) { - grpc_server_secure_state *state = statep; - grpc_tcp_server *tcp; - gpr_mu_lock(&state->mu); - state->is_shutdown = 1; - state->destroy_callback = callback; - tcp = state->tcp; - gpr_mu_unlock(&state->mu); - grpc_tcp_server_unref(exec_ctx, tcp); -} - -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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE( - "grpc_server_add_secure_http2_port(" - "server=%p, addr=%s, creds=%p)", - 3, (server, addr, creds)); - - /* 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; - } - sc->channel_args = grpc_server_get_channel_args(server); - - /* resolve address */ - resolved = grpc_blocking_resolve_address(addr, "https"); - if (!resolved) { - goto error; - } - state = gpr_malloc(sizeof(*state)); - memset(state, 0, sizeof(*state)); - grpc_closure_init(&state->destroy_closure, destroy_done, state); - tcp = grpc_tcp_server_create(&state->destroy_closure); - if (!tcp) { - goto error; - } - - state->server = server; - state->tcp = tcp; - state->sc = sc; - state->creds = grpc_server_credentials_ref(creds); - state->is_shutdown = 0; - gpr_mu_init(&state->mu); - gpr_ref_init(&state->refcount, 1); - - 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); - - /* Register with the server only upon success */ - grpc_server_add_listener(&exec_ctx, server, state, start, destroy); - - grpc_exec_ctx_finish(&exec_ctx); - return port_num; - -/* Error path: cleanup and return */ -error: - if (resolved) { - grpc_resolved_addresses_destroy(resolved); - } - if (tcp) { - grpc_tcp_server_unref(&exec_ctx, tcp); - } else { - if (sc) { - GRPC_SECURITY_CONNECTOR_UNREF(sc, "server"); - } - if (state) { - gpr_free(state); - } - } - grpc_exec_ctx_finish(&exec_ctx); - return 0; -} diff --git a/third_party/grpc/src/core/statistics/census_init.c b/third_party/grpc/src/core/statistics/census_init.c deleted file mode 100644 index b6a962f228f5ed..00000000000000 --- a/third_party/grpc/src/core/statistics/census_init.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/third_party/grpc/src/core/statistics/census_interface.h b/third_party/grpc/src/core/statistics/census_interface.h deleted file mode 100644 index c43acbd317fbbd..00000000000000 --- a/third_party/grpc/src/core/statistics/census_interface.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 { - uint32_t upper; - uint32_t 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/third_party/grpc/src/core/statistics/census_log.c b/third_party/grpc/src/core/statistics/census_log.c deleted file mode 100644 index 257ba586a32d5f..00000000000000 --- a/third_party/grpc/src/core/statistics/census_log.c +++ /dev/null @@ -1,603 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 */ - int32_t 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 - int32_t 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 { - int32_t 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 */ - int32_t 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. */ - uint32_t 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, - int32_t bytes_committed) { - gpr_atm_rel_store(&block->bytes_committed, bytes_committed); -} - -static int32_t 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) { - int32_t 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(int32_t 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) { - uintptr_t p = (uintptr_t)((char *)record - g_log.buffer); - uintptr_t 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) { - int32_t 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. */ - int32_t attempts_remaining = g_log.num_blocks; - /* TODO(aveitch): move this inside the do loop when current_cpu is fixed */ - int32_t 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/third_party/grpc/src/core/statistics/census_log.h b/third_party/grpc/src/core/statistics/census_log.h deleted file mode 100644 index 356437c346e948..00000000000000 --- a/third_party/grpc/src/core/statistics/census_log.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/statistics/census_rpc_stats.c b/third_party/grpc/src/core/statistics/census_rpc_stats.c deleted file mode 100644 index 524a60793ae662..00000000000000 --- a/third_party/grpc/src/core/statistics/census_rpc_stats.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 uint64_t simple_hash(const void *k) { - size_t len = strlen(k); - uint64_t 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/third_party/grpc/src/core/statistics/census_rpc_stats.h b/third_party/grpc/src/core/statistics/census_rpc_stats.h deleted file mode 100644 index 8bdbc494903872..00000000000000 --- a/third_party/grpc/src/core/statistics/census_rpc_stats.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 { - uint64_t cnt; - uint64_t rpc_error_cnt; - uint64_t 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/third_party/grpc/src/core/statistics/census_tracing.c b/third_party/grpc/src/core/statistics/census_tracing.c deleted file mode 100644 index dc0f8a26f518d9..00000000000000 --- a/third_party/grpc/src/core/statistics/census_tracing.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 uint64_t g_id = 0; - -static census_ht_key op_id_as_key(census_op_id *id) { - return *(census_ht_key *)id; -} - -static uint64_t op_id_2_uint64(census_op_id *id) { - uint64_t 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/third_party/grpc/src/core/statistics/census_tracing.h b/third_party/grpc/src/core/statistics/census_tracing.h deleted file mode 100644 index bb3f2556d20349..00000000000000 --- a/third_party/grpc/src/core/statistics/census_tracing.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/statistics/hash_table.c b/third_party/grpc/src/core/statistics/hash_table.c deleted file mode 100644 index 0cadcd4740218f..00000000000000 --- a/third_party/grpc/src/core/statistics/hash_table.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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. */ - int32_t prev_non_empty_bucket; - /* -1 if all buckets are empty. */ - int32_t next_non_empty_bucket; -} bucket; - -struct unresizable_hash_table { - /* Number of entries in the table */ - size_t size; - /* Number of buckets */ - uint32_t 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. */ - int32_t first_non_empty_bucket; - /* Index of the last non_empty bucket. -1 iff size == 0. */ - int32_t last_non_empty_bucket; - /* Immutable options of this hash table, initialized at creation time. */ - census_ht_option options; -}; - -typedef struct entry_locator { - int32_t 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 uint64_t 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 int32_t 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}; - int32_t 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) { - int32_t 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; - int32_t 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/third_party/grpc/src/core/statistics/hash_table.h b/third_party/grpc/src/core/statistics/hash_table.h deleted file mode 100644 index d9860a909f0afd..00000000000000 --- a/third_party/grpc/src/core/statistics/hash_table.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 { - uint64_t 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 */ - int32_t num_buckets; - /* Fucntion to calculate uint64 hash value of the key. Only takes effect if - key_type is POINTER. */ - uint64_t (*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. */ -uint64_t census_ht_for_all(const census_ht *ht, census_ht_itr_cb); - -#endif /* GRPC_INTERNAL_CORE_STATISTICS_HASH_TABLE_H */ diff --git a/third_party/grpc/src/core/statistics/window_stats.c b/third_party/grpc/src/core/statistics/window_stats.c deleted file mode 100644 index 3f2940853aaf85..00000000000000 --- a/third_party/grpc/src/core/statistics/window_stats.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 { - int64_t 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. */ - int64_t bottom; - /* The largest time storable in the current window + 1ns */ - int64_t top; - /* The width of each bucket in ns. */ - int64_t 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. */ - int64_t 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 int64_t max_seconds = (GPR_INT64_MAX - GPR_NS_PER_SEC) / GPR_NS_PER_SEC; - -static int64_t timespec_to_ns(const gpr_timespec ts) { - if (ts.tv_sec > max_seconds) { - return GPR_INT64_MAX - 1; - } - return 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++) { - int64_t 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++) { - int64_t 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, int64_t 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; - int64_t 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; - int64_t 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) { - int64_t 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/third_party/grpc/src/core/statistics/window_stats.h b/third_party/grpc/src/core/statistics/window_stats.h deleted file mode 100644 index f4732e96a02e40..00000000000000 --- a/third_party/grpc/src/core/statistics/window_stats.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/alloc.c b/third_party/grpc/src/core/support/alloc.c deleted file mode 100644 index 0a064b2c188b37..00000000000000 --- a/third_party/grpc/src/core/support/alloc.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/profiling/timers.h" - -static gpr_allocation_functions g_alloc_functions = {malloc, realloc, free}; - -gpr_allocation_functions gpr_get_allocation_functions() { - return g_alloc_functions; -} - -void gpr_set_allocation_functions(gpr_allocation_functions functions) { - GPR_ASSERT(functions.malloc_fn != NULL); - GPR_ASSERT(functions.realloc_fn != NULL); - GPR_ASSERT(functions.free_fn != NULL); - g_alloc_functions = functions; -} - -void *gpr_malloc(size_t size) { - void *p; - GPR_TIMER_BEGIN("gpr_malloc", 0); - p = g_alloc_functions.malloc_fn(size); - if (!p) { - abort(); - } - GPR_TIMER_END("gpr_malloc", 0); - return p; -} - -void gpr_free(void *p) { - GPR_TIMER_BEGIN("gpr_free", 0); - g_alloc_functions.free_fn(p); - GPR_TIMER_END("gpr_free", 0); -} - -void *gpr_realloc(void *p, size_t size) { - GPR_TIMER_BEGIN("gpr_realloc", 0); - p = g_alloc_functions.realloc_fn(p, size); - if (!p) { - abort(); - } - GPR_TIMER_END("gpr_realloc", 0); - 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 **)(((uintptr_t)p + extra) & ~(alignment - 1)); - ret[-1] = p; - return (void *)ret; -} - -void gpr_free_aligned(void *ptr) { free(((void **)ptr)[-1]); } diff --git a/third_party/grpc/src/core/support/avl.c b/third_party/grpc/src/core/support/avl.c deleted file mode 100644 index 9734c9987fee92..00000000000000 --- a/third_party/grpc/src/core/support/avl.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -gpr_avl gpr_avl_create(const gpr_avl_vtable *vtable) { - gpr_avl out; - out.vtable = vtable; - out.root = NULL; - return out; -} - -static gpr_avl_node *ref_node(gpr_avl_node *node) { - if (node) { - gpr_ref(&node->refs); - } - return node; -} - -static void unref_node(const gpr_avl_vtable *vtable, gpr_avl_node *node) { - if (node == NULL) { - return; - } - if (gpr_unref(&node->refs)) { - vtable->destroy_key(node->key); - vtable->destroy_value(node->value); - unref_node(vtable, node->left); - unref_node(vtable, node->right); - gpr_free(node); - } -} - -static long node_height(gpr_avl_node *node) { - return node == NULL ? 0 : node->height; -} - -#ifndef NDEBUG -static long calculate_height(gpr_avl_node *node) { - return node == NULL ? 0 : 1 + GPR_MAX(calculate_height(node->left), - calculate_height(node->right)); -} - -static gpr_avl_node *assert_invariants(gpr_avl_node *n) { - if (n == NULL) return NULL; - assert_invariants(n->left); - assert_invariants(n->right); - assert(calculate_height(n) == n->height); - assert(labs(node_height(n->left) - node_height(n->right)) <= 1); - return n; -} -#else -static gpr_avl_node *assert_invariants(gpr_avl_node *n) { return n; } -#endif - -gpr_avl_node *new_node(void *key, void *value, gpr_avl_node *left, - gpr_avl_node *right) { - gpr_avl_node *node = gpr_malloc(sizeof(*node)); - gpr_ref_init(&node->refs, 1); - node->key = key; - node->value = value; - node->left = assert_invariants(left); - node->right = assert_invariants(right); - node->height = 1 + GPR_MAX(node_height(left), node_height(right)); - return node; -} - -static gpr_avl_node *get(const gpr_avl_vtable *vtable, gpr_avl_node *node, - void *key) { - long cmp; - - if (node == NULL) { - return NULL; - } - - cmp = vtable->compare_keys(node->key, key); - if (cmp == 0) { - return node; - } else if (cmp > 0) { - return get(vtable, node->left, key); - } else { - return get(vtable, node->right, key); - } -} - -void *gpr_avl_get(gpr_avl avl, void *key) { - gpr_avl_node *node = get(avl.vtable, avl.root, key); - return node ? node->value : NULL; -} - -static gpr_avl_node *rotate_left(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right) { - gpr_avl_node *n = - new_node(vtable->copy_key(right->key), vtable->copy_value(right->value), - new_node(key, value, left, ref_node(right->left)), - ref_node(right->right)); - unref_node(vtable, right); - return n; -} - -static gpr_avl_node *rotate_right(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right) { - gpr_avl_node *n = new_node( - vtable->copy_key(left->key), vtable->copy_value(left->value), - ref_node(left->left), new_node(key, value, ref_node(left->right), right)); - unref_node(vtable, left); - return n; -} - -static gpr_avl_node *rotate_left_right(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right) { - /* rotate_right(..., rotate_left(left), right) */ - gpr_avl_node *n = new_node( - vtable->copy_key(left->right->key), - vtable->copy_value(left->right->value), - new_node(vtable->copy_key(left->key), vtable->copy_value(left->value), - ref_node(left->left), ref_node(left->right->left)), - new_node(key, value, ref_node(left->right->right), right)); - unref_node(vtable, left); - return n; -} - -static gpr_avl_node *rotate_right_left(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right) { - /* rotate_left(..., left, rotate_right(right)) */ - gpr_avl_node *n = new_node( - vtable->copy_key(right->left->key), - vtable->copy_value(right->left->value), - new_node(key, value, left, ref_node(right->left->left)), - new_node(vtable->copy_key(right->key), vtable->copy_key(right->value), - ref_node(right->left->right), ref_node(right->right))); - unref_node(vtable, right); - return n; -} - -static gpr_avl_node *rebalance(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right) { - switch (node_height(left) - node_height(right)) { - case 2: - if (node_height(left->left) - node_height(left->right) == -1) { - return assert_invariants( - rotate_left_right(vtable, key, value, left, right)); - } else { - return assert_invariants(rotate_right(vtable, key, value, left, right)); - } - case -2: - if (node_height(right->left) - node_height(right->right) == 1) { - return assert_invariants( - rotate_right_left(vtable, key, value, left, right)); - } else { - return assert_invariants(rotate_left(vtable, key, value, left, right)); - } - default: - return assert_invariants(new_node(key, value, left, right)); - } -} - -static gpr_avl_node *add(const gpr_avl_vtable *vtable, gpr_avl_node *node, - void *key, void *value) { - long cmp; - if (node == NULL) { - return new_node(key, value, NULL, NULL); - } - cmp = vtable->compare_keys(node->key, key); - if (cmp == 0) { - return new_node(key, value, ref_node(node->left), ref_node(node->right)); - } else if (cmp > 0) { - return rebalance( - vtable, vtable->copy_key(node->key), vtable->copy_value(node->value), - add(vtable, node->left, key, value), ref_node(node->right)); - } else { - return rebalance(vtable, vtable->copy_key(node->key), - vtable->copy_value(node->value), ref_node(node->left), - add(vtable, node->right, key, value)); - } -} - -gpr_avl gpr_avl_add(gpr_avl avl, void *key, void *value) { - gpr_avl_node *old_root = avl.root; - avl.root = add(avl.vtable, avl.root, key, value); - assert_invariants(avl.root); - unref_node(avl.vtable, old_root); - return avl; -} - -static gpr_avl_node *in_order_head(gpr_avl_node *node) { - while (node->left != NULL) { - node = node->left; - } - return node; -} - -static gpr_avl_node *in_order_tail(gpr_avl_node *node) { - while (node->right != NULL) { - node = node->right; - } - return node; -} - -static gpr_avl_node *remove(const gpr_avl_vtable *vtable, gpr_avl_node *node, - void *key) { - long cmp; - if (node == NULL) { - return NULL; - } - cmp = vtable->compare_keys(node->key, key); - if (cmp == 0) { - if (node->left == NULL) { - return ref_node(node->right); - } else if (node->right == NULL) { - return ref_node(node->left); - } else if (node->left->height < node->right->height) { - gpr_avl_node *h = in_order_head(node->right); - return rebalance(vtable, vtable->copy_key(h->key), - vtable->copy_value(h->value), ref_node(node->left), - remove(vtable, node->right, h->key)); - } else { - gpr_avl_node *h = in_order_tail(node->left); - return rebalance( - vtable, vtable->copy_key(h->key), vtable->copy_value(h->value), - remove(vtable, node->left, h->key), ref_node(node->right)); - } - } else if (cmp > 0) { - return rebalance(vtable, vtable->copy_key(node->key), - vtable->copy_value(node->value), - remove(vtable, node->left, key), ref_node(node->right)); - } else { - return rebalance(vtable, vtable->copy_key(node->key), - vtable->copy_value(node->value), ref_node(node->left), - remove(vtable, node->right, key)); - } -} - -gpr_avl gpr_avl_remove(gpr_avl avl, void *key) { - gpr_avl_node *old_root = avl.root; - avl.root = remove(avl.vtable, avl.root, key); - assert_invariants(avl.root); - unref_node(avl.vtable, old_root); - return avl; -} - -gpr_avl gpr_avl_ref(gpr_avl avl) { - ref_node(avl.root); - return avl; -} - -void gpr_avl_unref(gpr_avl avl) { unref_node(avl.vtable, avl.root); } diff --git a/third_party/grpc/src/core/support/block_annotate.h b/third_party/grpc/src/core/support/block_annotate.h deleted file mode 100644 index 3cd8eee272d3f3..00000000000000 --- a/third_party/grpc/src/core/support/block_annotate.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_BLOCK_ANNOTATE_H -#define GRPC_INTERNAL_CORE_SUPPORT_BLOCK_ANNOTATE_H - -/* These annotations identify the beginning and end of regions where - the code may block for reasons other than synchronization functions. - These include poll, epoll, and getaddrinfo. */ - -#define GRPC_SCHEDULING_START_BLOCKING_REGION \ - do { \ - } while (0) -#define GRPC_SCHEDULING_END_BLOCKING_REGION \ - do { \ - } while (0) - -#endif /* GRPC_INTERNAL_CORE_SUPPORT_BLOCK_ANNOTATE_H */ diff --git a/third_party/grpc/src/core/support/cmdline.c b/third_party/grpc/src/core/support/cmdline.c deleted file mode 100644 index b517f30b2d3a3a..00000000000000 --- a/third_party/grpc/src/core/support/cmdline.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - - int (*state)(gpr_cmdline *cl, char *arg); - arg *cur_arg; - - int survive_failure; -}; - -static int 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_set_survive_failure(gpr_cmdline *cl) { - cl->survive_failure = 1; -} - -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 int print_usage_and_die(gpr_cmdline *cl) { - char *usage = gpr_cmdline_usage_string(cl, cl->argv0); - fprintf(stderr, "%s", usage); - gpr_free(usage); - if (!cl->survive_failure) { - exit(1); - } - return 0; -} - -static int extra_state(gpr_cmdline *cl, char *str) { - if (!cl->extra_arg) { - return print_usage_and_die(cl); - } - cl->extra_arg(cl->extra_arg_user_data, str); - return 1; -} - -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); - return NULL; - } - - return a; -} - -static int value_state(gpr_cmdline *cl, char *str) { - long intval; - char *end; - - GPR_ASSERT(cl->cur_arg); - - switch (cl->cur_arg->type) { - case ARGTYPE_INT: - intval = strtol(str, &end, 0); - if (*end || intval < INT_MIN || intval > INT_MAX) { - fprintf(stderr, "expected integer, got '%s' for %s\n", str, - cl->cur_arg->name); - return print_usage_and_die(cl); - } - *(int *)cl->cur_arg->value = (int)intval; - break; - case ARGTYPE_BOOL: - if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) { - *(int *)cl->cur_arg->value = 1; - } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) { - *(int *)cl->cur_arg->value = 0; - } else { - fprintf(stderr, "expected boolean, got '%s' for %s\n", str, - cl->cur_arg->name); - return print_usage_and_die(cl); - } - break; - case ARGTYPE_STRING: - *(char **)cl->cur_arg->value = str; - break; - } - - cl->state = normal_state; - return 1; -} - -static int normal_state(gpr_cmdline *cl, char *str) { - char *eq = NULL; - char *tmp = NULL; - char *arg_name = NULL; - int r = 1; - - if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") || - 0 == strcmp(str, "-h")) { - return print_usage_and_die(cl); - } - - cl->cur_arg = NULL; - - if (str[0] == '-') { - if (str[1] == '-') { - if (str[2] == 0) { - /* handle '--' to move to just extra args */ - cl->state = extra_state; - return 1; - } - str += 2; - } else { - str += 1; - } - /* first byte of str is now past the leading '-' or '--' */ - if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') { - /* str is of the form '--no-foo' - it's a flag disable */ - str += 3; - cl->cur_arg = find_arg(cl, str); - if (cl->cur_arg == NULL) { - return print_usage_and_die(cl); - } - if (cl->cur_arg->type != ARGTYPE_BOOL) { - fprintf(stderr, "%s is not a flag argument\n", str); - return print_usage_and_die(cl); - } - *(int *)cl->cur_arg->value = 0; - return 1; /* early out */ - } - eq = strchr(str, '='); - if (eq != NULL) { - /* copy the string into a temp buffer and extract the name */ - tmp = arg_name = gpr_malloc((size_t)(eq - str + 1)); - memcpy(arg_name, str, (size_t)(eq - str)); - arg_name[eq - str] = 0; - } else { - arg_name = str; - } - cl->cur_arg = find_arg(cl, arg_name); - if (cl->cur_arg == NULL) { - return print_usage_and_die(cl); - } - if (eq != NULL) { - /* str was of the type --foo=value, parse the value */ - r = 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 { - r = extra_state(cl, str); - } - - gpr_free(tmp); - return r; -} - -int 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++) { - if (!cl->state(cl, argv[i])) { - return 0; - } - } - return 1; -} diff --git a/third_party/grpc/src/core/support/cpu_iphone.c b/third_party/grpc/src/core/support/cpu_iphone.c deleted file mode 100644 index 82b49b47bc0a0c..00000000000000 --- a/third_party/grpc/src/core/support/cpu_iphone.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/cpu_linux.c b/third_party/grpc/src/core/support/cpu_linux.c deleted file mode 100644 index 7af6a8f0090b4d..00000000000000 --- a/third_party/grpc/src/core/support/cpu_linux.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/cpu_posix.c b/third_party/grpc/src/core/support/cpu_posix.c deleted file mode 100644 index 8f01c284ca425c..00000000000000 --- a/third_party/grpc/src/core/support/cpu_posix.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_CPU_POSIX - -#include -#include -#include - -#include -#include - -static __thread char magic_thread_local; - -static long ncpus = 0; - -static void init_ncpus() { - ncpus = sysconf(_SC_NPROCESSORS_ONLN); - if (ncpus < 1 || ncpus > INT32_MAX) { - 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 (unsigned)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 (unsigned)shard_ptr(&magic_thread_local); -} - -#endif /* GPR_CPU_POSIX */ diff --git a/third_party/grpc/src/core/support/cpu_windows.c b/third_party/grpc/src/core/support/cpu_windows.c deleted file mode 100644 index ce32eb0a9d66a5..00000000000000 --- a/third_party/grpc/src/core/support/cpu_windows.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/env.h b/third_party/grpc/src/core/support/env.h deleted file mode 100644 index 24172d86737ece..00000000000000 --- a/third_party/grpc/src/core/support/env.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/env_linux.c b/third_party/grpc/src/core/support/env_linux.c deleted file mode 100644 index 442cd8298e2403..00000000000000 --- a/third_party/grpc/src/core/support/env_linux.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* 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 -#include - -#include "src/core/support/string.h" - -char *gpr_getenv(const char *name) { - typedef char *(*getenv_type)(const char *); - static getenv_type getenv_func = NULL; - /* Check to see which getenv variant is supported (go from most - * to least secure) */ - const char *names[] = {"secure_getenv", "__secure_getenv", "getenv"}; - for (size_t i = 0; getenv_func == NULL && i < GPR_ARRAY_SIZE(names); i++) { - getenv_func = (getenv_type)dlsym(RTLD_DEFAULT, names[i]); - } - char *result = getenv_func(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/third_party/grpc/src/core/support/env_posix.c b/third_party/grpc/src/core/support/env_posix.c deleted file mode 100644 index 1dd2af56bcb745..00000000000000 --- a/third_party/grpc/src/core/support/env_posix.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/env_win32.c b/third_party/grpc/src/core/support/env_win32.c deleted file mode 100644 index 10258283bafe46..00000000000000 --- a/third_party/grpc/src/core/support/env_win32.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_WIN32 - -#include "src/core/support/env.h" -#include "src/core/support/string.h" - -#ifdef __MINGW32__ -errno_t getenv_s(size_t *size_needed, char *buffer, size_t size, - const char *varname); -#else -#include -#endif - -#include -#include -#include - -char *gpr_getenv(const char *name) { - size_t size; - char *result = NULL; - errno_t err; - - err = getenv_s(&size, NULL, 0, name); - if (err || (size == 0)) return NULL; - result = gpr_malloc(size); - err = getenv_s(&size, result, size, name); - if (err) { - gpr_free(result); - return NULL; - } - return result; -} - -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/third_party/grpc/src/core/support/file.c b/third_party/grpc/src/core/support/file.c deleted file mode 100644 index 8c673dbcc66ab6..00000000000000 --- a/third_party/grpc/src/core/support/file.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/block_annotate.h" -#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; - size_t bytes_read = 0; - - GRPC_SCHEDULING_START_BLOCKING_REGION; - file = fopen(filename, "rb"); - 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); - GRPC_SCHEDULING_END_BLOCKING_REGION; - return result; -} diff --git a/third_party/grpc/src/core/support/file.h b/third_party/grpc/src/core/support/file.h deleted file mode 100644 index d8b7cea44fd799..00000000000000 --- a/third_party/grpc/src/core/support/file.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/file_posix.c b/third_party/grpc/src/core/support/file_posix.c deleted file mode 100644 index c11c07148ab9c1..00000000000000 --- a/third_party/grpc/src/core/support/file_posix.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/file_win32.c b/third_party/grpc/src/core/support/file_win32.c deleted file mode 100644 index 355744f79a537e..00000000000000 --- a/third_party/grpc/src/core/support/file_win32.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/histogram.c b/third_party/grpc/src/core/support/histogram.c deleted file mode 100644 index 20ed2b14b140b6..00000000000000 --- a/third_party/grpc/src/core/support/histogram.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 */ - uint32_t *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(uint32_t) * h->num_buckets); - memset(h->buckets, 0, sizeof(uint32_t) * 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, const 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 uint32_t *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 != 0); - 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 uint32_t *gpr_histogram_get_contents(gpr_histogram *h, size_t *size) { - *size = h->num_buckets; - return h->buckets; -} diff --git a/third_party/grpc/src/core/support/host_port.c b/third_party/grpc/src/core/support/host_port.c deleted file mode 100644 index 23f65b15812bef..00000000000000 --- a/third_party/grpc/src/core/support/host_port.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/log.c b/third_party/grpc/src/core/support/log.c deleted file mode 100644 index 04156a5b1fc092..00000000000000 --- a/third_party/grpc/src/core/support/log.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -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"; - } - GPR_UNREACHABLE_CODE(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/third_party/grpc/src/core/support/log_android.c b/third_party/grpc/src/core/support/log_android.c deleted file mode 100644 index 5d0c7d820d82d2..00000000000000 --- a/third_party/grpc/src/core/support/log_android.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/log_linux.c b/third_party/grpc/src/core/support/log_linux.c deleted file mode 100644 index 93a0c1b4c6ed55..00000000000000 --- a/third_party/grpc/src/core/support/log_linux.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -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]; - time_t timer; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - struct tm tm; - - timer = (time_t)now.tv_sec; - final_slash = strrchr(args->file, '/'); - if (final_slash == NULL) - display_file = args->file; - else - display_file = final_slash + 1; - - if (!localtime_r(&timer, &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/third_party/grpc/src/core/support/log_posix.c b/third_party/grpc/src/core/support/log_posix.c deleted file mode 100644 index 3ff171f99cb8b7..00000000000000 --- a/third_party/grpc/src/core/support/log_posix.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 intptr_t gettid(void) { return (intptr_t)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((size_t)ret + 1); - va_start(args, format); - vsnprintf(message, (size_t)(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]; - time_t timer; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - struct tm tm; - - timer = (time_t)now.tv_sec; - final_slash = strrchr(args->file, '/'); - if (final_slash == NULL) - display_file = args->file; - else - display_file = final_slash + 1; - - if (!localtime_r(&timer, &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/third_party/grpc/src/core/support/log_win32.c b/third_party/grpc/src/core/support/log_win32.c deleted file mode 100644 index e18e667fe5f567..00000000000000 --- a/third_party/grpc/src/core/support/log_win32.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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]; - time_t timer; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - struct tm tm; - - timer = (time_t)now.tv_sec; - final_slash = strrchr(args->file, '\\'); - if (final_slash == NULL) - display_file = args->file; - else - display_file = final_slash + 1; - - if (localtime_s(&tm, &timer)) { - 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); - fflush(stderr); -} - -char *gpr_format_message(int messageid) { - LPTSTR tmessage; - char *message; - DWORD status = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, (DWORD)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/third_party/grpc/src/core/support/murmur_hash.c b/third_party/grpc/src/core/support/murmur_hash.c deleted file mode 100644 index a5261c0cc04360..00000000000000 --- a/third_party/grpc/src/core/support/murmur_hash.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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)] - -uint32_t gpr_murmur_hash3(const void *key, size_t len, uint32_t seed) { - const uint8_t *data = (const uint8_t *)key; - const size_t nblocks = len / 4; - int i; - - uint32_t h1 = seed; - uint32_t k1; - - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; - - const uint32_t *blocks = ((const uint32_t *)key) + nblocks; - const uint8_t *tail = (const uint8_t *)(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 ^= ((uint32_t)tail[2]) << 16; - case 2: - k1 ^= ((uint32_t)tail[1]) << 8; - case 1: - k1 ^= tail[0]; - k1 *= c1; - k1 = ROTL32(k1, 15); - k1 *= c2; - h1 ^= k1; - }; - - /* finalization */ - h1 ^= (uint32_t)len; - FMIX32(h1); - return h1; -} diff --git a/third_party/grpc/src/core/support/murmur_hash.h b/third_party/grpc/src/core/support/murmur_hash.h deleted file mode 100644 index 0bf04f731ad855..00000000000000 --- a/third_party/grpc/src/core/support/murmur_hash.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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) */ -uint32_t gpr_murmur_hash3(const void *key, size_t len, uint32_t seed); - -#endif /* GRPC_INTERNAL_CORE_SUPPORT_MURMUR_HASH_H */ diff --git a/third_party/grpc/src/core/support/slice.c b/third_party/grpc/src/core/support/slice.c deleted file mode 100644 index b9a7c77bda6e64..00000000000000 --- a/third_party/grpc/src/core/support/slice.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_from_static_string support structure - a refcount that does - nothing */ -static void noop_ref_or_unref(void *unused) {} - -static gpr_slice_refcount noop_refcount = {noop_ref_or_unref, - noop_ref_or_unref}; - -gpr_slice gpr_slice_from_static_string(const char *s) { - gpr_slice slice; - slice.refcount = &noop_refcount; - slice.data.refcounted.bytes = (uint8_t *)s; - slice.data.refcounted.length = strlen(s); - return slice; -} - -/* 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 = (uint8_t *)(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 = (uint8_t)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 = (uint8_t)(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 = (uint8_t)(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 = (uint8_t)(source->data.inlined.length - split); - memcpy(tail.data.inlined.bytes, source->data.inlined.bytes + split, - tail.data.inlined.length); - source->data.inlined.length = (uint8_t)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 = (uint8_t)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 = (uint8_t)split; - memcpy(head.data.inlined.bytes, source->data.inlined.bytes, split); - source->data.inlined.length = - (uint8_t)(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 = (uint8_t)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); -} diff --git a/third_party/grpc/src/core/support/slice_buffer.c b/third_party/grpc/src/core/support/slice_buffer.c deleted file mode 100644 index 66f111d767c719..00000000000000 --- a/third_party/grpc/src/core/support/slice_buffer.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -/* 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); - } -} - -uint8_t *gpr_slice_buffer_tiny_add(gpr_slice_buffer *sb, size_t n) { - gpr_slice *back; - uint8_t *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 = (uint8_t)(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 = (uint8_t)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 = - (uint8_t)(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 = (uint8_t)(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_move_first(gpr_slice_buffer *src, size_t n, - gpr_slice_buffer *dst) { - size_t src_idx; - size_t output_len = dst->length + n; - size_t new_input_len = src->length - n; - GPR_ASSERT(src->length >= n); - if (src->length == n) { - gpr_slice_buffer_move_into(src, dst); - return; - } - src_idx = 0; - while (src_idx < src->capacity) { - gpr_slice slice = src->slices[src_idx]; - size_t slice_len = GPR_SLICE_LENGTH(slice); - if (n > slice_len) { - gpr_slice_buffer_add(dst, slice); - n -= slice_len; - src_idx++; - } else if (n == slice_len) { - gpr_slice_buffer_add(dst, slice); - src_idx++; - break; - } else { /* n < slice_len */ - src->slices[src_idx] = gpr_slice_split_tail(&slice, n); - GPR_ASSERT(GPR_SLICE_LENGTH(slice) == n); - GPR_ASSERT(GPR_SLICE_LENGTH(src->slices[src_idx]) == slice_len - n); - gpr_slice_buffer_add(dst, slice); - break; - } - } - GPR_ASSERT(dst->length == output_len); - memmove(src->slices, src->slices + src_idx, - sizeof(gpr_slice) * (src->count - src_idx)); - src->count -= src_idx; - src->length = new_input_len; - GPR_ASSERT(src->count > 0); -} - -void gpr_slice_buffer_trim_end(gpr_slice_buffer *sb, size_t n, - gpr_slice_buffer *garbage) { - 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_split_head(&slice, slice_len - n); - gpr_slice_buffer_add_indexed(garbage, slice); - return; - } else if (slice_len == n) { - gpr_slice_buffer_add_indexed(garbage, slice); - sb->count = idx; - return; - } else { - gpr_slice_buffer_add_indexed(garbage, slice); - n -= slice_len; - sb->count = idx; - } - } -} - -gpr_slice gpr_slice_buffer_take_first(gpr_slice_buffer *sb) { - gpr_slice slice; - GPR_ASSERT(sb->count > 0); - slice = sb->slices[0]; - memmove(&sb->slices[0], &sb->slices[1], (sb->count - 1) * sizeof(gpr_slice)); - sb->count--; - sb->length -= GPR_SLICE_LENGTH(slice); - return slice; -} diff --git a/third_party/grpc/src/core/support/stack_lockfree.c b/third_party/grpc/src/core/support/stack_lockfree.c deleted file mode 100644 index 9daecd2e18a772..00000000000000 --- a/third_party/grpc/src/core/support/stack_lockfree.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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 */ - uint16_t index; -#ifdef GPR_ARCH_64 - uint16_t pad; - uint32_t aba_ctr; -#else -#ifdef GPR_ARCH_32 - uint16_t 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(size_t 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; -/* Fill in the pad and aba_ctr to avoid confusing memcheck tools */ -#ifdef GPR_ARCH_64 - stack->head.contents.pad = 0; -#endif - stack->head.contents.aba_ctr = 0; - 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 = (uint16_t)entry; -#ifdef GPR_ARCH_64 - /* Fill in the pad to avoid confusing memcheck tools */ - newhead.contents.pad = 0; -#endif - - /* 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 / (int)(8 * sizeof(gpr_atm)); - int pushed_bit = entry % (int)(8 * sizeof(gpr_atm)); - gpr_atm old_val; - - old_val = gpr_atm_no_barrier_fetch_add(&stack->pushed[pushed_index], - ((gpr_atm)1 << pushed_bit)); - GPR_ASSERT((old_val & (((gpr_atm)1) << 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)1 << pushed_bit)); - GPR_ASSERT((old_val & (((gpr_atm)1) << pushed_bit)) != 0); - } -#endif - - return head.contents.index; -} diff --git a/third_party/grpc/src/core/support/stack_lockfree.h b/third_party/grpc/src/core/support/stack_lockfree.h deleted file mode 100644 index ca58dd007a2ef7..00000000000000 --- a/third_party/grpc/src/core/support/stack_lockfree.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -#include - -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(size_t 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/third_party/grpc/src/core/support/string.c b/third_party/grpc/src/core/support/string.c deleted file mode 100644 index 1f541de40f1078..00000000000000 --- a/third_party/grpc/src/core/support/string.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 uint8_t *const beg = (const uint8_t *)buf; - const uint8_t *const end = beg + len; - const uint8_t *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 uint8_t *const beg = (const uint8_t *)buf; - const uint8_t *const end = beg + len; - const uint8_t *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, (char)(isprint(*cur) ? *(char *)cur : '.')); - } - if (!out_was_empty) { - dump_out_append(out, '\''); - } -} - -char *gpr_dump(const char *buf, size_t len, uint32_t 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, uint32_t 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, uint32_t *result) { - uint32_t out = 0; - uint32_t 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 + (uint32_t)(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) { - long sign; - int i = 0; - - if (value == 0) { - string[0] = '0'; - string[1] = 0; - return 1; - } - - sign = value < 0 ? -1 : 1; - while (value) { - string[i++] = (char)('0' + sign * (value % 10)); - value /= 10; - } - if (sign < 0) string[i++] = '-'; - gpr_reverse_bytes(string, i); - string[i] = 0; - return i; -} - -int int64_ttoa(int64_t value, char *string) { - int64_t sign; - int i = 0; - - if (value == 0) { - string[0] = '0'; - string[1] = 0; - return 1; - } - - sign = value < 0 ? -1 : 1; - while (value) { - string[i++] = (char)('0' + sign * (value % 10)); - value /= 10; - } - if (sign < 0) 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 uint8_t *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/third_party/grpc/src/core/support/string.h b/third_party/grpc/src/core/support/string.h deleted file mode 100644 index e6755de106cbc4..00000000000000 --- a/third_party/grpc/src/core/support/string.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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, uint32_t flags); - -/* Calls gpr_dump on a slice. */ -char *gpr_dump_slice(gpr_slice slice, uint32_t 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, - uint32_t *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); - -/* Minimum buffer size for calling int64toa */ -#define GPR_INT64TOA_MIN_BUFSIZE (3 * sizeof(int64_t)) - -/* Convert an int64 to a string in base 10; returns the length of the -output string (or 0 on failure). -output must be at least GPR_INT64TOA_MIN_BUFSIZE bytes long. -NOTE: This function ensures sufficient bit width even on Win x64, -where long is 32bit is size.*/ -int int64_ttoa(int64_t 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/third_party/grpc/src/core/support/string_posix.c b/third_party/grpc/src/core/support/string_posix.c deleted file mode 100644 index 25c333db4edf0d..00000000000000 --- a/third_party/grpc/src/core/support/string_posix.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/string_win32.c b/third_party/grpc/src/core/support/string_win32.c deleted file mode 100644 index aedc98792a0ca2..00000000000000 --- a/third_party/grpc/src/core/support/string_win32.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* Posix code for gpr snprintf support. */ - -#include -#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((unsigned)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((unsigned)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/third_party/grpc/src/core/support/string_win32.h b/third_party/grpc/src/core/support/string_win32.h deleted file mode 100644 index e3043656fbaa51..00000000000000 --- a/third_party/grpc/src/core/support/string_win32.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/subprocess_posix.c b/third_party/grpc/src/core/support/subprocess_posix.c deleted file mode 100644 index 171054e4da653d..00000000000000 --- a/third_party/grpc/src/core/support/subprocess_posix.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/subprocess_windows.c b/third_party/grpc/src/core/support/subprocess_windows.c deleted file mode 100644 index 2b25ef063a92a2..00000000000000 --- a/third_party/grpc/src/core/support/subprocess_windows.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * - * Copyright 2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_WINDOWS_SUBPROCESS - -#include -#include -#include - -#include -#include -#include -#include "src/core/support/string.h" -#include "src/core/support/string_win32.h" - -struct gpr_subprocess { - PROCESS_INFORMATION pi; - int joined; - int interrupted; -}; - -const char *gpr_subprocess_binary_extension() { return ".exe"; } - -gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { - gpr_subprocess *r; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - char *args = gpr_strjoin_sep(argv, (size_t)argc, " ", NULL); - TCHAR *args_tchar; - - args_tchar = gpr_char_to_tchar(args); - gpr_free(args); - - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - memset(&pi, 0, sizeof(pi)); - - if (!CreateProcess(NULL, args_tchar, NULL, NULL, FALSE, - CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) { - gpr_free(args_tchar); - return NULL; - } - gpr_free(args_tchar); - - r = gpr_malloc(sizeof(gpr_subprocess)); - memset(r, 0, sizeof(*r)); - r->pi = pi; - return r; -} - -void gpr_subprocess_destroy(gpr_subprocess *p) { - if (p) { - if (!p->joined) { - gpr_subprocess_interrupt(p); - gpr_subprocess_join(p); - } - if (p->pi.hProcess) { - CloseHandle(p->pi.hProcess); - } - if (p->pi.hThread) { - CloseHandle(p->pi.hThread); - } - gpr_free(p); - } -} - -int gpr_subprocess_join(gpr_subprocess *p) { - DWORD dwExitCode; - if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { - if (dwExitCode == STILL_ACTIVE) { - if (WaitForSingleObject(p->pi.hProcess, INFINITE) == WAIT_OBJECT_0) { - p->joined = 1; - goto getExitCode; - } - return -1; // failed to join - } else { - goto getExitCode; - } - } else { - return -1; // failed to get exit code - } - -getExitCode: - if (p->interrupted) { - return 0; - } - if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { - return (int)dwExitCode; - } else { - return -1; // failed to get exit code - } -} - -void gpr_subprocess_interrupt(gpr_subprocess *p) { - DWORD dwExitCode; - if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { - if (dwExitCode == STILL_ACTIVE) { - gpr_log(GPR_INFO, "sending ctrl-break"); - GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, p->pi.dwProcessId); - p->joined = 1; - p->interrupted = 1; - } - } - return; -} - -#endif /* GPR_WINDOWS_SUBPROCESS */ diff --git a/third_party/grpc/src/core/support/sync.c b/third_party/grpc/src/core/support/sync.c deleted file mode 100644 index d368422d9eaf69..00000000000000 --- a/third_party/grpc/src/core/support/sync.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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[((uintptr_t)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, intptr_t n) { - gpr_atm_rel_store(&c->value, n); -} - -void gpr_stats_inc(gpr_stats_counter *c, intptr_t inc) { - gpr_atm_no_barrier_fetch_add(&c->value, inc); -} - -intptr_t 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/third_party/grpc/src/core/support/sync_posix.c b/third_party/grpc/src/core/support/sync_posix.c deleted file mode 100644 index d3c483f1b538c9..00000000000000 --- a/third_party/grpc/src/core/support/sync_posix.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#ifdef GPR_POSIX_SYNC - -#include -#include -#include -#include -#include -#include "src/core/profiling/timers.h" - -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_TIMER_BEGIN("gpr_mu_lock", 0); - GPR_ASSERT(pthread_mutex_lock(mu) == 0); - GPR_TIMER_END("gpr_mu_lock", 0); -} - -void gpr_mu_unlock(gpr_mu* mu) { - GPR_TIMER_BEGIN("gpr_mu_unlock", 0); - GPR_ASSERT(pthread_mutex_unlock(mu) == 0); - GPR_TIMER_END("gpr_mu_unlock", 0); -} - -int gpr_mu_trylock(gpr_mu* mu) { - int err; - GPR_TIMER_BEGIN("gpr_mu_trylock", 0); - err = pthread_mutex_trylock(mu); - GPR_ASSERT(err == 0 || err == EBUSY); - GPR_TIMER_END("gpr_mu_trylock", 0); - 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 = (time_t)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/third_party/grpc/src/core/support/sync_win32.c b/third_party/grpc/src/core/support/sync_win32.c deleted file mode 100644 index 41998ebcb6b2c5..00000000000000 --- a/third_party/grpc/src/core/support/sync_win32.c +++ /dev/null @@ -1,133 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* 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 { - abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); - gpr_timespec now = gpr_now(abs_deadline.clock_type); - int64_t now_ms = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000; - int64_t deadline_ms = - (int64_t)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; - if (now_ms >= deadline_ms) { - timeout = 1; - } else { - if ((deadline_ms - now_ms) >= INFINITE) { - timeout_max_ms = INFINITE - 1; - } else { - timeout_max_ms = (DWORD)(deadline_ms - now_ms); - } - 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/third_party/grpc/src/core/support/thd.c b/third_party/grpc/src/core/support/thd.c deleted file mode 100644 index 41daeb5d0e5e3c..00000000000000 --- a/third_party/grpc/src/core/support/thd.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/thd_internal.h b/third_party/grpc/src/core/support/thd_internal.h deleted file mode 100644 index 1508c4691f249c..00000000000000 --- a/third_party/grpc/src/core/support/thd_internal.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/thd_posix.c b/third_party/grpc/src/core/support/thd_posix.c deleted file mode 100644 index 653a1c88c13b81..00000000000000 --- a/third_party/grpc/src/core/support/thd_posix.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - 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; - /* don't use gpr_malloc as we may cause an infinite recursion with - * the profiling code */ - struct thd_arg *a = malloc(sizeof(*a)); - GPR_ASSERT(a != NULL); - 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) { - 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/third_party/grpc/src/core/support/thd_win32.c b/third_party/grpc/src/core/support/thd_win32.c deleted file mode 100644 index a9db180c1b0574..00000000000000 --- a/third_party/grpc/src/core/support/thd_win32.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/support/time_precise.c b/third_party/grpc/src/core/support/time_precise.c deleted file mode 100644 index a2cf74bc847344..00000000000000 --- a/third_party/grpc/src/core/support/time_precise.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -#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 = (long long)(high << 32) | (long long)low; -} -#endif - -static double cycles_per_second = 0; -static long long int start_cycle; -void gpr_precise_clock_init(void) { - time_t start; - long long end_cycle; - gpr_log(GPR_DEBUG, "Calibrating timers"); - start = time(NULL); - while (time(NULL) == start) - ; - gpr_get_cycle_counter(&start_cycle); - while (time(NULL) <= start + 10) - ; - gpr_get_cycle_counter(&end_cycle); - cycles_per_second = (double)(end_cycle - start_cycle) / 10.0; - gpr_log(GPR_DEBUG, "... cycles_per_second = %f\n", cycles_per_second); -} - -void gpr_precise_clock_now(gpr_timespec *clk) { - long long int counter; - double secs; - gpr_get_cycle_counter(&counter); - secs = (double)(counter - start_cycle) / cycles_per_second; - clk->clock_type = GPR_CLOCK_PRECISE; - clk->tv_sec = (int64_t)secs; - clk->tv_nsec = (int32_t)(1e9 * (secs - (double)clk->tv_sec)); -} - -#else /* GRPC_TIMERS_RDTSC */ -void gpr_precise_clock_init(void) {} - -void gpr_precise_clock_now(gpr_timespec *clk) { - *clk = gpr_now(GPR_CLOCK_REALTIME); - clk->clock_type = GPR_CLOCK_PRECISE; -} -#endif /* GRPC_TIMERS_RDTSC */ diff --git a/third_party/grpc/src/core/support/time_precise.h b/third_party/grpc/src/core/support/time_precise.h deleted file mode 100644 index 80c5000123d6bf..00000000000000 --- a/third_party/grpc/src/core/support/time_precise.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -void gpr_precise_clock_init(void); -void gpr_precise_clock_now(gpr_timespec *clk); - -#endif /* GRPC_CORE_SUPPORT_TIME_PRECISE_ */ diff --git a/third_party/grpc/src/core/support/time_win32.c b/third_party/grpc/src/core/support/time_win32.c deleted file mode 100644 index 8af957e6f4f4cf..00000000000000 --- a/third_party/grpc/src/core/support/time_win32.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* Win32 code for gpr time support. */ - -#include - -#ifdef GPR_WIN32 - -#include -#include -#include -#include -#include -#include - -#include "src/core/support/block_annotate.h" - -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 / (double)frequency.QuadPart; -} - -gpr_timespec gpr_now(gpr_clock_type clock) { - gpr_timespec now_tv; - LONGLONG diff; - 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 = (int64_t)now_tb.time; - now_tv.tv_nsec = now_tb.millitm * 1000000; - break; - case GPR_CLOCK_MONOTONIC: - case GPR_CLOCK_PRECISE: - QueryPerformanceCounter(×tamp); - diff = timestamp.QuadPart - g_start_time.QuadPart; - now_dbl = (double)diff * g_time_scale; - now_tv.tv_sec = (int64_t)now_dbl; - now_tv.tv_nsec = (int32_t)((now_dbl - (double)now_tv.tv_sec) * 1e9); - break; - case GPR_TIMESPAN: - abort(); - break; - } - return now_tv; -} - -void gpr_sleep_until(gpr_timespec until) { - gpr_timespec now; - gpr_timespec delta; - int64_t 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 = - delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; - GPR_ASSERT((sleep_millis >= 0) && (sleep_millis <= INT_MAX)); - GRPC_SCHEDULING_START_BLOCKING_REGION; - Sleep((DWORD)sleep_millis); - GRPC_SCHEDULING_END_BLOCKING_REGION; - } -} - -#endif /* GPR_WIN32 */ diff --git a/third_party/grpc/src/core/support/tls_pthread.c b/third_party/grpc/src/core/support/tls_pthread.c deleted file mode 100644 index 9683a6e547d564..00000000000000 --- a/third_party/grpc/src/core/support/tls_pthread.c +++ /dev/null @@ -1,45 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -intptr_t gpr_tls_set(struct gpr_pthread_thread_local *tls, intptr_t value) { - GPR_ASSERT(0 == pthread_setspecific(tls->key, (void *)value)); - return value; -} - -#endif /* GPR_PTHREAD_TLS */ diff --git a/third_party/grpc/src/core/support/wrap_memcpy.c b/third_party/grpc/src/core/support/wrap_memcpy.c deleted file mode 100644 index 15c289f7b8bb41..00000000000000 --- a/third_party/grpc/src/core/support/wrap_memcpy.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -/* Provide a wrapped memcpy for targets that need to be backwards - * compatible with older libc's. - * - * Enable by setting LDFLAGS=-Wl,-wrap,memcpy when linking. - */ - -#ifdef __linux__ -#ifdef __x86_64__ -__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); -void *__wrap_memcpy(void *destination, const void *source, size_t num) { - return memcpy(destination, source, num); -} -#else /* !__x86_64__ */ -void *__wrap_memcpy(void *destination, const void *source, size_t num) { - return memmove(destination, source, num); -} -#endif -#endif diff --git a/third_party/grpc/src/core/surface/alarm.c b/third_party/grpc/src/core/surface/alarm.c deleted file mode 100644 index d753023ca9c8a7..00000000000000 --- a/third_party/grpc/src/core/surface/alarm.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/iomgr/timer.h" -#include "src/core/surface/completion_queue.h" -#include -#include - -struct grpc_alarm { - grpc_timer alarm; - grpc_cq_completion completion; - /** completion queue where events about this alarm will be posted */ - grpc_completion_queue *cq; - /** user supplied tag */ - void *tag; -}; - -static void do_nothing_end_completion(grpc_exec_ctx *exec_ctx, void *arg, - grpc_cq_completion *c) {} - -static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - grpc_alarm *alarm = arg; - grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, success, - do_nothing_end_completion, NULL, &alarm->completion); -} - -grpc_alarm *grpc_alarm_create(grpc_completion_queue *cq, gpr_timespec deadline, - void *tag) { - grpc_alarm *alarm = gpr_malloc(sizeof(grpc_alarm)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_CQ_INTERNAL_REF(cq, "alarm"); - alarm->cq = cq; - alarm->tag = tag; - - grpc_timer_init(&exec_ctx, &alarm->alarm, deadline, alarm_cb, alarm, - gpr_now(GPR_CLOCK_MONOTONIC)); - grpc_cq_begin_op(cq, tag); - grpc_exec_ctx_finish(&exec_ctx); - return alarm; -} - -void grpc_alarm_cancel(grpc_alarm *alarm) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_timer_cancel(&exec_ctx, &alarm->alarm); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_alarm_destroy(grpc_alarm *alarm) { - grpc_alarm_cancel(alarm); - GRPC_CQ_INTERNAL_UNREF(alarm->cq, "alarm"); - gpr_free(alarm); -} diff --git a/third_party/grpc/src/core/surface/api_trace.c b/third_party/grpc/src/core/surface/api_trace.c deleted file mode 100644 index 9f0b900d4671dd..00000000000000 --- a/third_party/grpc/src/core/surface/api_trace.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/api_trace.h" - -int grpc_api_trace = 0; diff --git a/third_party/grpc/src/core/surface/api_trace.h b/third_party/grpc/src/core/surface/api_trace.h deleted file mode 100644 index 82bbf3b62b3a22..00000000000000 --- a/third_party/grpc/src/core/surface/api_trace.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_API_TRACE_H -#define GRPC_INTERNAL_CORE_SURFACE_API_TRACE_H - -#include "src/core/debug/trace.h" -#include - -extern int grpc_api_trace; - -/* Provide unwrapping macros because we're in C89 and variadic macros weren't - introduced until C99... */ -#define GRPC_API_TRACE_UNWRAP0() -#define GRPC_API_TRACE_UNWRAP1(a) , a -#define GRPC_API_TRACE_UNWRAP2(a, b) , a, b -#define GRPC_API_TRACE_UNWRAP3(a, b, c) , a, b, c -#define GRPC_API_TRACE_UNWRAP4(a, b, c, d) , a, b, c, d -#define GRPC_API_TRACE_UNWRAP5(a, b, c, d, e) , a, b, c, d, e -#define GRPC_API_TRACE_UNWRAP6(a, b, c, d, e, f) , a, b, c, d, e, f -#define GRPC_API_TRACE_UNWRAP7(a, b, c, d, e, f, g) , a, b, c, d, e, f, g -#define GRPC_API_TRACE_UNWRAP8(a, b, c, d, e, f, g, h) , a, b, c, d, e, f, g, h -#define GRPC_API_TRACE_UNWRAP9(a, b, c, d, e, f, g, h, i) \ - , a, b, c, d, e, f, g, h, i -#define GRPC_API_TRACE_UNWRAP10(a, b, c, d, e, f, g, h, i, j) \ - , a, b, c, d, e, f, g, h, i, j - -/* Due to the limitations of C89's preprocessor, the arity of the var-arg list - 'nargs' must be specified. */ -#define GRPC_API_TRACE(fmt, nargs, args) \ - if (grpc_api_trace) { \ - gpr_log(GPR_INFO, fmt GRPC_API_TRACE_UNWRAP##nargs args); \ - } - -#endif /* GRPC_INTERNAL_CORE_SURFACE_API_TRACE_H */ diff --git a/third_party/grpc/src/core/surface/byte_buffer.c b/third_party/grpc/src/core/surface/byte_buffer.c deleted file mode 100644 index fb39c4531d9acc..00000000000000 --- a/third_party/grpc/src/core/surface/byte_buffer.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_UNREACHABLE_CODE(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_UNREACHABLE_CODE(return 0); -} diff --git a/third_party/grpc/src/core/surface/byte_buffer_reader.c b/third_party/grpc/src/core/surface/byte_buffer_reader.c deleted file mode 100644 index 4679854227377b..00000000000000 --- a/third_party/grpc/src/core/surface/byte_buffer_reader.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "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; -} - -gpr_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader) { - gpr_slice in_slice; - size_t bytes_read = 0; - const size_t input_size = grpc_byte_buffer_length(reader->buffer_out); - gpr_slice out_slice = gpr_slice_malloc(input_size); - uint8_t *const outbuf = GPR_SLICE_START_PTR(out_slice); /* just an alias */ - - while (grpc_byte_buffer_reader_next(reader, &in_slice) != 0) { - const size_t slice_length = GPR_SLICE_LENGTH(in_slice); - memcpy(&(outbuf[bytes_read]), GPR_SLICE_START_PTR(in_slice), slice_length); - bytes_read += slice_length; - gpr_slice_unref(in_slice); - GPR_ASSERT(bytes_read <= input_size); - } - return out_slice; -} diff --git a/third_party/grpc/src/core/surface/call.c b/third_party/grpc/src/core/surface/call.c deleted file mode 100644 index 9495e748b5f9df..00000000000000 --- a/third_party/grpc/src/core/surface/call.c +++ /dev/null @@ -1,1433 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/channel/channel_stack.h" -#include "src/core/compression/algorithm_metadata.h" -#include "src/core/iomgr/timer.h" -#include "src/core/profiling/timers.h" -#include "src/core/support/string.h" -#include "src/core/surface/api_trace.h" -#include "src/core/surface/call.h" -#include "src/core/surface/channel.h" -#include "src/core/surface/completion_queue.h" -#include "src/core/transport/static_metadata.h" - -/** The maximum number of concurrent batches 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_BATCHES 6 - -typedef struct { - grpc_ioreq_completion_func on_complete; - void *user_data; - int success; -} completed_request; - -#define MAX_SEND_EXTRA_METADATA_COUNT 3 - -/* 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 { - uint8_t 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; - -typedef struct batch_control { - grpc_call *call; - grpc_cq_completion cq_completion; - grpc_closure finish_batch; - void *notify_tag; - gpr_refcount steps_to_complete; - - uint8_t send_initial_metadata; - uint8_t send_message; - uint8_t send_final_op; - uint8_t recv_initial_metadata; - uint8_t recv_message; - uint8_t recv_final_op; - uint8_t is_notify_tag_closure; - uint8_t success; -} batch_control; - -struct grpc_call { - grpc_completion_queue *cq; - grpc_channel *channel; - grpc_call *parent; - grpc_call *first_child; - /* TODO(ctiller): share with cq if possible? */ - gpr_mu mu; - - /* client or server call */ - uint8_t is_client; - /* is the alarm set */ - uint8_t have_alarm; - /** has grpc_call_destroy been called */ - uint8_t destroy_called; - /** flag indicating that cancellation is inherited */ - uint8_t cancellation_is_inherited; - /** bitmask of live batches */ - uint8_t used_batches; - /** which ops are in-flight */ - uint8_t sent_initial_metadata; - uint8_t sending_message; - uint8_t sent_final_op; - uint8_t received_initial_metadata; - uint8_t receiving_message; - uint8_t received_final_op; - - batch_control active_batches[MAX_CONCURRENT_BATCHES]; - - /* first idx: is_receiving, second idx: is_trailing */ - grpc_metadata_batch metadata_batch[2][2]; - - /* 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]; - - /* 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 */ - uint32_t 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_timer alarm; - - /* for the client, extra metadata is initial metadata; for the - server, it's trailing metadata */ - grpc_linked_mdelem send_extra_metadata[MAX_SEND_EXTRA_METADATA_COUNT]; - int send_extra_metadata_count; - gpr_timespec send_deadline; - - /** 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; - - grpc_slice_buffer_stream sending_stream; - grpc_byte_stream *receiving_stream; - grpc_byte_buffer **receiving_buffer; - gpr_slice receiving_slice; - grpc_closure receiving_slice_ready; - grpc_closure receiving_stream_ready; - uint32_t test_only_last_message_flags; - - union { - struct { - grpc_status_code *status; - char **status_details; - size_t *status_details_capacity; - } client; - struct { - int *cancelled; - } server; - } final_op; -}; - -#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_exec_ctx *exec_ctx, grpc_call *call, - gpr_timespec deadline); -static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call, - grpc_transport_stream_op *op); -static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c, - grpc_status_code status, - const char *description); -static void destroy_call(grpc_exec_ctx *exec_ctx, void *call_stack, - bool success); -static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp, - bool success); - -grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, - uint32_t 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, j; - grpc_channel_stack *channel_stack = grpc_channel_get_channel_stack(channel); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_call *call; - GPR_TIMER_BEGIN("grpc_call_create", 0); - call = gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); - memset(call, 0, sizeof(grpc_call)); - gpr_mu_init(&call->mu); - call->channel = channel; - call->cq = cq; - call->parent = parent_call; - call->is_client = server_transport_data == NULL; - if (call->is_client) { - GPR_ASSERT(add_initial_metadata_count < MAX_SEND_EXTRA_METADATA_COUNT); - for (i = 0; i < add_initial_metadata_count; i++) { - call->send_extra_metadata[i].md = add_initial_metadata[i]; - } - call->send_extra_metadata_count = (int)add_initial_metadata_count; - } else { - GPR_ASSERT(add_initial_metadata_count == 0); - call->send_extra_metadata_count = 0; - } - for (i = 0; i < 2; i++) { - for (j = 0; j < 2; j++) { - call->metadata_batch[i][j].deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - } - } - call->send_deadline = send_deadline; - GRPC_CHANNEL_INTERNAL_REF(channel, "call"); - /* initial refcount dropped by grpc_call_destroy */ - grpc_call_stack_init(&exec_ctx, channel_stack, 1, destroy_call, call, - call->context, server_transport_data, - CALL_STACK_FROM_CALL(call)); - if (cq != NULL) { - GRPC_CQ_INTERNAL_REF(cq, "bind"); - grpc_call_stack_set_pollset(&exec_ctx, CALL_STACK_FROM_CALL(call), - grpc_cq_pollset(cq)); - } - 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(&exec_ctx, call, send_deadline); - } - grpc_exec_ctx_finish(&exec_ctx); - GPR_TIMER_END("grpc_call_create", 0); - return call; -} - -void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call, - grpc_completion_queue *cq) { - GPR_ASSERT(cq); - call->cq = cq; - GRPC_CQ_INTERNAL_REF(cq, "bind"); - grpc_call_stack_set_pollset(exec_ctx, CALL_STACK_FROM_CALL(call), - grpc_cq_pollset(cq)); -} - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -#define REF_REASON reason -#define REF_ARG , const char *reason -#else -#define REF_REASON "" -#define REF_ARG -#endif -void grpc_call_internal_ref(grpc_call *c REF_ARG) { - GRPC_CALL_STACK_REF(CALL_STACK_FROM_CALL(c), REF_REASON); -} -void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c REF_ARG) { - GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON); -} - -static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, bool success) { - size_t i; - int ii; - grpc_call *c = call; - GPR_TIMER_BEGIN("destroy_call", 0); - for (i = 0; i < 2; i++) { - grpc_metadata_batch_destroy( - &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]); - } - if (c->receiving_stream != NULL) { - grpc_byte_stream_destroy(exec_ctx, c->receiving_stream); - } - grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c)); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, c->channel, "call"); - gpr_mu_destroy(&c->mu); - for (i = 0; i < STATUS_SOURCE_COUNT; i++) { - if (c->status[i].details) { - GRPC_MDSTR_UNREF(c->status[i].details); - } - } - for (ii = 0; ii < c->send_extra_metadata_count; ii++) { - GRPC_MDELEM_UNREF(c->send_extra_metadata[ii].md); - } - for (i = 0; i < GRPC_CONTEXT_COUNT; i++) { - if (c->context[i].destroy) { - c->context[i].destroy(c->context[i].value); - } - } - if (c->cq) { - GRPC_CQ_INTERNAL_UNREF(c->cq, "bind"); - } - gpr_free(c); - GPR_TIMER_END("destroy_call", 0); -} - -static void set_status_code(grpc_call *call, status_source source, - uint32_t status) { - if (call->status[source].is_set) return; - - call->status[source].is_set = 1; - call->status[source].code = (grpc_status_code)status; - - /* TODO(ctiller): what to do about the flush that was previously here */ -} - -static void set_compression_algorithm(grpc_call *call, - grpc_compression_algorithm algo) { - call->compression_algorithm = algo; -} - -grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( - grpc_call *call) { - grpc_compression_algorithm algorithm; - gpr_mu_lock(&call->mu); - algorithm = call->compression_algorithm; - gpr_mu_unlock(&call->mu); - return algorithm; -} - -uint32_t grpc_call_test_only_get_message_flags(grpc_call *call) { - uint32_t flags; - gpr_mu_lock(&call->mu); - flags = call->test_only_last_message_flags; - gpr_mu_unlock(&call->mu); - return flags; -} - -static void destroy_encodings_accepted_by_peer(void *p) { return; } - -static void set_encodings_accepted_by_peer(grpc_call *call, grpc_mdelem *mdel) { - size_t i; - grpc_compression_algorithm algorithm; - gpr_slice_buffer accept_encoding_parts; - gpr_slice accept_encoding_slice; - void *accepted_user_data; - - accepted_user_data = - grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer); - if (accepted_user_data != NULL) { - call->encodings_accepted_by_peer = - (uint32_t)(((uintptr_t)accepted_user_data) - 1); - return; - } - - accept_encoding_slice = mdel->value->slice; - 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_slice_buffer_destroy(&accept_encoding_parts); - - grpc_mdelem_set_user_data( - mdel, destroy_encodings_accepted_by_peer, - (void *)(((uintptr_t)call->encodings_accepted_by_peer) + 1)); -} - -uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call) { - uint32_t encodings_accepted_by_peer; - gpr_mu_lock(&call->mu); - encodings_accepted_by_peer = call->encodings_accepted_by_peer; - gpr_mu_unlock(&call->mu); - return encodings_accepted_by_peer; -} - -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 void get_final_status(grpc_call *call, - void (*set_value)(grpc_status_code code, - void *user_data), - void *set_value_user_data) { - int i; - for (i = 0; i < STATUS_SOURCE_COUNT; i++) { - if (call->status[i].is_set) { - set_value(call->status[i].code, set_value_user_data); - return; - } - } - if (call->is_client) { - set_value(GRPC_STATUS_UNKNOWN, set_value_user_data); - } else { - set_value(GRPC_STATUS_OK, set_value_user_data); - } -} - -static void get_final_details(grpc_call *call, char **out_details, - size_t *out_details_capacity) { - 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_details_capacity) { - *out_details_capacity = - GPR_MAX(len + 1, *out_details_capacity * 3 / 2); - *out_details = gpr_realloc(*out_details, *out_details_capacity); - } - memcpy(*out_details, GPR_SLICE_START_PTR(details), len); - (*out_details)[len] = 0; - } else { - goto no_details; - } - return; - } - } - -no_details: - if (0 == *out_details_capacity) { - *out_details_capacity = 8; - *out_details = gpr_malloc(*out_details_capacity); - } - **out_details = 0; -} - -static grpc_linked_mdelem *linked_from_md(grpc_metadata *md) { - return (grpc_linked_mdelem *)&md->internal_data; -} - -static int prepare_application_metadata(grpc_call *call, int count, - grpc_metadata *metadata, - int is_trailing, - int prepend_extra_metadata) { - int i; - grpc_metadata_batch *batch = - &call->metadata_batch[0 /* is_receiving */][is_trailing]; - if (prepend_extra_metadata) { - if (call->send_extra_metadata_count == 0) { - prepend_extra_metadata = 0; - } else { - for (i = 0; i < call->send_extra_metadata_count; i++) { - GRPC_MDELEM_REF(call->send_extra_metadata[i].md); - } - for (i = 1; i < call->send_extra_metadata_count; i++) { - call->send_extra_metadata[i].prev = &call->send_extra_metadata[i - 1]; - } - for (i = 0; i < call->send_extra_metadata_count - 1; i++) { - call->send_extra_metadata[i].next = &call->send_extra_metadata[i + 1]; - } - } - } - for (i = 0; i < count; i++) { - grpc_metadata *md = &metadata[i]; - 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( - md->key, (const uint8_t *)md->value, md->value_length); - if (!grpc_header_key_is_legal(grpc_mdstr_as_c_string(l->md->key), - GRPC_MDSTR_LENGTH(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_is_binary_header(grpc_mdstr_as_c_string(l->md->key), - GRPC_MDSTR_LENGTH(l->md->key)) && - !grpc_header_nonbin_value_is_legal( - grpc_mdstr_as_c_string(l->md->value), - GRPC_MDSTR_LENGTH(l->md->value))) { - gpr_log(GPR_ERROR, "attempt to send invalid metadata value"); - return 0; - } - } - for (i = 1; i < count; i++) { - linked_from_md(&metadata[i])->prev = linked_from_md(&metadata[i - 1]); - } - for (i = 0; i < count - 1; i++) { - linked_from_md(&metadata[i])->next = linked_from_md(&metadata[i + 1]); - } - switch (prepend_extra_metadata * 2 + (count != 0)) { - case 0: - /* no prepend, no metadata => nothing to do */ - batch->list.head = batch->list.tail = NULL; - break; - case 1: - /* metadata, but no prepend */ - batch->list.head = linked_from_md(&metadata[0]); - batch->list.tail = linked_from_md(&metadata[count - 1]); - batch->list.head->prev = NULL; - batch->list.tail->next = NULL; - break; - case 2: - /* prepend, but no md */ - batch->list.head = &call->send_extra_metadata[0]; - batch->list.tail = - &call->send_extra_metadata[call->send_extra_metadata_count - 1]; - batch->list.head->prev = NULL; - batch->list.tail->next = NULL; - break; - case 3: - /* prepend AND md */ - batch->list.head = &call->send_extra_metadata[0]; - call->send_extra_metadata[call->send_extra_metadata_count - 1].next = - linked_from_md(&metadata[0]); - linked_from_md(&metadata[0])->prev = - &call->send_extra_metadata[call->send_extra_metadata_count - 1]; - batch->list.tail = linked_from_md(&metadata[count - 1]); - batch->list.head->prev = NULL; - batch->list.tail->next = NULL; - break; - default: - GPR_UNREACHABLE_CODE(return 0); - } - - return 1; -} - -void grpc_call_destroy(grpc_call *c) { - int cancel; - grpc_call *parent = c->parent; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GPR_TIMER_BEGIN("grpc_call_destroy", 0); - GRPC_API_TRACE("grpc_call_destroy(c=%p)", 1, (c)); - - 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(&exec_ctx, parent, "child"); - } - - gpr_mu_lock(&c->mu); - GPR_ASSERT(!c->destroy_called); - c->destroy_called = 1; - if (c->have_alarm) { - grpc_timer_cancel(&exec_ctx, &c->alarm); - } - cancel = !c->received_final_op; - gpr_mu_unlock(&c->mu); - if (cancel) grpc_call_cancel(c, NULL); - GRPC_CALL_INTERNAL_UNREF(&exec_ctx, c, "destroy"); - grpc_exec_ctx_finish(&exec_ctx); - GPR_TIMER_END("grpc_call_destroy", 0); -} - -grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved) { - GRPC_API_TRACE("grpc_call_cancel(call=%p, reserved=%p)", 2, (call, 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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE( - "grpc_call_cancel_with_status(" - "c=%p, status=%d, description=%s, reserved=%p)", - 4, (c, (int)status, description, reserved)); - GPR_ASSERT(reserved == NULL); - gpr_mu_lock(&c->mu); - r = cancel_with_status(&exec_ctx, c, status, description); - gpr_mu_unlock(&c->mu); - grpc_exec_ctx_finish(&exec_ctx); - return r; -} - -typedef struct cancel_closure { - grpc_closure closure; - grpc_call *call; - grpc_status_code status; -} cancel_closure; - -static void done_cancel(grpc_exec_ctx *exec_ctx, void *ccp, bool success) { - cancel_closure *cc = ccp; - GRPC_CALL_INTERNAL_UNREF(exec_ctx, cc->call, "cancel"); - gpr_free(cc); -} - -static void send_cancel(grpc_exec_ctx *exec_ctx, void *ccp, bool success) { - grpc_transport_stream_op op; - cancel_closure *cc = ccp; - memset(&op, 0, sizeof(op)); - op.cancel_with_status = cc->status; - /* reuse closure to catch completion */ - grpc_closure_init(&cc->closure, done_cancel, cc); - op.on_complete = &cc->closure; - execute_op(exec_ctx, cc->call, &op); -} - -static grpc_call_error cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c, - grpc_status_code status, - const char *description) { - grpc_mdstr *details = - description ? grpc_mdstr_from_string(description) : NULL; - cancel_closure *cc = gpr_malloc(sizeof(*cc)); - - GPR_ASSERT(status != GRPC_STATUS_OK); - - set_status_code(c, STATUS_FROM_API_OVERRIDE, (uint32_t)status); - set_status_details(c, STATUS_FROM_API_OVERRIDE, details); - - grpc_closure_init(&cc->closure, send_cancel, cc); - cc->call = c; - cc->status = status; - GRPC_CALL_INTERNAL_REF(c, "cancel"); - grpc_exec_ctx_enqueue(exec_ctx, &cc->closure, true, NULL); - - return GRPC_CALL_OK; -} - -static void execute_op(grpc_exec_ctx *exec_ctx, grpc_call *call, - grpc_transport_stream_op *op) { - grpc_call_element *elem; - - GPR_TIMER_BEGIN("execute_op", 0); - elem = CALL_ELEM_FROM_CALL(call, 0); - op->context = call->context; - elem->filter->start_transport_stream_op(exec_ctx, elem, op); - GPR_TIMER_END("execute_op", 0); -} - -char *grpc_call_get_peer(grpc_call *call) { - grpc_call_element *elem = CALL_ELEM_FROM_CALL(call, 0); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - char *result; - GRPC_API_TRACE("grpc_call_get_peer(%p)", 1, (call)); - result = elem->filter->get_peer(&exec_ctx, elem); - if (result == NULL) { - result = grpc_channel_get_target(call->channel); - } - if (result == NULL) { - result = gpr_strdup("unknown"); - } - grpc_exec_ctx_finish(&exec_ctx); - return result; -} - -grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { - return CALL_FROM_TOP_ELEM(elem); -} - -static void call_alarm(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - grpc_call *call = arg; - gpr_mu_lock(&call->mu); - call->have_alarm = 0; - if (success) { - cancel_with_status(exec_ctx, call, GRPC_STATUS_DEADLINE_EXCEEDED, - "Deadline Exceeded"); - } - gpr_mu_unlock(&call->mu); - GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "alarm"); -} - -static void set_deadline_alarm(grpc_exec_ctx *exec_ctx, 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_timer_init(exec_ctx, &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 uint32_t decode_status(grpc_mdelem *md) { - uint32_t status; - void *user_data; - if (md == GRPC_MDELEM_GRPC_STATUS_0) return 0; - if (md == GRPC_MDELEM_GRPC_STATUS_1) return 1; - if (md == GRPC_MDELEM_GRPC_STATUS_2) return 2; - user_data = grpc_mdelem_get_user_data(md, destroy_status); - if (user_data != NULL) { - status = ((uint32_t)(intptr_t)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 *)(intptr_t)(status + STATUS_OFFSET)); - } - return status; -} - -static uint32_t decode_compression(grpc_mdelem *md) { - grpc_compression_algorithm algorithm = - grpc_compression_algorithm_from_mdstr(md->value); - if (algorithm == GRPC_COMPRESS_ALGORITHMS_COUNT) { - const char *md_c_str = grpc_mdstr_as_c_string(md->value); - gpr_log(GPR_ERROR, "Invalid compression algorithm: '%s'", md_c_str); - } - return algorithm; -} - -static grpc_mdelem *recv_common_filter(grpc_call *call, grpc_mdelem *elem) { - if (elem->key == GRPC_MDSTR_GRPC_STATUS) { - GPR_TIMER_BEGIN("status", 0); - set_status_code(call, STATUS_FROM_WIRE, decode_status(elem)); - GPR_TIMER_END("status", 0); - return NULL; - } else if (elem->key == GRPC_MDSTR_GRPC_MESSAGE) { - GPR_TIMER_BEGIN("status-details", 0); - set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(elem->value)); - GPR_TIMER_END("status-details", 0); - return NULL; - } - return elem; -} - -static grpc_mdelem *publish_app_metadata(grpc_call *call, grpc_mdelem *elem, - int is_trailing) { - grpc_metadata_array *dest; - grpc_metadata *mdusr; - GPR_TIMER_BEGIN("publish_app_metadata", 0); - 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(elem->key); - mdusr->value = grpc_mdstr_as_c_string(elem->value); - mdusr->value_length = GPR_SLICE_LENGTH(elem->value->slice); - GPR_TIMER_END("publish_app_metadata", 0); - return elem; -} - -static grpc_mdelem *recv_initial_filter(void *callp, grpc_mdelem *elem) { - grpc_call *call = callp; - elem = recv_common_filter(call, elem); - if (elem == NULL) { - return NULL; - } else if (elem->key == GRPC_MDSTR_GRPC_ENCODING) { - GPR_TIMER_BEGIN("compression_algorithm", 0); - set_compression_algorithm(call, decode_compression(elem)); - GPR_TIMER_END("compression_algorithm", 0); - return NULL; - } else if (elem->key == GRPC_MDSTR_GRPC_ACCEPT_ENCODING) { - GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0); - set_encodings_accepted_by_peer(call, elem); - GPR_TIMER_END("encodings_accepted_by_peer", 0); - return NULL; - } else { - return publish_app_metadata(call, elem, 0); - } -} - -static grpc_mdelem *recv_trailing_filter(void *callp, grpc_mdelem *elem) { - grpc_call *call = callp; - elem = recv_common_filter(call, elem); - if (elem == NULL) { - return NULL; - } else { - return publish_app_metadata(call, elem, 1); - } -} - -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) { - *(int *)dest = (status != GRPC_STATUS_OK); -} - -static int are_write_flags_valid(uint32_t flags) { - /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ - const uint32_t allowed_write_positions = - (GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK); - const uint32_t invalid_positions = ~allowed_write_positions; - return !(flags & invalid_positions); -} - -static batch_control *allocate_batch_control(grpc_call *call) { - size_t i; - for (i = 0; i < MAX_CONCURRENT_BATCHES; i++) { - if ((call->used_batches & (1 << i)) == 0) { - call->used_batches = (uint8_t)(call->used_batches | (uint8_t)(1 << i)); - return &call->active_batches[i]; - } - } - return NULL; -} - -static void finish_batch_completion(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_cq_completion *storage) { - batch_control *bctl = user_data; - grpc_call *call = bctl->call; - gpr_mu_lock(&call->mu); - call->used_batches = (uint8_t)( - call->used_batches & ~(uint8_t)(1 << (bctl - call->active_batches))); - gpr_mu_unlock(&call->mu); - GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion"); -} - -static void post_batch_completion(grpc_exec_ctx *exec_ctx, - batch_control *bctl) { - grpc_call *call = bctl->call; - if (bctl->is_notify_tag_closure) { - grpc_exec_ctx_enqueue(exec_ctx, bctl->notify_tag, bctl->success, NULL); - gpr_mu_lock(&call->mu); - bctl->call->used_batches = - (uint8_t)(bctl->call->used_batches & - ~(uint8_t)(1 << (bctl - bctl->call->active_batches))); - gpr_mu_unlock(&call->mu); - GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion"); - } else { - grpc_cq_end_op(exec_ctx, bctl->call->cq, bctl->notify_tag, bctl->success, - finish_batch_completion, bctl, &bctl->cq_completion); - } -} - -static void continue_receiving_slices(grpc_exec_ctx *exec_ctx, - batch_control *bctl) { - grpc_call *call = bctl->call; - for (;;) { - size_t remaining = call->receiving_stream->length - - (*call->receiving_buffer)->data.raw.slice_buffer.length; - if (remaining == 0) { - call->receiving_message = 0; - grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); - call->receiving_stream = NULL; - if (gpr_unref(&bctl->steps_to_complete)) { - post_batch_completion(exec_ctx, bctl); - } - return; - } - if (grpc_byte_stream_next(exec_ctx, call->receiving_stream, - &call->receiving_slice, remaining, - &call->receiving_slice_ready)) { - gpr_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, - call->receiving_slice); - } else { - return; - } - } -} - -static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp, - bool success) { - batch_control *bctl = bctlp; - grpc_call *call = bctl->call; - - if (success) { - gpr_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, - call->receiving_slice); - continue_receiving_slices(exec_ctx, bctl); - } else { - grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); - call->receiving_stream = NULL; - grpc_byte_buffer_destroy(*call->receiving_buffer); - *call->receiving_buffer = NULL; - if (gpr_unref(&bctl->steps_to_complete)) { - post_batch_completion(exec_ctx, bctl); - } - } -} - -static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp, bool success) { - batch_control *bctl = bctlp; - grpc_call *call = bctl->call; - grpc_call *child_call; - grpc_call *next_child_call; - - gpr_mu_lock(&call->mu); - if (bctl->send_initial_metadata) { - grpc_metadata_batch_destroy( - &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]); - } - if (bctl->send_message) { - call->sending_message = 0; - } - if (bctl->send_final_op) { - grpc_metadata_batch_destroy( - &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]); - } - if (bctl->recv_initial_metadata) { - grpc_metadata_batch *md = - &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; - grpc_metadata_batch_filter(md, recv_initial_filter, call); - - if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) != - 0 && - !call->is_client) { - GPR_TIMER_BEGIN("set_deadline_alarm", 0); - set_deadline_alarm(exec_ctx, call, md->deadline); - GPR_TIMER_END("set_deadline_alarm", 0); - } - } - if (bctl->recv_final_op) { - grpc_metadata_batch *md = - &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - grpc_metadata_batch_filter(md, recv_trailing_filter, call); - - if (call->have_alarm) { - grpc_timer_cancel(exec_ctx, &call->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(exec_ctx, child_call, "propagate_cancel"); - } - child_call = next_child_call; - } while (child_call != call->first_child); - } - - if (call->is_client) { - get_final_status(call, set_status_value_directly, - call->final_op.client.status); - get_final_details(call, call->final_op.client.status_details, - call->final_op.client.status_details_capacity); - } else { - get_final_status(call, set_cancelled_value, - call->final_op.server.cancelled); - } - - success = 1; - } - bctl->success = success != 0; - gpr_mu_unlock(&call->mu); - if (gpr_unref(&bctl->steps_to_complete)) { - post_batch_completion(exec_ctx, bctl); - } -} - -static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp, - bool success) { - batch_control *bctl = bctlp; - grpc_call *call = bctl->call; - - if (call->receiving_stream == NULL) { - *call->receiving_buffer = NULL; - call->receiving_message = 0; - if (gpr_unref(&bctl->steps_to_complete)) { - post_batch_completion(exec_ctx, bctl); - } - } else if (call->receiving_stream->length > - grpc_channel_get_max_message_length(call->channel)) { - cancel_with_status(exec_ctx, call, GRPC_STATUS_INTERNAL, - "Max message size exceeded"); - grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); - call->receiving_stream = NULL; - *call->receiving_buffer = NULL; - call->receiving_message = 0; - if (gpr_unref(&bctl->steps_to_complete)) { - post_batch_completion(exec_ctx, bctl); - } - } else { - call->test_only_last_message_flags = call->receiving_stream->flags; - if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) && - (call->compression_algorithm > GRPC_COMPRESS_NONE)) { - *call->receiving_buffer = grpc_raw_compressed_byte_buffer_create( - NULL, 0, call->compression_algorithm); - } else { - *call->receiving_buffer = grpc_raw_byte_buffer_create(NULL, 0); - } - grpc_closure_init(&call->receiving_slice_ready, receiving_slice_ready, - bctl); - continue_receiving_slices(exec_ctx, bctl); - /* early out */ - return; - } -} - -static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, - grpc_call *call, const grpc_op *ops, - size_t nops, void *notify_tag, - int is_notify_tag_closure) { - grpc_transport_stream_op stream_op; - size_t i; - const grpc_op *op; - batch_control *bctl; - int num_completion_callbacks_needed = 1; - grpc_call_error error = GRPC_CALL_OK; - - GPR_TIMER_BEGIN("grpc_call_start_batch", 0); - - GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, notify_tag); - - memset(&stream_op, 0, sizeof(stream_op)); - - /* TODO(ctiller): this feels like it could be made lock-free */ - gpr_mu_lock(&call->mu); - bctl = allocate_batch_control(call); - memset(bctl, 0, sizeof(*bctl)); - bctl->call = call; - bctl->notify_tag = notify_tag; - bctl->is_notify_tag_closure = (uint8_t)(is_notify_tag_closure != 0); - - if (nops == 0) { - GRPC_CALL_INTERNAL_REF(call, "completion"); - bctl->success = 1; - if (!is_notify_tag_closure) { - grpc_cq_begin_op(call->cq, notify_tag); - } - gpr_mu_unlock(&call->mu); - post_batch_completion(exec_ctx, bctl); - error = GRPC_CALL_OK; - goto done; - } - - /* rewrite batch ops into a transport op */ - for (i = 0; i < nops; i++) { - op = &ops[i]; - if (op->reserved != NULL) { - error = GRPC_CALL_ERROR; - goto done_with_error; - } - switch (op->op) { - case GRPC_OP_SEND_INITIAL_METADATA: - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->sent_initial_metadata) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - if (op->data.send_initial_metadata.count > INT_MAX) { - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - bctl->send_initial_metadata = 1; - call->sent_initial_metadata = 1; - if (!prepare_application_metadata( - call, (int)op->data.send_initial_metadata.count, - op->data.send_initial_metadata.metadata, 0, call->is_client)) { - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - /* TODO(ctiller): just make these the same variable? */ - call->metadata_batch[0][0].deadline = call->send_deadline; - stream_op.send_initial_metadata = - &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]; - break; - case GRPC_OP_SEND_MESSAGE: - if (!are_write_flags_valid(op->flags)) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (op->data.send_message == NULL) { - error = GRPC_CALL_ERROR_INVALID_MESSAGE; - goto done_with_error; - } - if (call->sending_message) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - bctl->send_message = 1; - call->sending_message = 1; - grpc_slice_buffer_stream_init( - &call->sending_stream, - &op->data.send_message->data.raw.slice_buffer, op->flags); - stream_op.send_message = &call->sending_stream.base; - break; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (!call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_SERVER; - goto done_with_error; - } - if (call->sent_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - bctl->send_final_op = 1; - call->sent_final_op = 1; - stream_op.send_trailing_metadata = - &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; - break; - case GRPC_OP_SEND_STATUS_FROM_SERVER: - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_CLIENT; - goto done_with_error; - } - if (call->sent_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - if (op->data.send_status_from_server.trailing_metadata_count > - INT_MAX) { - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - bctl->send_final_op = 1; - call->sent_final_op = 1; - call->send_extra_metadata_count = 1; - call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem( - call->channel, op->data.send_status_from_server.status); - if (op->data.send_status_from_server.status_details != NULL) { - call->send_extra_metadata[1].md = grpc_mdelem_from_metadata_strings( - GRPC_MDSTR_GRPC_MESSAGE, - grpc_mdstr_from_string( - op->data.send_status_from_server.status_details)); - call->send_extra_metadata_count++; - set_status_details( - call, STATUS_FROM_API_OVERRIDE, - GRPC_MDSTR_REF(call->send_extra_metadata[1].md->value)); - } - set_status_code(call, STATUS_FROM_API_OVERRIDE, - (uint32_t)op->data.send_status_from_server.status); - if (!prepare_application_metadata( - call, - (int)op->data.send_status_from_server.trailing_metadata_count, - op->data.send_status_from_server.trailing_metadata, 1, 1)) { - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - stream_op.send_trailing_metadata = - &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; - break; - case GRPC_OP_RECV_INITIAL_METADATA: - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->received_initial_metadata) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->received_initial_metadata = 1; - call->buffered_metadata[0] = op->data.recv_initial_metadata; - bctl->recv_initial_metadata = 1; - stream_op.recv_initial_metadata = - &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; - break; - case GRPC_OP_RECV_MESSAGE: - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->receiving_message) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->receiving_message = 1; - bctl->recv_message = 1; - call->receiving_buffer = op->data.recv_message; - stream_op.recv_message = &call->receiving_stream; - grpc_closure_init(&call->receiving_stream_ready, receiving_stream_ready, - bctl); - stream_op.recv_message_ready = &call->receiving_stream_ready; - num_completion_callbacks_needed++; - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (!call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_SERVER; - goto done_with_error; - } - if (call->received_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->received_final_op = 1; - call->buffered_metadata[1] = - op->data.recv_status_on_client.trailing_metadata; - call->final_op.client.status = op->data.recv_status_on_client.status; - call->final_op.client.status_details = - op->data.recv_status_on_client.status_details; - call->final_op.client.status_details_capacity = - op->data.recv_status_on_client.status_details_capacity; - bctl->recv_final_op = 1; - stream_op.recv_trailing_metadata = - &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - break; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_CLIENT; - goto done_with_error; - } - if (call->received_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->received_final_op = 1; - call->final_op.server.cancelled = - op->data.recv_close_on_server.cancelled; - bctl->recv_final_op = 1; - stream_op.recv_trailing_metadata = - &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - break; - } - } - - GRPC_CALL_INTERNAL_REF(call, "completion"); - if (!is_notify_tag_closure) { - grpc_cq_begin_op(call->cq, notify_tag); - } - gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed); - - stream_op.context = call->context; - grpc_closure_init(&bctl->finish_batch, finish_batch, bctl); - stream_op.on_complete = &bctl->finish_batch; - gpr_mu_unlock(&call->mu); - - execute_op(exec_ctx, call, &stream_op); - -done: - GPR_TIMER_END("grpc_call_start_batch", 0); - return error; - -done_with_error: - /* reverse any mutations that occured */ - if (bctl->send_initial_metadata) { - call->sent_initial_metadata = 0; - grpc_metadata_batch_clear(&call->metadata_batch[0][0]); - } - if (bctl->send_message) { - call->sending_message = 0; - grpc_byte_stream_destroy(exec_ctx, &call->sending_stream.base); - } - if (bctl->send_final_op) { - call->sent_final_op = 0; - grpc_metadata_batch_clear(&call->metadata_batch[0][1]); - } - if (bctl->recv_initial_metadata) { - call->received_initial_metadata = 0; - } - if (bctl->recv_message) { - call->receiving_message = 0; - } - if (bctl->recv_final_op) { - call->received_final_op = 0; - } - gpr_mu_unlock(&call->mu); - goto done; -} - -grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, - size_t nops, void *tag, void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_call_error err; - - GRPC_API_TRACE( - "grpc_call_start_batch(call=%p, ops=%p, nops=%lu, tag=%p, reserved=%p)", - 5, (call, ops, (unsigned long)nops, tag, reserved)); - - if (reserved != NULL) { - err = GRPC_CALL_ERROR; - } else { - err = call_start_batch(&exec_ctx, call, ops, nops, tag, 0); - } - - grpc_exec_ctx_finish(&exec_ctx); - return err; -} - -grpc_call_error grpc_call_start_batch_and_execute(grpc_exec_ctx *exec_ctx, - grpc_call *call, - const grpc_op *ops, - size_t nops, - grpc_closure *closure) { - return call_start_batch(exec_ctx, call, ops, nops, closure, 1); -} - -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; -} - -uint8_t grpc_call_is_client(grpc_call *call) { return call->is_client; } diff --git a/third_party/grpc/src/core/surface/call.h b/third_party/grpc/src/core/surface/call.h deleted file mode 100644 index 0bbffb98ae074a..00000000000000 --- a/third_party/grpc/src/core/surface/call.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/surface/api_trace.h" -#include "src/core/surface/surface_trace.h" -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*grpc_ioreq_completion_func)(grpc_exec_ctx *exec_ctx, - grpc_call *call, int success, - void *user_data); - -grpc_call *grpc_call_create(grpc_channel *channel, grpc_call *parent_call, - uint32_t 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_exec_ctx *exec_ctx, grpc_call *call, - grpc_completion_queue *cq); - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_call_internal_ref(grpc_call *call, const char *reason); -void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *call, - const char *reason); -#define GRPC_CALL_INTERNAL_REF(call, reason) \ - grpc_call_internal_ref(call, reason) -#define GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, reason) \ - grpc_call_internal_unref(exec_ctx, call, reason) -#else -void grpc_call_internal_ref(grpc_call *call); -void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *call); -#define GRPC_CALL_INTERNAL_REF(call, reason) grpc_call_internal_ref(call) -#define GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, reason) \ - grpc_call_internal_unref(exec_ctx, call) -#endif - -grpc_call_stack *grpc_call_get_call_stack(grpc_call *call); - -grpc_call_error grpc_call_start_batch_and_execute(grpc_exec_ctx *exec_ctx, - grpc_call *call, - const grpc_op *ops, - size_t nops, - grpc_closure *closure); - -/* Given the top call_element, get the call object. */ -grpc_call *grpc_call_from_top_element(grpc_call_element *surface_element); - -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); - -/* 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_api_trace) grpc_call_log_batch(sev, call, ops, nops, tag) - -uint8_t grpc_call_is_client(grpc_call *call); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_INTERNAL_CORE_SURFACE_CALL_H */ diff --git a/third_party/grpc/src/core/surface/call_details.c b/third_party/grpc/src/core/surface/call_details.c deleted file mode 100644 index 60f0029819c02c..00000000000000 --- a/third_party/grpc/src/core/surface/call_details.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/core/surface/api_trace.h" - -void grpc_call_details_init(grpc_call_details* cd) { - GRPC_API_TRACE("grpc_call_details_init(cd=%p)", 1, (cd)); - memset(cd, 0, sizeof(*cd)); -} - -void grpc_call_details_destroy(grpc_call_details* cd) { - GRPC_API_TRACE("grpc_call_details_destroy(cd=%p)", 1, (cd)); - gpr_free(cd->method); - gpr_free(cd->host); -} diff --git a/third_party/grpc/src/core/surface/call_log_batch.c b/third_party/grpc/src/core/surface/call_log_batch.c deleted file mode 100644 index 46756f418b499d..00000000000000 --- a/third_party/grpc/src/core/surface/call_log_batch.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 - -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; - 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); - } -} diff --git a/third_party/grpc/src/core/surface/call_test_only.h b/third_party/grpc/src/core/surface/call_test_only.h deleted file mode 100644 index b57c95c64a5b9a..00000000000000 --- a/third_party/grpc/src/core/surface/call_test_only.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_TEST_ONLY_H -#define GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** Return the compression algorithm from \a call. - * - * \warning This function should \b only be used in test code. */ -grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( - grpc_call *call); - -/** Return the message flags from \a call. - * - * \warning This function should \b only be used in test code. */ -uint32_t grpc_call_test_only_get_message_flags(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. */ -uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call); - -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_INTERNAL_CORE_SURFACE_CALL_TEST_ONLY_H */ diff --git a/third_party/grpc/src/core/surface/channel.c b/third_party/grpc/src/core/surface/channel.c deleted file mode 100644 index 12d8ebceb9045f..00000000000000 --- a/third_party/grpc/src/core/surface/channel.c +++ /dev/null @@ -1,327 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/api_trace.h" -#include "src/core/surface/call.h" -#include "src/core/surface/init.h" -#include "src/core/transport/static_metadata.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; - uint32_t max_message_length; - grpc_mdelem *default_authority; - - gpr_mu registered_call_mu; - registered_call *registered_calls; - 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) - -static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, bool success); - -grpc_channel *grpc_channel_create_from_filters( - grpc_exec_ctx *exec_ctx, const char *target, - const grpc_channel_filter **filters, size_t num_filters, - const grpc_channel_args *args, 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; - 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 = (uint32_t)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 ignored: it must be a 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( - ":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 ignored: it must be a 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 ignored: default host already set some other way", - GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); - } else { - channel->default_authority = grpc_mdelem_from_strings( - ":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(":authority", default_authority); - } - gpr_free(default_authority); - } - - grpc_channel_stack_init(exec_ctx, 1, destroy_channel, channel, filters, - num_filters, args, - is_client ? "CLIENT_CHANNEL" : "SERVER_CHANNEL", - CHANNEL_STACK_FROM_CHANNEL(channel)); - - return channel; -} - -char *grpc_channel_get_target(grpc_channel *channel) { - GRPC_API_TRACE("grpc_channel_get_target(channel=%p)", 1, (channel)); - return gpr_strdup(channel->target); -} - -static grpc_call *grpc_channel_create_call_internal( - grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, - grpc_completion_queue *cq, grpc_mdelem *path_mdelem, - grpc_mdelem *authority_mdelem, gpr_timespec deadline) { - grpc_mdelem *send_metadata[2]; - size_t 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, - uint32_t propagation_mask, - grpc_completion_queue *cq, - const char *method, const char *host, - gpr_timespec deadline, void *reserved) { - GRPC_API_TRACE( - "grpc_channel_create_call(" - "channel=%p, parent_call=%p, propagation_mask=%x, cq=%p, method=%s, " - "host=%s, " - "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 10, (channel, parent_call, (unsigned)propagation_mask, cq, method, host, - (long long)deadline.tv_sec, (int)deadline.tv_nsec, - (int)deadline.clock_type, reserved)); - GPR_ASSERT(!reserved); - return grpc_channel_create_call_internal( - channel, parent_call, propagation_mask, cq, - grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH, - grpc_mdstr_from_string(method)), - host ? grpc_mdelem_from_metadata_strings(GRPC_MDSTR_AUTHORITY, - grpc_mdstr_from_string(host)) - : 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)); - GRPC_API_TRACE( - "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)", - 4, (channel, method, host, reserved)); - GPR_ASSERT(!reserved); - rc->path = grpc_mdelem_from_metadata_strings(GRPC_MDSTR_PATH, - grpc_mdstr_from_string(method)); - rc->authority = host ? grpc_mdelem_from_metadata_strings( - GRPC_MDSTR_AUTHORITY, grpc_mdstr_from_string(host)) - : 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, uint32_t propagation_mask, - grpc_completion_queue *completion_queue, void *registered_call_handle, - gpr_timespec deadline, void *reserved) { - registered_call *rc = registered_call_handle; - GRPC_API_TRACE( - "grpc_channel_create_registered_call(" - "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, " - "registered_call_handle=%p, " - "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 9, (channel, parent_call, (unsigned)propagation_mask, completion_queue, - registered_call_handle, (long long)deadline.tv_sec, - (int)deadline.tv_nsec, (int)deadline.clock_type, reserved)); - 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_STREAM_REFCOUNT_DEBUG -#define REF_REASON reason -#define REF_ARG , const char *reason -#else -#define REF_REASON "" -#define REF_ARG -#endif -void grpc_channel_internal_ref(grpc_channel *c REF_ARG) { - GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); -} - -void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, - grpc_channel *c REF_ARG) { - GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); -} - -static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_success) { - grpc_channel *channel = arg; - grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel)); - 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); - } - gpr_mu_destroy(&channel->registered_call_mu); - gpr_free(channel->target); - gpr_free(channel); -} - -void grpc_channel_destroy(grpc_channel *channel) { - grpc_transport_op op; - grpc_channel_element *elem; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel)); - memset(&op, 0, sizeof(op)); - op.disconnect = 1; - elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); - elem->filter->start_transport_op(&exec_ctx, elem, &op); - - GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, channel, "channel"); - - grpc_exec_ctx_finish(&exec_ctx); -} - -grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) { - return CHANNEL_STACK_FROM_CHANNEL(channel); -} - -grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) { - char tmp[GPR_LTOA_MIN_BUFSIZE]; - switch (i) { - case 0: - return GRPC_MDELEM_GRPC_STATUS_0; - case 1: - return GRPC_MDELEM_GRPC_STATUS_1; - case 2: - return GRPC_MDELEM_GRPC_STATUS_2; - } - gpr_ltoa(i, tmp); - return grpc_mdelem_from_metadata_strings(GRPC_MDSTR_GRPC_STATUS, - grpc_mdstr_from_string(tmp)); -} - -uint32_t grpc_channel_get_max_message_length(grpc_channel *channel) { - return channel->max_message_length; -} diff --git a/third_party/grpc/src/core/surface/channel.h b/third_party/grpc/src/core/surface/channel.h deleted file mode 100644 index 00240c637fdcf5..00000000000000 --- a/third_party/grpc/src/core/surface/channel.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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( - grpc_exec_ctx *exec_ctx, const char *target, - const grpc_channel_filter **filters, size_t count, - const grpc_channel_args *args, 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 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); -uint32_t grpc_channel_get_max_message_length(grpc_channel *channel); - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_channel_internal_ref(grpc_channel *channel, const char *reason); -void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, grpc_channel *channel, - const char *reason); -#define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \ - grpc_channel_internal_ref(channel, reason) -#define GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, reason) \ - grpc_channel_internal_unref(exec_ctx, channel, reason) -#else -void grpc_channel_internal_ref(grpc_channel *channel); -void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, - grpc_channel *channel); -#define GRPC_CHANNEL_INTERNAL_REF(channel, reason) \ - grpc_channel_internal_ref(channel) -#define GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, reason) \ - grpc_channel_internal_unref(exec_ctx, channel) -#endif - -#endif /* GRPC_INTERNAL_CORE_SURFACE_CHANNEL_H */ diff --git a/third_party/grpc/src/core/surface/channel_connectivity.c b/third_party/grpc/src/core/surface/channel_connectivity.c deleted file mode 100644 index 2dd4fce26b2905..00000000000000 --- a/third_party/grpc/src/core/surface/channel_connectivity.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/surface/channel.h" - -#include -#include - -#include "src/core/channel/client_channel.h" -#include "src/core/channel/client_uchannel.h" -#include "src/core/iomgr/timer.h" -#include "src/core/surface/api_trace.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)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_connectivity_state state; - GRPC_API_TRACE( - "grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2, - (channel, try_to_connect)); - if (client_channel_elem->filter == &grpc_client_channel_filter) { - state = grpc_client_channel_check_connectivity_state( - &exec_ctx, client_channel_elem, try_to_connect); - grpc_exec_ctx_finish(&exec_ctx); - return state; - } - if (client_channel_elem->filter == &grpc_client_uchannel_filter) { - state = grpc_client_uchannel_check_connectivity_state( - &exec_ctx, client_channel_elem, try_to_connect); - grpc_exec_ctx_finish(&exec_ctx); - return state; - } - gpr_log(GPR_ERROR, - "grpc_channel_check_connectivity_state called on something that is " - "not a (u)client channel, but '%s'", - client_channel_elem->filter->name); - grpc_exec_ctx_finish(&exec_ctx); - return GRPC_CHANNEL_FATAL_FAILURE; -} - -typedef enum { - WAITING, - CALLING_BACK, - CALLING_BACK_AND_FINISHED, - CALLED_BACK -} callback_phase; - -typedef struct { - gpr_mu mu; - callback_phase phase; - int success; - grpc_closure on_complete; - grpc_timer 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(grpc_exec_ctx *exec_ctx, state_watcher *w) { - grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( - grpc_channel_get_channel_stack(w->channel)); - if (client_channel_elem->filter == &grpc_client_channel_filter) { - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel, - "watch_channel_connectivity"); - } else if (client_channel_elem->filter == &grpc_client_uchannel_filter) { - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel, - "watch_uchannel_connectivity"); - } else { - abort(); - } - gpr_mu_destroy(&w->mu); - gpr_free(w); -} - -static void finished_completion(grpc_exec_ctx *exec_ctx, 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_UNREACHABLE_CODE(return ); - 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(exec_ctx, w); - } -} - -static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w, - int due_to_completion) { - int delete = 0; - - if (due_to_completion) { - grpc_timer_cancel(exec_ctx, &w->alarm); - } - - gpr_mu_lock(&w->mu); - if (due_to_completion) { - w->success = 1; - } - switch (w->phase) { - case WAITING: - w->phase = CALLING_BACK; - grpc_cq_end_op(exec_ctx, 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_UNREACHABLE_CODE(return ); - case CALLED_BACK: - delete = 1; - break; - } - gpr_mu_unlock(&w->mu); - - if (delete) { - delete_state_watcher(exec_ctx, w); - } -} - -static void watch_complete(grpc_exec_ctx *exec_ctx, void *pw, bool success) { - partly_done(exec_ctx, pw, 1); -} - -static void timeout_complete(grpc_exec_ctx *exec_ctx, void *pw, bool success) { - partly_done(exec_ctx, 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)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - state_watcher *w = gpr_malloc(sizeof(*w)); - - GRPC_API_TRACE( - "grpc_channel_watch_connectivity_state(" - "channel=%p, last_observed_state=%d, " - "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " - "cq=%p, tag=%p)", - 7, (channel, (int)last_observed_state, (long long)deadline.tv_sec, - (int)deadline.tv_nsec, (int)deadline.clock_type, cq, tag)); - - grpc_cq_begin_op(cq, tag); - - gpr_mu_init(&w->mu); - grpc_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_timer_init(&exec_ctx, &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) { - GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity"); - grpc_client_channel_watch_connectivity_state(&exec_ctx, client_channel_elem, - grpc_cq_pollset(cq), &w->state, - &w->on_complete); - } else if (client_channel_elem->filter == &grpc_client_uchannel_filter) { - GRPC_CHANNEL_INTERNAL_REF(channel, "watch_uchannel_connectivity"); - grpc_client_uchannel_watch_connectivity_state( - &exec_ctx, client_channel_elem, grpc_cq_pollset(cq), &w->state, - &w->on_complete); - } - - grpc_exec_ctx_finish(&exec_ctx); -} diff --git a/third_party/grpc/src/core/surface/channel_create.c b/third_party/grpc/src/core/surface/channel_create.c deleted file mode 100644 index 4d4337d2889005..00000000000000 --- a/third_party/grpc/src/core/surface/channel_create.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include -#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/api_trace.h" -#include "src/core/surface/channel.h" -#include "src/core/transport/chttp2_transport.h" - -typedef struct { - grpc_connector base; - gpr_refcount refs; - - grpc_closure *notify; - grpc_connect_in_args args; - grpc_connect_out_args *result; - grpc_closure initial_string_sent; - gpr_slice_buffer initial_string_buffer; - - grpc_endpoint *tcp; - - grpc_closure connected; -} connector; - -static void connector_ref(grpc_connector *con) { - connector *c = (connector *)con; - gpr_ref(&c->refs); -} - -static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) { - connector *c = (connector *)con; - if (gpr_unref(&c->refs)) { - /* c->initial_string_buffer does not need to be destroyed */ - gpr_free(c); - } -} - -static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg, - bool success) { - connector_unref(exec_ctx, arg); -} - -static void connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - connector *c = arg; - grpc_closure *notify; - grpc_endpoint *tcp = c->tcp; - if (tcp != NULL) { - if (!GPR_SLICE_IS_EMPTY(c->args.initial_connect_string)) { - grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent, - c); - gpr_slice_buffer_init(&c->initial_string_buffer); - gpr_slice_buffer_add(&c->initial_string_buffer, - c->args.initial_connect_string); - connector_ref(arg); - grpc_endpoint_write(exec_ctx, tcp, &c->initial_string_buffer, - &c->initial_string_sent); - } - c->result->transport = - grpc_create_chttp2_transport(exec_ctx, c->args.channel_args, tcp, 1); - grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL, - 0); - GPR_ASSERT(c->result->transport); - c->result->channel_args = c->args.channel_args; - 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; - notify->cb(exec_ctx, notify->cb_arg, 1); -} - -static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) {} - -static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, - const grpc_connect_in_args *args, - grpc_connect_out_args *result, - grpc_closure *notify) { - connector *c = (connector *)con; - GPR_ASSERT(c->notify == NULL); - GPR_ASSERT(notify->cb); - c->notify = notify; - c->args = *args; - c->result = result; - c->tcp = NULL; - grpc_closure_init(&c->connected, connected, c); - grpc_tcp_client_connect(exec_ctx, &c->connected, &c->tcp, - args->interested_parties, args->addr, args->addr_len, - args->deadline); -} - -static const grpc_connector_vtable connector_vtable = { - connector_ref, connector_unref, connector_shutdown, connector_connect}; - -typedef struct { - grpc_subchannel_factory base; - gpr_refcount refs; - 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_exec_ctx *exec_ctx, - grpc_subchannel_factory *scf) { - subchannel_factory *f = (subchannel_factory *)scf; - if (gpr_unref(&f->refs)) { - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, f->master, "subchannel_factory"); - grpc_channel_args_destroy(f->merge_args); - gpr_free(f); - } -} - -static grpc_subchannel *subchannel_factory_create_subchannel( - grpc_exec_ctx *exec_ctx, 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->args = final_args; - s = grpc_subchannel_create(&c->base, args); - grpc_connector_unref(exec_ctx, &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_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - size_t n = 0; - GRPC_API_TRACE( - "grpc_insecure_channel_create(target=%p, args=%p, reserved=%p)", 3, - (target, args, reserved)); - 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(&exec_ctx, target, filters, n, args, 1); - - f = gpr_malloc(sizeof(*f)); - f->base.vtable = &subchannel_factory_vtable; - gpr_ref_init(&f->refs, 1); - 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) { - GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, f->master, "subchannel_factory"); - grpc_subchannel_factory_unref(&exec_ctx, &f->base); - grpc_exec_ctx_finish(&exec_ctx); - return NULL; - } - - grpc_client_channel_set_resolver( - &exec_ctx, grpc_channel_get_channel_stack(channel), resolver); - GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "create"); - grpc_subchannel_factory_unref(&exec_ctx, &f->base); - - grpc_exec_ctx_finish(&exec_ctx); - - return channel; -} diff --git a/third_party/grpc/src/core/surface/channel_ping.c b/third_party/grpc/src/core/surface/channel_ping.c deleted file mode 100644 index 983f1c8a66d7f2..00000000000000 --- a/third_party/grpc/src/core/surface/channel_ping.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/surface/channel.h" - -#include - -#include -#include - -#include "src/core/surface/api_trace.h" -#include "src/core/surface/completion_queue.h" - -typedef struct { - grpc_closure closure; - void *tag; - grpc_completion_queue *cq; - grpc_cq_completion completion_storage; -} ping_result; - -static void ping_destroy(grpc_exec_ctx *exec_ctx, void *arg, - grpc_cq_completion *storage) { - gpr_free(arg); -} - -static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - ping_result *pr = arg; - grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, success, ping_destroy, pr, - &pr->completion_storage); -} - -void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq, - void *tag, void *reserved) { - grpc_transport_op op; - ping_result *pr = gpr_malloc(sizeof(*pr)); - grpc_channel_element *top_elem = - grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GPR_ASSERT(reserved == NULL); - memset(&op, 0, sizeof(op)); - pr->tag = tag; - pr->cq = cq; - grpc_closure_init(&pr->closure, ping_done, pr); - op.send_ping = &pr->closure; - op.bind_pollset = grpc_cq_pollset(cq); - grpc_cq_begin_op(cq, tag); - top_elem->filter->start_transport_op(&exec_ctx, top_elem, &op); - grpc_exec_ctx_finish(&exec_ctx); -} diff --git a/third_party/grpc/src/core/surface/completion_queue.c b/third_party/grpc/src/core/surface/completion_queue.c deleted file mode 100644 index 75298eb795c4fb..00000000000000 --- a/third_party/grpc/src/core/surface/completion_queue.c +++ /dev/null @@ -1,480 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/surface/completion_queue.h" - -#include -#include - -#include "src/core/iomgr/timer.h" -#include "src/core/iomgr/pollset.h" -#include "src/core/support/string.h" -#include "src/core/surface/api_trace.h" -#include "src/core/surface/call.h" -#include "src/core/surface/event_string.h" -#include "src/core/surface/surface_trace.h" -#include "src/core/profiling/timers.h" -#include -#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_closure pollset_shutdown_done; - -#ifndef NDEBUG - void **outstanding_tags; - size_t outstanding_tag_count; - size_t outstanding_tag_capacity; -#endif - - grpc_completion_queue *next_free; -}; - -static gpr_mu g_freelist_mu; -grpc_completion_queue *g_freelist; - -static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cc, - bool success); - -void grpc_cq_global_init(void) { gpr_mu_init(&g_freelist_mu); } - -void grpc_cq_global_shutdown(void) { - gpr_mu_destroy(&g_freelist_mu); - while (g_freelist) { - grpc_completion_queue *next = g_freelist->next_free; - grpc_pollset_destroy(&g_freelist->pollset); -#ifndef NDEBUG - gpr_free(g_freelist->outstanding_tags); -#endif - gpr_free(g_freelist); - g_freelist = next; - } -} - -struct grpc_cq_alarm { - grpc_timer alarm; - grpc_cq_completion completion; - /** completion queue where events about this alarm will be posted */ - grpc_completion_queue *cq; - /** user supplied tag */ - void *tag; -}; - -grpc_completion_queue *grpc_completion_queue_create(void *reserved) { - grpc_completion_queue *cc; - GPR_ASSERT(!reserved); - - GPR_TIMER_BEGIN("grpc_completion_queue_create", 0); - - GRPC_API_TRACE("grpc_completion_queue_create(reserved=%p)", 1, (reserved)); - - gpr_mu_lock(&g_freelist_mu); - if (g_freelist == NULL) { - gpr_mu_unlock(&g_freelist_mu); - - cc = gpr_malloc(sizeof(grpc_completion_queue)); - grpc_pollset_init(&cc->pollset); -#ifndef NDEBUG - cc->outstanding_tags = NULL; - cc->outstanding_tag_capacity = 0; -#endif - } else { - cc = g_freelist; - g_freelist = g_freelist->next_free; - gpr_mu_unlock(&g_freelist_mu); - /* pollset already initialized */ - } - - /* 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); - cc->completed_tail = &cc->completed_head; - cc->completed_head.next = (uintptr_t)cc->completed_tail; - cc->shutdown = 0; - cc->shutdown_called = 0; - cc->is_server_cq = 0; - cc->num_pluckers = 0; -#ifndef NDEBUG - cc->outstanding_tag_count = 0; -#endif - grpc_closure_init(&cc->pollset_shutdown_done, on_pollset_shutdown_done, cc); - - GPR_TIMER_END("grpc_completion_queue_create", 0); - - 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_shutdown_done(grpc_exec_ctx *exec_ctx, void *arg, - bool success) { - 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 == (uintptr_t)&cc->completed_head); - grpc_pollset_reset(&cc->pollset); - gpr_mu_lock(&g_freelist_mu); - cc->next_free = g_freelist; - g_freelist = cc; - gpr_mu_unlock(&g_freelist_mu); - } -} - -void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag) { -#ifndef NDEBUG - gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); - GPR_ASSERT(!cc->shutdown_called); - if (cc->outstanding_tag_count == cc->outstanding_tag_capacity) { - cc->outstanding_tag_capacity = GPR_MAX(4, 2 * cc->outstanding_tag_capacity); - cc->outstanding_tags = - gpr_realloc(cc->outstanding_tags, sizeof(*cc->outstanding_tags) * - cc->outstanding_tag_capacity); - } - cc->outstanding_tags[cc->outstanding_tag_count++] = tag; - 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_exec_ctx *exec_ctx, grpc_completion_queue *cc, - void *tag, int success, - void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage) { - int shutdown; - int i; - grpc_pollset_worker *pluck_worker; -#ifndef NDEBUG - int found = 0; -#endif - - GPR_TIMER_BEGIN("grpc_cq_end_op", 0); - - storage->tag = tag; - storage->done = done; - storage->done_arg = done_arg; - storage->next = - ((uintptr_t)&cc->completed_head) | ((uintptr_t)(success != 0)); - - gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); -#ifndef NDEBUG - for (i = 0; i < (int)cc->outstanding_tag_count; i++) { - if (cc->outstanding_tags[i] == tag) { - cc->outstanding_tag_count--; - GPR_SWAP(void *, cc->outstanding_tags[i], - cc->outstanding_tags[cc->outstanding_tag_count]); - found = 1; - break; - } - } - GPR_ASSERT(found); -#endif - shutdown = gpr_unref(&cc->pending_events); - if (!shutdown) { - cc->completed_tail->next = - ((uintptr_t)storage) | (1u & (uintptr_t)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 = - ((uintptr_t)storage) | (1u & (uintptr_t)cc->completed_tail->next); - cc->completed_tail = storage; - GPR_ASSERT(!cc->shutdown); - GPR_ASSERT(cc->shutdown_called); - cc->shutdown = 1; - grpc_pollset_shutdown(exec_ctx, &cc->pollset, &cc->pollset_shutdown_done); - gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - } - - GPR_TIMER_END("grpc_cq_end_op", 0); -} - -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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GPR_TIMER_BEGIN("grpc_completion_queue_next", 0); - - GRPC_API_TRACE( - "grpc_completion_queue_next(" - "cc=%p, " - "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 5, (cc, (long long)deadline.tv_sec, (int)deadline.tv_nsec, - (int)deadline.clock_type, reserved)); - 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 & ~(uintptr_t)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(&exec_ctx, 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(&exec_ctx, &cc->pollset, &worker, now, deadline); - } - GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); - GRPC_CQ_INTERNAL_UNREF(cc, "next"); - grpc_exec_ctx_finish(&exec_ctx); - - GPR_TIMER_END("grpc_completion_queue_next", 0); - - 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_UNREACHABLE_CODE(return ); -} - -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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0); - - GRPC_API_TRACE( - "grpc_completion_queue_pluck(" - "cc=%p, tag=%p, " - "deadline=gpr_timespec { tv_sec: %lld, tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 6, (cc, tag, (long long)deadline.tv_sec, (int)deadline.tv_nsec, - (int)deadline.clock_type, reserved)); - 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 & ~(uintptr_t)1)) != - &cc->completed_head) { - if (c->tag == tag) { - prev->next = (prev->next & (uintptr_t)1) | (c->next & ~(uintptr_t)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(&exec_ctx, 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(&exec_ctx, &cc->pollset, &worker, now, deadline); - del_plucker(cc, tag, &worker); - } -done: - GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); - GRPC_CQ_INTERNAL_UNREF(cc, "pluck"); - grpc_exec_ctx_finish(&exec_ctx); - - GPR_TIMER_END("grpc_completion_queue_pluck", 0); - - 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) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GPR_TIMER_BEGIN("grpc_completion_queue_shutdown", 0); - GRPC_API_TRACE("grpc_completion_queue_shutdown(cc=%p)", 1, (cc)); - gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); - if (cc->shutdown_called) { - gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - GPR_TIMER_END("grpc_completion_queue_shutdown", 0); - return; - } - cc->shutdown_called = 1; - if (gpr_unref(&cc->pending_events)) { - GPR_ASSERT(!cc->shutdown); - cc->shutdown = 1; - grpc_pollset_shutdown(&exec_ctx, &cc->pollset, &cc->pollset_shutdown_done); - } - gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); - grpc_exec_ctx_finish(&exec_ctx); - GPR_TIMER_END("grpc_completion_queue_shutdown", 0); -} - -void grpc_completion_queue_destroy(grpc_completion_queue *cc) { - GRPC_API_TRACE("grpc_completion_queue_destroy(cc=%p)", 1, (cc)); - GPR_TIMER_BEGIN("grpc_completion_queue_destroy", 0); - grpc_completion_queue_shutdown(cc); - GRPC_CQ_INTERNAL_UNREF(cc, "destroy"); - GPR_TIMER_END("grpc_completion_queue_destroy", 0); -} - -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/third_party/grpc/src/core/surface/completion_queue.h b/third_party/grpc/src/core/surface/completion_queue.h deleted file mode 100644 index 27ef90f2d58a50..00000000000000 --- a/third_party/grpc/src/core/surface/completion_queue.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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)(grpc_exec_ctx *exec_ctx, void *done_arg, - struct grpc_cq_completion *c); - void *done_arg; - /** next pointer; low bit is used to indicate success or not */ - uintptr_t 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. - \a tag is currently used only in debug builds. */ -void grpc_cq_begin_op(grpc_completion_queue *cc, void *tag); - -/* Queue a GRPC_OP_COMPLETED operation; tag must correspond to the tag passed to - grpc_cq_begin_op */ -void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cc, - void *tag, int success, - void (*done)(grpc_exec_ctx *exec_ctx, 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); - -void grpc_cq_global_init(void); -void grpc_cq_global_shutdown(void); - -#endif /* GRPC_INTERNAL_CORE_SURFACE_COMPLETION_QUEUE_H */ diff --git a/third_party/grpc/src/core/surface/event_string.c b/third_party/grpc/src/core/surface/event_string.c deleted file mode 100644 index 33cd4a43aa0393..00000000000000 --- a/third_party/grpc/src/core/surface/event_string.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/surface/event_string.h b/third_party/grpc/src/core/surface/event_string.h deleted file mode 100644 index 07c474e3a021a4..00000000000000 --- a/third_party/grpc/src/core/surface/event_string.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/surface/init.c b/third_party/grpc/src/core/surface/init.c deleted file mode 100644 index e3ab70dba70e78..00000000000000 --- a/third_party/grpc/src/core/surface/init.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include - -#include -#include -#include -#include -#include "src/core/channel/channel_stack.h" -#include "src/core/client_config/lb_policy_registry.h" -#include "src/core/client_config/lb_policies/pick_first.h" -#include "src/core/client_config/lb_policies/round_robin.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/executor.h" -#include "src/core/iomgr/iomgr.h" -#include "src/core/profiling/timers.h" -#include "src/core/surface/api_trace.h" -#include "src/core/surface/call.h" -#include "src/core/surface/completion_queue.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" - -#ifndef GRPC_DEFAULT_NAME_PREFIX -#define GRPC_DEFAULT_NAME_PREFIX "dns:///" -#endif - -#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)) { - GRPC_API_TRACE("grpc_register_plugin(init=%p, destroy=%p)", 2, - ((void*)(intptr_t)init, (void*)(intptr_t)destroy)); - 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_mdctx_global_init(); - grpc_lb_policy_registry_init(grpc_pick_first_lb_factory_create()); - grpc_register_lb_policy(grpc_pick_first_lb_factory_create()); - grpc_register_lb_policy(grpc_round_robin_lb_factory_create()); - grpc_resolver_registry_init(GRPC_DEFAULT_NAME_PREFIX); - 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("api", &grpc_api_trace); - grpc_register_tracer("channel", &grpc_trace_channel); - grpc_register_tracer("http", &grpc_http_trace); - grpc_register_tracer("flowctl", &grpc_flowctl_trace); - grpc_register_tracer("connectivity_state", &grpc_connectivity_state_trace); - grpc_security_pre_init(); - grpc_iomgr_init(); - grpc_executor_init(); - grpc_tracer_init("GRPC_TRACE"); - /* Only initialize census if no one else has and some features are - * available. */ - if (census_enabled() == CENSUS_FEATURE_NONE && - census_supported() != CENSUS_FEATURE_NONE) { - if (census_initialize(census_supported())) { /* enable all features. */ - gpr_log(GPR_ERROR, "Could not initialize census."); - } - } - gpr_timers_global_init(); - grpc_cq_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); - GRPC_API_TRACE("grpc_init(void)", 0, ()); -} - -void grpc_shutdown(void) { - int i; - GRPC_API_TRACE("grpc_shutdown(void)", 0, ()); - gpr_mu_lock(&g_init_mu); - if (--g_initializations == 0) { - grpc_executor_shutdown(); - grpc_cq_global_shutdown(); - grpc_iomgr_shutdown(); - census_shutdown(); - gpr_timers_global_destroy(); - grpc_tracer_shutdown(); - grpc_resolver_registry_shutdown(); - grpc_lb_policy_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(); - } - } - grpc_mdctx_global_shutdown(); - } - 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/third_party/grpc/src/core/surface/init.h b/third_party/grpc/src/core/surface/init.h deleted file mode 100644 index 771c30f4125784..00000000000000 --- a/third_party/grpc/src/core/surface/init.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/surface/init_secure.c b/third_party/grpc/src/core/surface/init_secure.c deleted file mode 100644 index fa20e91583c996..00000000000000 --- a/third_party/grpc/src/core/surface/init_secure.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/surface/init_unsecure.c b/third_party/grpc/src/core/surface/init_unsecure.c deleted file mode 100644 index 630d564a7d849c..00000000000000 --- a/third_party/grpc/src/core/surface/init_unsecure.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/surface/lame_client.c b/third_party/grpc/src/core/surface/lame_client.c deleted file mode 100644 index 705996cad35259..00000000000000 --- a/third_party/grpc/src/core/surface/lame_client.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include - -#include "src/core/channel/channel_stack.h" -#include "src/core/support/string.h" -#include "src/core/surface/api_trace.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_status_code error_code; - const char *error_message; -} channel_data; - -static void fill_metadata(grpc_call_element *elem, grpc_metadata_batch *mdb) { - call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; - char tmp[GPR_LTOA_MIN_BUFSIZE]; - gpr_ltoa(chand->error_code, tmp); - calld->status.md = grpc_mdelem_from_strings("grpc-status", tmp); - calld->details.md = - grpc_mdelem_from_strings("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->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); -} - -static void lame_start_transport_stream_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op *op) { - GRPC_CALL_LOG_OP(GPR_INFO, elem, op); - if (op->recv_initial_metadata != NULL) { - fill_metadata(elem, op->recv_initial_metadata); - } else if (op->recv_trailing_metadata != NULL) { - fill_metadata(elem, op->recv_trailing_metadata); - } - grpc_exec_ctx_enqueue(exec_ctx, op->on_complete, false, NULL); - grpc_exec_ctx_enqueue(exec_ctx, op->recv_message_ready, false, NULL); -} - -static char *lame_get_peer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - return NULL; -} - -static void lame_start_transport_op(grpc_exec_ctx *exec_ctx, - 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( - exec_ctx, op->on_connectivity_state_change->cb_arg, 1); - } - if (op->on_consumed != NULL) { - op->on_consumed->cb(exec_ctx, op->on_consumed->cb_arg, 1); - } -} - -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) {} - -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) {} - -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - GPR_ASSERT(args->is_first); - GPR_ASSERT(args->is_last); -} - -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - 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, grpc_call_stack_ignore_set_pollset, 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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - static const grpc_channel_filter *filters[] = {&lame_filter}; - channel = - grpc_channel_create_from_filters(&exec_ctx, target, filters, 1, NULL, 1); - elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); - GRPC_API_TRACE( - "grpc_lame_client_channel_create(target=%s, error_code=%d, " - "error_message=%s)", - 3, (target, (int)error_code, error_message)); - GPR_ASSERT(elem->filter == &lame_filter); - chand = (channel_data *)elem->channel_data; - chand->error_code = error_code; - chand->error_message = error_message; - grpc_exec_ctx_finish(&exec_ctx); - return channel; -} diff --git a/third_party/grpc/src/core/surface/metadata_array.c b/third_party/grpc/src/core/surface/metadata_array.c deleted file mode 100644 index 4c7bf17835aada..00000000000000 --- a/third_party/grpc/src/core/surface/metadata_array.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/core/surface/api_trace.h" - -void grpc_metadata_array_init(grpc_metadata_array* array) { - GRPC_API_TRACE("grpc_metadata_array_init(array=%p)", 1, (array)); - memset(array, 0, sizeof(*array)); -} - -void grpc_metadata_array_destroy(grpc_metadata_array* array) { - GRPC_API_TRACE("grpc_metadata_array_destroy(array=%p)", 1, (array)); - gpr_free(array->metadata); -} diff --git a/third_party/grpc/src/core/surface/secure_channel_create.c b/third_party/grpc/src/core/surface/secure_channel_create.c deleted file mode 100644 index dd1441ad67a737..00000000000000 --- a/third_party/grpc/src/core/surface/secure_channel_create.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include -#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/security_context.h" -#include "src/core/surface/api_trace.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_closure *notify; - grpc_connect_in_args args; - grpc_connect_out_args *result; - grpc_closure initial_string_sent; - gpr_slice_buffer initial_string_buffer; - - gpr_mu mu; - grpc_endpoint *connecting_endpoint; - grpc_endpoint *newly_connecting_endpoint; - - grpc_closure connected_closure; -} connector; - -static void connector_ref(grpc_connector *con) { - connector *c = (connector *)con; - gpr_ref(&c->refs); -} - -static void connector_unref(grpc_exec_ctx *exec_ctx, grpc_connector *con) { - connector *c = (connector *)con; - if (gpr_unref(&c->refs)) { - /* c->initial_string_buffer does not need to be destroyed */ - gpr_free(c); - } -} - -static void on_secure_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_security_status status, - grpc_endpoint *secure_endpoint, - grpc_auth_context *auth_context) { - connector *c = arg; - grpc_closure *notify; - grpc_channel_args *args_copy = NULL; - gpr_mu_lock(&c->mu); - if (c->connecting_endpoint == NULL) { - memset(c->result, 0, sizeof(*c->result)); - gpr_mu_unlock(&c->mu); - } else if (status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, "Secure handshake failed with error %d.", status); - memset(c->result, 0, sizeof(*c->result)); - c->connecting_endpoint = NULL; - gpr_mu_unlock(&c->mu); - } else { - grpc_arg auth_context_arg; - c->connecting_endpoint = NULL; - gpr_mu_unlock(&c->mu); - c->result->transport = grpc_create_chttp2_transport( - exec_ctx, c->args.channel_args, secure_endpoint, 1); - grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, NULL, - 0); - auth_context_arg = grpc_auth_context_to_arg(auth_context); - args_copy = grpc_channel_args_copy_and_add(c->args.channel_args, - &auth_context_arg, 1); - c->result->channel_args = args_copy; - 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; - /* look at c->args which are connector args. */ - notify->cb(exec_ctx, notify->cb_arg, 1); - if (args_copy != NULL) grpc_channel_args_destroy(args_copy); -} - -static void on_initial_connect_string_sent(grpc_exec_ctx *exec_ctx, void *arg, - bool success) { - connector *c = arg; - grpc_security_connector_do_handshake(exec_ctx, &c->security_connector->base, - c->connecting_endpoint, - on_secure_handshake_done, c); -} - -static void connected(grpc_exec_ctx *exec_ctx, void *arg, bool success) { - connector *c = arg; - grpc_closure *notify; - grpc_endpoint *tcp = c->newly_connecting_endpoint; - if (tcp != NULL) { - gpr_mu_lock(&c->mu); - GPR_ASSERT(c->connecting_endpoint == NULL); - c->connecting_endpoint = tcp; - gpr_mu_unlock(&c->mu); - if (!GPR_SLICE_IS_EMPTY(c->args.initial_connect_string)) { - grpc_closure_init(&c->initial_string_sent, on_initial_connect_string_sent, - c); - gpr_slice_buffer_init(&c->initial_string_buffer); - gpr_slice_buffer_add(&c->initial_string_buffer, - c->args.initial_connect_string); - grpc_endpoint_write(exec_ctx, tcp, &c->initial_string_buffer, - &c->initial_string_sent); - } else { - grpc_security_connector_do_handshake(exec_ctx, - &c->security_connector->base, tcp, - on_secure_handshake_done, c); - } - } else { - memset(c->result, 0, sizeof(*c->result)); - notify = c->notify; - c->notify = NULL; - notify->cb(exec_ctx, notify->cb_arg, 1); - } -} - -static void connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *con) { - connector *c = (connector *)con; - grpc_endpoint *ep; - gpr_mu_lock(&c->mu); - ep = c->connecting_endpoint; - c->connecting_endpoint = NULL; - gpr_mu_unlock(&c->mu); - if (ep) { - grpc_endpoint_shutdown(exec_ctx, ep); - } -} - -static void connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *con, - const grpc_connect_in_args *args, - grpc_connect_out_args *result, - grpc_closure *notify) { - connector *c = (connector *)con; - GPR_ASSERT(c->notify == NULL); - GPR_ASSERT(notify->cb); - c->notify = notify; - c->args = *args; - c->result = result; - gpr_mu_lock(&c->mu); - GPR_ASSERT(c->connecting_endpoint == NULL); - gpr_mu_unlock(&c->mu); - grpc_closure_init(&c->connected_closure, connected, c); - grpc_tcp_client_connect( - exec_ctx, &c->connected_closure, &c->newly_connecting_endpoint, - args->interested_parties, args->addr, args->addr_len, args->deadline); -} - -static const grpc_connector_vtable connector_vtable = { - connector_ref, connector_unref, connector_shutdown, connector_connect}; - -typedef struct { - grpc_subchannel_factory base; - gpr_refcount refs; - 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_exec_ctx *exec_ctx, - 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(exec_ctx, f->master, "subchannel_factory"); - grpc_channel_args_destroy(f->merge_args); - gpr_free(f); - } -} - -static grpc_subchannel *subchannel_factory_create_subchannel( - grpc_exec_ctx *exec_ctx, 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_mu_init(&c->mu); - gpr_ref_init(&c->refs, 1); - args->args = final_args; - s = grpc_subchannel_create(&c->base, args); - grpc_connector_unref(exec_ctx, &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_channel_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 *security_connector; - grpc_resolver *resolver; - subchannel_factory *f; -#define MAX_FILTERS 3 - const grpc_channel_filter *filters[MAX_FILTERS]; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - size_t n = 0; - - GRPC_API_TRACE( - "grpc_secure_channel_create(creds=%p, target=%s, args=%p, " - "reserved=%p)", - 4, (creds, target, args, reserved)); - GPR_ASSERT(reserved == NULL); - - if (grpc_find_security_connector_in_args(args) != NULL) { - gpr_log(GPR_ERROR, "Cannot set security context in channel args."); - grpc_exec_ctx_finish(&exec_ctx); - return grpc_lame_client_channel_create( - target, GRPC_STATUS_INVALID_ARGUMENT, - "Security connector exists in channel args."); - } - - if (grpc_channel_credentials_create_security_connector( - creds, target, args, &security_connector, &new_args_from_connector) != - GRPC_SECURITY_OK) { - grpc_exec_ctx_finish(&exec_ctx); - return grpc_lame_client_channel_create( - target, GRPC_STATUS_INVALID_ARGUMENT, - "Failed to create security connector."); - } - - connector_arg = grpc_security_connector_to_arg(&security_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(&exec_ctx, target, filters, n, - args_copy, 1); - - f = gpr_malloc(sizeof(*f)); - f->base.vtable = &subchannel_factory_vtable; - gpr_ref_init(&f->refs, 1); - GRPC_SECURITY_CONNECTOR_REF(&security_connector->base, "subchannel_factory"); - f->security_connector = security_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) { - grpc_client_channel_set_resolver( - &exec_ctx, grpc_channel_get_channel_stack(channel), resolver); - GRPC_RESOLVER_UNREF(&exec_ctx, resolver, "create"); - } - grpc_subchannel_factory_unref(&exec_ctx, &f->base); - GRPC_SECURITY_CONNECTOR_UNREF(&security_connector->base, "channel_create"); - grpc_channel_args_destroy(args_copy); - if (new_args_from_connector != NULL) { - grpc_channel_args_destroy(new_args_from_connector); - } - - if (!resolver) { - GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, channel, "subchannel_factory"); - channel = NULL; - } - grpc_exec_ctx_finish(&exec_ctx); - - return channel; -} diff --git a/third_party/grpc/src/core/surface/server.c b/third_party/grpc/src/core/surface/server.c deleted file mode 100644 index 42cffccb4c9e00..00000000000000 --- a/third_party/grpc/src/core/surface/server.c +++ /dev/null @@ -1,1343 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/surface/server.h" - -#include -#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/api_trace.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" -#include "src/core/transport/static_metadata.h" - -typedef struct listener { - void *arg; - void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_pollset **pollsets, size_t pollset_count); - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_closure *closure); - struct listener *next; - grpc_closure destroy_done; -} 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; - grpc_metadata_array *initial_metadata; - union { - struct { - grpc_call_details *details; - } batch; - struct { - registered_method *registered_method; - gpr_timespec *deadline; - grpc_byte_buffer **optional_payload; - } registered; - } data; - grpc_closure publish; -} 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; - /* linked list of all channels on a server */ - channel_data *next; - channel_data *prev; - channel_registered_method *registered_methods; - uint32_t registered_method_slots; - uint32_t registered_method_max_probes; - grpc_closure finish_destroy_channel_closure; - grpc_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; - - grpc_completion_queue *cq_new; - - grpc_metadata_batch *recv_initial_metadata; - grpc_metadata_array initial_metadata; - - grpc_closure got_initial_metadata; - grpc_closure server_on_recv_initial_metadata; - grpc_closure kill_zombie_closure; - grpc_closure *on_done_recv_initial_metadata; - - 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; - grpc_channel_filter const **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; - size_t max_requested_calls; - - gpr_atm shutdown_flag; - uint8_t 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_exec_ctx *exec_ctx, grpc_server *server, - call_data *calld, requested_call *rc); -static void fail_call(grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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_closure closure; - gpr_slice slice; -}; - -static void shutdown_cleanup(grpc_exec_ctx *exec_ctx, void *arg, - bool iomgr_status_ignored) { - struct shutdown_cleanup_args *a = arg; - gpr_slice_unref(a->slice); - gpr_free(a); -} - -static void send_shutdown(grpc_exec_ctx *exec_ctx, 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_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(exec_ctx, elem, &op); -} - -static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx, - channel_broadcaster *cb, - int send_goaway, - int force_disconnect) { - size_t i; - - for (i = 0; i < cb->num_channels; i++) { - send_shutdown(exec_ctx, cb->channels[i], send_goaway, force_disconnect); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, cb->channels[i], "broadcast"); - } - gpr_free(cb->channels); -} - -/* - * request_matcher - */ - -static void request_matcher_init(request_matcher *rm, size_t entries) { - memset(rm, 0, sizeof(*rm)); - rm->requests = gpr_stack_lockfree_create(entries); -} - -static void request_matcher_destroy(request_matcher *rm) { - GPR_ASSERT(gpr_stack_lockfree_pop(rm->requests) == -1); - gpr_stack_lockfree_destroy(rm->requests); -} - -static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem, bool success) { - grpc_call_destroy(grpc_call_from_top_element(elem)); -} - -static void request_matcher_zombify_all_pending_calls(grpc_exec_ctx *exec_ctx, - request_matcher *rm) { - while (rm->pending_head) { - call_data *calld = rm->pending_head; - rm->pending_head = calld->pending_next; - gpr_mu_lock(&calld->mu_state); - calld->state = ZOMBIED; - gpr_mu_unlock(&calld->mu_state); - grpc_closure_init( - &calld->kill_zombie_closure, kill_zombie, - grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); - grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL); - } -} - -static void request_matcher_kill_requests(grpc_exec_ctx *exec_ctx, - grpc_server *server, - request_matcher *rm) { - int request_id; - while ((request_id = gpr_stack_lockfree_pop(rm->requests)) != -1) { - fail_call(exec_ctx, 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_exec_ctx *exec_ctx, 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((void *)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_exec_ctx *exec_ctx, grpc_server *server) { - if (gpr_unref(&server->internal_refcount)) { - server_delete(exec_ctx, 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(grpc_exec_ctx *exec_ctx, void *cd, - bool success) { - channel_data *chand = cd; - grpc_server *server = chand->server; - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "server"); - server_unref(exec_ctx, server); -} - -static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand) { - if (is_channel_orphaned(chand)) return; - GPR_ASSERT(chand->server != NULL); - orphan_channel(chand); - server_ref(chand->server); - maybe_finish_shutdown(exec_ctx, chand->server); - chand->finish_destroy_channel_closure.cb = finish_destroy_channel; - chand->finish_destroy_channel_closure.cb_arg = chand; - grpc_exec_ctx_enqueue(exec_ctx, &chand->finish_destroy_channel_closure, true, - NULL); -} - -static void finish_start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_server *server, - grpc_call_element *elem, request_matcher *rm) { - 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_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); - grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL); - return; - } - - request_id = gpr_stack_lockfree_pop(rm->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 (rm->pending_head == NULL) { - rm->pending_tail = rm->pending_head = calld; - } else { - rm->pending_tail->pending_next = calld; - rm->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(exec_ctx, server, calld, &server->requested_calls[request_id]); - } -} - -static void start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - channel_data *chand = elem->channel_data; - call_data *calld = elem->call_data; - grpc_server *server = chand->server; - uint32_t i; - uint32_t 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(exec_ctx, 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(exec_ctx, server, elem, - &rm->server_registered_method->request_matcher); - return; - } - } - finish_start_new_rpc(exec_ctx, 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(grpc_exec_ctx *exec_ctx, void *server, - grpc_cq_completion *completion) { - server_unref(exec_ctx, 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_exec_ctx *exec_ctx, - grpc_server *server) { - registered_method *rm; - request_matcher_kill_requests(exec_ctx, server, - &server->unregistered_request_matcher); - request_matcher_zombify_all_pending_calls( - exec_ctx, &server->unregistered_request_matcher); - for (rm = server->registered_methods; rm; rm = rm->next) { - request_matcher_kill_requests(exec_ctx, server, &rm->request_matcher); - request_matcher_zombify_all_pending_calls(exec_ctx, &rm->request_matcher); - } -} - -static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, - grpc_server *server) { - size_t i; - if (!gpr_atm_acq_load(&server->shutdown_flag) || server->shutdown_published) { - return; - } - - kill_pending_work_locked(exec_ctx, 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(exec_ctx, 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; - call_data *calld = elem->call_data; - if (md->key == GRPC_MDSTR_PATH) { - calld->path = GRPC_MDSTR_REF(md->value); - return NULL; - } else if (md->key == GRPC_MDSTR_AUTHORITY) { - calld->host = GRPC_MDSTR_REF(md->value); - return NULL; - } - return md; -} - -static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr, - bool success) { - grpc_call_element *elem = ptr; - call_data *calld = elem->call_data; - gpr_timespec op_deadline; - - grpc_metadata_batch_filter(calld->recv_initial_metadata, server_filter, elem); - op_deadline = calld->recv_initial_metadata->deadline; - if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) { - calld->deadline = op_deadline; - } - if (calld->host && calld->path) { - /* do nothing */ - } else { - success = 0; - } - - calld->on_done_recv_initial_metadata->cb( - exec_ctx, calld->on_done_recv_initial_metadata->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_initial_metadata != NULL) { - calld->recv_initial_metadata = op->recv_initial_metadata; - calld->on_done_recv_initial_metadata = op->on_complete; - op->on_complete = &calld->server_on_recv_initial_metadata; - } -} - -static void server_start_transport_stream_op(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, elem, op); -} - -static void got_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr, - bool success) { - grpc_call_element *elem = ptr; - call_data *calld = elem->call_data; - if (success) { - start_new_rpc(exec_ctx, elem); - } else { - gpr_mu_lock(&calld->mu_state); - if (calld->state == NOT_STARTED) { - calld->state = ZOMBIED; - gpr_mu_unlock(&calld->mu_state); - grpc_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); - grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, NULL); - } 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); - } - } -} - -static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd, - grpc_transport *transport, - const void *transport_server_data) { - channel_data *chand = cd; - /* create a call */ - grpc_call *call = - grpc_call_create(chand->channel, NULL, 0, NULL, transport_server_data, - NULL, 0, gpr_inf_future(GPR_CLOCK_MONOTONIC)); - grpc_call_element *elem = - grpc_call_stack_element(grpc_call_get_call_stack(call), 0); - call_data *calld = elem->call_data; - grpc_op op; - memset(&op, 0, sizeof(op)); - op.op = GRPC_OP_RECV_INITIAL_METADATA; - op.data.recv_initial_metadata = &calld->initial_metadata; - grpc_closure_init(&calld->got_initial_metadata, got_initial_metadata, elem); - grpc_call_start_batch_and_execute(exec_ctx, call, &op, 1, - &calld->got_initial_metadata); -} - -static void channel_connectivity_changed(grpc_exec_ctx *exec_ctx, void *cd, - bool 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(exec_ctx, - grpc_channel_stack_element( - grpc_channel_get_channel_stack(chand->channel), 0), - &op); - } else { - gpr_mu_lock(&server->mu_global); - destroy_channel(exec_ctx, chand); - gpr_mu_unlock(&server->mu_global); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "connectivity"); - } -} - -static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_call_element_args *args) { - 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_closure_init(&calld->server_on_recv_initial_metadata, - server_on_recv_initial_metadata, elem); - - server_ref(chand->server); -} - -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, - 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); - } - grpc_metadata_array_destroy(&calld->initial_metadata); - - gpr_mu_destroy(&calld->mu_state); - - server_unref(exec_ctx, chand->server); -} - -static void init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = elem->channel_data; - GPR_ASSERT(args->is_first); - GPR_ASSERT(!args->is_last); - chand->server = NULL; - chand->channel = NULL; - chand->next = chand->prev = chand; - chand->registered_methods = NULL; - chand->connectivity_state = GRPC_CHANNEL_IDLE; - grpc_closure_init(&chand->channel_connectivity_changed, - channel_connectivity_changed, chand); -} - -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, chand->server); - gpr_mu_unlock(&chand->server->mu_global); - server_unref(exec_ctx, 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, grpc_call_stack_ignore_set_pollset, 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; - GRPC_API_TRACE( - "grpc_server_register_completion_queue(server=%p, cq=%p, reserved=%p)", 3, - (server, cq, reserved)); - 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; - int census_enabled = grpc_channel_args_is_census_enabled(args); - - 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, (int)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 + 1u + (census_enabled ? 1u : 0u); - 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 + 1u + (census_enabled ? 1u : 0u)] = 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; - GRPC_API_TRACE("grpc_server_register_method(server=%p, method=%s, host=%s)", - 3, (server, method, host)); - 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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server)); - - 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(&exec_ctx, server, l->arg, server->pollsets, server->cq_count); - } - - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s, - grpc_transport *transport, - grpc_channel_filter const **extra_filters, - size_t num_extra_filters, - 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; - uint32_t hash; - size_t slots; - uint32_t probes; - uint32_t 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(exec_ctx, transport, &op); - } - - channel = grpc_channel_create_from_filters(exec_ctx, NULL, filters, - num_filters, args, 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(rm->host) : NULL; - method = grpc_mdstr_from_string(rm->method); - 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; - } - GPR_ASSERT(slots <= UINT32_MAX); - chand->registered_method_slots = (uint32_t)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((void *)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) != 0; - grpc_transport_perform_op(exec_ctx, transport, &op); -} - -void done_published_shutdown(grpc_exec_ctx *exec_ctx, void *done_arg, - grpc_cq_completion *storage) { - (void)done_arg; - gpr_free(storage); -} - -static void listener_destroy_done(grpc_exec_ctx *exec_ctx, void *s, - bool success) { - grpc_server *server = s; - gpr_mu_lock(&server->mu_global); - server->listeners_destroyed++; - maybe_finish_shutdown(exec_ctx, server); - gpr_mu_unlock(&server->mu_global); -} - -void grpc_server_shutdown_and_notify(grpc_server *server, - grpc_completion_queue *cq, void *tag) { - listener *l; - shutdown_tag *sdt; - channel_broadcaster broadcaster; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", 3, - (server, cq, tag)); - - /* lock, and gather up some stuff to do */ - gpr_mu_lock(&server->mu_global); - grpc_cq_begin_op(cq, tag); - if (server->shutdown_published) { - grpc_cq_end_op(&exec_ctx, cq, tag, 1, done_published_shutdown, NULL, - gpr_malloc(sizeof(grpc_cq_completion))); - gpr_mu_unlock(&server->mu_global); - goto done; - } - 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); - goto done; - } - - server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); - - channel_broadcaster_init(server, &broadcaster); - - gpr_atm_rel_store(&server->shutdown_flag, 1); - - /* collect all unregistered then registered calls */ - gpr_mu_lock(&server->mu_call); - kill_pending_work_locked(&exec_ctx, server); - gpr_mu_unlock(&server->mu_call); - - maybe_finish_shutdown(&exec_ctx, server); - gpr_mu_unlock(&server->mu_global); - - /* Shutdown listeners */ - for (l = server->listeners; l; l = l->next) { - grpc_closure_init(&l->destroy_done, listener_destroy_done, server); - l->destroy(&exec_ctx, server, l->arg, &l->destroy_done); - } - - channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 1, 0); - -done: - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_cancel_all_calls(grpc_server *server) { - channel_broadcaster broadcaster; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_cancel_all_calls(server=%p)", 1, (server)); - - gpr_mu_lock(&server->mu_global); - channel_broadcaster_init(server, &broadcaster); - gpr_mu_unlock(&server->mu_global); - - channel_broadcaster_shutdown(&exec_ctx, &broadcaster, 0, 1); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_destroy(grpc_server *server) { - listener *l; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_destroy(server=%p)", 1, (server)); - - 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(&exec_ctx, server); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_add_listener( - grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_pollset **pollsets, size_t pollset_count), - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_closure *on_done)) { - 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_exec_ctx *exec_ctx, - grpc_server *server, - requested_call *rc) { - call_data *calld = NULL; - request_matcher *rm = NULL; - int request_id; - if (gpr_atm_acq_load(&server->shutdown_flag)) { - fail_call(exec_ctx, 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(exec_ctx, server, rc); - return GRPC_CALL_OK; - } - switch (rc->type) { - case BATCH_CALL: - rm = &server->unregistered_request_matcher; - break; - case REGISTERED_CALL: - rm = &rc->data.registered.registered_method->request_matcher; - break; - } - server->requested_calls[request_id] = *rc; - gpr_free(rc); - if (gpr_stack_lockfree_push(rm->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 = rm->pending_head) != NULL) { - request_id = gpr_stack_lockfree_pop(rm->requests); - if (request_id == -1) break; - rm->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_closure_init( - &calld->kill_zombie_closure, kill_zombie, - grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0)); - grpc_exec_ctx_enqueue(exec_ctx, &calld->kill_zombie_closure, true, - NULL); - } else { - GPR_ASSERT(calld->state == PENDING); - calld->state = ACTIVATED; - gpr_mu_unlock(&calld->mu_state); - begin_call(exec_ctx, 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) { - grpc_call_error error; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - requested_call *rc = gpr_malloc(sizeof(*rc)); - GRPC_API_TRACE( - "grpc_server_request_call(" - "server=%p, call=%p, details=%p, initial_metadata=%p, " - "cq_bound_to_call=%p, cq_for_notification=%p, tag=%p)", - 7, (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); - error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; - goto done; - } - grpc_cq_begin_op(cq_for_notification, tag); - 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->initial_metadata = initial_metadata; - error = queue_call_request(&exec_ctx, server, rc); -done: - grpc_exec_ctx_finish(&exec_ctx); - return error; -} - -grpc_call_error grpc_server_request_registered_call( - grpc_server *server, void *rmp, 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) { - grpc_call_error error; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - requested_call *rc = gpr_malloc(sizeof(*rc)); - registered_method *rm = rmp; - GRPC_API_TRACE( - "grpc_server_request_registered_call(" - "server=%p, rmp=%p, call=%p, deadline=%p, initial_metadata=%p, " - "optional_payload=%p, cq_bound_to_call=%p, cq_for_notification=%p, " - "tag=%p)", - 9, (server, rmp, call, deadline, initial_metadata, optional_payload, - cq_bound_to_call, cq_for_notification, tag)); - if (!grpc_cq_is_server_cq(cq_for_notification)) { - gpr_free(rc); - error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; - goto done; - } - grpc_cq_begin_op(cq_for_notification, tag); - 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 = rm; - rc->data.registered.deadline = deadline; - rc->initial_metadata = initial_metadata; - rc->data.registered.optional_payload = optional_payload; - error = queue_call_request(&exec_ctx, server, rc); -done: - grpc_exec_ctx_finish(&exec_ctx); - return error; -} - -static void publish_registered_or_batch(grpc_exec_ctx *exec_ctx, - void *user_data, bool success); - -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_exec_ctx *exec_ctx, grpc_server *server, - call_data *calld, requested_call *rc) { - grpc_op ops[1]; - grpc_op *op = ops; - - memset(ops, 0, sizeof(ops)); - - /* 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(exec_ctx, calld->call, rc->cq_bound_to_call); - grpc_closure_init(&rc->publish, publish_registered_or_batch, rc); - *rc->call = calld->call; - calld->cq_new = rc->cq_for_notification; - GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, calld->initial_metadata); - 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; - break; - case REGISTERED_CALL: - *rc->data.registered.deadline = calld->deadline; - if (rc->data.registered.optional_payload) { - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message = rc->data.registered.optional_payload; - op++; - } - break; - default: - GPR_UNREACHABLE_CODE(return ); - } - - GRPC_CALL_INTERNAL_REF(calld->call, "server"); - grpc_call_start_batch_and_execute(exec_ctx, calld->call, ops, - (size_t)(op - ops), &rc->publish); -} - -static void done_request_event(grpc_exec_ctx *exec_ctx, 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_ASSERT(rc - server->requested_calls <= INT_MAX); - gpr_stack_lockfree_push(server->request_freelist, - (int)(rc - server->requested_calls)); - } else { - gpr_free(req); - } - - server_unref(exec_ctx, server); -} - -static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, - requested_call *rc) { - *rc->call = NULL; - rc->initial_metadata->count = 0; - - server_ref(server); - grpc_cq_end_op(exec_ctx, rc->cq_for_notification, rc->tag, 0, - done_request_event, rc, &rc->completion); -} - -static void publish_registered_or_batch(grpc_exec_ctx *exec_ctx, void *prc, - bool success) { - requested_call *rc = prc; - grpc_call *call = *rc->call; - grpc_call_element *elem = - grpc_call_stack_element(grpc_call_get_call_stack(call), 0); - call_data *calld = elem->call_data; - channel_data *chand = elem->channel_data; - server_ref(chand->server); - grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, success, done_request_event, - rc, &rc->completion); - GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "server"); -} - -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/third_party/grpc/src/core/surface/server.h b/third_party/grpc/src/core/surface/server.h deleted file mode 100644 index a957fdb3605fb9..00000000000000 --- a/third_party/grpc/src/core/surface/server.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, grpc_server *server, void *listener, - void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_pollset **pollsets, size_t npollsets), - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_closure *on_done)); - -/* Setup a transport - creates a channel stack, binds the transport to the - server */ -void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *server, - grpc_transport *transport, - grpc_channel_filter const **extra_filters, - size_t num_extra_filters, - 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/third_party/grpc/src/core/surface/server_chttp2.c b/third_party/grpc/src/core/surface/server_chttp2.c deleted file mode 100644 index ce970dfe7370eb..00000000000000 --- a/third_party/grpc/src/core/surface/server_chttp2.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/api_trace.h" -#include "src/core/surface/server.h" -#include "src/core/transport/chttp2_transport.h" -#include -#include -#include - -static void setup_transport(grpc_exec_ctx *exec_ctx, void *server, - grpc_transport *transport) { - static grpc_channel_filter const *extra_filters[] = { - &grpc_http_server_filter}; - grpc_server_setup_transport(exec_ctx, server, transport, extra_filters, - GPR_ARRAY_SIZE(extra_filters), - grpc_server_get_channel_args(server)); -} - -static void new_transport(grpc_exec_ctx *exec_ctx, void *server, - grpc_endpoint *tcp, - grpc_tcp_server_acceptor *acceptor) { - /* - * 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_transport *transport = grpc_create_chttp2_transport( - exec_ctx, grpc_server_get_channel_args(server), tcp, 0); - setup_transport(exec_ctx, server, transport); - grpc_chttp2_transport_start_reading(exec_ctx, transport, NULL, 0); -} - -/* Server callback: start listening on our ports */ -static void start(grpc_exec_ctx *exec_ctx, grpc_server *server, void *tcpp, - grpc_pollset **pollsets, size_t pollset_count) { - grpc_tcp_server *tcp = tcpp; - grpc_tcp_server_start(exec_ctx, tcp, pollsets, pollset_count, new_transport, - server); -} - -/* Server callback: destroy the tcp listener (so we don't generate further - callbacks) */ -static void destroy(grpc_exec_ctx *exec_ctx, grpc_server *server, void *tcpp, - grpc_closure *destroy_done) { - grpc_tcp_server *tcp = tcpp; - grpc_tcp_server_unref(exec_ctx, tcp); - grpc_exec_ctx_enqueue(exec_ctx, destroy_done, true, NULL); -} - -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; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2, - (server, addr)); - - resolved = grpc_blocking_resolve_address(addr, "http"); - if (!resolved) { - goto error; - } - - tcp = grpc_tcp_server_create(NULL); - GPR_ASSERT(tcp); - - 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(&exec_ctx, server, tcp, start, destroy); - goto done; - -/* Error path: cleanup and return */ -error: - if (resolved) { - grpc_resolved_addresses_destroy(resolved); - } - if (tcp) { - grpc_tcp_server_unref(&exec_ctx, tcp); - } - port_num = 0; - -done: - grpc_exec_ctx_finish(&exec_ctx); - return port_num; -} diff --git a/third_party/grpc/src/core/surface/server_create.c b/third_party/grpc/src/core/surface/server_create.c deleted file mode 100644 index 5e37e80948e4b0..00000000000000 --- a/third_party/grpc/src/core/surface/server_create.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include "src/core/census/grpc_filter.h" -#include "src/core/channel/channel_args.h" -#include "src/core/channel/compress_filter.h" -#include "src/core/surface/api_trace.h" -#include "src/core/surface/completion_queue.h" -#include "src/core/surface/server.h" - -grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) { - const grpc_channel_filter *filters[3]; - size_t num_filters = 0; - filters[num_filters++] = &grpc_compress_filter; - GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved)); - return grpc_server_create_from_filters(filters, num_filters, args); -} diff --git a/third_party/grpc/src/core/surface/surface_trace.h b/third_party/grpc/src/core/surface/surface_trace.h deleted file mode 100644 index 93b2859ac5b273..00000000000000 --- a/third_party/grpc/src/core/surface/surface_trace.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/surface/api_trace.h" -#include - -#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \ - if (grpc_api_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/third_party/grpc/src/core/surface/validate_metadata.c b/third_party/grpc/src/core/surface/validate_metadata.c deleted file mode 100644 index df2e80b4b78cdf..00000000000000 --- a/third_party/grpc/src/core/surface/validate_metadata.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * - * Copyright 2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -#include - -static int conforms_to(const char *s, size_t len, const uint8_t *legal_bits) { - const char *p = s; - const char *e = s + len; - 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_header_key_is_legal(const char *key, size_t length) { - static const uint8_t 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}; - if (length == 0) { - return 0; - } - return conforms_to(key, length, legal_header_bits); -} - -int grpc_header_nonbin_value_is_legal(const char *value, size_t length) { - static const uint8_t legal_header_bits[256 / 8] = { - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 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(value, length, legal_header_bits); -} - -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/third_party/grpc/src/core/surface/version.c b/third_party/grpc/src/core/surface/version.c deleted file mode 100644 index c8de7537f1f49d..00000000000000 --- a/third_party/grpc/src/core/surface/version.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* This file is autogenerated from: - templates/src/core/surface/version.c.template */ - -#include - -const char *grpc_version_string(void) { return "0.13.0"; } diff --git a/third_party/grpc/src/core/transport/byte_stream.c b/third_party/grpc/src/core/transport/byte_stream.c deleted file mode 100644 index 8e6fb2cbefd052..00000000000000 --- a/third_party/grpc/src/core/transport/byte_stream.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/byte_stream.h" - -#include - -#include - -int grpc_byte_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, gpr_slice *slice, - size_t max_size_hint, grpc_closure *on_complete) { - return byte_stream->next(exec_ctx, byte_stream, slice, max_size_hint, - on_complete); -} - -void grpc_byte_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream) { - byte_stream->destroy(exec_ctx, byte_stream); -} - -/* slice_buffer_stream */ - -static int slice_buffer_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - gpr_slice *slice, size_t max_size_hint, - grpc_closure *on_complete) { - grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; - GPR_ASSERT(stream->cursor < stream->backing_buffer->count); - *slice = gpr_slice_ref(stream->backing_buffer->slices[stream->cursor]); - stream->cursor++; - return 1; -} - -static void slice_buffer_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream) {} - -void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream *stream, - gpr_slice_buffer *slice_buffer, - uint32_t flags) { - GPR_ASSERT(slice_buffer->length <= UINT32_MAX); - stream->base.length = (uint32_t)slice_buffer->length; - stream->base.flags = flags; - stream->base.next = slice_buffer_stream_next; - stream->base.destroy = slice_buffer_stream_destroy; - stream->backing_buffer = slice_buffer; - stream->cursor = 0; -} diff --git a/third_party/grpc/src/core/transport/byte_stream.h b/third_party/grpc/src/core/transport/byte_stream.h deleted file mode 100644 index d2e51e7b45e1da..00000000000000 --- a/third_party/grpc/src/core/transport/byte_stream.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_BYTE_STREAM_H -#define GRPC_INTERNAL_CORE_TRANSPORT_BYTE_STREAM_H - -#include "src/core/iomgr/exec_ctx.h" -#include - -/** 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) - -struct grpc_byte_stream; -typedef struct grpc_byte_stream grpc_byte_stream; - -struct grpc_byte_stream { - uint32_t length; - uint32_t flags; - int (*next)(grpc_exec_ctx *exec_ctx, grpc_byte_stream *byte_stream, - gpr_slice *slice, size_t max_size_hint, - grpc_closure *on_complete); - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_byte_stream *byte_stream); -}; - -/* returns 1 if the bytes are available immediately (in which case - * on_complete will not be called), 0 if the bytes will be available - * asynchronously. - * - * on entry, *remaining can be set as a hint as to the maximum number - * of bytes that would be acceptable to read. - * - * fills *buffer, *length, *remaining with the bytes, length of bytes - * and length of data remaining to be read before either returning 1 - * or calling on_complete. - * - * once a slice is returned into *slice, it is owned by the caller. - */ -int grpc_byte_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, gpr_slice *slice, - size_t max_size_hint, grpc_closure *on_complete); - -void grpc_byte_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream); - -/* grpc_byte_stream that wraps a slice buffer */ -typedef struct grpc_slice_buffer_stream { - grpc_byte_stream base; - gpr_slice_buffer *backing_buffer; - size_t cursor; -} grpc_slice_buffer_stream; - -void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream *stream, - gpr_slice_buffer *slice_buffer, - uint32_t flags); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_BYTE_STREAM_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/alpn.c b/third_party/grpc/src/core/transport/chttp2/alpn.c deleted file mode 100644 index 69da4e6718e638..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/alpn.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/alpn.h b/third_party/grpc/src/core/transport/chttp2/alpn.h deleted file mode 100644 index f38b4c3167e0ed..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/alpn.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/bin_encoder.c b/third_party/grpc/src/core/transport/chttp2/bin_encoder.c deleted file mode 100644 index f26bc7e29b1472..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/bin_encoder.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/transport/chttp2/bin_encoder.h" - -#include - -#include "src/core/transport/chttp2/huffsyms.h" -#include - -static const char alphabet[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -typedef struct { - uint16_t bits; - uint8_t 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 uint8_t 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); - uint8_t *in = GPR_SLICE_START_PTR(input); - char *out = (char *)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 == (char *)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; - uint8_t *in; - uint8_t *out; - gpr_slice output; - uint32_t temp = 0; - uint32_t 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++ = (uint8_t)(temp >> temp_length); - } - } - - if (temp_length) { - /* NB: the following integer arithmetic operation needs to be in its - * expanded form due to the "integral promotion" performed (see section - * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type - * is then required to avoid the compiler warning */ - *out++ = (uint8_t)((uint8_t)(temp << (8u - temp_length)) | - (uint8_t)(0xffu >> temp_length)); - } - - GPR_ASSERT(out == GPR_SLICE_END_PTR(output)); - - return output; -} - -typedef struct { - uint32_t temp; - uint32_t temp_length; - uint8_t *out; -} huff_out; - -static void enc_flush_some(huff_out *out) { - while (out->temp_length > 8) { - out->temp_length -= 8; - *out->out++ = (uint8_t)(out->temp >> out->temp_length); - } -} - -static void enc_add2(huff_out *out, uint8_t a, uint8_t b) { - b64_huff_sym sa = huff_alphabet[a]; - b64_huff_sym sb = huff_alphabet[b]; - out->temp = (out->temp << (sa.length + sb.length)) | - ((uint32_t)sa.bits << sb.length) | sb.bits; - out->temp_length += (uint32_t)sa.length + (uint32_t)sb.length; - enc_flush_some(out); -} - -static void enc_add1(huff_out *out, uint8_t 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); - uint8_t *in = GPR_SLICE_START_PTR(input); - uint8_t *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, (uint8_t)((in[0] & 0x3) << 4) | (in[1] >> 4)); - enc_add2(&out, (uint8_t)((in[1] & 0xf) << 2) | (in[2] >> 6), - (uint8_t)(in[2] & 0x3f)); - in += 3; - } - - /* encode the remaining bytes */ - switch (tail_case) { - case 0: - break; - case 1: - enc_add2(&out, in[0] >> 2, (uint8_t)((in[0] & 0x3) << 4)); - in += 1; - break; - case 2: - enc_add2(&out, in[0] >> 2, - (uint8_t)((in[0] & 0x3) << 4) | (uint8_t)(in[1] >> 4)); - enc_add1(&out, (uint8_t)((in[1] & 0xf) << 2)); - in += 2; - break; - } - - if (out.temp_length) { - /* NB: the following integer arithmetic operation needs to be in its - * expanded form due to the "integral promotion" performed (see section - * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type - * is then required to avoid the compiler warning */ - *out.out++ = (uint8_t)((uint8_t)(out.temp << (8u - out.temp_length)) | - (uint8_t)(0xffu >> 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; -} diff --git a/third_party/grpc/src/core/transport/chttp2/bin_encoder.h b/third_party/grpc/src/core/transport/chttp2/bin_encoder.h deleted file mode 100644 index 036fddf9981e10..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/bin_encoder.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_BIN_ENCODER_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/frame.h b/third_party/grpc/src/core/transport/chttp2/frame.h deleted file mode 100644 index 879ee036fac0f7..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/frame_data.c b/third_party/grpc/src/core/transport/chttp2/frame_data.c deleted file mode 100644 index f9a1af88736239..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_data.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - parser->parsing_frame = NULL; - return GRPC_CHTTP2_PARSE_OK; -} - -void grpc_chttp2_data_parser_destroy(grpc_exec_ctx *exec_ctx, - grpc_chttp2_data_parser *parser) { - grpc_byte_stream *bs; - if (parser->parsing_frame) { - grpc_chttp2_incoming_byte_stream_finished(exec_ctx, parser->parsing_frame, - 0, 1); - } - while ( - (bs = grpc_chttp2_incoming_frame_queue_pop(&parser->incoming_frames))) { - grpc_byte_stream_destroy(exec_ctx, bs); - } -} - -grpc_chttp2_parse_error grpc_chttp2_data_parser_begin_frame( - grpc_chttp2_data_parser *parser, uint8_t 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; -} - -void grpc_chttp2_incoming_frame_queue_merge( - grpc_chttp2_incoming_frame_queue *head_dst, - grpc_chttp2_incoming_frame_queue *tail_src) { - if (tail_src->head == NULL) { - return; - } - - if (head_dst->head == NULL) { - *head_dst = *tail_src; - memset(tail_src, 0, sizeof(*tail_src)); - return; - } - - head_dst->tail->next_message = tail_src->head; - head_dst->tail = tail_src->tail; - memset(tail_src, 0, sizeof(*tail_src)); -} - -grpc_byte_stream *grpc_chttp2_incoming_frame_queue_pop( - grpc_chttp2_incoming_frame_queue *q) { - grpc_byte_stream *out; - if (q->head == NULL) { - return NULL; - } - out = &q->head->base; - if (q->head == q->tail) { - memset(q, 0, sizeof(*q)); - } else { - q->head = q->head->next_message; - } - return out; -} - -void grpc_chttp2_encode_data(uint32_t id, gpr_slice_buffer *inbuf, - uint32_t write_bytes, int is_eof, - gpr_slice_buffer *outbuf) { - gpr_slice hdr; - uint8_t *p; - - hdr = gpr_slice_malloc(9); - p = GPR_SLICE_START_PTR(hdr); - GPR_ASSERT(write_bytes < (1 << 24)); - *p++ = (uint8_t)(write_bytes >> 16); - *p++ = (uint8_t)(write_bytes >> 8); - *p++ = (uint8_t)(write_bytes); - *p++ = GRPC_CHTTP2_FRAME_DATA; - *p++ = is_eof ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0; - *p++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(id); - gpr_slice_buffer_add(outbuf, hdr); - - gpr_slice_buffer_move_first(inbuf, write_bytes, outbuf); -} - -grpc_chttp2_parse_error grpc_chttp2_data_parser_parse( - grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { - uint8_t *const beg = GPR_SLICE_START_PTR(slice); - uint8_t *const end = GPR_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_chttp2_data_parser *p = parser; - uint32_t message_flags; - grpc_chttp2_incoming_byte_stream *incoming_byte_stream; - - 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 = ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*cur); - p->state = GRPC_CHTTP2_DATA_FRAME; - ++cur; - message_flags = 0; - if (p->is_frame_compressed) { - message_flags |= GRPC_WRITE_INTERNAL_COMPRESS; - } - p->parsing_frame = incoming_byte_stream = - grpc_chttp2_incoming_byte_stream_create( - exec_ctx, transport_parsing, stream_parsing, p->frame_size, - message_flags, &p->incoming_frames); - /* 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 ((uint32_t)(end - cur) == p->frame_size) { - grpc_chttp2_incoming_byte_stream_push( - exec_ctx, p->parsing_frame, - gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); - grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame, 1, - 1); - p->parsing_frame = NULL; - p->state = GRPC_CHTTP2_DATA_FH_0; - return GRPC_CHTTP2_PARSE_OK; - } else if ((uint32_t)(end - cur) > p->frame_size) { - grpc_chttp2_incoming_byte_stream_push( - exec_ctx, p->parsing_frame, - gpr_slice_sub(slice, (size_t)(cur - beg), - (size_t)(cur + p->frame_size - beg))); - grpc_chttp2_incoming_byte_stream_finished(exec_ctx, p->parsing_frame, 1, - 1); - p->parsing_frame = NULL; - cur += p->frame_size; - goto fh_0; /* loop */ - } else { - grpc_chttp2_incoming_byte_stream_push( - exec_ctx, p->parsing_frame, - gpr_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); - GPR_ASSERT((size_t)(end - cur) <= p->frame_size); - p->frame_size -= (uint32_t)(end - cur); - return GRPC_CHTTP2_PARSE_OK; - } - } - - GPR_UNREACHABLE_CODE(return GRPC_CHTTP2_CONNECTION_ERROR); -} diff --git a/third_party/grpc/src/core/transport/chttp2/frame_data.h b/third_party/grpc/src/core/transport/chttp2/frame_data.h deleted file mode 100644 index 936b7a25899cb6..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_data.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/iomgr/exec_ctx.h" -#include -#include -#include "src/core/transport/byte_stream.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_incoming_byte_stream - grpc_chttp2_incoming_byte_stream; - -typedef struct grpc_chttp2_incoming_frame_queue { - grpc_chttp2_incoming_byte_stream *head; - grpc_chttp2_incoming_byte_stream *tail; -} grpc_chttp2_incoming_frame_queue; - -typedef struct { - grpc_chttp2_stream_state state; - uint8_t is_last_frame; - uint8_t frame_type; - uint32_t frame_size; - - int is_frame_compressed; - grpc_chttp2_incoming_frame_queue incoming_frames; - grpc_chttp2_incoming_byte_stream *parsing_frame; -} grpc_chttp2_data_parser; - -void grpc_chttp2_incoming_frame_queue_merge( - grpc_chttp2_incoming_frame_queue *head_dst, - grpc_chttp2_incoming_frame_queue *tail_src); -grpc_byte_stream *grpc_chttp2_incoming_frame_queue_pop( - grpc_chttp2_incoming_frame_queue *q); - -/* 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_exec_ctx *exec_ctx, - 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, uint8_t 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( - grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); - -void grpc_chttp2_encode_data(uint32_t id, gpr_slice_buffer *inbuf, - uint32_t write_bytes, int is_eof, - gpr_slice_buffer *outbuf); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_DATA_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/frame_goaway.c b/third_party/grpc/src/core/transport/chttp2/frame_goaway.c deleted file mode 100644 index 2fa525e989e6c5..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_goaway.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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, uint32_t length, uint8_t 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( - grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { - uint8_t *const beg = GPR_SLICE_START_PTR(slice); - uint8_t *const end = GPR_SLICE_END_PTR(slice); - uint8_t *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 = ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*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 = ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*cur); - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_DEBUG: - memcpy(p->debug_data + p->debug_pos, cur, (size_t)(end - cur)); - GPR_ASSERT((size_t)(end - cur) < UINT32_MAX - p->debug_pos); - p->debug_pos += (uint32_t)(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 = (grpc_status_code)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_UNREACHABLE_CODE(return GRPC_CHTTP2_CONNECTION_ERROR); -} - -void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code, - gpr_slice debug_data, - gpr_slice_buffer *slice_buffer) { - gpr_slice header = gpr_slice_malloc(9 + 4 + 4); - uint8_t *p = GPR_SLICE_START_PTR(header); - uint32_t frame_length; - GPR_ASSERT(GPR_SLICE_LENGTH(debug_data) < UINT32_MAX - 4 - 4); - frame_length = 4 + 4 + (uint32_t)GPR_SLICE_LENGTH(debug_data); - - /* frame header: length */ - *p++ = (uint8_t)(frame_length >> 16); - *p++ = (uint8_t)(frame_length >> 8); - *p++ = (uint8_t)(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++ = (uint8_t)(last_stream_id >> 24); - *p++ = (uint8_t)(last_stream_id >> 16); - *p++ = (uint8_t)(last_stream_id >> 8); - *p++ = (uint8_t)(last_stream_id); - /* payload: error code */ - *p++ = (uint8_t)(error_code >> 24); - *p++ = (uint8_t)(error_code >> 16); - *p++ = (uint8_t)(error_code >> 8); - *p++ = (uint8_t)(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/third_party/grpc/src/core/transport/chttp2/frame_goaway.h b/third_party/grpc/src/core/transport/chttp2/frame_goaway.h deleted file mode 100644 index e1a72b40134c1d..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_goaway.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/iomgr/exec_ctx.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; - uint32_t last_stream_id; - uint32_t error_code; - char *debug_data; - uint32_t debug_length; - uint32_t 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, uint32_t length, uint8_t flags); -grpc_chttp2_parse_error grpc_chttp2_goaway_parser_parse( - grpc_exec_ctx *exec_ctx, 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(uint32_t last_stream_id, uint32_t error_code, - gpr_slice debug_data, - gpr_slice_buffer *slice_buffer); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_FRAME_GOAWAY_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/frame_ping.c b/third_party/grpc/src/core/transport/chttp2/frame_ping.c deleted file mode 100644 index c6ab522283d689..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_ping.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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(uint8_t ack, uint8_t *opaque_8bytes) { - gpr_slice slice = gpr_slice_malloc(9 + 8); - uint8_t *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, uint32_t length, uint8_t 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( - grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { - uint8_t *const beg = GPR_SLICE_START_PTR(slice); - uint8_t *const end = GPR_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_chttp2_ping_parser *p = parser; - - 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) { - grpc_chttp2_ack_ping(exec_ctx, transport_parsing, p->opaque_8bytes); - } else { - gpr_slice_buffer_add(&transport_parsing->qbuf, - grpc_chttp2_ping_create(1, p->opaque_8bytes)); - } - } - - return GRPC_CHTTP2_PARSE_OK; -} diff --git a/third_party/grpc/src/core/transport/chttp2/frame_ping.h b/third_party/grpc/src/core/transport/chttp2/frame_ping.h deleted file mode 100644 index 16d7a726186a31..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_ping.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/iomgr/exec_ctx.h" -#include -#include "src/core/transport/chttp2/frame.h" - -typedef struct { - uint8_t byte; - uint8_t is_ack; - uint8_t opaque_8bytes[8]; -} grpc_chttp2_ping_parser; - -gpr_slice grpc_chttp2_ping_create(uint8_t ack, uint8_t *opaque_8bytes); - -grpc_chttp2_parse_error grpc_chttp2_ping_parser_begin_frame( - grpc_chttp2_ping_parser *parser, uint32_t length, uint8_t flags); -grpc_chttp2_parse_error grpc_chttp2_ping_parser_parse( - grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/transport/chttp2/frame_rst_stream.c b/third_party/grpc/src/core/transport/chttp2/frame_rst_stream.c deleted file mode 100644 index 754529e4b92cee..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_rst_stream.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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(uint32_t id, uint32_t code) { - gpr_slice slice = gpr_slice_malloc(13); - uint8_t *p = GPR_SLICE_START_PTR(slice); - - *p++ = 0; - *p++ = 0; - *p++ = 4; - *p++ = GRPC_CHTTP2_FRAME_RST_STREAM; - *p++ = 0; - *p++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(id); - *p++ = (uint8_t)(code >> 24); - *p++ = (uint8_t)(code >> 16); - *p++ = (uint8_t)(code >> 8); - *p++ = (uint8_t)(code); - - return slice; -} - -grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame( - grpc_chttp2_rst_stream_parser *parser, uint32_t length, uint8_t 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( - grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { - uint8_t *const beg = GPR_SLICE_START_PTR(slice); - uint8_t *const end = GPR_SLICE_END_PTR(slice); - uint8_t *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 = (((uint32_t)p->reason_bytes[0]) << 24) | - (((uint32_t)p->reason_bytes[1]) << 16) | - (((uint32_t)p->reason_bytes[2]) << 8) | - (((uint32_t)p->reason_bytes[3])); - } - - return GRPC_CHTTP2_PARSE_OK; -} diff --git a/third_party/grpc/src/core/transport/chttp2/frame_rst_stream.h b/third_party/grpc/src/core/transport/chttp2/frame_rst_stream.h deleted file mode 100644 index 72ca654c3243b5..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_rst_stream.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" -#include "src/core/iomgr/exec_ctx.h" - -typedef struct { - uint8_t byte; - uint8_t reason_bytes[4]; -} grpc_chttp2_rst_stream_parser; - -gpr_slice grpc_chttp2_rst_stream_create(uint32_t stream_id, uint32_t code); - -grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_begin_frame( - grpc_chttp2_rst_stream_parser *parser, uint32_t length, uint8_t flags); -grpc_chttp2_parse_error grpc_chttp2_rst_stream_parser_parse( - grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/transport/chttp2/frame_settings.c b/third_party/grpc/src/core/transport/chttp2/frame_settings.c deleted file mode 100644 index cc49dd4f692c49..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_settings.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 -#include - -#include "src/core/debug/trace.h" -#include "src/core/transport/chttp2/frame.h" -#include "src/core/transport/chttp2/http2_errors.h" -#include "src/core/transport/chttp2_transport.h" - -#define MAX_MAX_HEADER_LIST_SIZE (1024 * 1024 * 1024) - -/* 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, - GRPC_CHTTP2_PROTOCOL_ERROR}, - {"HEADER_TABLE_SIZE", 4096, 0, 0xffffffff, - GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR}, - {"ENABLE_PUSH", 1, 0, 1, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, - GRPC_CHTTP2_PROTOCOL_ERROR}, - {"MAX_CONCURRENT_STREAMS", 0xffffffffu, 0, 0xffffffffu, - GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR}, - {"INITIAL_WINDOW_SIZE", 65535, 0, 0x7fffffffu, - GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, - GRPC_CHTTP2_FLOW_CONTROL_ERROR}, - {"MAX_FRAME_SIZE", 16384, 16384, 16777215, - GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_CHTTP2_PROTOCOL_ERROR}, - {"MAX_HEADER_LIST_SIZE", MAX_MAX_HEADER_LIST_SIZE, 0, - MAX_MAX_HEADER_LIST_SIZE, GRPC_CHTTP2_CLAMP_INVALID_VALUE, - GRPC_CHTTP2_PROTOCOL_ERROR}, -}; - -static uint8_t *fill_header(uint8_t *out, uint32_t length, uint8_t flags) { - *out++ = (uint8_t)(length >> 16); - *out++ = (uint8_t)(length >> 8); - *out++ = (uint8_t)(length); - *out++ = GRPC_CHTTP2_FRAME_SETTINGS; - *out++ = flags; - *out++ = 0; - *out++ = 0; - *out++ = 0; - *out++ = 0; - return out; -} - -gpr_slice grpc_chttp2_settings_create(uint32_t *old, const uint32_t *new, - uint32_t force_mask, size_t count) { - size_t i; - uint32_t n = 0; - gpr_slice output; - uint8_t *p; - - for (i = 0; i < count; i++) { - n += (new[i] != old[i] || (force_mask & (1u << 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 & (1u << i)) != 0) { - GPR_ASSERT(i); - *p++ = (uint8_t)(i >> 8); - *p++ = (uint8_t)(i); - *p++ = (uint8_t)(new[i] >> 24); - *p++ = (uint8_t)(new[i] >> 16); - *p++ = (uint8_t)(new[i] >> 8); - *p++ = (uint8_t)(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, uint32_t length, uint8_t flags, - uint32_t *settings) { - parser->target_settings = settings; - memcpy(parser->incoming_settings, settings, - GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); - 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( - grpc_exec_ctx *exec_ctx, 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 uint8_t *cur = GPR_SLICE_START_PTR(slice); - const uint8_t *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(uint32_t)); - gpr_slice_buffer_add(&transport_parsing->qbuf, - grpc_chttp2_settings_ack_create()); - } - return GRPC_CHTTP2_PARSE_OK; - } - parser->id = (uint16_t)(((uint16_t)*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 = (uint16_t)(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 = ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*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: - grpc_chttp2_goaway_append( - transport_parsing->last_incoming_stream_id, sp->error_value, - gpr_slice_from_static_string("HTTP2 settings error"), - &transport_parsing->qbuf); - 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 = - (int64_t)parser->value - parser->incoming_settings[parser->id]; - if (grpc_http_trace) { - 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/third_party/grpc/src/core/transport/chttp2/frame_settings.h b/third_party/grpc/src/core/transport/chttp2/frame_settings.h deleted file mode 100644 index 3c918e3a2a9098..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_settings.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" -#include "src/core/iomgr/exec_ctx.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; - uint32_t *target_settings; - uint8_t is_ack; - uint16_t id; - uint32_t value; - uint32_t 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; - uint32_t default_value; - uint32_t min_value; - uint32_t max_value; - grpc_chttp2_invalid_value_behavior invalid_value_behavior; - uint32_t error_value; -} 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(uint32_t *old, const uint32_t *new, - uint32_t 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, uint32_t length, uint8_t flags, - uint32_t *settings); -grpc_chttp2_parse_error grpc_chttp2_settings_parser_parse( - grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/transport/chttp2/frame_window_update.c b/third_party/grpc/src/core/transport/chttp2/frame_window_update.c deleted file mode 100644 index 62d9bac117908d..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_window_update.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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(uint32_t id, - uint32_t window_update) { - gpr_slice slice = gpr_slice_malloc(13); - uint8_t *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++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(id); - *p++ = (uint8_t)(window_update >> 24); - *p++ = (uint8_t)(window_update >> 16); - *p++ = (uint8_t)(window_update >> 8); - *p++ = (uint8_t)(window_update); - - return slice; -} - -grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( - grpc_chttp2_window_update_parser *parser, uint32_t length, uint8_t 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( - grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last) { - uint8_t *const beg = GPR_SLICE_START_PTR(slice); - uint8_t *const end = GPR_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_chttp2_window_update_parser *p = parser; - - while (p->byte != 4 && cur != end) { - p->amount |= ((uint32_t)*cur) << (8 * (3 - p->byte)); - cur++; - p->byte++; - } - - if (p->byte == 4) { - uint32_t received_update = p->amount; - if (received_update == 0 || (received_update & 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_FLOW_CREDIT_STREAM("parse", transport_parsing, - stream_parsing, outgoing_window, - received_update); - grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, - stream_parsing); - } - } else { - GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parse", transport_parsing, - outgoing_window, received_update); - } - } - - return GRPC_CHTTP2_PARSE_OK; -} diff --git a/third_party/grpc/src/core/transport/chttp2/frame_window_update.h b/third_party/grpc/src/core/transport/chttp2/frame_window_update.h deleted file mode 100644 index 89d835c07979e6..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/frame_window_update.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/iomgr/exec_ctx.h" -#include -#include "src/core/transport/chttp2/frame.h" - -typedef struct { - uint8_t byte; - uint8_t is_connection_update; - uint32_t amount; -} grpc_chttp2_window_update_parser; - -gpr_slice grpc_chttp2_window_update_create(uint32_t id, uint32_t window_delta); - -grpc_chttp2_parse_error grpc_chttp2_window_update_parser_begin_frame( - grpc_chttp2_window_update_parser *parser, uint32_t length, uint8_t flags); -grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse( - grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/transport/chttp2/hpack_encoder.c b/third_party/grpc/src/core/transport/chttp2/hpack_encoder.c deleted file mode 100644 index f30f574d0635c9..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/hpack_encoder.c +++ /dev/null @@ -1,568 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/transport/chttp2/hpack_encoder.h" - -#include -#include - -/* This is here for grpc_is_binary_header - * TODO(murgatroid99): Remove this - */ -#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" -#include "src/core/transport/static_metadata.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 - -typedef struct { - int is_first_frame; - /* 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; - /* have we seen a regular (non-colon-prefixed) header yet? */ - uint8_t seen_regular_header; - /* output stream id */ - uint32_t 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(uint8_t *p, uint8_t type, uint32_t id, size_t len, - uint8_t flags) { - GPR_ASSERT(len < 16777316); - *p++ = (uint8_t)(len >> 16); - *p++ = (uint8_t)(len >> 8); - *p++ = (uint8_t)(len); - *p++ = type; - *p++ = flags; - *p++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(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) { - uint8_t type = 0xff; - type = st->is_first_frame ? GRPC_CHTTP2_FRAME_HEADER - : GRPC_CHTTP2_FRAME_CONTINUATION; - 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, - (uint8_t)((is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | - (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0))); - st->is_first_frame = 0; -} - -/* begin a new frame: reserve off header space, remember how many bytes we'd - output before beginning */ -static void begin_frame(framer_state *st) { - st->header_idx = - gpr_slice_buffer_add_indexed(st->output, gpr_slice_malloc(9)); - st->output_length_at_start_of_frame = st->output->length; -} - -/* 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_space(framer_state *st, size_t need_bytes) { - if (st->output->length - st->output_length_at_start_of_frame + need_bytes <= - GRPC_CHTTP2_MAX_PAYLOAD_LENGTH) { - return; - } - finish_frame(st, 0, 0); - begin_frame(st); -} - -/* increment a filter count, halve all counts if one element reaches max */ -static void inc_filter(uint8_t idx, uint32_t *sum, uint8_t *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; - 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)); - finish_frame(st, 0, 0); - begin_frame(st); - add_header_data(st, slice); - } -} - -static uint8_t *add_tiny_header_data(framer_state *st, size_t len) { - ensure_space(st, len); - return gpr_slice_buffer_tiny_add(st->output, len); -} - -static void evict_entry(grpc_chttp2_hpack_compressor *c) { - c->tail_remote_index++; - GPR_ASSERT(c->tail_remote_index > 0); - GPR_ASSERT(c->table_size >= - c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); - GPR_ASSERT(c->table_elems > 0); - c->table_size = - (uint16_t)(c->table_size - - c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); - c->table_elems--; -} - -/* add an element to the decoder table */ -static void add_elem(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem) { - uint32_t key_hash = elem->key->hash; - uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash); - uint32_t new_index = c->tail_remote_index + c->table_elems + 1; - size_t elem_size = 32 + GPR_SLICE_LENGTH(elem->key->slice) + - GPR_SLICE_LENGTH(elem->value->slice); - - GPR_ASSERT(elem_size < 65536); - - if (elem_size > c->max_table_size) { - while (c->table_size > 0) { - evict_entry(c); - } - return; - } - - /* Reserve space for this element in the remote table: if this overflows - the current table, drop elements until it fits, matching the decompressor - algorithm */ - while (c->table_size + elem_size > c->max_table_size) { - evict_entry(c); - } - GPR_ASSERT(c->table_elems < c->max_table_size); - c->table_elem_size[new_index % c->cap_table_elems] = (uint16_t)elem_size; - c->table_size = (uint16_t)(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; - } 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; - } 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)] = GRPC_MDELEM_REF(elem); - c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; - } 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)] = GRPC_MDELEM_REF(elem); - c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; - } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] < - c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) { - /* not there: replace oldest */ - GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_2(elem_hash)]); - c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem); - c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; - } else { - /* not there: replace oldest */ - GRPC_MDELEM_UNREF(c->entries_elems[HASH_FRAGMENT_3(elem_hash)]); - c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(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; - } -} - -static void emit_indexed(grpc_chttp2_hpack_compressor *c, uint32_t elem_index, - framer_state *st) { - uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(elem_index, 1); - GRPC_CHTTP2_WRITE_VARINT(elem_index, 1, 0x80, add_tiny_header_data(st, len), - len); -} - -static gpr_slice get_wire_value(grpc_mdelem *elem, uint8_t *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, - uint32_t key_index, grpc_mdelem *elem, - framer_state *st) { - uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); - uint8_t huffman_prefix; - gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - size_t len_val = GPR_SLICE_LENGTH(value_slice); - uint32_t len_val_len; - GPR_ASSERT(len_val <= UINT32_MAX); - len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); - GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, - add_tiny_header_data(st, len_pfx), len_pfx); - GRPC_CHTTP2_WRITE_VARINT((uint32_t)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(grpc_chttp2_hpack_compressor *c, - uint32_t key_index, grpc_mdelem *elem, - framer_state *st) { - uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); - uint8_t huffman_prefix; - gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - size_t len_val = GPR_SLICE_LENGTH(value_slice); - uint32_t len_val_len; - GPR_ASSERT(len_val <= UINT32_MAX); - len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); - GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, - add_tiny_header_data(st, len_pfx), len_pfx); - GRPC_CHTTP2_WRITE_VARINT((uint32_t)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_incidx_v(grpc_chttp2_hpack_compressor *c, - grpc_mdelem *elem, framer_state *st) { - uint32_t len_key = (uint32_t)GPR_SLICE_LENGTH(elem->key->slice); - uint8_t huffman_prefix; - gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - uint32_t len_val = (uint32_t)GPR_SLICE_LENGTH(value_slice); - uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); - uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); - GPR_ASSERT(len_key <= UINT32_MAX); - GPR_ASSERT(GPR_SLICE_LENGTH(value_slice) <= UINT32_MAX); - *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) { - uint32_t len_key = (uint32_t)GPR_SLICE_LENGTH(elem->key->slice); - uint8_t huffman_prefix; - gpr_slice value_slice = get_wire_value(elem, &huffman_prefix); - uint32_t len_val = (uint32_t)GPR_SLICE_LENGTH(value_slice); - uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); - uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); - GPR_ASSERT(len_key <= UINT32_MAX); - GPR_ASSERT(GPR_SLICE_LENGTH(value_slice) <= UINT32_MAX); - *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 void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor *c, - framer_state *st) { - uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(c->max_table_size, 3); - GRPC_CHTTP2_WRITE_VARINT(c->max_table_size, 3, 0x20, - add_tiny_header_data(st, len), len); - c->advertise_table_size_change = 0; -} - -static uint32_t dynidx(grpc_chttp2_hpack_compressor *c, uint32_t elem_index) { - return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + - c->table_elems - elem_index; -} - -/* encode an mdelem */ -static void hpack_enc(grpc_chttp2_hpack_compressor *c, grpc_mdelem *elem, - framer_state *st) { - uint32_t key_hash = elem->key->hash; - uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, elem->value->hash); - size_t decoder_space_usage; - uint32_t 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 { - GPR_ASSERT( - st->seen_regular_header == 0 && - "Reserved header (colon-prefixed) happening after regular ones."); - } - - 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; - } - - 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; - } - - /* 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); - add_elem(c, elem); - return; - } else { - emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); - return; - } - GPR_UNREACHABLE_CODE(return ); - } - - 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); - add_elem(c, elem); - return; - } else { - emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st); - return; - } - GPR_UNREACHABLE_CODE(return ); - } - - /* no elem, key in the table... fall back to literal emission */ - - if (should_add_elem) { - emit_lithdr_incidx_v(c, elem, st); - add_elem(c, elem); - return; - } else { - emit_lithdr_noidx_v(c, elem, st); - return; - } - GPR_UNREACHABLE_CODE(return ); -} - -#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( - GRPC_MDSTR_GRPC_TIMEOUT, grpc_mdstr_from_string(timeout_str)); - hpack_enc(c, mdelem, st); - GRPC_MDELEM_UNREF(mdelem); -} - -static uint32_t elems_for_bytes(uint32_t bytes) { return (bytes + 31) / 32; } - -void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c) { - memset(c, 0, sizeof(*c)); - c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; - c->cap_table_elems = elems_for_bytes(c->max_table_size); - c->max_table_elems = c->cap_table_elems; - c->max_usable_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; - c->table_elem_size = - gpr_malloc(sizeof(*c->table_elem_size) * c->cap_table_elems); - memset(c->table_elem_size, 0, - sizeof(*c->table_elem_size) * c->cap_table_elems); -} - -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]); - } - gpr_free(c->table_elem_size); -} - -void grpc_chttp2_hpack_compressor_set_max_usable_size( - grpc_chttp2_hpack_compressor *c, uint32_t max_table_size) { - c->max_usable_size = max_table_size; - grpc_chttp2_hpack_compressor_set_max_table_size( - c, GPR_MIN(c->max_table_size, max_table_size)); -} - -static void rebuild_elems(grpc_chttp2_hpack_compressor *c, uint32_t new_cap) { - uint16_t *table_elem_size = gpr_malloc(sizeof(*table_elem_size) * new_cap); - uint32_t i; - - memset(table_elem_size, 0, sizeof(*table_elem_size) * new_cap); - GPR_ASSERT(c->table_elems <= new_cap); - - for (i = 0; i < c->table_elems; i++) { - uint32_t ofs = c->tail_remote_index + i + 1; - table_elem_size[ofs % new_cap] = - c->table_elem_size[ofs % c->cap_table_elems]; - } - - c->cap_table_elems = new_cap; - gpr_free(c->table_elem_size); - c->table_elem_size = table_elem_size; -} - -void grpc_chttp2_hpack_compressor_set_max_table_size( - grpc_chttp2_hpack_compressor *c, uint32_t max_table_size) { - max_table_size = GPR_MIN(max_table_size, c->max_usable_size); - if (max_table_size == c->max_table_size) { - return; - } - while (c->table_size > 0 && c->table_size > max_table_size) { - evict_entry(c); - } - c->max_table_size = max_table_size; - c->max_table_elems = elems_for_bytes(max_table_size); - if (c->max_table_elems > c->cap_table_elems) { - rebuild_elems(c, GPR_MAX(c->max_table_elems, 2 * c->cap_table_elems)); - } else if (c->max_table_elems < c->cap_table_elems / 3) { - uint32_t new_cap = GPR_MAX(c->max_table_elems, 16); - if (new_cap != c->cap_table_elems) { - rebuild_elems(c, new_cap); - } - } - c->advertise_table_size_change = 1; - gpr_log(GPR_DEBUG, "set max table size from encoder to %d", max_table_size); -} - -void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, - uint32_t stream_id, - grpc_metadata_batch *metadata, int is_eof, - gpr_slice_buffer *outbuf) { - framer_state st; - grpc_linked_mdelem *l; - gpr_timespec deadline; - - GPR_ASSERT(stream_id != 0); - - st.seen_regular_header = 0; - st.stream_id = stream_id; - st.output = outbuf; - st.is_first_frame = 1; - - /* 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_frame(&st); - if (c->advertise_table_size_change != 0) { - emit_advertise_table_size_change(c, &st); - } - grpc_metadata_batch_assert_ok(metadata); - for (l = metadata->list.head; l; l = l->next) { - hpack_enc(c, l->md, &st); - } - deadline = metadata->deadline; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) != 0) { - deadline_enc(c, deadline, &st); - } - - finish_frame(&st, 1, is_eof); -} diff --git a/third_party/grpc/src/core/transport/chttp2/hpack_encoder.h b/third_party/grpc/src/core/transport/chttp2/hpack_encoder.h deleted file mode 100644 index 19b5cb72ae1579..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/hpack_encoder.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_ENCODER_H -#define GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_ENCODER_H - -#include "src/core/transport/chttp2/frame.h" -#include "src/core/transport/metadata.h" -#include "src/core/transport/metadata_batch.h" -#include -#include -#include - -#define GRPC_CHTTP2_HPACKC_NUM_FILTERS 256 -#define GRPC_CHTTP2_HPACKC_NUM_VALUES 256 -/* initial table size, per spec */ -#define GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE 4096 -/* maximum table size we'll actually use */ -#define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024) - -typedef struct { - uint32_t filter_elems_sum; - uint32_t max_table_size; - uint32_t max_table_elems; - uint32_t cap_table_elems; - /** if non-zero, advertise to the decoder that we'll start using a table - of this size */ - uint8_t advertise_table_size_change; - /** maximum number of bytes we'll use for the decode table (to guard against - peers ooming us by setting decode table size high) */ - uint32_t max_usable_size; - /* one before the lowest usable table index */ - uint32_t tail_remote_index; - uint32_t table_size; - uint32_t 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. */ - uint8_t filter_elems[GRPC_CHTTP2_HPACKC_NUM_FILTERS]; - - /* 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]; - uint32_t indices_keys[GRPC_CHTTP2_HPACKC_NUM_VALUES]; - uint32_t indices_elems[GRPC_CHTTP2_HPACKC_NUM_VALUES]; - - uint16_t *table_elem_size; -} grpc_chttp2_hpack_compressor; - -void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c); -void grpc_chttp2_hpack_compressor_destroy(grpc_chttp2_hpack_compressor *c); -void grpc_chttp2_hpack_compressor_set_max_table_size( - grpc_chttp2_hpack_compressor *c, uint32_t max_table_size); -void grpc_chttp2_hpack_compressor_set_max_usable_size( - grpc_chttp2_hpack_compressor *c, uint32_t max_table_size); - -void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor *c, uint32_t id, - grpc_metadata_batch *metadata, int is_eof, - gpr_slice_buffer *outbuf); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_HPACK_ENCODER_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/hpack_parser.h b/third_party/grpc/src/core/transport/chttp2/hpack_parser.h deleted file mode 100644 index 1ad0c64fb9cae2..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/hpack_parser.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/iomgr/exec_ctx.h" -#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 uint8_t *beg, - const uint8_t *end); - -typedef struct { - char *str; - uint32_t length; - uint32_t 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 { - uint32_t *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 */ - uint32_t index; - /* length of source bytes for the currently parsing string */ - uint32_t strlen; - /* number of source bytes read for the currently parsing string */ - uint32_t strgot; - /* huffman decoding state */ - int16_t huff_state; - /* is the string being decoded binary? */ - uint8_t binary; - /* is the current string huffman encoded? */ - uint8_t huff; - /* is a dynamic table update allowed? */ - uint8_t dynamic_table_update_allowed; - /* set by higher layers, used by grpc_chttp2_header_parser_parse to signal - it should append a metadata boundary at the end of frame */ - uint8_t is_boundary; - uint8_t is_eof; - uint32_t base64_buffer; - - /* hpack table */ - grpc_chttp2_hptbl table; -}; - -void grpc_chttp2_hpack_parser_init(grpc_chttp2_hpack_parser *p); -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 uint8_t *beg, const uint8_t *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( - grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/transport/chttp2/hpack_table.h b/third_party/grpc/src/core/transport/chttp2/hpack_table.h deleted file mode 100644 index e7431255fc7fbb..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/hpack_table.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 -#if 0 -/* 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) -#endif - -/* hpack decoder table */ -typedef struct { - /* the first used entry in ents */ - uint32_t first_ent; - /* how many entries are in the table */ - uint32_t num_ents; - /* the amount of memory used by the table, according to the hpack algorithm */ - uint32_t mem_used; - /* the max memory allowed to be used by the table, according to the hpack - algorithm */ - uint32_t max_bytes; - /* the currently agreed size of the table, according to the hpack algorithm */ - uint32_t current_table_bytes; - /* Maximum number of entries we could possibly fit in the table, given defined - overheads */ - uint32_t max_entries; - /* Number of entries allocated in ents */ - uint32_t cap_entries; - /* 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_mdelem *static_ents[GRPC_CHTTP2_LAST_STATIC_ENTRY]; -} grpc_chttp2_hptbl; - -/* initialize a hpack table */ -void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl); -void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl); -void grpc_chttp2_hptbl_set_max_bytes(grpc_chttp2_hptbl *tbl, - uint32_t max_bytes); -int grpc_chttp2_hptbl_set_current_table_size(grpc_chttp2_hptbl *tbl, - uint32_t bytes); - -/* lookup a table entry based on its hpack index */ -grpc_mdelem *grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, - uint32_t index); -/* add a table entry to the index */ -int grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, - grpc_mdelem *md) GRPC_MUST_USE_RESULT; -/* 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 { - uint32_t index; - int 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/third_party/grpc/src/core/transport/chttp2/http2_errors.h b/third_party/grpc/src/core/transport/chttp2/http2_errors.h deleted file mode 100644 index a4f309e0565d40..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/http2_errors.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/huffsyms.c b/third_party/grpc/src/core/transport/chttp2/huffsyms.c deleted file mode 100644 index 7b138e9b5db575..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/huffsyms.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/huffsyms.h b/third_party/grpc/src/core/transport/chttp2/huffsyms.h deleted file mode 100644 index a3cdba8235a1a7..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/huffsyms.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/incoming_metadata.c b/third_party/grpc/src/core/transport/chttp2/incoming_metadata.c deleted file mode 100644 index 315bc2faa19c4b..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/incoming_metadata.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - if (!buffer->published) { - 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) { - GPR_ASSERT(!buffer->published); - 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) { - GPR_ASSERT(!buffer->published); - buffer->deadline = deadline; -} - -void grpc_chttp2_incoming_metadata_buffer_publish( - grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch) { - GPR_ASSERT(!buffer->published); - buffer->published = 1; - if (buffer->count > 0) { - size_t i; - for (i = 1; i < buffer->count; i++) { - buffer->elems[i].prev = &buffer->elems[i - 1]; - } - for (i = 0; i < buffer->count - 1; i++) { - buffer->elems[i].next = &buffer->elems[i + 1]; - } - buffer->elems[0].prev = NULL; - buffer->elems[buffer->count - 1].next = NULL; - batch->list.head = &buffer->elems[0]; - batch->list.tail = &buffer->elems[buffer->count - 1]; - } else { - batch->list.head = batch->list.tail = NULL; - } - batch->deadline = buffer->deadline; -} diff --git a/third_party/grpc/src/core/transport/chttp2/incoming_metadata.h b/third_party/grpc/src/core/transport/chttp2/incoming_metadata.h deleted file mode 100644 index ea74cfc64b2a7c..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/incoming_metadata.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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; - int published; -} grpc_chttp2_incoming_metadata_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_publish( - grpc_chttp2_incoming_metadata_buffer *buffer, grpc_metadata_batch *batch); - -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); - -#endif /* GRPC_INTERNAL_CORE_CHTTP2_INCOMING_METADATA_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/internal.h b/third_party/grpc/src/core/transport/chttp2/internal.h deleted file mode 100644 index c611496e7e5793..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/internal.h +++ /dev/null @@ -1,768 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef GRPC_INTERNAL_CORE_CHTTP2_INTERNAL_H -#define GRPC_INTERNAL_CORE_CHTTP2_INTERNAL_H - -#include -#include - -#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_encoder.h" -#include "src/core/transport/chttp2/hpack_parser.h" -#include "src/core/transport/chttp2/incoming_metadata.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_CHECK_READ_OPS, - GRPC_CHTTP2_LIST_UNANNOUNCED_INCOMING_WINDOW_AVAILABLE, - 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_CLOSED_WAITING_FOR_WRITING, - GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT, - /* streams waiting for the outgoing window in the writing path, they will be - * merged to the stalled list or writable list under transport lock. */ - GRPC_CHTTP2_LIST_WRITING_STALLED_BY_TRANSPORT, - /** 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 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 { - uint8_t id[8]; - grpc_closure *on_recv; - struct grpc_chttp2_outstanding_ping *next; - struct grpc_chttp2_outstanding_ping *prev; -} grpc_chttp2_outstanding_ping; - -/* forward declared in frame_data.h */ -struct grpc_chttp2_incoming_byte_stream { - grpc_byte_stream base; - gpr_refcount refs; - struct grpc_chttp2_incoming_byte_stream *next_message; - int failed; - - grpc_chttp2_transport *transport; - grpc_chttp2_stream *stream; - int is_tail; - gpr_slice_buffer slices; - grpc_closure *on_next; - gpr_slice *next; -}; - -typedef struct { - /** data to write next write */ - gpr_slice_buffer qbuf; - - /** window available for us to send to peer */ - int64_t outgoing_window; - /** window available to announce to peer */ - int64_t announce_incoming_window; - /** how much window would we like to have for incoming_window */ - uint32_t connection_window_target; - - /** have we seen a goaway */ - uint8_t seen_goaway; - /** have we sent a goaway */ - uint8_t sent_goaway; - - /** is this transport a client? */ - uint8_t is_client; - /** are the local settings dirty and need to be sent? */ - uint8_t dirtied_local_settings; - /** have local settings been sent? */ - uint8_t sent_local_settings; - /** bitmask of setting indexes to send out */ - uint32_t force_send_settings; - /** settings values */ - uint32_t 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 */ - uint32_t next_stream_id; - - /** how far to lookahead in a stream? */ - uint32_t stream_lookahead; - - /** last received stream id */ - uint32_t last_incoming_stream_id; - - /** pings awaiting responses */ - grpc_chttp2_outstanding_ping pings; - /** next payload for an outgoing ping */ - uint64_t ping_counter; - - /** concurrent stream count: updated when not parsing, - so this is a strict over-estimation on the client */ - uint32_t 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; - int64_t outgoing_window; - /** is this a client? */ - uint8_t is_client; - /** callback for when writing is done */ - grpc_closure done_cb; -} grpc_chttp2_transport_writing; - -struct grpc_chttp2_transport_parsing { - /** is this transport a client? (boolean) */ - uint8_t is_client; - - /** were settings updated? */ - uint8_t settings_updated; - /** was a settings ack received? */ - uint8_t settings_ack_received; - /** was a goaway frame received? */ - uint8_t goaway_received; - - /** the last sent max_table_size setting */ - uint32_t last_sent_max_table_size; - - /** initial window change */ - int64_t initial_window_update; - - /** data to write later - after parsing */ - gpr_slice_buffer qbuf; - /** 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 */ - int64_t incoming_window; - - /** next stream id available at the time of beginning parsing */ - uint32_t next_stream_id; - uint32_t last_incoming_stream_id; - - /* deframing */ - grpc_chttp2_deframe_transport_state deframe_state; - uint8_t incoming_frame_type; - uint8_t incoming_frame_flags; - uint8_t header_eof; - uint32_t expect_continuation_stream_id; - uint32_t incoming_frame_size; - uint32_t incoming_stream_id; - - /* active parser */ - void *parser_data; - grpc_chttp2_stream_parsing *incoming_stream; - grpc_chttp2_parse_error (*parser)( - grpc_exec_ctx *exec_ctx, void *parser_user_data, - grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, gpr_slice slice, int is_last); - - /* received settings */ - uint32_t settings[GRPC_CHTTP2_NUM_SETTINGS]; - - /* goaway data */ - grpc_status_code goaway_error; - uint32_t goaway_last_stream_index; - gpr_slice goaway_text; - - int64_t outgoing_window; -}; - -struct grpc_chttp2_transport { - grpc_transport base; /* must be first */ - grpc_endpoint *ep; - 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? */ - uint8_t destroying; - /** has the upper layer closed the transport? */ - uint8_t closed; - - /** is a thread currently writing */ - uint8_t writing_active; - /** is a thread currently parsing */ - uint8_t parsing_active; - - /** is there a read request to the endpoint outstanding? */ - uint8_t 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_closure writing_action; - /** closure to finish reading from the endpoint */ - grpc_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)(grpc_exec_ctx *exec_ctx, 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 */ - uint32_t id; - - /** window available for us to send to peer */ - int64_t 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. */ - uint32_t 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. */ - uint32_t unannounced_incoming_window_for_parse; - uint32_t unannounced_incoming_window_for_writing; - /** things the upper layers would like to send */ - grpc_metadata_batch *send_initial_metadata; - grpc_closure *send_initial_metadata_finished; - grpc_byte_stream *send_message; - grpc_closure *send_message_finished; - grpc_metadata_batch *send_trailing_metadata; - grpc_closure *send_trailing_metadata_finished; - - grpc_metadata_batch *recv_initial_metadata; - grpc_closure *recv_initial_metadata_finished; - grpc_byte_stream **recv_message; - grpc_closure *recv_message_ready; - grpc_metadata_batch *recv_trailing_metadata; - grpc_closure *recv_trailing_metadata_finished; - - /** 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' */ - uint8_t write_closed; - /** is this stream reading half-closed (boolean) */ - uint8_t read_closed; - /** is this stream in the stream map? (boolean) */ - uint8_t in_stream_map; - /** has this stream seen an error? if 1, then pending incoming frames - can be thrown away */ - uint8_t seen_error; - - uint8_t published_initial_metadata; - uint8_t published_trailing_metadata; - uint8_t faked_trailing_metadata; - - grpc_chttp2_incoming_metadata_buffer received_initial_metadata; - grpc_chttp2_incoming_metadata_buffer received_trailing_metadata; - - grpc_chttp2_incoming_frame_queue incoming_frames; -} grpc_chttp2_stream_global; - -typedef struct { - /** HTTP2 stream id for this stream, or zero if one has not been assigned */ - uint32_t id; - uint8_t fetching; - uint8_t sent_initial_metadata; - uint8_t sent_message; - uint8_t sent_trailing_metadata; - uint8_t read_closed; - /** send this initial metadata */ - grpc_metadata_batch *send_initial_metadata; - grpc_byte_stream *send_message; - grpc_metadata_batch *send_trailing_metadata; - int64_t outgoing_window; - /** how much window should we announce? */ - uint32_t announce_window; - gpr_slice_buffer flow_controlled_buffer; - gpr_slice fetching_slice; - size_t stream_fetched; - grpc_closure finished_fetch; -} grpc_chttp2_stream_writing; - -struct grpc_chttp2_stream_parsing { - /** HTTP2 stream id for this stream, or zero if one has not been assigned */ - uint32_t id; - /** has this stream received a close */ - uint8_t received_close; - /** saw a rst_stream */ - uint8_t saw_rst_stream; - /** how many header frames have we received? */ - uint8_t header_frames_received; - /** which metadata did we get (on this parse) */ - uint8_t got_metadata_on_parse[2]; - /** should we raise the seen_error flag in transport_global */ - uint8_t seen_error; - /** window available for peer to send to us */ - int64_t incoming_window; - /** parsing state for data frames */ - grpc_chttp2_data_parser data_parser; - /** reason give to rst_stream */ - uint32_t rst_stream_reason; - /** amount of window given */ - int64_t outgoing_window; - /** number of bytes received - reset at end of parse thread execution */ - int64_t received_bytes; - - /** incoming metadata */ - grpc_chttp2_incoming_metadata_buffer metadata_buffer[2]; -}; - -struct grpc_chttp2_stream { - grpc_stream_refcount *refcount; - grpc_chttp2_stream_global global; - grpc_chttp2_stream_writing writing; - grpc_chttp2_stream_parsing parsing; - - grpc_chttp2_stream_link links[STREAM_LIST_COUNT]; - uint8_t 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, - int is_parsing); -void grpc_chttp2_perform_writes( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing, - grpc_endpoint *endpoint); -void grpc_chttp2_terminate_writing(grpc_exec_ctx *exec_ctx, - void *transport_writing, bool success); -void grpc_chttp2_cleanup_writing(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_chttp2_transport_parsing *transport_parsing, - gpr_slice slice); -void grpc_chttp2_publish_reads(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *global, - grpc_chttp2_transport_parsing *parsing); - -void grpc_chttp2_list_add_writable_stream( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global); -/** Get a writable stream - returns non-zero if there was a stream available */ -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); - -/* returns 1 if stream added, 0 if it was already present */ -int grpc_chttp2_list_add_writing_stream( - grpc_chttp2_transport_writing *transport_writing, - grpc_chttp2_stream_writing *stream_writing) GRPC_MUST_USE_RESULT; -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_check_read_ops( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global); -int grpc_chttp2_list_pop_check_read_ops( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global **stream_global); - -void grpc_chttp2_list_add_writing_stalled_by_transport( - grpc_chttp2_transport_writing *transport_writing, - grpc_chttp2_stream_writing *stream_writing); -void grpc_chttp2_list_flush_writing_stalled_by_transport( - grpc_chttp2_transport_writing *transport_writing, bool is_window_available); - -int grpc_chttp2_list_pop_stalled_by_transport( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global **stream_global); -void grpc_chttp2_list_remove_stalled_by_transport( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global); - -void grpc_chttp2_list_add_unannounced_incoming_window_available( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global); -void grpc_chttp2_list_remove_unannounced_incoming_window_available( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global); -int grpc_chttp2_list_pop_unannounced_incoming_window_available( - 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_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_closed_waiting_for_writing( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global); -int grpc_chttp2_list_pop_closed_waiting_for_writing( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global **stream_global); - -grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream( - grpc_chttp2_transport_parsing *transport_parsing, uint32_t id); -grpc_chttp2_stream_parsing *grpc_chttp2_parsing_accept_stream( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - uint32_t id); - -void grpc_chttp2_add_incoming_goaway( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, - uint32_t 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_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing); - -void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx, - grpc_closure **pclosure, int success); - -#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 - -typedef enum { - GRPC_CHTTP2_FLOWCTL_MOVE, - GRPC_CHTTP2_FLOWCTL_CREDIT, - GRPC_CHTTP2_FLOWCTL_DEBIT -} grpc_chttp2_flowctl_op; - -#define GRPC_CHTTP2_FLOW_MOVE_COMMON(phase, transport, id1, id2, dst_context, \ - dst_var, src_context, src_var) \ - do { \ - assert(id1 == id2); \ - if (grpc_flowctl_trace) { \ - grpc_chttp2_flowctl_trace( \ - __FILE__, __LINE__, phase, GRPC_CHTTP2_FLOWCTL_MOVE, #dst_context, \ - #dst_var, #src_context, #src_var, transport->is_client, id1, \ - dst_context->dst_var, src_context->src_var); \ - } \ - dst_context->dst_var += src_context->src_var; \ - src_context->src_var = 0; \ - } while (0) - -#define GRPC_CHTTP2_FLOW_MOVE_STREAM(phase, transport, dst_context, dst_var, \ - src_context, src_var) \ - GRPC_CHTTP2_FLOW_MOVE_COMMON(phase, transport, dst_context->id, \ - src_context->id, dst_context, dst_var, \ - src_context, src_var) -#define GRPC_CHTTP2_FLOW_MOVE_TRANSPORT(phase, dst_context, dst_var, \ - src_context, src_var) \ - GRPC_CHTTP2_FLOW_MOVE_COMMON(phase, dst_context, 0, 0, dst_context, dst_var, \ - src_context, src_var) - -#define GRPC_CHTTP2_FLOW_CREDIT_COMMON(phase, transport, id, dst_context, \ - dst_var, amount) \ - do { \ - if (grpc_flowctl_trace) { \ - grpc_chttp2_flowctl_trace(__FILE__, __LINE__, phase, \ - GRPC_CHTTP2_FLOWCTL_CREDIT, #dst_context, \ - #dst_var, NULL, #amount, transport->is_client, \ - id, dst_context->dst_var, amount); \ - } \ - dst_context->dst_var += amount; \ - } while (0) - -#define GRPC_CHTTP2_FLOW_CREDIT_STREAM(phase, transport, dst_context, dst_var, \ - amount) \ - GRPC_CHTTP2_FLOW_CREDIT_COMMON(phase, transport, dst_context->id, \ - dst_context, dst_var, amount) -#define GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT(phase, dst_context, dst_var, amount) \ - GRPC_CHTTP2_FLOW_CREDIT_COMMON(phase, dst_context, 0, dst_context, dst_var, \ - amount) - -#define GRPC_CHTTP2_FLOW_DEBIT_COMMON(phase, transport, id, dst_context, \ - dst_var, amount) \ - do { \ - if (grpc_flowctl_trace) { \ - grpc_chttp2_flowctl_trace(__FILE__, __LINE__, phase, \ - GRPC_CHTTP2_FLOWCTL_DEBIT, #dst_context, \ - #dst_var, NULL, #amount, transport->is_client, \ - id, dst_context->dst_var, amount); \ - } \ - dst_context->dst_var -= amount; \ - } while (0) - -#define GRPC_CHTTP2_FLOW_DEBIT_STREAM(phase, transport, dst_context, dst_var, \ - amount) \ - GRPC_CHTTP2_FLOW_DEBIT_COMMON(phase, transport, dst_context->id, \ - dst_context, dst_var, amount) -#define GRPC_CHTTP2_FLOW_DEBIT_TRANSPORT(phase, dst_context, dst_var, amount) \ - GRPC_CHTTP2_FLOW_DEBIT_COMMON(phase, dst_context, 0, dst_context, dst_var, \ - amount) - -void grpc_chttp2_flowctl_trace(const char *file, int line, const char *phase, - grpc_chttp2_flowctl_op op, const char *context1, - const char *var1, const char *context2, - const char *var2, int is_client, - uint32_t stream_id, int64_t val1, int64_t val2); - -void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream, - grpc_status_code status, gpr_slice *details); -void grpc_chttp2_mark_stream_closed( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, int close_reads, - int close_writes); -void grpc_chttp2_start_writing(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *transport_global); - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -#define GRPC_CHTTP2_STREAM_REF(stream_global, reason) \ - grpc_chttp2_stream_ref(stream_global, reason) -#define GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, reason) \ - grpc_chttp2_stream_unref(exec_ctx, stream_global, reason) -void grpc_chttp2_stream_ref(grpc_chttp2_stream_global *stream_global, - const char *reason); -void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_chttp2_stream_global *stream_global, - const char *reason); -#else -#define GRPC_CHTTP2_STREAM_REF(stream_global, reason) \ - grpc_chttp2_stream_ref(stream_global) -#define GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, reason) \ - grpc_chttp2_stream_unref(exec_ctx, stream_global) -void grpc_chttp2_stream_ref(grpc_chttp2_stream_global *stream_global); -void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_chttp2_stream_global *stream_global); -#endif - -grpc_chttp2_incoming_byte_stream *grpc_chttp2_incoming_byte_stream_create( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, uint32_t frame_size, - uint32_t flags, grpc_chttp2_incoming_frame_queue *add_to_queue); -void grpc_chttp2_incoming_byte_stream_push(grpc_exec_ctx *exec_ctx, - grpc_chttp2_incoming_byte_stream *bs, - gpr_slice slice); -void grpc_chttp2_incoming_byte_stream_finished( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, int success, - int from_parsing_thread); - -void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_parsing *parsing, - const uint8_t *opaque_8bytes); - -#endif diff --git a/third_party/grpc/src/core/transport/chttp2/parsing.c b/third_party/grpc/src/core/transport/chttp2/parsing.c deleted file mode 100644 index 8fdebd7f139857..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/parsing.c +++ /dev/null @@ -1,866 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 -#include -#include - -#include "src/core/profiling/timers.h" -#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 "src/core/transport/static_metadata.h" - -static int init_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_parsing *transport_parsing); -static int init_header_frame_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - int is_continuation); -static int init_data_frame_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing); -static int init_rst_stream_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing); -static int init_settings_frame_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing); -static int init_window_update_frame_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing); -static int init_ping_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_parsing *transport_parsing); -static int init_goaway_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_parsing *transport_parsing); -static int init_skip_frame_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - int is_header); - -static int parse_frame_slice(grpc_exec_ctx *exec_ctx, - 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; - - GPR_TIMER_BEGIN("grpc_chttp2_prepare_to_read", 0); - - transport_parsing->next_stream_id = transport_global->next_stream_id; - transport_parsing->last_sent_max_table_size = - transport_global->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]; - - /* update the parsing view of incoming window */ - while (grpc_chttp2_list_pop_unannounced_incoming_window_available( - transport_global, transport_parsing, &stream_global, &stream_parsing)) { - GRPC_CHTTP2_FLOW_MOVE_STREAM("parse", transport_parsing, stream_parsing, - incoming_window, stream_global, - unannounced_incoming_window_for_parse); - } - - GPR_TIMER_END("grpc_chttp2_prepare_to_read", 0); -} - -void grpc_chttp2_publish_reads( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, - grpc_chttp2_transport_parsing *transport_parsing) { - grpc_chttp2_stream_global *stream_global; - grpc_chttp2_stream_parsing *stream_parsing; - int was_zero; - int is_zero; - - /* 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; - } - - /* 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(uint32_t)); - transport_parsing->settings_ack_received = 0; - transport_global->sent_local_settings = 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(exec_ctx, transport_global, - (uint32_t)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 */ - was_zero = transport_global->outgoing_window <= 0; - GRPC_CHTTP2_FLOW_MOVE_TRANSPORT("parsed", transport_global, outgoing_window, - transport_parsing, outgoing_window); - is_zero = transport_global->outgoing_window <= 0; - if (was_zero && !is_zero) { - while (grpc_chttp2_list_pop_stalled_by_transport(transport_global, - &stream_global)) { - grpc_chttp2_list_add_writable_stream(transport_global, stream_global); - } - } - - if (transport_parsing->incoming_window < - transport_global->connection_window_target * 3 / 4) { - int64_t announce_bytes = transport_global->connection_window_target - - transport_parsing->incoming_window; - GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parsed", transport_global, - announce_incoming_window, announce_bytes); - GRPC_CHTTP2_FLOW_CREDIT_TRANSPORT("parsed", transport_parsing, - incoming_window, announce_bytes); - } - - /* 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)) { - if (stream_parsing->seen_error) { - stream_global->seen_error = 1; - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - - /* update outgoing flow control window */ - was_zero = stream_global->outgoing_window <= 0; - GRPC_CHTTP2_FLOW_MOVE_STREAM("parsed", transport_global, stream_global, - outgoing_window, stream_parsing, - outgoing_window); - is_zero = stream_global->outgoing_window <= 0; - if (was_zero && !is_zero) { - grpc_chttp2_list_add_writable_stream(transport_global, stream_global); - } - - stream_global->max_recv_bytes -= (uint32_t)GPR_MIN( - stream_global->max_recv_bytes, stream_parsing->received_bytes); - stream_parsing->received_bytes = 0; - - /* publish incoming stream ops */ - if (stream_global->incoming_frames.tail != NULL) { - stream_global->incoming_frames.tail->is_tail = 0; - } - if (stream_parsing->data_parser.incoming_frames.head != NULL) { - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - grpc_chttp2_incoming_frame_queue_merge( - &stream_global->incoming_frames, - &stream_parsing->data_parser.incoming_frames); - if (stream_global->incoming_frames.tail != NULL) { - stream_global->incoming_frames.tail->is_tail = 1; - } - - if (!stream_global->published_initial_metadata && - stream_parsing->got_metadata_on_parse[0]) { - stream_parsing->got_metadata_on_parse[0] = 0; - stream_global->published_initial_metadata = 1; - GPR_SWAP(grpc_chttp2_incoming_metadata_buffer, - stream_parsing->metadata_buffer[0], - stream_global->received_initial_metadata); - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - if (!stream_global->published_trailing_metadata && - stream_parsing->got_metadata_on_parse[1]) { - stream_parsing->got_metadata_on_parse[1] = 0; - stream_global->published_trailing_metadata = 1; - GPR_SWAP(grpc_chttp2_incoming_metadata_buffer, - stream_parsing->metadata_buffer[1], - stream_global->received_trailing_metadata); - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - - if (stream_parsing->saw_rst_stream) { - if (stream_parsing->rst_stream_reason != GRPC_CHTTP2_NO_ERROR) { - grpc_status_code status_code = grpc_chttp2_http2_error_to_grpc_status( - (grpc_chttp2_error_code)stream_parsing->rst_stream_reason); - char *status_details; - gpr_slice slice_details; - gpr_asprintf(&status_details, "Received RST_STREAM err=%d", - stream_parsing->rst_stream_reason); - slice_details = gpr_slice_from_copied_string(status_details); - gpr_free(status_details); - grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, - status_code, &slice_details); - } - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, - 1, 1); - } - - if (stream_parsing->received_close) { - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, - 1, 0); - } - } -} - -int grpc_chttp2_perform_read(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_parsing *transport_parsing, - gpr_slice slice) { - uint8_t *beg = GPR_SLICE_START_PTR(slice); - uint8_t *end = GPR_SLICE_END_PTR(slice); - uint8_t *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)(uint8_t)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 = ((uint32_t)*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 |= ((uint32_t)*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 = (((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*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 |= ((uint32_t)*cur); - transport_parsing->deframe_state = GRPC_DTS_FRAME; - if (!init_frame_parser(exec_ctx, 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(exec_ctx, 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 ((uint32_t)(end - cur) == transport_parsing->incoming_frame_size) { - if (!parse_frame_slice(exec_ctx, transport_parsing, - gpr_slice_sub_no_ref(slice, (size_t)(cur - beg), - (size_t)(end - beg)), - 1)) { - return 0; - } - transport_parsing->deframe_state = GRPC_DTS_FH_0; - transport_parsing->incoming_stream = NULL; - return 1; - } else if ((uint32_t)(end - cur) > - transport_parsing->incoming_frame_size) { - size_t cur_offset = (size_t)(cur - beg); - if (!parse_frame_slice( - exec_ctx, transport_parsing, - gpr_slice_sub_no_ref( - slice, cur_offset, - cur_offset + transport_parsing->incoming_frame_size), - 1)) { - return 0; - } - cur += transport_parsing->incoming_frame_size; - transport_parsing->incoming_stream = NULL; - goto dts_fh_0; /* loop */ - } else { - if (!parse_frame_slice(exec_ctx, transport_parsing, - gpr_slice_sub_no_ref(slice, (size_t)(cur - beg), - (size_t)(end - beg)), - 0)) { - return 0; - } - transport_parsing->incoming_frame_size -= (uint32_t)(end - cur); - return 1; - } - GPR_UNREACHABLE_CODE(return 0); - } - - GPR_UNREACHABLE_CODE(return 0); -} - -static int init_frame_parser(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, transport_parsing, 1); - } - switch (transport_parsing->incoming_frame_type) { - case GRPC_CHTTP2_FRAME_DATA: - return init_data_frame_parser(exec_ctx, transport_parsing); - case GRPC_CHTTP2_FRAME_HEADER: - return init_header_frame_parser(exec_ctx, 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(exec_ctx, transport_parsing); - case GRPC_CHTTP2_FRAME_SETTINGS: - return init_settings_frame_parser(exec_ctx, transport_parsing); - case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: - return init_window_update_frame_parser(exec_ctx, transport_parsing); - case GRPC_CHTTP2_FRAME_PING: - return init_ping_parser(exec_ctx, transport_parsing); - case GRPC_CHTTP2_FRAME_GOAWAY: - return init_goaway_parser(exec_ctx, transport_parsing); - default: - gpr_log(GPR_ERROR, "Unknown frame type %02x", - transport_parsing->incoming_frame_type); - return init_skip_frame_parser(exec_ctx, transport_parsing, 0); - } -} - -static grpc_chttp2_parse_error skip_parser( - grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - int is_header) { - if (is_header) { - uint8_t 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 = - (uint8_t)(is_eoh ? transport_parsing->header_eof : 0); - } else { - transport_parsing->parser = skip_parser; - } - return 1; -} - -void grpc_chttp2_parsing_become_skip_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing) { - init_skip_frame_parser( - exec_ctx, transport_parsing, - transport_parsing->parser == grpc_chttp2_header_parser_parse); -} - -static grpc_chttp2_parse_error update_incoming_window( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing) { - uint32_t incoming_frame_size = transport_parsing->incoming_frame_size; - if (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 (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_FLOW_DEBIT_TRANSPORT("parse", transport_parsing, incoming_window, - incoming_frame_size); - GRPC_CHTTP2_FLOW_DEBIT_STREAM("parse", transport_parsing, stream_parsing, - incoming_window, incoming_frame_size); - stream_parsing->received_bytes += 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_exec_ctx *exec_ctx, 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(exec_ctx, transport_parsing, 0); - if (err == GRPC_CHTTP2_PARSE_OK) { - err = update_incoming_window(exec_ctx, 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(exec_ctx, transport_parsing, 0); - case GRPC_CHTTP2_CONNECTION_ERROR: - return 0; - } - GPR_UNREACHABLE_CODE(return 0); -} - -static void free_timeout(void *p) { gpr_free(p); } - -static void on_initial_header(void *tp, grpc_mdelem *md) { - grpc_chttp2_transport_parsing *transport_parsing = tp; - grpc_chttp2_stream_parsing *stream_parsing = - transport_parsing->incoming_stream; - - GPR_TIMER_BEGIN("on_initial_header", 0); - - 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 == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) { - /* TODO(ctiller): check for a status like " 0" */ - stream_parsing->seen_error = 1; - } - - if (md->key == GRPC_MDSTR_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_TIMESPAN); - } - grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); - } - grpc_chttp2_incoming_metadata_buffer_set_deadline( - &stream_parsing->metadata_buffer[0], - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), *cached_timeout)); - GRPC_MDELEM_UNREF(md); - } else { - grpc_chttp2_incoming_metadata_buffer_add( - &stream_parsing->metadata_buffer[0], md); - } - - grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, stream_parsing); - - GPR_TIMER_END("on_initial_header", 0); -} - -static void on_trailing_header(void *tp, grpc_mdelem *md) { - grpc_chttp2_transport_parsing *transport_parsing = tp; - grpc_chttp2_stream_parsing *stream_parsing = - transport_parsing->incoming_stream; - - GPR_TIMER_BEGIN("on_trailing_header", 0); - - GPR_ASSERT(stream_parsing); - - GRPC_CHTTP2_IF_TRACING(gpr_log( - GPR_INFO, "HTTP:%d:TRL:%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 == GRPC_MDSTR_GRPC_STATUS && md != GRPC_MDELEM_GRPC_STATUS_0) { - /* TODO(ctiller): check for a status like " 0" */ - stream_parsing->seen_error = 1; - } - - grpc_chttp2_incoming_metadata_buffer_add(&stream_parsing->metadata_buffer[1], - md); - - grpc_chttp2_list_add_parsing_seen_stream(transport_parsing, stream_parsing); - - GPR_TIMER_END("on_trailing_header", 0); -} - -static int init_header_frame_parser( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - int is_continuation) { - uint8_t is_eoh = (transport_parsing->incoming_frame_flags & - GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; - int via_accept = 0; - grpc_chttp2_stream_parsing *stream_parsing; - - /* TODO(ctiller): when to increment header_frames_received? */ - - 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(exec_ctx, 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(exec_ctx, 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(exec_ctx, 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(exec_ctx, transport_parsing, 1); - } - stream_parsing = transport_parsing->incoming_stream = - grpc_chttp2_parsing_accept_stream( - exec_ctx, 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(exec_ctx, 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(exec_ctx, transport_parsing, 1); - } - transport_parsing->parser = grpc_chttp2_header_parser_parse; - transport_parsing->parser_data = &transport_parsing->hpack_parser; - switch (stream_parsing->header_frames_received) { - case 0: - transport_parsing->hpack_parser.on_header = on_initial_header; - break; - case 1: - transport_parsing->hpack_parser.on_header = on_trailing_header; - break; - case 2: - gpr_log(GPR_ERROR, "too many header frames received"); - return init_skip_frame_parser(exec_ctx, transport_parsing, 1); - } - transport_parsing->hpack_parser.on_header_user_data = transport_parsing; - transport_parsing->hpack_parser.is_boundary = is_eoh; - transport_parsing->hpack_parser.is_eof = - (uint8_t)(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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, 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(exec_ctx, 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_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, 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; - grpc_chttp2_hptbl_set_max_bytes( - &transport_parsing->hpack_parser.table, - transport_parsing->last_sent_max_table_size); - } - transport_parsing->parser = grpc_chttp2_settings_parser_parse; - transport_parsing->parser_data = &transport_parsing->simple.settings; - return ok; -} - -/* -static int is_window_update_legal(int64_t window_update, int64_t window) { - return window + window_update < MAX_WINDOW; -} -*/ - -static int parse_frame_slice(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, 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(exec_ctx, 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_UNREACHABLE_CODE(return 0); -} diff --git a/third_party/grpc/src/core/transport/chttp2/status_conversion.c b/third_party/grpc/src/core/transport/chttp2/status_conversion.c deleted file mode 100644 index bf214b017a21bc..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/status_conversion.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/status_conversion.h b/third_party/grpc/src/core/transport/chttp2/status_conversion.h deleted file mode 100644 index 0ec5b560b8ed0d..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/status_conversion.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/stream_lists.c b/third_party/grpc/src/core/transport/chttp2/stream_lists.c deleted file mode 100644 index 2f31a47cb3df6a..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/stream_lists.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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_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 int stream_list_add(grpc_chttp2_transport *t, grpc_chttp2_stream *s, - grpc_chttp2_stream_list_id id) { - if (s->included[id]) { - return 0; - } - stream_list_add_tail(t, s, id); - return 1; -} - -/* 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); -} - -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); -} - -int grpc_chttp2_list_add_writing_stream( - grpc_chttp2_transport_writing *transport_writing, - grpc_chttp2_stream_writing *stream_writing) { - return 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_unannounced_incoming_window_available( - 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_UNANNOUNCED_INCOMING_WINDOW_AVAILABLE); -} - -void grpc_chttp2_list_remove_unannounced_incoming_window_available( - 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_UNANNOUNCED_INCOMING_WINDOW_AVAILABLE); -} - -int grpc_chttp2_list_pop_unannounced_incoming_window_available( - 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_UNANNOUNCED_INCOMING_WINDOW_AVAILABLE); - if (r != 0) { - *stream_global = &stream->global; - *stream_parsing = &stream->parsing; - } - 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_check_read_ops( - 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_CHECK_READ_OPS); -} - -int grpc_chttp2_list_pop_check_read_ops( - 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_CHECK_READ_OPS); - if (r != 0) { - *stream_global = &stream->global; - } - return r; -} - -void grpc_chttp2_list_add_writing_stalled_by_transport( - 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_STALLED_BY_TRANSPORT); -} - -void grpc_chttp2_list_flush_writing_stalled_by_transport( - grpc_chttp2_transport_writing *transport_writing, - bool is_window_available) { - grpc_chttp2_stream *stream; - grpc_chttp2_transport *transport = TRANSPORT_FROM_WRITING(transport_writing); - while (stream_list_pop(transport, &stream, - GRPC_CHTTP2_LIST_WRITING_STALLED_BY_TRANSPORT)) { - if (is_window_available) { - grpc_chttp2_list_add_writable_stream(&transport->global, &stream->global); - } else { - stream_list_add(transport, stream, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); - } - } -} - -int grpc_chttp2_list_pop_stalled_by_transport( - 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_STALLED_BY_TRANSPORT); - if (r != 0) { - *stream_global = &stream->global; - } - return r; -} - -void grpc_chttp2_list_remove_stalled_by_transport( - 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_STALLED_BY_TRANSPORT); -} - -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_closed_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_CLOSED_WAITING_FOR_WRITING); -} - -int grpc_chttp2_list_pop_closed_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_CLOSED_WAITING_FOR_WRITING); - 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/third_party/grpc/src/core/transport/chttp2/stream_map.c b/third_party/grpc/src/core/transport/chttp2/stream_map.c deleted file mode 100644 index 555a16fb72bd7b..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/stream_map.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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(uint32_t) * 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(uint32_t *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, uint32_t key, - void *value) { - size_t count = map->count; - size_t capacity = map->capacity; - uint32_t *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(uint32_t)); - 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(uint32_t)); - dst->values = gpr_realloc(dst->values, dst->capacity * sizeof(void *)); - } - memcpy(dst->keys + dst->count, src->keys, src->count * sizeof(uint32_t)); - 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, uint32_t key) { - size_t min_idx = 0; - size_t max_idx = map->count; - size_t mid_idx; - uint32_t *keys = map->keys; - void **values = map->values; - uint32_t 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, uint32_t 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, uint32_t 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, uint32_t 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/third_party/grpc/src/core/transport/chttp2/stream_map.h b/third_party/grpc/src/core/transport/chttp2/stream_map.h deleted file mode 100644 index 7a0e45fab2ca14..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/stream_map.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 uint32_t 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 { - uint32_t *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, uint32_t 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, uint32_t 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, uint32_t 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, uint32_t key, - void *value), - void *user_data); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_STREAM_MAP_H */ diff --git a/third_party/grpc/src/core/transport/chttp2/timeout_encoding.c b/third_party/grpc/src/core/transport/chttp2/timeout_encoding.c deleted file mode 100644 index a6f7081d21b964..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/timeout_encoding.c +++ /dev/null @@ -1,188 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/transport/chttp2/timeout_encoding.h" - -#include -#include - -#include -#include "src/core/support/string.h" - -static int64_t round_up(int64_t x, int64_t divisor) { - return (x / divisor + (x % divisor != 0)) * divisor; -} - -/* round an integer up to the next value with three significant figures */ -static int64_t round_up_to_three_sig_figs(int64_t 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, int64_t value, char ext) { - int n = int64_ttoa(value, buffer); - buffer[n] = ext; - buffer[n + 1] = 0; -} - -static void enc_seconds(char *buffer, int64_t 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, int64_t 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, int64_t 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, - (int64_t)(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) { - int32_t x = 0; - const uint8_t *p = (const uint8_t *)buffer; - int have_digit = 0; - /* skip whitespace */ - for (; *p == ' '; p++) - ; - /* decode numeric part */ - for (; *p >= '0' && *p <= '9'; p++) { - int32_t digit = (int32_t)(*p - (uint8_t)'0'); - have_digit = 1; - /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */ - if (x >= (100 * 1000 * 1000)) { - if (x != (100 * 1000 * 1000) || digit != 0) { - *timeout = gpr_inf_future(GPR_CLOCK_REALTIME); - return 1; - } - } - x = x * 10 + digit; - } - 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((const char *)p); -} diff --git a/third_party/grpc/src/core/transport/chttp2/timeout_encoding.h b/third_party/grpc/src/core/transport/chttp2/timeout_encoding.h deleted file mode 100644 index 9d8756e799d3d8..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/timeout_encoding.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/core/transport/chttp2/varint.c b/third_party/grpc/src/core/transport/chttp2/varint.c deleted file mode 100644 index 1cc235e9899ec7..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/varint.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" - -uint32_t grpc_chttp2_hpack_varint_length(uint32_t 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(uint32_t tail_value, uint8_t* target, - uint32_t tail_length) { - switch (tail_length) { - case 5: - target[4] = (uint8_t)((tail_value >> 28) | 0x80); - case 4: - target[3] = (uint8_t)((tail_value >> 21) | 0x80); - case 3: - target[2] = (uint8_t)((tail_value >> 14) | 0x80); - case 2: - target[1] = (uint8_t)((tail_value >> 7) | 0x80); - case 1: - target[0] = (uint8_t)((tail_value) | 0x80); - } - target[tail_length - 1] &= 0x7f; -} diff --git a/third_party/grpc/src/core/transport/chttp2/writing.c b/third_party/grpc/src/core/transport/chttp2/writing.c deleted file mode 100644 index cafecf10465818..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2/writing.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/transport/chttp2/internal.h" - -#include - -#include - -#include "src/core/profiling/timers.h" -#include "src/core/transport/chttp2/http2_errors.h" - -static void finalize_outbuf(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_writing *transport_writing); - -int grpc_chttp2_unlocking_check_writes( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_transport_writing *transport_writing, int is_parsing) { - grpc_chttp2_stream_global *stream_global; - grpc_chttp2_stream_writing *stream_writing; - - GPR_TIMER_BEGIN("grpc_chttp2_unlocking_check_writes", 0); - - /* 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); - - grpc_chttp2_hpack_compressor_set_max_table_size( - &transport_writing->hpack_compressor, - transport_global->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); - - if (transport_global->dirtied_local_settings && - !transport_global->sent_local_settings && !is_parsing) { - 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; - } - - GRPC_CHTTP2_FLOW_MOVE_TRANSPORT("write", transport_writing, outgoing_window, - transport_global, outgoing_window); - bool is_window_available = transport_writing->outgoing_window > 0; - grpc_chttp2_list_flush_writing_stalled_by_transport(transport_writing, - is_window_available); - - /* 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)) { - uint8_t sent_initial_metadata; - - stream_writing->id = stream_global->id; - stream_writing->read_closed = stream_global->read_closed; - - GRPC_CHTTP2_FLOW_MOVE_STREAM("write", transport_writing, stream_writing, - outgoing_window, stream_global, - outgoing_window); - - sent_initial_metadata = stream_writing->sent_initial_metadata; - if (!sent_initial_metadata && stream_global->send_initial_metadata) { - stream_writing->send_initial_metadata = - stream_global->send_initial_metadata; - stream_global->send_initial_metadata = NULL; - if (grpc_chttp2_list_add_writing_stream(transport_writing, - stream_writing)) { - GRPC_CHTTP2_STREAM_REF(stream_global, "chttp2_writing"); - } - sent_initial_metadata = 1; - } - if (sent_initial_metadata) { - if (stream_global->send_message != NULL) { - gpr_slice hdr = gpr_slice_malloc(5); - uint8_t *p = GPR_SLICE_START_PTR(hdr); - uint32_t len = stream_global->send_message->length; - GPR_ASSERT(stream_writing->send_message == NULL); - p[0] = (stream_global->send_message->flags & - GRPC_WRITE_INTERNAL_COMPRESS) != 0; - p[1] = (uint8_t)(len >> 24); - p[2] = (uint8_t)(len >> 16); - p[3] = (uint8_t)(len >> 8); - p[4] = (uint8_t)(len); - gpr_slice_buffer_add(&stream_writing->flow_controlled_buffer, hdr); - if (stream_global->send_message->length > 0) { - stream_writing->send_message = stream_global->send_message; - } else { - stream_writing->send_message = NULL; - } - stream_writing->stream_fetched = 0; - stream_global->send_message = NULL; - } - if ((stream_writing->send_message != NULL || - stream_writing->flow_controlled_buffer.length > 0) && - stream_writing->outgoing_window > 0) { - if (transport_writing->outgoing_window > 0) { - if (grpc_chttp2_list_add_writing_stream(transport_writing, - stream_writing)) { - GRPC_CHTTP2_STREAM_REF(stream_global, "chttp2_writing"); - } - } else { - grpc_chttp2_list_add_writing_stalled_by_transport(transport_writing, - stream_writing); - } - } - if (stream_global->send_trailing_metadata) { - stream_writing->send_trailing_metadata = - stream_global->send_trailing_metadata; - stream_global->send_trailing_metadata = NULL; - if (grpc_chttp2_list_add_writing_stream(transport_writing, - stream_writing)) { - GRPC_CHTTP2_STREAM_REF(stream_global, "chttp2_writing"); - } - } - } - - if (!stream_global->read_closed && - stream_global->unannounced_incoming_window_for_writing > 1024) { - GRPC_CHTTP2_FLOW_MOVE_STREAM("write", transport_global, stream_writing, - announce_window, stream_global, - unannounced_incoming_window_for_writing); - if (grpc_chttp2_list_add_writing_stream(transport_writing, - stream_writing)) { - GRPC_CHTTP2_STREAM_REF(stream_global, "chttp2_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->announce_incoming_window > 0) { - uint32_t announced = (uint32_t)GPR_MIN( - transport_global->announce_incoming_window, UINT32_MAX); - GRPC_CHTTP2_FLOW_DEBIT_TRANSPORT("write", transport_global, - announce_incoming_window, announced); - gpr_slice_buffer_add(&transport_writing->outbuf, - grpc_chttp2_window_update_create(0, announced)); - } - - GPR_TIMER_END("grpc_chttp2_unlocking_check_writes", 0); - - return transport_writing->outbuf.count > 0 || - grpc_chttp2_list_have_writing_streams(transport_writing); -} - -void grpc_chttp2_perform_writes( - grpc_exec_ctx *exec_ctx, 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(exec_ctx, transport_writing); - - GPR_ASSERT(endpoint); - - if (transport_writing->outbuf.count > 0) { - grpc_endpoint_write(exec_ctx, endpoint, &transport_writing->outbuf, - &transport_writing->done_cb); - } else { - grpc_exec_ctx_enqueue(exec_ctx, &transport_writing->done_cb, true, NULL); - } -} - -static void finalize_outbuf(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_writing *transport_writing) { - grpc_chttp2_stream_writing *stream_writing; - - GPR_TIMER_BEGIN("finalize_outbuf", 0); - - while ( - grpc_chttp2_list_pop_writing_stream(transport_writing, &stream_writing)) { - uint32_t max_outgoing = - (uint32_t)GPR_MIN(GRPC_CHTTP2_MAX_PAYLOAD_LENGTH, - GPR_MIN(stream_writing->outgoing_window, - transport_writing->outgoing_window)); - /* send initial metadata if it's available */ - if (stream_writing->send_initial_metadata != NULL) { - grpc_chttp2_encode_header( - &transport_writing->hpack_compressor, stream_writing->id, - stream_writing->send_initial_metadata, 0, &transport_writing->outbuf); - stream_writing->send_initial_metadata = NULL; - stream_writing->sent_initial_metadata = 1; - } - /* send any window updates */ - if (stream_writing->announce_window > 0 && - stream_writing->send_initial_metadata == NULL) { - uint32_t announce = stream_writing->announce_window; - gpr_slice_buffer_add( - &transport_writing->outbuf, - grpc_chttp2_window_update_create(stream_writing->id, - stream_writing->announce_window)); - GRPC_CHTTP2_FLOW_DEBIT_STREAM("write", transport_writing, stream_writing, - announce_window, announce); - stream_writing->announce_window = 0; - } - /* fetch any body bytes */ - while (!stream_writing->fetching && stream_writing->send_message && - stream_writing->flow_controlled_buffer.length < max_outgoing && - stream_writing->stream_fetched < - stream_writing->send_message->length) { - if (grpc_byte_stream_next(exec_ctx, stream_writing->send_message, - &stream_writing->fetching_slice, max_outgoing, - &stream_writing->finished_fetch)) { - stream_writing->stream_fetched += - GPR_SLICE_LENGTH(stream_writing->fetching_slice); - if (stream_writing->stream_fetched == - stream_writing->send_message->length) { - stream_writing->send_message = NULL; - } - gpr_slice_buffer_add(&stream_writing->flow_controlled_buffer, - stream_writing->fetching_slice); - } else { - stream_writing->fetching = 1; - } - } - /* send any body bytes */ - if (stream_writing->flow_controlled_buffer.length > 0) { - if (max_outgoing > 0) { - uint32_t send_bytes = (uint32_t)GPR_MIN( - max_outgoing, stream_writing->flow_controlled_buffer.length); - int is_last_data_frame = - stream_writing->send_message == NULL && - send_bytes == stream_writing->flow_controlled_buffer.length; - int is_last_frame = is_last_data_frame && - stream_writing->send_trailing_metadata != NULL && - grpc_metadata_batch_is_empty( - stream_writing->send_trailing_metadata); - grpc_chttp2_encode_data( - stream_writing->id, &stream_writing->flow_controlled_buffer, - send_bytes, is_last_frame, &transport_writing->outbuf); - GRPC_CHTTP2_FLOW_DEBIT_STREAM("write", transport_writing, - stream_writing, outgoing_window, - send_bytes); - GRPC_CHTTP2_FLOW_DEBIT_TRANSPORT("write", transport_writing, - outgoing_window, send_bytes); - if (is_last_frame) { - stream_writing->send_trailing_metadata = NULL; - stream_writing->sent_trailing_metadata = 1; - } - if (is_last_data_frame) { - GPR_ASSERT(stream_writing->send_message == NULL); - stream_writing->sent_message = 1; - } - } else if (transport_writing->outgoing_window == 0) { - grpc_chttp2_list_add_writing_stalled_by_transport(transport_writing, - stream_writing); - grpc_chttp2_list_add_written_stream(transport_writing, stream_writing); - } - } - /* send trailing metadata if it's available and we're ready for it */ - if (stream_writing->send_message == NULL && - stream_writing->flow_controlled_buffer.length == 0 && - stream_writing->send_trailing_metadata != NULL) { - if (grpc_metadata_batch_is_empty( - stream_writing->send_trailing_metadata)) { - grpc_chttp2_encode_data(stream_writing->id, - &stream_writing->flow_controlled_buffer, 0, 1, - &transport_writing->outbuf); - } else { - grpc_chttp2_encode_header(&transport_writing->hpack_compressor, - stream_writing->id, - stream_writing->send_trailing_metadata, 1, - &transport_writing->outbuf); - } - if (!transport_writing->is_client && !stream_writing->read_closed) { - gpr_slice_buffer_add(&transport_writing->outbuf, - grpc_chttp2_rst_stream_create( - stream_writing->id, GRPC_CHTTP2_NO_ERROR)); - } - stream_writing->send_trailing_metadata = NULL; - stream_writing->sent_trailing_metadata = 1; - } - /* if there's more to write, then loop, otherwise prepare to finish the - * write */ - if ((stream_writing->flow_controlled_buffer.length > 0 || - (stream_writing->send_message && !stream_writing->fetching)) && - stream_writing->outgoing_window > 0) { - if (transport_writing->outgoing_window > 0) { - if (grpc_chttp2_list_add_writing_stream(transport_writing, - stream_writing)) { - /* do nothing - already reffed */ - } - } else { - grpc_chttp2_list_add_writing_stalled_by_transport(transport_writing, - stream_writing); - grpc_chttp2_list_add_written_stream(transport_writing, stream_writing); - } - } else { - grpc_chttp2_list_add_written_stream(transport_writing, stream_writing); - } - } - - GPR_TIMER_END("finalize_outbuf", 0); -} - -void grpc_chttp2_cleanup_writing( - grpc_exec_ctx *exec_ctx, 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)) { - if (stream_writing->sent_initial_metadata) { - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_initial_metadata_finished, 1); - } - if (stream_writing->sent_message) { - GPR_ASSERT(stream_writing->send_message == NULL); - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_message_finished, 1); - stream_writing->sent_message = 0; - } - if (stream_writing->sent_trailing_metadata) { - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_trailing_metadata_finished, 1); - } - if (stream_writing->sent_trailing_metadata) { - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, - !transport_global->is_client, 1); - } - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2_writing"); - } - gpr_slice_buffer_reset_and_unref(&transport_writing->outbuf); -} diff --git a/third_party/grpc/src/core/transport/chttp2_transport.c b/third_party/grpc/src/core/transport/chttp2_transport.c deleted file mode 100644 index 9298573c7f4c56..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2_transport.c +++ /dev/null @@ -1,1733 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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/static_metadata.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))) - -#define STREAM_FROM_PARSING(sg) \ - ((grpc_chttp2_stream *)((char *)(sg)-offsetof(grpc_chttp2_stream, parsing))) - -static const grpc_transport_vtable vtable; - -static void lock(grpc_chttp2_transport *t); -static void unlock(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t); - -/* forward declarations of various callbacks that we'll build closures around */ -static void writing_action(grpc_exec_ctx *exec_ctx, void *t, - bool 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, - uint32_t value); - -/** Endpoint callback to process incoming data */ -static void recv_data(grpc_exec_ctx *exec_ctx, void *tp, bool success); - -/** Start disconnection chain */ -static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t); - -/** Perform a transport_op */ -static void perform_stream_op_locked( - grpc_exec_ctx *exec_ctx, 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_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, - grpc_status_code status); - -static void close_from_api(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_pollset *pollset); -static void add_to_pollset_set_locked(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global); - -static void connectivity_state_set( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, - grpc_connectivity_state state, const char *reason); - -static void check_read_ops(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *transport_global); - -static void incoming_byte_stream_update_flow_control( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, size_t max_size_hint, - size_t have_already); - -static void fail_pending_writes(grpc_exec_ctx *exec_ctx, - grpc_chttp2_stream_global *stream_global); - -/* - * CONSTRUCTION/DESTRUCTION/REFCOUNTING - */ - -static void destruct_transport(grpc_exec_ctx *exec_ctx, - 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); - - 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(exec_ctx, &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_exec_ctx_enqueue(exec_ctx, ping->on_recv, false, NULL); - ping->next->prev = ping->prev; - ping->prev->next = ping->next; - gpr_free(ping); - } - - 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(cl, t, r) unref_transport(cl, t, r, __FILE__, __LINE__) -static void unref_transport(grpc_exec_ctx *exec_ctx, 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(exec_ctx, 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(cl, t, r) unref_transport(cl, t) -static void unref_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { - if (!gpr_unref(&t->refs)) return; - destruct_transport(exec_ctx, t); -} - -static void ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); } -#endif - -static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - const grpc_channel_args *channel_args, - grpc_endpoint *ep, uint8_t 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); - t->peer_string = grpc_endpoint_get_peer(ep); - t->endpoint_reading = 1; - t->global.next_stream_id = is_client ? 1 : 2; - t->global.is_client = is_client; - t->writing.outgoing_window = DEFAULT_WINDOW; - t->parsing.incoming_window = DEFAULT_WINDOW; - t->global.stream_lookahead = 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.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, - is_client ? "client_transport" : "server_transport"); - - gpr_slice_buffer_init(&t->global.qbuf); - - gpr_slice_buffer_init(&t->writing.outbuf); - grpc_chttp2_hpack_compressor_init(&t->writing.hpack_compressor); - grpc_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); - - grpc_closure_init(&t->writing.done_cb, grpc_chttp2_terminate_writing, - &t->writing); - grpc_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, - (uint32_t)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 = - (uint32_t)channel_args->args[i].value.integer; - } - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES)) { - if (channel_args->args[i].type != GRPC_ARG_INTEGER) { - gpr_log(GPR_ERROR, "%s: must be an integer", - GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES); - } else if (channel_args->args[i].value.integer <= 5) { - gpr_log(GPR_ERROR, "%s: must be at least 5", - GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES); - } else { - t->global.stream_lookahead = - (uint32_t)channel_args->args[i].value.integer; - } - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER)) { - if (channel_args->args[i].type != GRPC_ARG_INTEGER) { - gpr_log(GPR_ERROR, "%s: must be an integer", - GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER); - } else if (channel_args->args[i].value.integer < 0) { - gpr_log(GPR_ERROR, "%s: must be non-negative", - GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER); - } else { - push_setting(t, GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE, - (uint32_t)channel_args->args[i].value.integer); - } - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) { - if (channel_args->args[i].type != GRPC_ARG_INTEGER) { - gpr_log(GPR_ERROR, "%s: must be an integer", - GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER); - } else if (channel_args->args[i].value.integer < 0) { - gpr_log(GPR_ERROR, "%s: must be non-negative", - GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER); - } else { - grpc_chttp2_hpack_compressor_set_max_usable_size( - &t->writing.hpack_compressor, - (uint32_t)channel_args->args[i].value.integer); - } - } - } - } -} - -static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - - lock(t); - t->destroying = 1; - drop_connection(exec_ctx, t); - unlock(exec_ctx, t); - - UNREF_TRANSPORT(exec_ctx, 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_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (gpr_unref(&t->shutdown_ep_refs)) { - if (t->ep) { - grpc_endpoint_shutdown(exec_ctx, t->ep); - } - } -} - -static void allow_endpoint_shutdown_unlocked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (gpr_unref(&t->shutdown_ep_refs)) { - gpr_mu_lock(&t->mu); - if (t->ep) { - grpc_endpoint_shutdown(exec_ctx, t->ep); - } - gpr_mu_unlock(&t->mu); - } -} - -static void destroy_endpoint(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_endpoint_destroy(exec_ctx, t->ep); - t->ep = NULL; - /* safe because we'll still have the ref for write */ - UNREF_TRANSPORT(exec_ctx, t, "disconnect"); -} - -static void close_transport_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (!t->closed) { - t->closed = 1; - connectivity_state_set(exec_ctx, &t->global, GRPC_CHANNEL_FATAL_FAILURE, - "close_transport"); - if (t->ep) { - allow_endpoint_shutdown_locked(exec_ctx, t); - } - } -} - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_chttp2_stream_ref(grpc_chttp2_stream_global *stream_global, - const char *reason) { - grpc_stream_ref(STREAM_FROM_GLOBAL(stream_global)->refcount, reason); -} -void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_chttp2_stream_global *stream_global, - const char *reason) { - grpc_stream_unref(exec_ctx, STREAM_FROM_GLOBAL(stream_global)->refcount, - reason); -} -#else -void grpc_chttp2_stream_ref(grpc_chttp2_stream_global *stream_global) { - grpc_stream_ref(STREAM_FROM_GLOBAL(stream_global)->refcount); -} -void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_chttp2_stream_global *stream_global) { - grpc_stream_unref(exec_ctx, STREAM_FROM_GLOBAL(stream_global)->refcount); -} -#endif - -static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_stream_refcount *refcount, - const void *server_data) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; - - memset(s, 0, sizeof(*s)); - - s->refcount = refcount; - GRPC_CHTTP2_STREAM_REF(&s->global, "chttp2"); - - grpc_chttp2_incoming_metadata_buffer_init(&s->parsing.metadata_buffer[0]); - grpc_chttp2_incoming_metadata_buffer_init(&s->parsing.metadata_buffer[1]); - grpc_chttp2_incoming_metadata_buffer_init( - &s->global.received_initial_metadata); - grpc_chttp2_incoming_metadata_buffer_init( - &s->global.received_trailing_metadata); - grpc_chttp2_data_parser_init(&s->parsing.data_parser); - gpr_slice_buffer_init(&s->writing.flow_controlled_buffer); - - REF_TRANSPORT(t, "stream"); - - lock(t); - grpc_chttp2_register_stream(t, s); - if (server_data) { - GPR_ASSERT(t->parsing_active); - s->global.id = (uint32_t)(uintptr_t)server_data; - s->parsing.id = s->global.id; - s->global.outgoing_window = - t->global.settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - s->parsing.incoming_window = s->global.max_recv_bytes = - 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; - } - unlock(exec_ctx, t); - - return 0; -} - -static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; - int i; - grpc_byte_stream *bs; - - GPR_TIMER_BEGIN("destroy_stream", 0); - - gpr_mu_lock(&t->mu); - - GPR_ASSERT((s->global.write_closed && s->global.read_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(exec_ctx, 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_writable_stream(&t->global, &s->global); - grpc_chttp2_list_remove_unannounced_incoming_window_available(&t->global, - &s->global); - grpc_chttp2_list_remove_stalled_by_transport(&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(); - } - } - - while ( - (bs = grpc_chttp2_incoming_frame_queue_pop(&s->global.incoming_frames))) { - grpc_byte_stream_destroy(exec_ctx, bs); - } - - GPR_ASSERT(s->global.send_initial_metadata_finished == NULL); - GPR_ASSERT(s->global.send_message_finished == NULL); - GPR_ASSERT(s->global.send_trailing_metadata_finished == NULL); - GPR_ASSERT(s->global.recv_initial_metadata_finished == NULL); - GPR_ASSERT(s->global.recv_message_ready == NULL); - GPR_ASSERT(s->global.recv_trailing_metadata_finished == NULL); - grpc_chttp2_data_parser_destroy(exec_ctx, &s->parsing.data_parser); - grpc_chttp2_incoming_metadata_buffer_destroy(&s->parsing.metadata_buffer[0]); - grpc_chttp2_incoming_metadata_buffer_destroy(&s->parsing.metadata_buffer[1]); - grpc_chttp2_incoming_metadata_buffer_destroy( - &s->global.received_initial_metadata); - grpc_chttp2_incoming_metadata_buffer_destroy( - &s->global.received_trailing_metadata); - gpr_slice_buffer_destroy(&s->writing.flow_controlled_buffer); - - UNREF_TRANSPORT(exec_ctx, t, "stream"); - - GPR_TIMER_END("destroy_stream", 0); -} - -grpc_chttp2_stream_parsing *grpc_chttp2_parsing_lookup_stream( - grpc_chttp2_transport_parsing *transport_parsing, uint32_t 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_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - uint32_t 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(exec_ctx, - t->channel_callback.accept_stream_user_data, - &t->base, (void *)(uintptr_t)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_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { - GPR_TIMER_BEGIN("unlock", 0); - if (!t->writing_active && !t->closed && - grpc_chttp2_unlocking_check_writes(&t->global, &t->writing, - t->parsing_active)) { - t->writing_active = 1; - REF_TRANSPORT(t, "writing"); - grpc_exec_ctx_enqueue(exec_ctx, &t->writing_action, true, NULL); - prevent_endpoint_shutdown(t); - } - check_read_ops(exec_ctx, &t->global); - - gpr_mu_unlock(&t->mu); - GPR_TIMER_END("unlock", 0); -} - -/* - * OUTPUT PROCESSING - */ - -static void push_setting(grpc_chttp2_transport *t, grpc_chttp2_setting_id id, - uint32_t value) { - const grpc_chttp2_setting_parameters *sp = - &grpc_chttp2_settings_parameters[id]; - uint32_t 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(grpc_exec_ctx *exec_ctx, - void *transport_writing_ptr, bool success) { - grpc_chttp2_transport_writing *transport_writing = transport_writing_ptr; - grpc_chttp2_transport *t = TRANSPORT_FROM_WRITING(transport_writing); - grpc_chttp2_stream_global *stream_global; - - GPR_TIMER_BEGIN("grpc_chttp2_terminate_writing", 0); - - lock(t); - - allow_endpoint_shutdown_locked(exec_ctx, t); - - if (!success) { - drop_connection(exec_ctx, t); - } - - grpc_chttp2_cleanup_writing(exec_ctx, &t->global, &t->writing); - - while (grpc_chttp2_list_pop_closed_waiting_for_writing(&t->global, - &stream_global)) { - fail_pending_writes(exec_ctx, stream_global); - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "finish_writes"); - } - - /* 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(exec_ctx, t); - } - - unlock(exec_ctx, t); - - UNREF_TRANSPORT(exec_ctx, t, "writing"); - - GPR_TIMER_END("grpc_chttp2_terminate_writing", 0); -} - -static void writing_action(grpc_exec_ctx *exec_ctx, void *gt, - bool iomgr_success_ignored) { - grpc_chttp2_transport *t = gt; - GPR_TIMER_BEGIN("writing_action", 0); - grpc_chttp2_perform_writes(exec_ctx, &t->writing, t->ep); - GPR_TIMER_END("writing_action", 0); -} - -void grpc_chttp2_add_incoming_goaway( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, - uint32_t 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(exec_ctx, transport_global, GRPC_CHANNEL_FATAL_FAILURE, - "got_goaway"); -} - -static void maybe_start_some_streams( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global) { - grpc_chttp2_stream_global *stream_global; - uint32_t stream_incoming_window; - /* 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)) { - /* safe since we can't (legally) be parsing this stream yet */ - grpc_chttp2_stream_parsing *stream_parsing = - &STREAM_FROM_GLOBAL(stream_global)->parsing; - 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 = stream_parsing->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(exec_ctx, 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_parsing->incoming_window = stream_incoming_window = - transport_global->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - stream_global->max_recv_bytes = - GPR_MAX(stream_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_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(exec_ctx, transport_global, stream_global, - GRPC_STATUS_UNAVAILABLE); - } -} - -static grpc_closure *add_closure_barrier(grpc_closure *closure) { - closure->final_data += 2; - return closure; -} - -void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx, - grpc_closure **pclosure, int success) { - grpc_closure *closure = *pclosure; - if (closure == NULL) { - return; - } - closure->final_data -= 2; - if (!success) { - closure->final_data |= 1; - } - if (closure->final_data < 2) { - grpc_exec_ctx_enqueue(exec_ctx, closure, closure->final_data == 0, NULL); - } - *pclosure = NULL; -} - -static int contains_non_ok_status( - grpc_chttp2_transport_global *transport_global, - grpc_metadata_batch *batch) { - grpc_linked_mdelem *l; - for (l = batch->list.head; l; l = l->next) { - if (l->md->key == GRPC_MDSTR_GRPC_STATUS && - l->md != GRPC_MDELEM_GRPC_STATUS_0) { - return 1; - } - } - return 0; -} - -static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, bool success) {} - -static void perform_stream_op_locked( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, grpc_transport_stream_op *op) { - grpc_closure *on_complete; - - GPR_TIMER_BEGIN("perform_stream_op_locked", 0); - - on_complete = op->on_complete; - if (on_complete == NULL) { - on_complete = grpc_closure_create(do_nothing, NULL); - } - /* use final_data as a barrier until enqueue time; the inital counter is - dropped at the end of this function */ - on_complete->final_data = 2; - - if (op->cancel_with_status != GRPC_STATUS_OK) { - cancel_from_api(exec_ctx, transport_global, stream_global, - op->cancel_with_status); - } - - if (op->close_with_status != GRPC_STATUS_OK) { - close_from_api(exec_ctx, transport_global, stream_global, - op->close_with_status, op->optional_close_message); - } - - if (op->send_initial_metadata != NULL) { - GPR_ASSERT(stream_global->send_initial_metadata_finished == NULL); - stream_global->send_initial_metadata_finished = - add_closure_barrier(on_complete); - stream_global->send_initial_metadata = op->send_initial_metadata; - if (contains_non_ok_status(transport_global, op->send_initial_metadata)) { - stream_global->seen_error = 1; - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - if (!stream_global->write_closed) { - if (transport_global->is_client) { - GPR_ASSERT(stream_global->id == 0); - grpc_chttp2_list_add_waiting_for_concurrency(transport_global, - stream_global); - maybe_start_some_streams(exec_ctx, transport_global); - } else { - GPR_ASSERT(stream_global->id != 0); - grpc_chttp2_list_add_writable_stream(transport_global, stream_global); - } - } else { - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_initial_metadata_finished, 0); - } - } - - if (op->send_message != NULL) { - GPR_ASSERT(stream_global->send_message_finished == NULL); - GPR_ASSERT(stream_global->send_message == NULL); - stream_global->send_message_finished = add_closure_barrier(on_complete); - if (stream_global->write_closed) { - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_message_finished, 0); - } else if (stream_global->id != 0) { - stream_global->send_message = op->send_message; - grpc_chttp2_list_add_writable_stream(transport_global, stream_global); - } - } - - if (op->send_trailing_metadata != NULL) { - GPR_ASSERT(stream_global->send_trailing_metadata_finished == NULL); - stream_global->send_trailing_metadata_finished = - add_closure_barrier(on_complete); - stream_global->send_trailing_metadata = op->send_trailing_metadata; - if (contains_non_ok_status(transport_global, op->send_trailing_metadata)) { - stream_global->seen_error = 1; - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - if (stream_global->write_closed) { - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_trailing_metadata_finished, - grpc_metadata_batch_is_empty(op->send_trailing_metadata)); - } else if (stream_global->id != 0) { - /* TODO(ctiller): check if there's flow control for any outstanding - bytes before going writable */ - grpc_chttp2_list_add_writable_stream(transport_global, stream_global); - } - } - - if (op->recv_initial_metadata != NULL) { - GPR_ASSERT(stream_global->recv_initial_metadata_finished == NULL); - stream_global->recv_initial_metadata_finished = - add_closure_barrier(on_complete); - stream_global->recv_initial_metadata = op->recv_initial_metadata; - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - - if (op->recv_message != NULL) { - GPR_ASSERT(stream_global->recv_message_ready == NULL); - stream_global->recv_message_ready = op->recv_message_ready; - stream_global->recv_message = op->recv_message; - if (stream_global->id != 0 && - (stream_global->incoming_frames.head == NULL || - stream_global->incoming_frames.head->is_tail)) { - incoming_byte_stream_update_flow_control( - transport_global, stream_global, transport_global->stream_lookahead, - 0); - } - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - - if (op->recv_trailing_metadata != NULL) { - GPR_ASSERT(stream_global->recv_trailing_metadata_finished == NULL); - stream_global->recv_trailing_metadata_finished = - add_closure_barrier(on_complete); - stream_global->recv_trailing_metadata = op->recv_trailing_metadata; - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - - grpc_chttp2_complete_closure_step(exec_ctx, &on_complete, 1); - - GPR_TIMER_END("perform_stream_op_locked", 0); -} - -static void perform_stream_op(grpc_exec_ctx *exec_ctx, 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(exec_ctx, &t->global, &s->global, op); - unlock(exec_ctx, t); -} - -static void send_ping_locked(grpc_chttp2_transport *t, grpc_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] = (uint8_t)((t->global.ping_counter >> 56) & 0xff); - p->id[1] = (uint8_t)((t->global.ping_counter >> 48) & 0xff); - p->id[2] = (uint8_t)((t->global.ping_counter >> 40) & 0xff); - p->id[3] = (uint8_t)((t->global.ping_counter >> 32) & 0xff); - p->id[4] = (uint8_t)((t->global.ping_counter >> 24) & 0xff); - p->id[5] = (uint8_t)((t->global.ping_counter >> 16) & 0xff); - p->id[6] = (uint8_t)((t->global.ping_counter >> 8) & 0xff); - p->id[7] = (uint8_t)(t->global.ping_counter & 0xff); - p->on_recv = on_recv; - gpr_slice_buffer_add(&t->global.qbuf, grpc_chttp2_ping_create(0, p->id)); -} - -void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_parsing *transport_parsing, - const uint8_t *opaque_8bytes) { - grpc_chttp2_outstanding_ping *ping; - grpc_chttp2_transport *t = TRANSPORT_FROM_PARSING(transport_parsing); - grpc_chttp2_transport_global *transport_global = &t->global; - lock(t); - for (ping = transport_global->pings.next; ping != &transport_global->pings; - ping = ping->next) { - if (0 == memcmp(opaque_8bytes, ping->id, 8)) { - grpc_exec_ctx_enqueue(exec_ctx, ping->on_recv, true, NULL); - ping->next->prev = ping->prev; - ping->prev->next = ping->next; - gpr_free(ping); - break; - } - } - unlock(exec_ctx, t); -} - -static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_transport_op *op) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - int close_transport = 0; - - lock(t); - - grpc_exec_ctx_enqueue(exec_ctx, op->on_consumed, true, NULL); - - if (op->on_connectivity_state_change != NULL) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &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, - (uint32_t)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(exec_ctx, t, op->bind_pollset); - } - - if (op->bind_pollset_set) { - add_to_pollset_set_locked(exec_ctx, t, op->bind_pollset_set); - } - - if (op->send_ping) { - send_ping_locked(t, op->send_ping); - } - - if (op->disconnect) { - close_transport_locked(exec_ctx, t); - } - - unlock(exec_ctx, t); - - if (close_transport) { - lock(t); - close_transport_locked(exec_ctx, t); - unlock(exec_ctx, t); - } -} - -/* - * INPUT PROCESSING - */ - -static void check_read_ops(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *transport_global) { - grpc_chttp2_stream_global *stream_global; - grpc_byte_stream *bs; - while ( - grpc_chttp2_list_pop_check_read_ops(transport_global, &stream_global)) { - if (stream_global->recv_initial_metadata_finished != NULL && - stream_global->published_initial_metadata) { - grpc_chttp2_incoming_metadata_buffer_publish( - &stream_global->received_initial_metadata, - stream_global->recv_initial_metadata); - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->recv_initial_metadata_finished, 1); - } - if (stream_global->recv_message_ready != NULL) { - if (stream_global->incoming_frames.head != NULL) { - *stream_global->recv_message = grpc_chttp2_incoming_frame_queue_pop( - &stream_global->incoming_frames); - GPR_ASSERT(*stream_global->recv_message != NULL); - grpc_exec_ctx_enqueue(exec_ctx, stream_global->recv_message_ready, true, - NULL); - stream_global->recv_message_ready = NULL; - } else if (stream_global->published_trailing_metadata) { - *stream_global->recv_message = NULL; - grpc_exec_ctx_enqueue(exec_ctx, stream_global->recv_message_ready, true, - NULL); - stream_global->recv_message_ready = NULL; - } - } - if (stream_global->recv_trailing_metadata_finished != NULL && - stream_global->read_closed && stream_global->write_closed) { - while (stream_global->seen_error && - (bs = grpc_chttp2_incoming_frame_queue_pop( - &stream_global->incoming_frames)) != NULL) { - grpc_byte_stream_destroy(exec_ctx, bs); - } - if (stream_global->incoming_frames.head == NULL) { - grpc_chttp2_incoming_metadata_buffer_publish( - &stream_global->received_trailing_metadata, - stream_global->recv_trailing_metadata); - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->recv_trailing_metadata_finished, 1); - } - } - } -} - -static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - uint32_t 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(exec_ctx, &t->parsing); - } - if (s->parsing.data_parser.parsing_frame != NULL) { - grpc_chttp2_incoming_byte_stream_finished( - exec_ctx, s->parsing.data_parser.parsing_frame, 0, 0); - s->parsing.data_parser.parsing_frame = NULL; - } - - if (grpc_chttp2_unregister_stream(t, s) && t->global.sent_goaway) { - close_transport_locked(exec_ctx, t); - } - - new_stream_count = grpc_chttp2_stream_map_size(&t->parsing_stream_map) + - grpc_chttp2_stream_map_size(&t->new_stream_map); - GPR_ASSERT(new_stream_count <= UINT32_MAX); - if (new_stream_count != t->global.concurrent_stream_count) { - t->global.concurrent_stream_count = (uint32_t)new_stream_count; - maybe_start_some_streams(exec_ctx, &t->global); - } -} - -static void cancel_from_api(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, - grpc_status_code status) { - if (stream_global->id != 0) { - gpr_slice_buffer_add( - &transport_global->qbuf, - grpc_chttp2_rst_stream_create( - stream_global->id, - (uint32_t)grpc_chttp2_grpc_status_to_http2_error(status))); - } - grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, status, - NULL); - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1, - 1); -} - -void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, - grpc_status_code status, gpr_slice *slice) { - if (status != GRPC_STATUS_OK) { - stream_global->seen_error = 1; - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - /* stream_global->recv_trailing_metadata_finished gives us a - last chance replacement: we've received trailing metadata, - but something more important has become available to signal - to the upper layers - drop what we've got, and then publish - what we want - which is safe because we haven't told anyone - about the metadata yet */ - if (!stream_global->published_trailing_metadata || - stream_global->recv_trailing_metadata_finished != NULL) { - char status_string[GPR_LTOA_MIN_BUFSIZE]; - gpr_ltoa(status, status_string); - grpc_chttp2_incoming_metadata_buffer_add( - &stream_global->received_trailing_metadata, - grpc_mdelem_from_metadata_strings( - GRPC_MDSTR_GRPC_STATUS, grpc_mdstr_from_string(status_string))); - if (slice) { - grpc_chttp2_incoming_metadata_buffer_add( - &stream_global->received_trailing_metadata, - grpc_mdelem_from_metadata_strings( - GRPC_MDSTR_GRPC_MESSAGE, - grpc_mdstr_from_slice(gpr_slice_ref(*slice)))); - } - stream_global->published_trailing_metadata = 1; - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - } - if (slice) { - gpr_slice_unref(*slice); - } -} - -static void fail_pending_writes(grpc_exec_ctx *exec_ctx, - grpc_chttp2_stream_global *stream_global) { - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_initial_metadata_finished, 0); - grpc_chttp2_complete_closure_step( - exec_ctx, &stream_global->send_trailing_metadata_finished, 0); - grpc_chttp2_complete_closure_step(exec_ctx, - &stream_global->send_message_finished, 0); -} - -void grpc_chttp2_mark_stream_closed( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, int close_reads, - int close_writes) { - if (stream_global->read_closed && stream_global->write_closed) { - /* already closed */ - return; - } - grpc_chttp2_list_add_check_read_ops(transport_global, stream_global); - if (close_reads && !stream_global->read_closed) { - stream_global->read_closed = 1; - stream_global->published_initial_metadata = 1; - stream_global->published_trailing_metadata = 1; - } - if (close_writes && !stream_global->write_closed) { - stream_global->write_closed = 1; - if (TRANSPORT_FROM_GLOBAL(transport_global)->writing_active) { - GRPC_CHTTP2_STREAM_REF(stream_global, "finish_writes"); - grpc_chttp2_list_add_closed_waiting_for_writing(transport_global, - stream_global); - } else { - fail_pending_writes(exec_ctx, stream_global); - } - } - if (stream_global->read_closed && stream_global->write_closed) { - if (stream_global->id != 0 && - TRANSPORT_FROM_GLOBAL(transport_global)->parsing_active) { - grpc_chttp2_list_add_closed_waiting_for_parsing(transport_global, - stream_global); - } else { - if (stream_global->id != 0) { - remove_stream(exec_ctx, TRANSPORT_FROM_GLOBAL(transport_global), - stream_global->id); - } - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2"); - } - } -} - -static void close_from_api(grpc_exec_ctx *exec_ctx, - 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; - uint8_t *p; - uint32_t len = 0; - - GPR_ASSERT(status >= 0 && (int)status < 100); - - GPR_ASSERT(stream_global->id != 0); - - /* 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++ = (uint8_t)('0' + status); - } else { - *p++ = 2; - *p++ = (uint8_t)('0' + (status / 10)); - *p++ = (uint8_t)('0' + (status % 10)); - } - GPR_ASSERT(p == GPR_SLICE_END_PTR(status_hdr)); - len += (uint32_t)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++ = (uint8_t)GPR_SLICE_LENGTH(*optional_message); - GPR_ASSERT(p == GPR_SLICE_END_PTR(message_pfx)); - len += (uint32_t)GPR_SLICE_LENGTH(message_pfx); - len += (uint32_t)GPR_SLICE_LENGTH(*optional_message); - } - - hdr = gpr_slice_malloc(9); - p = GPR_SLICE_START_PTR(hdr); - *p++ = (uint8_t)(len >> 16); - *p++ = (uint8_t)(len >> 8); - *p++ = (uint8_t)(len); - *p++ = GRPC_CHTTP2_FRAME_HEADER; - *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; - *p++ = (uint8_t)(stream_global->id >> 24); - *p++ = (uint8_t)(stream_global->id >> 16); - *p++ = (uint8_t)(stream_global->id >> 8); - *p++ = (uint8_t)(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)); - - if (optional_message) { - gpr_slice_ref(*optional_message); - } - grpc_chttp2_fake_status(exec_ctx, transport_global, stream_global, status, - optional_message); - grpc_chttp2_mark_stream_closed(exec_ctx, transport_global, stream_global, 1, - 1); -} - -static void cancel_stream_cb(grpc_chttp2_transport_global *transport_global, - void *user_data, - grpc_chttp2_stream_global *stream_global) { - cancel_from_api(user_data, transport_global, stream_global, - GRPC_STATUS_UNAVAILABLE); -} - -static void end_all_the_calls(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_chttp2_for_all_streams(&t->global, exec_ctx, cancel_stream_cb); -} - -static void drop_connection(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { - close_transport_locked(exec_ctx, t); - end_all_the_calls(exec_ctx, t); -} - -/** update window from a settings change */ -static void update_global_window(void *args, uint32_t 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; - int64_t initial_window_update = t->parsing.initial_window_update; - - was_zero = stream_global->outgoing_window <= 0; - GRPC_CHTTP2_FLOW_CREDIT_STREAM("settings", transport_global, stream_global, - outgoing_window, 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_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - t->endpoint_reading = 0; - if (!t->writing_active && t->ep) { - destroy_endpoint(exec_ctx, t); - } -} - -/* tcp read callback */ -static void recv_data(grpc_exec_ctx *exec_ctx, void *tp, bool success) { - size_t i; - int keep_reading = 0; - grpc_chttp2_transport *t = tp; - grpc_chttp2_transport_global *transport_global = &t->global; - grpc_chttp2_transport_parsing *transport_parsing = &t->parsing; - grpc_chttp2_stream_global *stream_global; - - GPR_TIMER_BEGIN("recv_data", 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(transport_global, transport_parsing); - gpr_mu_unlock(&t->mu); - GPR_TIMER_BEGIN("recv_data.parse", 0); - for (; i < t->read_buffer.count && - grpc_chttp2_perform_read(exec_ctx, transport_parsing, - t->read_buffer.slices[i]); - i++) - ; - GPR_TIMER_END("recv_data.parse", 0); - gpr_mu_lock(&t->mu); - /* copy parsing qbuf to global qbuf */ - gpr_slice_buffer_move_into(&t->parsing.qbuf, &t->global.qbuf); - if (i != t->read_buffer.count) { - unlock(exec_ctx, t); - lock(t); - drop_connection(exec_ctx, t); - } - /* merge stream lists */ - grpc_chttp2_stream_map_move_into(&t->new_stream_map, - &t->parsing_stream_map); - transport_global->concurrent_stream_count = - (uint32_t)grpc_chttp2_stream_map_size(&t->parsing_stream_map); - if (transport_parsing->initial_window_update != 0) { - grpc_chttp2_stream_map_for_each(&t->parsing_stream_map, - update_global_window, t); - transport_parsing->initial_window_update = 0; - } - /* handle higher level things */ - grpc_chttp2_publish_reads(exec_ctx, transport_global, transport_parsing); - t->parsing_active = 0; - /* 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_closed); - GPR_ASSERT(stream_global->read_closed); - remove_stream(exec_ctx, t, stream_global->id); - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2"); - } - } - if (!success || i != t->read_buffer.count || t->closed) { - drop_connection(exec_ctx, t); - read_error_locked(exec_ctx, 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(exec_ctx, t); - - if (keep_reading) { - grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, &t->recv_data); - allow_endpoint_shutdown_unlocked(exec_ctx, t); - UNREF_TRANSPORT(exec_ctx, t, "keep_reading"); - } else { - UNREF_TRANSPORT(exec_ctx, t, "recv_data"); - } - - GPR_TIMER_END("recv_data", 0); -} - -/* - * CALLBACK LOOP - */ - -static void connectivity_state_set( - grpc_exec_ctx *exec_ctx, 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(exec_ctx, &TRANSPORT_FROM_GLOBAL(transport_global) - ->channel_callback.state_tracker, - state, reason); -} - -/* - * POLLSET STUFF - */ - -static void add_to_pollset_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_pollset *pollset) { - if (t->ep) { - grpc_endpoint_add_to_pollset(exec_ctx, t->ep, pollset); - } -} - -static void add_to_pollset_set_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_pollset_set *pollset_set) { - if (t->ep) { - grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, pollset_set); - } -} - -static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_pollset *pollset) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - lock(t); - add_to_pollset_locked(exec_ctx, t, pollset); - unlock(exec_ctx, t); -} - -/* - * BYTE STREAM - */ - -static void incoming_byte_stream_update_flow_control( - grpc_chttp2_transport_global *transport_global, - grpc_chttp2_stream_global *stream_global, size_t max_size_hint, - size_t have_already) { - uint32_t max_recv_bytes; - - /* clamp max recv hint to an allowable size */ - if (max_size_hint >= UINT32_MAX - transport_global->stream_lookahead) { - max_recv_bytes = UINT32_MAX - transport_global->stream_lookahead; - } else { - max_recv_bytes = (uint32_t)max_size_hint; - } - - /* account for bytes already received but unknown to higher layers */ - if (max_recv_bytes >= have_already) { - max_recv_bytes -= (uint32_t)have_already; - } else { - max_recv_bytes = 0; - } - - /* add some small lookahead to keep pipelines flowing */ - GPR_ASSERT(max_recv_bytes <= UINT32_MAX - transport_global->stream_lookahead); - max_recv_bytes += transport_global->stream_lookahead; - if (stream_global->max_recv_bytes < max_recv_bytes) { - uint32_t add_max_recv_bytes = - max_recv_bytes - stream_global->max_recv_bytes; - GRPC_CHTTP2_FLOW_CREDIT_STREAM("op", transport_global, stream_global, - max_recv_bytes, add_max_recv_bytes); - GRPC_CHTTP2_FLOW_CREDIT_STREAM("op", transport_global, stream_global, - unannounced_incoming_window_for_parse, - add_max_recv_bytes); - GRPC_CHTTP2_FLOW_CREDIT_STREAM("op", transport_global, stream_global, - unannounced_incoming_window_for_writing, - add_max_recv_bytes); - grpc_chttp2_list_add_unannounced_incoming_window_available(transport_global, - stream_global); - grpc_chttp2_list_add_writable_stream(transport_global, stream_global); - } -} - -static int incoming_byte_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - gpr_slice *slice, size_t max_size_hint, - grpc_closure *on_complete) { - grpc_chttp2_incoming_byte_stream *bs = - (grpc_chttp2_incoming_byte_stream *)byte_stream; - grpc_chttp2_transport_global *transport_global = &bs->transport->global; - grpc_chttp2_stream_global *stream_global = &bs->stream->global; - - lock(bs->transport); - if (bs->is_tail) { - incoming_byte_stream_update_flow_control(transport_global, stream_global, - max_size_hint, bs->slices.length); - } - if (bs->slices.count > 0) { - *slice = gpr_slice_buffer_take_first(&bs->slices); - unlock(exec_ctx, bs->transport); - return 1; - } else if (bs->failed) { - grpc_exec_ctx_enqueue(exec_ctx, on_complete, false, NULL); - unlock(exec_ctx, bs->transport); - return 0; - } else { - bs->on_next = on_complete; - bs->next = slice; - unlock(exec_ctx, bs->transport); - return 0; - } -} - -static void incoming_byte_stream_unref(grpc_chttp2_incoming_byte_stream *bs) { - if (gpr_unref(&bs->refs)) { - gpr_slice_buffer_destroy(&bs->slices); - gpr_free(bs); - } -} - -static void incoming_byte_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream) { - incoming_byte_stream_unref((grpc_chttp2_incoming_byte_stream *)byte_stream); -} - -void grpc_chttp2_incoming_byte_stream_push(grpc_exec_ctx *exec_ctx, - grpc_chttp2_incoming_byte_stream *bs, - gpr_slice slice) { - gpr_mu_lock(&bs->transport->mu); - if (bs->on_next != NULL) { - *bs->next = slice; - grpc_exec_ctx_enqueue(exec_ctx, bs->on_next, true, NULL); - bs->on_next = NULL; - } else { - gpr_slice_buffer_add(&bs->slices, slice); - } - gpr_mu_unlock(&bs->transport->mu); -} - -void grpc_chttp2_incoming_byte_stream_finished( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, int success, - int from_parsing_thread) { - if (!success) { - if (from_parsing_thread) { - gpr_mu_lock(&bs->transport->mu); - } - grpc_exec_ctx_enqueue(exec_ctx, bs->on_next, false, NULL); - bs->on_next = NULL; - bs->failed = 1; - if (from_parsing_thread) { - gpr_mu_unlock(&bs->transport->mu); - } - } else { -#ifndef NDEBUG - if (from_parsing_thread) { - gpr_mu_lock(&bs->transport->mu); - } - GPR_ASSERT(bs->on_next == NULL); - if (from_parsing_thread) { - gpr_mu_unlock(&bs->transport->mu); - } -#endif - } - incoming_byte_stream_unref(bs); -} - -grpc_chttp2_incoming_byte_stream *grpc_chttp2_incoming_byte_stream_create( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport_parsing *transport_parsing, - grpc_chttp2_stream_parsing *stream_parsing, uint32_t frame_size, - uint32_t flags, grpc_chttp2_incoming_frame_queue *add_to_queue) { - grpc_chttp2_incoming_byte_stream *incoming_byte_stream = - gpr_malloc(sizeof(*incoming_byte_stream)); - incoming_byte_stream->base.length = frame_size; - incoming_byte_stream->base.flags = flags; - incoming_byte_stream->base.next = incoming_byte_stream_next; - incoming_byte_stream->base.destroy = incoming_byte_stream_destroy; - gpr_ref_init(&incoming_byte_stream->refs, 2); - incoming_byte_stream->next_message = NULL; - incoming_byte_stream->transport = TRANSPORT_FROM_PARSING(transport_parsing); - incoming_byte_stream->stream = STREAM_FROM_PARSING(stream_parsing); - gpr_slice_buffer_init(&incoming_byte_stream->slices); - incoming_byte_stream->on_next = NULL; - incoming_byte_stream->is_tail = 1; - incoming_byte_stream->failed = 0; - if (add_to_queue->head == NULL) { - add_to_queue->head = incoming_byte_stream; - } else { - add_to_queue->tail->is_tail = 0; - add_to_queue->tail->next_message = incoming_byte_stream; - } - add_to_queue->tail = incoming_byte_stream; - return incoming_byte_stream; -} - -/* - * TRACING - */ - -static char *format_flowctl_context_var(const char *context, const char *var, - int64_t val, uint32_t id, - char **scope) { - char *underscore_pos; - char *result; - if (context == NULL) { - *scope = NULL; - gpr_asprintf(&result, "%s(%lld)", var, val); - return result; - } - underscore_pos = strchr(context, '_'); - *scope = gpr_strdup(context); - (*scope)[underscore_pos - context] = 0; - if (id != 0) { - char *tmp = *scope; - gpr_asprintf(scope, "%s[%d]", tmp, id); - gpr_free(tmp); - } - gpr_asprintf(&result, "%s.%s(%lld)", underscore_pos + 1, var, val); - return result; -} - -static int samestr(char *a, char *b) { - if (a == NULL) { - return b == NULL; - } - if (b == NULL) { - return 0; - } - return 0 == strcmp(a, b); -} - -void grpc_chttp2_flowctl_trace(const char *file, int line, const char *phase, - grpc_chttp2_flowctl_op op, const char *context1, - const char *var1, const char *context2, - const char *var2, int is_client, - uint32_t stream_id, int64_t val1, int64_t val2) { - char *scope1; - char *scope2; - char *label1 = - format_flowctl_context_var(context1, var1, val1, stream_id, &scope1); - char *label2 = - format_flowctl_context_var(context2, var2, val2, stream_id, &scope2); - char *clisvr = is_client ? "client" : "server"; - char *prefix; - - gpr_asprintf(&prefix, "FLOW % 8s: %s % 11s ", phase, clisvr, scope1); - - switch (op) { - case GRPC_CHTTP2_FLOWCTL_MOVE: - GPR_ASSERT(samestr(scope1, scope2)); - if (val2 != 0) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "%sMOVE % 40s <- % 40s giving %d", prefix, label1, label2, - val1 + val2); - } - break; - case GRPC_CHTTP2_FLOWCTL_CREDIT: - GPR_ASSERT(val2 >= 0); - if (val2 != 0) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "%sCREDIT % 40s by % 40s giving %d", prefix, label1, label2, - val1 + val2); - } - break; - case GRPC_CHTTP2_FLOWCTL_DEBIT: - GPR_ASSERT(val2 >= 0); - if (val2 != 0) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "%sDEBIT % 40s by % 40s giving %d", prefix, label1, label2, - val1 - val2); - } - break; - } - - gpr_free(scope1); - gpr_free(scope2); - gpr_free(label1); - gpr_free(label2); - gpr_free(prefix); -} - -/* - * INTEGRATION GLUE - */ - -static char *chttp2_get_peer(grpc_exec_ctx *exec_ctx, grpc_transport *t) { - return gpr_strdup(((grpc_chttp2_transport *)t)->peer_string); -} - -static const grpc_transport_vtable vtable = { - sizeof(grpc_chttp2_stream), init_stream, set_pollset, perform_stream_op, - perform_transport_op, destroy_stream, destroy_transport, chttp2_get_peer}; - -grpc_transport *grpc_create_chttp2_transport( - grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args, - grpc_endpoint *ep, int is_client) { - grpc_chttp2_transport *t = gpr_malloc(sizeof(grpc_chttp2_transport)); - init_transport(exec_ctx, t, channel_args, ep, is_client != 0); - return &t->base; -} - -void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx, - 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(exec_ctx, t, 1); -} diff --git a/third_party/grpc/src/core/transport/chttp2_transport.h b/third_party/grpc/src/core/transport/chttp2_transport.h deleted file mode 100644 index 95520501edde98..00000000000000 --- a/third_party/grpc/src/core/transport/chttp2_transport.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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( - grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args, - grpc_endpoint *ep, int is_client); - -void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - gpr_slice *slices, size_t nslices); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CHTTP2_TRANSPORT_H */ diff --git a/third_party/grpc/src/core/transport/connectivity_state.c b/third_party/grpc/src/core/transport/connectivity_state.c deleted file mode 100644 index 87765b9799d6c6..00000000000000 --- a/third_party/grpc/src/core/transport/connectivity_state.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/transport/connectivity_state.h" - -#include - -#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"; - } - GPR_UNREACHABLE_CODE(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_exec_ctx *exec_ctx, - grpc_connectivity_state_tracker *tracker) { - int success; - 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; - success = 1; - } else { - success = 0; - } - grpc_exec_ctx_enqueue(exec_ctx, w->notify, success, NULL); - gpr_free(w); - } - gpr_free(tracker->name); -} - -grpc_connectivity_state grpc_connectivity_state_check( - grpc_connectivity_state_tracker *tracker) { - if (grpc_connectivity_state_trace) { - gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name, - grpc_connectivity_state_name(tracker->current_state)); - } - return tracker->current_state; -} - -int grpc_connectivity_state_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state *current, grpc_closure *notify) { - if (grpc_connectivity_state_trace) { - if (current == NULL) { - gpr_log(GPR_DEBUG, "CONWATCH: %p %s: unsubscribe notify=%p", tracker, - tracker->name, notify); - } else { - gpr_log(GPR_DEBUG, "CONWATCH: %p %s: from %s [cur=%s] notify=%p", tracker, - tracker->name, grpc_connectivity_state_name(*current), - grpc_connectivity_state_name(tracker->current_state), notify); - } - } - if (current == NULL) { - grpc_connectivity_state_watcher *w = tracker->watchers; - if (w != NULL && w->notify == notify) { - grpc_exec_ctx_enqueue(exec_ctx, notify, false, NULL); - tracker->watchers = w->next; - gpr_free(w); - return 0; - } - while (w != NULL) { - grpc_connectivity_state_watcher *rm_candidate = w->next; - if (rm_candidate != NULL && rm_candidate->notify == notify) { - grpc_exec_ctx_enqueue(exec_ctx, notify, false, NULL); - w->next = w->next->next; - gpr_free(rm_candidate); - return 0; - } - w = w->next; - } - return 0; - } else { - if (tracker->current_state != *current) { - *current = tracker->current_state; - grpc_exec_ctx_enqueue(exec_ctx, notify, true, NULL); - } 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(grpc_exec_ctx *exec_ctx, - grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state state, - const char *reason) { - grpc_connectivity_state_watcher *w; - if (grpc_connectivity_state_trace) { - gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s]", tracker, 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) != NULL) { - *w->current = tracker->current_state; - tracker->watchers = w->next; - grpc_exec_ctx_enqueue(exec_ctx, w->notify, true, NULL); - gpr_free(w); - } -} diff --git a/third_party/grpc/src/core/transport/connectivity_state.h b/third_party/grpc/src/core/transport/connectivity_state.h deleted file mode 100644 index a4eb6652e551b8..00000000000000 --- a/third_party/grpc/src/core/transport/connectivity_state.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/exec_ctx.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_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; - -const char *grpc_connectivity_state_name(grpc_connectivity_state state); - -void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state init_state, - const char *name); -void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx, - grpc_connectivity_state_tracker *tracker); - -/** Set connectivity state; not thread safe; access must be serialized with an - * external lock */ -void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, - grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state state, - 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. - If current==NULL cancel notify if it is already queued (success==0 in that - case) */ -int grpc_connectivity_state_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state *current, grpc_closure *notify); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_CONNECTIVITY_STATE_H */ diff --git a/third_party/grpc/src/core/transport/metadata.c b/third_party/grpc/src/core/transport/metadata.c deleted file mode 100644 index 14912af7df1caf..00000000000000 --- a/third_party/grpc/src/core/transport/metadata.c +++ /dev/null @@ -1,690 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/transport/metadata.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "src/core/profiling/timers.h" -#include "src/core/support/murmur_hash.h" -#include "src/core/support/string.h" -#include "src/core/transport/chttp2/bin_encoder.h" -#include "src/core/transport/static_metadata.h" - -/* There are two kinds of mdelem and mdstr instances. - * Static instances are declared in static_metadata.{h,c} and - * are initialized by grpc_mdctx_global_init(). - * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed - * by internal_string and internal_element structures. - * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are - * used to determine which kind of element a pointer refers to. - */ - -#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 REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__) -#else -#define DEBUG_ARGS -#define FWD_DEBUG_ARGS -#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s)) -#endif - -#define TABLE_IDX(hash, log2_shards, capacity) \ - (((hash) >> (log2_shards)) % (capacity)) -#define SHARD_IDX(hash, log2_shards) ((hash) & ((1 << (log2_shards)) - 1)) - -typedef void (*destroy_user_data_func)(void *user_data); - -/* Shadow structure for grpc_mdstr for non-static values */ -typedef struct internal_string { - /* must be byte compatible with grpc_mdstr */ - gpr_slice slice; - uint32_t hash; - - /* private only data */ - gpr_atm refcnt; - - uint8_t has_base64_and_huffman_encoded; - gpr_slice_refcount refcount; - - gpr_slice base64_and_huffman; - - struct internal_string *bucket_next; -} internal_string; - -/* Shadow structure for grpc_mdelem for non-static elements */ -typedef struct internal_metadata { - /* must be byte compatible with grpc_mdelem */ - internal_string *key; - internal_string *value; - - /* private only data */ - gpr_atm refcnt; - - gpr_mu mu_user_data; - gpr_atm destroy_user_data; - gpr_atm user_data; - - struct internal_metadata *bucket_next; -} internal_metadata; - -typedef struct strtab_shard { - gpr_mu mu; - internal_string **strs; - size_t count; - size_t capacity; -} strtab_shard; - -typedef struct mdtab_shard { - gpr_mu mu; - internal_metadata **elems; - size_t count; - size_t capacity; - size_t free; -} mdtab_shard; - -#define LOG2_STRTAB_SHARD_COUNT 5 -#define LOG2_MDTAB_SHARD_COUNT 4 -#define STRTAB_SHARD_COUNT ((size_t)(1 << LOG2_STRTAB_SHARD_COUNT)) -#define MDTAB_SHARD_COUNT ((size_t)(1 << LOG2_MDTAB_SHARD_COUNT)) - -/* hash seed: decided at initialization time */ -static uint32_t g_hash_seed; -static int g_forced_hash_seed = 0; - -/* linearly probed hash tables for static element lookup */ -static grpc_mdstr *g_static_strtab[GRPC_STATIC_MDSTR_COUNT * 2]; -static grpc_mdelem *g_static_mdtab[GRPC_STATIC_MDELEM_COUNT * 2]; -static size_t g_static_strtab_maxprobe; -static size_t g_static_mdtab_maxprobe; - -static strtab_shard g_strtab_shard[STRTAB_SHARD_COUNT]; -static mdtab_shard g_mdtab_shard[MDTAB_SHARD_COUNT]; - -static void gc_mdtab(mdtab_shard *shard); - -void grpc_test_only_set_metadata_hash_seed(uint32_t seed) { - g_hash_seed = seed; - g_forced_hash_seed = 1; -} - -void grpc_mdctx_global_init(void) { - size_t i, j; - if (!g_forced_hash_seed) { - g_hash_seed = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec; - } - g_static_strtab_maxprobe = 0; - g_static_mdtab_maxprobe = 0; - /* build static tables */ - memset(g_static_mdtab, 0, sizeof(g_static_mdtab)); - memset(g_static_strtab, 0, sizeof(g_static_strtab)); - for (i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) { - grpc_mdstr *elem = &grpc_static_mdstr_table[i]; - const char *str = grpc_static_metadata_strings[i]; - uint32_t hash = gpr_murmur_hash3(str, strlen(str), g_hash_seed); - *(gpr_slice *)&elem->slice = gpr_slice_from_static_string(str); - *(uint32_t *)&elem->hash = hash; - for (j = 0;; j++) { - size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_strtab); - if (g_static_strtab[idx] == NULL) { - g_static_strtab[idx] = &grpc_static_mdstr_table[i]; - break; - } - } - if (j > g_static_strtab_maxprobe) { - g_static_strtab_maxprobe = j; - } - } - for (i = 0; i < GRPC_STATIC_MDELEM_COUNT; i++) { - grpc_mdelem *elem = &grpc_static_mdelem_table[i]; - grpc_mdstr *key = - &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 0]]; - grpc_mdstr *value = - &grpc_static_mdstr_table[grpc_static_metadata_elem_indices[2 * i + 1]]; - uint32_t hash = GRPC_MDSTR_KV_HASH(key->hash, value->hash); - *(grpc_mdstr **)&elem->key = key; - *(grpc_mdstr **)&elem->value = value; - for (j = 0;; j++) { - size_t idx = (hash + j) % GPR_ARRAY_SIZE(g_static_mdtab); - if (g_static_mdtab[idx] == NULL) { - g_static_mdtab[idx] = elem; - break; - } - } - if (j > g_static_mdtab_maxprobe) { - g_static_mdtab_maxprobe = j; - } - } - /* initialize shards */ - for (i = 0; i < STRTAB_SHARD_COUNT; i++) { - strtab_shard *shard = &g_strtab_shard[i]; - gpr_mu_init(&shard->mu); - shard->count = 0; - shard->capacity = INITIAL_STRTAB_CAPACITY; - shard->strs = gpr_malloc(sizeof(*shard->strs) * shard->capacity); - memset(shard->strs, 0, sizeof(*shard->strs) * shard->capacity); - } - for (i = 0; i < MDTAB_SHARD_COUNT; i++) { - mdtab_shard *shard = &g_mdtab_shard[i]; - gpr_mu_init(&shard->mu); - shard->count = 0; - shard->free = 0; - shard->capacity = INITIAL_MDTAB_CAPACITY; - shard->elems = gpr_malloc(sizeof(*shard->elems) * shard->capacity); - memset(shard->elems, 0, sizeof(*shard->elems) * shard->capacity); - } -} - -void grpc_mdctx_global_shutdown(void) { - size_t i; - for (i = 0; i < MDTAB_SHARD_COUNT; i++) { - mdtab_shard *shard = &g_mdtab_shard[i]; - gpr_mu_destroy(&shard->mu); - gc_mdtab(shard); - /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ - if (shard->count != 0) { - gpr_log(GPR_DEBUG, "WARNING: %d metadata elements were leaked", - shard->count); - } - gpr_free(shard->elems); - } - for (i = 0; i < STRTAB_SHARD_COUNT; i++) { - strtab_shard *shard = &g_strtab_shard[i]; - gpr_mu_destroy(&shard->mu); - /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ - if (shard->count != 0) { - gpr_log(GPR_DEBUG, "WARNING: %d metadata strings were leaked", - shard->count); - } - gpr_free(shard->strs); - } -} - -static int is_mdstr_static(grpc_mdstr *s) { - return s >= &grpc_static_mdstr_table[0] && - s < &grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT]; -} - -static int is_mdelem_static(grpc_mdelem *e) { - return e >= &grpc_static_mdelem_table[0] && - e < &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; -} - -static void ref_md_locked(mdtab_shard *shard, - 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, 2)) { - shard->free--; - } else { - GPR_ASSERT(1 != gpr_atm_no_barrier_fetch_add(&md->refcnt, -1)); - } -} - -static void grow_strtab(strtab_shard *shard) { - size_t capacity = shard->capacity * 2; - size_t i; - internal_string **strtab; - internal_string *s, *next; - - GPR_TIMER_BEGIN("grow_strtab", 0); - - strtab = gpr_malloc(sizeof(internal_string *) * capacity); - memset(strtab, 0, sizeof(internal_string *) * capacity); - - for (i = 0; i < shard->capacity; i++) { - for (s = shard->strs[i]; s; s = next) { - size_t idx = TABLE_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT, capacity); - next = s->bucket_next; - s->bucket_next = strtab[idx]; - strtab[idx] = s; - } - } - - gpr_free(shard->strs); - shard->strs = strtab; - shard->capacity = capacity; - - GPR_TIMER_END("grow_strtab", 0); -} - -static void internal_destroy_string(strtab_shard *shard, internal_string *is) { - internal_string **prev_next; - internal_string *cur; - GPR_TIMER_BEGIN("internal_destroy_string", 0); - if (is->has_base64_and_huffman_encoded) { - gpr_slice_unref(is->base64_and_huffman); - } - for (prev_next = &shard->strs[TABLE_IDX(is->hash, LOG2_STRTAB_SHARD_COUNT, - shard->capacity)], - cur = *prev_next; - cur != is; prev_next = &cur->bucket_next, cur = cur->bucket_next) - ; - *prev_next = cur->bucket_next; - shard->count--; - gpr_free(is); - GPR_TIMER_END("internal_destroy_string", 0); -} - -static void slice_ref(void *p) { - internal_string *is = - (internal_string *)((char *)p - offsetof(internal_string, refcount)); - GRPC_MDSTR_REF((grpc_mdstr *)(is)); -} - -static void slice_unref(void *p) { - internal_string *is = - (internal_string *)((char *)p - offsetof(internal_string, refcount)); - GRPC_MDSTR_UNREF((grpc_mdstr *)(is)); -} - -grpc_mdstr *grpc_mdstr_from_string(const char *str) { - return grpc_mdstr_from_buffer((const uint8_t *)str, strlen(str)); -} - -grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice) { - grpc_mdstr *result = grpc_mdstr_from_buffer(GPR_SLICE_START_PTR(slice), - GPR_SLICE_LENGTH(slice)); - gpr_slice_unref(slice); - return result; -} - -grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *buf, size_t length) { - uint32_t hash = gpr_murmur_hash3(buf, length, g_hash_seed); - internal_string *s; - strtab_shard *shard = - &g_strtab_shard[SHARD_IDX(hash, LOG2_STRTAB_SHARD_COUNT)]; - size_t i; - size_t idx; - - GPR_TIMER_BEGIN("grpc_mdstr_from_buffer", 0); - - /* search for a static string */ - for (i = 0; i <= g_static_strtab_maxprobe; i++) { - grpc_mdstr *ss; - idx = (hash + i) % GPR_ARRAY_SIZE(g_static_strtab); - ss = g_static_strtab[idx]; - if (ss == NULL) break; - if (ss->hash == hash && GPR_SLICE_LENGTH(ss->slice) == length && - 0 == memcmp(buf, GPR_SLICE_START_PTR(ss->slice), length)) { - GPR_TIMER_END("grpc_mdstr_from_buffer", 0); - return ss; - } - } - - gpr_mu_lock(&shard->mu); - - /* search for an existing string */ - idx = TABLE_IDX(hash, LOG2_STRTAB_SHARD_COUNT, shard->capacity); - for (s = shard->strs[idx]; 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)) { - GRPC_MDSTR_REF((grpc_mdstr *)s); - gpr_mu_unlock(&shard->mu); - GPR_TIMER_END("grpc_mdstr_from_buffer", 0); - 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)); - gpr_atm_rel_store(&s->refcnt, 2); - s->slice.refcount = NULL; - memcpy(s->slice.data.inlined.bytes, buf, length); - s->slice.data.inlined.bytes[length] = 0; - s->slice.data.inlined.length = (uint8_t)length; - } else { - /* string data goes after the internal_string header, and we +1 for null - terminator */ - s = gpr_malloc(sizeof(internal_string) + length + 1); - gpr_atm_rel_store(&s->refcnt, 2); - s->refcount.ref = slice_ref; - s->refcount.unref = slice_unref; - s->slice.refcount = &s->refcount; - s->slice.data.refcounted.bytes = (uint8_t *)(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->bucket_next = shard->strs[idx]; - shard->strs[idx] = s; - - shard->count++; - - if (shard->count > shard->capacity * 2) { - grow_strtab(shard); - } - - gpr_mu_unlock(&shard->mu); - GPR_TIMER_END("grpc_mdstr_from_buffer", 0); - - return (grpc_mdstr *)s; -} - -static void gc_mdtab(mdtab_shard *shard) { - size_t i; - internal_metadata **prev_next; - internal_metadata *md, *next; - - GPR_TIMER_BEGIN("gc_mdtab", 0); - for (i = 0; i < shard->capacity; i++) { - prev_next = &shard->elems[i]; - for (md = shard->elems[i]; md; md = next) { - void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data); - next = md->bucket_next; - if (gpr_atm_acq_load(&md->refcnt) == 0) { - GRPC_MDSTR_UNREF((grpc_mdstr *)md->key); - GRPC_MDSTR_UNREF((grpc_mdstr *)md->value); - if (md->user_data) { - ((destroy_user_data_func)gpr_atm_no_barrier_load( - &md->destroy_user_data))(user_data); - } - gpr_free(md); - *prev_next = next; - shard->free--; - shard->count--; - } else { - prev_next = &md->bucket_next; - } - } - } - GPR_TIMER_END("gc_mdtab", 0); -} - -static void grow_mdtab(mdtab_shard *shard) { - size_t capacity = shard->capacity * 2; - size_t i; - internal_metadata **mdtab; - internal_metadata *md, *next; - uint32_t hash; - - GPR_TIMER_BEGIN("grow_mdtab", 0); - - mdtab = gpr_malloc(sizeof(internal_metadata *) * capacity); - memset(mdtab, 0, sizeof(internal_metadata *) * capacity); - - for (i = 0; i < shard->capacity; i++) { - for (md = shard->elems[i]; md; md = next) { - size_t idx; - hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash); - next = md->bucket_next; - idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, capacity); - md->bucket_next = mdtab[idx]; - mdtab[idx] = md; - } - } - - gpr_free(shard->elems); - shard->elems = mdtab; - shard->capacity = capacity; - - GPR_TIMER_END("grow_mdtab", 0); -} - -static void rehash_mdtab(mdtab_shard *shard) { - if (shard->free > shard->capacity / 4) { - gc_mdtab(shard); - } else { - grow_mdtab(shard); - } -} - -grpc_mdelem *grpc_mdelem_from_metadata_strings(grpc_mdstr *mkey, - grpc_mdstr *mvalue) { - internal_string *key = (internal_string *)mkey; - internal_string *value = (internal_string *)mvalue; - uint32_t hash = GRPC_MDSTR_KV_HASH(mkey->hash, mvalue->hash); - internal_metadata *md; - mdtab_shard *shard = &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)]; - size_t i; - size_t idx; - - GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0); - - if (is_mdstr_static(mkey) && is_mdstr_static(mvalue)) { - for (i = 0; i <= g_static_mdtab_maxprobe; i++) { - grpc_mdelem *smd; - idx = (hash + i) % GPR_ARRAY_SIZE(g_static_mdtab); - smd = g_static_mdtab[idx]; - if (smd == NULL) break; - if (smd->key == mkey && smd->value == mvalue) { - GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0); - return smd; - } - } - } - - gpr_mu_lock(&shard->mu); - - idx = TABLE_IDX(hash, LOG2_MDTAB_SHARD_COUNT, shard->capacity); - /* search for an existing pair */ - for (md = shard->elems[idx]; md; md = md->bucket_next) { - if (md->key == key && md->value == value) { - REF_MD_LOCKED(shard, md); - GRPC_MDSTR_UNREF((grpc_mdstr *)key); - GRPC_MDSTR_UNREF((grpc_mdstr *)value); - gpr_mu_unlock(&shard->mu); - GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0); - return (grpc_mdelem *)md; - } - } - - /* not found: create a new pair */ - md = gpr_malloc(sizeof(internal_metadata)); - gpr_atm_rel_store(&md->refcnt, 2); - md->key = key; - md->value = value; - md->user_data = 0; - md->destroy_user_data = 0; - md->bucket_next = shard->elems[idx]; - shard->elems[idx] = md; - 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 - shard->count++; - - if (shard->count > shard->capacity * 2) { - rehash_mdtab(shard); - } - - gpr_mu_unlock(&shard->mu); - - GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0); - - return (grpc_mdelem *)md; -} - -grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value) { - return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_string(key), - grpc_mdstr_from_string(value)); -} - -grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value) { - return grpc_mdelem_from_metadata_strings(grpc_mdstr_from_slice(key), - grpc_mdstr_from_slice(value)); -} - -grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key, - const uint8_t *value, - size_t value_length) { - return grpc_mdelem_from_metadata_strings( - grpc_mdstr_from_string(key), grpc_mdstr_from_buffer(value, value_length)); -} - -grpc_mdelem *grpc_mdelem_ref(grpc_mdelem *gmd DEBUG_ARGS) { - internal_metadata *md = (internal_metadata *)gmd; - if (is_mdelem_static(gmd)) return 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) >= 2); - 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; - if (!md) return; - if (is_mdelem_static(gmd)) return; -#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 - if (2 == gpr_atm_full_fetch_add(&md->refcnt, -1)) { - uint32_t hash = GRPC_MDSTR_KV_HASH(md->key->hash, md->value->hash); - mdtab_shard *shard = - &g_mdtab_shard[SHARD_IDX(hash, LOG2_MDTAB_SHARD_COUNT)]; - GPR_TIMER_BEGIN("grpc_mdelem_unref.to_zero", 0); - gpr_mu_lock(&shard->mu); - if (1 == gpr_atm_no_barrier_load(&md->refcnt)) { - shard->free++; - gpr_atm_no_barrier_store(&md->refcnt, 0); - } - gpr_mu_unlock(&shard->mu); - GPR_TIMER_END("grpc_mdelem_unref.to_zero", 0); - } -} - -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; - if (is_mdstr_static(gs)) return gs; - GPR_ASSERT(gpr_atm_full_fetch_add(&s->refcnt, 1) != 0); - return gs; -} - -void grpc_mdstr_unref(grpc_mdstr *gs DEBUG_ARGS) { - internal_string *s = (internal_string *)gs; - if (is_mdstr_static(gs)) return; - if (2 == gpr_atm_full_fetch_add(&s->refcnt, -1)) { - strtab_shard *shard = - &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)]; - gpr_mu_lock(&shard->mu); - if (1 == gpr_atm_no_barrier_load(&s->refcnt)) { - internal_destroy_string(shard, s); - } - gpr_mu_unlock(&shard->mu); - } -} - -void *grpc_mdelem_get_user_data(grpc_mdelem *md, void (*destroy_func)(void *)) { - internal_metadata *im = (internal_metadata *)md; - void *result; - if (is_mdelem_static(md)) { - return (void *)grpc_static_mdelem_user_data[md - grpc_static_mdelem_table]; - } - if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) { - return (void *)gpr_atm_no_barrier_load(&im->user_data); - } else { - return NULL; - } - 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(!is_mdelem_static(md)); - GPR_ASSERT((user_data == NULL) == (destroy_func == NULL)); - gpr_mu_lock(&im->mu_user_data); - if (gpr_atm_no_barrier_load(&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; - } - gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data); - gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func); - 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; - strtab_shard *shard = - &g_strtab_shard[SHARD_IDX(s->hash, LOG2_STRTAB_SHARD_COUNT)]; - gpr_mu_lock(&shard->mu); - 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; - gpr_mu_unlock(&shard->mu); - return slice; -} diff --git a/third_party/grpc/src/core/transport/metadata.h b/third_party/grpc/src/core/transport/metadata.h deleted file mode 100644 index 8742846be7e625..00000000000000 --- a/third_party/grpc/src/core/transport/metadata.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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. - - STATIC METADATA: in static_metadata.h we declare a set of static metadata. - These mdelems and mdstrs are available via pre-declared code generated macros - and are available to code anywhere between grpc_init() and grpc_shutdown(). - They are not refcounted, but can be passed to _ref and _unref functions - declared here - in which case those functions are effectively no-ops. */ - -/* Forward declarations */ -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 uint32_t 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 */ -}; - -void grpc_test_only_set_metadata_hash_seed(uint32_t seed); - -/* Constructors for grpc_mdstr instances; take a variety of data types that - clients may have handy */ -grpc_mdstr *grpc_mdstr_from_string(const char *str); -/* Unrefs the slice. */ -grpc_mdstr *grpc_mdstr_from_slice(gpr_slice slice); -grpc_mdstr *grpc_mdstr_from_buffer(const uint8_t *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_mdstr *key, - grpc_mdstr *value); -grpc_mdelem *grpc_mdelem_from_strings(const char *key, const char *value); -/* Unrefs the slices. */ -grpc_mdelem *grpc_mdelem_from_slices(gpr_slice key, gpr_slice value); -grpc_mdelem *grpc_mdelem_from_string_and_buffer(const char *key, - const uint8_t *value, - size_t value_length); - -/* 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); - -#define GRPC_MDSTR_LENGTH(s) (GPR_SLICE_LENGTH(s->slice)) - -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); - -#define GRPC_MDSTR_KV_HASH(k_hash, v_hash) (GPR_ROTL((k_hash), 2) ^ (v_hash)) - -void grpc_mdctx_global_init(void); -void grpc_mdctx_global_shutdown(void); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_METADATA_H */ diff --git a/third_party/grpc/src/core/transport/metadata_batch.c b/third_party/grpc/src/core/transport/metadata_batch.c deleted file mode 100644 index 1266862f8271ed..00000000000000 --- a/third_party/grpc/src/core/transport/metadata_batch.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/metadata_batch.h" - -#include - -#include -#include - -#include "src/core/profiling/timers.h" - -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); -} -#endif /* NDEBUG */ - -void grpc_metadata_batch_init(grpc_metadata_batch *batch) { - batch->list.head = batch->list.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); - } -} - -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_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; - - GPR_TIMER_BEGIN("grpc_metadata_batch_filter", 0); - - assert_valid_list(&batch->list); - 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); - GRPC_MDELEM_UNREF(l->md); - } else if (filt != orig) { - GRPC_MDELEM_UNREF(orig); - l->md = filt; - } - } - assert_valid_list(&batch->list); - - GPR_TIMER_END("grpc_metadata_batch_filter", 0); -} - -static grpc_mdelem *no_metadata_for_you(void *user_data, grpc_mdelem *elem) { - return NULL; -} - -void grpc_metadata_batch_clear(grpc_metadata_batch *batch) { - batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); - grpc_metadata_batch_filter(batch, no_metadata_for_you, NULL); -} - -int grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) { - return batch->list.head == NULL && - gpr_time_cmp(gpr_inf_future(batch->deadline.clock_type), - batch->deadline) == 0; -} diff --git a/third_party/grpc/src/core/transport/metadata_batch.h b/third_party/grpc/src/core/transport/metadata_batch.h deleted file mode 100644 index 1b0d1fda3e2205..00000000000000 --- a/third_party/grpc/src/core/transport/metadata_batch.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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" - -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; - /** 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_clear(grpc_metadata_batch *batch); -int grpc_metadata_batch_is_empty(grpc_metadata_batch *batch); - -/** 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 - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_STREAM_OP_H */ diff --git a/third_party/grpc/src/core/transport/static_metadata.c b/third_party/grpc/src/core/transport/static_metadata.c deleted file mode 100644 index 233e5c0d92a235..00000000000000 --- a/third_party/grpc/src/core/transport/static_metadata.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * WARNING: Auto-generated code. - * - * To make changes to this file, change - *tools/codegen/core/gen_static_metadata.py, - * and then re-run it. - * - * See metadata.h for an explanation of the interface here, and metadata.c for - *an - * explanation of what's going on. - */ - -#include "src/core/transport/static_metadata.h" - -grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT]; - -grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; -uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { - 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, 3, 7, 5, 2, 4, 8, 6, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -const uint8_t grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2] = - {11, 35, 10, 35, 12, 35, 12, 49, 13, 35, 14, 35, 15, 35, 16, 35, 17, 35, - 19, 35, 20, 35, 21, 35, 24, 35, 25, 35, 26, 35, 27, 35, 28, 35, 29, 35, - 30, 18, 30, 35, 31, 35, 32, 35, 36, 35, 37, 35, 38, 35, 39, 35, 42, 33, - 42, 34, 42, 48, 42, 53, 42, 54, 42, 55, 42, 56, 43, 33, 43, 48, 43, 53, - 46, 0, 46, 1, 46, 2, 50, 35, 57, 35, 58, 35, 59, 35, 60, 35, 61, 35, - 62, 35, 63, 35, 64, 35, 65, 35, 66, 40, 66, 68, 67, 78, 67, 79, 69, 35, - 70, 35, 71, 35, 72, 35, 73, 35, 74, 35, 75, 41, 75, 51, 75, 52, 76, 35, - 77, 35, 80, 3, 80, 4, 80, 5, 80, 6, 80, 7, 80, 8, 80, 9, 81, 35, - 82, 83, 84, 35, 85, 35, 86, 35, 87, 35, 88, 35}; - -const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT] = { - "0", "1", "2", "200", "204", "206", "304", "400", "404", "500", "accept", - "accept-charset", "accept-encoding", "accept-language", "accept-ranges", - "access-control-allow-origin", "age", "allow", "application/grpc", - ":authority", "authorization", "cache-control", "census", "census-bin", - "content-disposition", "content-encoding", "content-language", - "content-length", "content-location", "content-range", "content-type", - "cookie", "date", "deflate", "deflate,gzip", "", "etag", "expect", - "expires", "from", "GET", "grpc", "grpc-accept-encoding", "grpc-encoding", - "grpc-internal-encoding-request", "grpc-message", "grpc-status", - "grpc-timeout", "gzip", "gzip, deflate", "host", "http", "https", - "identity", "identity,deflate", "identity,deflate,gzip", "identity,gzip", - "if-match", "if-modified-since", "if-none-match", "if-range", - "if-unmodified-since", "last-modified", "link", "location", "max-forwards", - ":method", ":path", "POST", "proxy-authenticate", "proxy-authorization", - "range", "referer", "refresh", "retry-after", ":scheme", "server", - "set-cookie", "/", "/index.html", ":status", "strict-transport-security", - "te", "trailers", "transfer-encoding", "user-agent", "vary", "via", - "www-authenticate"}; - -const uint8_t grpc_static_accept_encoding_metadata[8] = {0, 29, 26, 30, - 28, 32, 27, 31}; diff --git a/third_party/grpc/src/core/transport/static_metadata.h b/third_party/grpc/src/core/transport/static_metadata.h deleted file mode 100644 index 3803a0488bfc06..00000000000000 --- a/third_party/grpc/src/core/transport/static_metadata.h +++ /dev/null @@ -1,408 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * WARNING: Auto-generated code. - * - * To make changes to this file, change - * tools/codegen/core/gen_static_metadata.py, - * and then re-run it. - * - * See metadata.h for an explanation of the interface here, and metadata.c for - * an - * explanation of what's going on. - */ - -#ifndef GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H -#define GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H - -#include "src/core/transport/metadata.h" - -#define GRPC_STATIC_MDSTR_COUNT 89 -extern grpc_mdstr grpc_static_mdstr_table[GRPC_STATIC_MDSTR_COUNT]; -/* "0" */ -#define GRPC_MDSTR_0 (&grpc_static_mdstr_table[0]) -/* "1" */ -#define GRPC_MDSTR_1 (&grpc_static_mdstr_table[1]) -/* "2" */ -#define GRPC_MDSTR_2 (&grpc_static_mdstr_table[2]) -/* "200" */ -#define GRPC_MDSTR_200 (&grpc_static_mdstr_table[3]) -/* "204" */ -#define GRPC_MDSTR_204 (&grpc_static_mdstr_table[4]) -/* "206" */ -#define GRPC_MDSTR_206 (&grpc_static_mdstr_table[5]) -/* "304" */ -#define GRPC_MDSTR_304 (&grpc_static_mdstr_table[6]) -/* "400" */ -#define GRPC_MDSTR_400 (&grpc_static_mdstr_table[7]) -/* "404" */ -#define GRPC_MDSTR_404 (&grpc_static_mdstr_table[8]) -/* "500" */ -#define GRPC_MDSTR_500 (&grpc_static_mdstr_table[9]) -/* "accept" */ -#define GRPC_MDSTR_ACCEPT (&grpc_static_mdstr_table[10]) -/* "accept-charset" */ -#define GRPC_MDSTR_ACCEPT_CHARSET (&grpc_static_mdstr_table[11]) -/* "accept-encoding" */ -#define GRPC_MDSTR_ACCEPT_ENCODING (&grpc_static_mdstr_table[12]) -/* "accept-language" */ -#define GRPC_MDSTR_ACCEPT_LANGUAGE (&grpc_static_mdstr_table[13]) -/* "accept-ranges" */ -#define GRPC_MDSTR_ACCEPT_RANGES (&grpc_static_mdstr_table[14]) -/* "access-control-allow-origin" */ -#define GRPC_MDSTR_ACCESS_CONTROL_ALLOW_ORIGIN (&grpc_static_mdstr_table[15]) -/* "age" */ -#define GRPC_MDSTR_AGE (&grpc_static_mdstr_table[16]) -/* "allow" */ -#define GRPC_MDSTR_ALLOW (&grpc_static_mdstr_table[17]) -/* "application/grpc" */ -#define GRPC_MDSTR_APPLICATION_SLASH_GRPC (&grpc_static_mdstr_table[18]) -/* ":authority" */ -#define GRPC_MDSTR_AUTHORITY (&grpc_static_mdstr_table[19]) -/* "authorization" */ -#define GRPC_MDSTR_AUTHORIZATION (&grpc_static_mdstr_table[20]) -/* "cache-control" */ -#define GRPC_MDSTR_CACHE_CONTROL (&grpc_static_mdstr_table[21]) -/* "census" */ -#define GRPC_MDSTR_CENSUS (&grpc_static_mdstr_table[22]) -/* "census-bin" */ -#define GRPC_MDSTR_CENSUS_BIN (&grpc_static_mdstr_table[23]) -/* "content-disposition" */ -#define GRPC_MDSTR_CONTENT_DISPOSITION (&grpc_static_mdstr_table[24]) -/* "content-encoding" */ -#define GRPC_MDSTR_CONTENT_ENCODING (&grpc_static_mdstr_table[25]) -/* "content-language" */ -#define GRPC_MDSTR_CONTENT_LANGUAGE (&grpc_static_mdstr_table[26]) -/* "content-length" */ -#define GRPC_MDSTR_CONTENT_LENGTH (&grpc_static_mdstr_table[27]) -/* "content-location" */ -#define GRPC_MDSTR_CONTENT_LOCATION (&grpc_static_mdstr_table[28]) -/* "content-range" */ -#define GRPC_MDSTR_CONTENT_RANGE (&grpc_static_mdstr_table[29]) -/* "content-type" */ -#define GRPC_MDSTR_CONTENT_TYPE (&grpc_static_mdstr_table[30]) -/* "cookie" */ -#define GRPC_MDSTR_COOKIE (&grpc_static_mdstr_table[31]) -/* "date" */ -#define GRPC_MDSTR_DATE (&grpc_static_mdstr_table[32]) -/* "deflate" */ -#define GRPC_MDSTR_DEFLATE (&grpc_static_mdstr_table[33]) -/* "deflate,gzip" */ -#define GRPC_MDSTR_DEFLATE_COMMA_GZIP (&grpc_static_mdstr_table[34]) -/* "" */ -#define GRPC_MDSTR_EMPTY (&grpc_static_mdstr_table[35]) -/* "etag" */ -#define GRPC_MDSTR_ETAG (&grpc_static_mdstr_table[36]) -/* "expect" */ -#define GRPC_MDSTR_EXPECT (&grpc_static_mdstr_table[37]) -/* "expires" */ -#define GRPC_MDSTR_EXPIRES (&grpc_static_mdstr_table[38]) -/* "from" */ -#define GRPC_MDSTR_FROM (&grpc_static_mdstr_table[39]) -/* "GET" */ -#define GRPC_MDSTR_GET (&grpc_static_mdstr_table[40]) -/* "grpc" */ -#define GRPC_MDSTR_GRPC (&grpc_static_mdstr_table[41]) -/* "grpc-accept-encoding" */ -#define GRPC_MDSTR_GRPC_ACCEPT_ENCODING (&grpc_static_mdstr_table[42]) -/* "grpc-encoding" */ -#define GRPC_MDSTR_GRPC_ENCODING (&grpc_static_mdstr_table[43]) -/* "grpc-internal-encoding-request" */ -#define GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST (&grpc_static_mdstr_table[44]) -/* "grpc-message" */ -#define GRPC_MDSTR_GRPC_MESSAGE (&grpc_static_mdstr_table[45]) -/* "grpc-status" */ -#define GRPC_MDSTR_GRPC_STATUS (&grpc_static_mdstr_table[46]) -/* "grpc-timeout" */ -#define GRPC_MDSTR_GRPC_TIMEOUT (&grpc_static_mdstr_table[47]) -/* "gzip" */ -#define GRPC_MDSTR_GZIP (&grpc_static_mdstr_table[48]) -/* "gzip, deflate" */ -#define GRPC_MDSTR_GZIP_COMMA_DEFLATE (&grpc_static_mdstr_table[49]) -/* "host" */ -#define GRPC_MDSTR_HOST (&grpc_static_mdstr_table[50]) -/* "http" */ -#define GRPC_MDSTR_HTTP (&grpc_static_mdstr_table[51]) -/* "https" */ -#define GRPC_MDSTR_HTTPS (&grpc_static_mdstr_table[52]) -/* "identity" */ -#define GRPC_MDSTR_IDENTITY (&grpc_static_mdstr_table[53]) -/* "identity,deflate" */ -#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE (&grpc_static_mdstr_table[54]) -/* "identity,deflate,gzip" */ -#define GRPC_MDSTR_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \ - (&grpc_static_mdstr_table[55]) -/* "identity,gzip" */ -#define GRPC_MDSTR_IDENTITY_COMMA_GZIP (&grpc_static_mdstr_table[56]) -/* "if-match" */ -#define GRPC_MDSTR_IF_MATCH (&grpc_static_mdstr_table[57]) -/* "if-modified-since" */ -#define GRPC_MDSTR_IF_MODIFIED_SINCE (&grpc_static_mdstr_table[58]) -/* "if-none-match" */ -#define GRPC_MDSTR_IF_NONE_MATCH (&grpc_static_mdstr_table[59]) -/* "if-range" */ -#define GRPC_MDSTR_IF_RANGE (&grpc_static_mdstr_table[60]) -/* "if-unmodified-since" */ -#define GRPC_MDSTR_IF_UNMODIFIED_SINCE (&grpc_static_mdstr_table[61]) -/* "last-modified" */ -#define GRPC_MDSTR_LAST_MODIFIED (&grpc_static_mdstr_table[62]) -/* "link" */ -#define GRPC_MDSTR_LINK (&grpc_static_mdstr_table[63]) -/* "location" */ -#define GRPC_MDSTR_LOCATION (&grpc_static_mdstr_table[64]) -/* "max-forwards" */ -#define GRPC_MDSTR_MAX_FORWARDS (&grpc_static_mdstr_table[65]) -/* ":method" */ -#define GRPC_MDSTR_METHOD (&grpc_static_mdstr_table[66]) -/* ":path" */ -#define GRPC_MDSTR_PATH (&grpc_static_mdstr_table[67]) -/* "POST" */ -#define GRPC_MDSTR_POST (&grpc_static_mdstr_table[68]) -/* "proxy-authenticate" */ -#define GRPC_MDSTR_PROXY_AUTHENTICATE (&grpc_static_mdstr_table[69]) -/* "proxy-authorization" */ -#define GRPC_MDSTR_PROXY_AUTHORIZATION (&grpc_static_mdstr_table[70]) -/* "range" */ -#define GRPC_MDSTR_RANGE (&grpc_static_mdstr_table[71]) -/* "referer" */ -#define GRPC_MDSTR_REFERER (&grpc_static_mdstr_table[72]) -/* "refresh" */ -#define GRPC_MDSTR_REFRESH (&grpc_static_mdstr_table[73]) -/* "retry-after" */ -#define GRPC_MDSTR_RETRY_AFTER (&grpc_static_mdstr_table[74]) -/* ":scheme" */ -#define GRPC_MDSTR_SCHEME (&grpc_static_mdstr_table[75]) -/* "server" */ -#define GRPC_MDSTR_SERVER (&grpc_static_mdstr_table[76]) -/* "set-cookie" */ -#define GRPC_MDSTR_SET_COOKIE (&grpc_static_mdstr_table[77]) -/* "/" */ -#define GRPC_MDSTR_SLASH (&grpc_static_mdstr_table[78]) -/* "/index.html" */ -#define GRPC_MDSTR_SLASH_INDEX_DOT_HTML (&grpc_static_mdstr_table[79]) -/* ":status" */ -#define GRPC_MDSTR_STATUS (&grpc_static_mdstr_table[80]) -/* "strict-transport-security" */ -#define GRPC_MDSTR_STRICT_TRANSPORT_SECURITY (&grpc_static_mdstr_table[81]) -/* "te" */ -#define GRPC_MDSTR_TE (&grpc_static_mdstr_table[82]) -/* "trailers" */ -#define GRPC_MDSTR_TRAILERS (&grpc_static_mdstr_table[83]) -/* "transfer-encoding" */ -#define GRPC_MDSTR_TRANSFER_ENCODING (&grpc_static_mdstr_table[84]) -/* "user-agent" */ -#define GRPC_MDSTR_USER_AGENT (&grpc_static_mdstr_table[85]) -/* "vary" */ -#define GRPC_MDSTR_VARY (&grpc_static_mdstr_table[86]) -/* "via" */ -#define GRPC_MDSTR_VIA (&grpc_static_mdstr_table[87]) -/* "www-authenticate" */ -#define GRPC_MDSTR_WWW_AUTHENTICATE (&grpc_static_mdstr_table[88]) - -#define GRPC_STATIC_MDELEM_COUNT 78 -extern grpc_mdelem grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; -extern uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT]; -/* "accept-charset": "" */ -#define GRPC_MDELEM_ACCEPT_CHARSET_EMPTY (&grpc_static_mdelem_table[0]) -/* "accept": "" */ -#define GRPC_MDELEM_ACCEPT_EMPTY (&grpc_static_mdelem_table[1]) -/* "accept-encoding": "" */ -#define GRPC_MDELEM_ACCEPT_ENCODING_EMPTY (&grpc_static_mdelem_table[2]) -/* "accept-encoding": "gzip, deflate" */ -#define GRPC_MDELEM_ACCEPT_ENCODING_GZIP_COMMA_DEFLATE \ - (&grpc_static_mdelem_table[3]) -/* "accept-language": "" */ -#define GRPC_MDELEM_ACCEPT_LANGUAGE_EMPTY (&grpc_static_mdelem_table[4]) -/* "accept-ranges": "" */ -#define GRPC_MDELEM_ACCEPT_RANGES_EMPTY (&grpc_static_mdelem_table[5]) -/* "access-control-allow-origin": "" */ -#define GRPC_MDELEM_ACCESS_CONTROL_ALLOW_ORIGIN_EMPTY \ - (&grpc_static_mdelem_table[6]) -/* "age": "" */ -#define GRPC_MDELEM_AGE_EMPTY (&grpc_static_mdelem_table[7]) -/* "allow": "" */ -#define GRPC_MDELEM_ALLOW_EMPTY (&grpc_static_mdelem_table[8]) -/* ":authority": "" */ -#define GRPC_MDELEM_AUTHORITY_EMPTY (&grpc_static_mdelem_table[9]) -/* "authorization": "" */ -#define GRPC_MDELEM_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[10]) -/* "cache-control": "" */ -#define GRPC_MDELEM_CACHE_CONTROL_EMPTY (&grpc_static_mdelem_table[11]) -/* "content-disposition": "" */ -#define GRPC_MDELEM_CONTENT_DISPOSITION_EMPTY (&grpc_static_mdelem_table[12]) -/* "content-encoding": "" */ -#define GRPC_MDELEM_CONTENT_ENCODING_EMPTY (&grpc_static_mdelem_table[13]) -/* "content-language": "" */ -#define GRPC_MDELEM_CONTENT_LANGUAGE_EMPTY (&grpc_static_mdelem_table[14]) -/* "content-length": "" */ -#define GRPC_MDELEM_CONTENT_LENGTH_EMPTY (&grpc_static_mdelem_table[15]) -/* "content-location": "" */ -#define GRPC_MDELEM_CONTENT_LOCATION_EMPTY (&grpc_static_mdelem_table[16]) -/* "content-range": "" */ -#define GRPC_MDELEM_CONTENT_RANGE_EMPTY (&grpc_static_mdelem_table[17]) -/* "content-type": "application/grpc" */ -#define GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC \ - (&grpc_static_mdelem_table[18]) -/* "content-type": "" */ -#define GRPC_MDELEM_CONTENT_TYPE_EMPTY (&grpc_static_mdelem_table[19]) -/* "cookie": "" */ -#define GRPC_MDELEM_COOKIE_EMPTY (&grpc_static_mdelem_table[20]) -/* "date": "" */ -#define GRPC_MDELEM_DATE_EMPTY (&grpc_static_mdelem_table[21]) -/* "etag": "" */ -#define GRPC_MDELEM_ETAG_EMPTY (&grpc_static_mdelem_table[22]) -/* "expect": "" */ -#define GRPC_MDELEM_EXPECT_EMPTY (&grpc_static_mdelem_table[23]) -/* "expires": "" */ -#define GRPC_MDELEM_EXPIRES_EMPTY (&grpc_static_mdelem_table[24]) -/* "from": "" */ -#define GRPC_MDELEM_FROM_EMPTY (&grpc_static_mdelem_table[25]) -/* "grpc-accept-encoding": "deflate" */ -#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE (&grpc_static_mdelem_table[26]) -/* "grpc-accept-encoding": "deflate,gzip" */ -#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_DEFLATE_COMMA_GZIP \ - (&grpc_static_mdelem_table[27]) -/* "grpc-accept-encoding": "gzip" */ -#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_GZIP (&grpc_static_mdelem_table[28]) -/* "grpc-accept-encoding": "identity" */ -#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY \ - (&grpc_static_mdelem_table[29]) -/* "grpc-accept-encoding": "identity,deflate" */ -#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE \ - (&grpc_static_mdelem_table[30]) -/* "grpc-accept-encoding": "identity,deflate,gzip" */ -#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP \ - (&grpc_static_mdelem_table[31]) -/* "grpc-accept-encoding": "identity,gzip" */ -#define GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_GZIP \ - (&grpc_static_mdelem_table[32]) -/* "grpc-encoding": "deflate" */ -#define GRPC_MDELEM_GRPC_ENCODING_DEFLATE (&grpc_static_mdelem_table[33]) -/* "grpc-encoding": "gzip" */ -#define GRPC_MDELEM_GRPC_ENCODING_GZIP (&grpc_static_mdelem_table[34]) -/* "grpc-encoding": "identity" */ -#define GRPC_MDELEM_GRPC_ENCODING_IDENTITY (&grpc_static_mdelem_table[35]) -/* "grpc-status": "0" */ -#define GRPC_MDELEM_GRPC_STATUS_0 (&grpc_static_mdelem_table[36]) -/* "grpc-status": "1" */ -#define GRPC_MDELEM_GRPC_STATUS_1 (&grpc_static_mdelem_table[37]) -/* "grpc-status": "2" */ -#define GRPC_MDELEM_GRPC_STATUS_2 (&grpc_static_mdelem_table[38]) -/* "host": "" */ -#define GRPC_MDELEM_HOST_EMPTY (&grpc_static_mdelem_table[39]) -/* "if-match": "" */ -#define GRPC_MDELEM_IF_MATCH_EMPTY (&grpc_static_mdelem_table[40]) -/* "if-modified-since": "" */ -#define GRPC_MDELEM_IF_MODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[41]) -/* "if-none-match": "" */ -#define GRPC_MDELEM_IF_NONE_MATCH_EMPTY (&grpc_static_mdelem_table[42]) -/* "if-range": "" */ -#define GRPC_MDELEM_IF_RANGE_EMPTY (&grpc_static_mdelem_table[43]) -/* "if-unmodified-since": "" */ -#define GRPC_MDELEM_IF_UNMODIFIED_SINCE_EMPTY (&grpc_static_mdelem_table[44]) -/* "last-modified": "" */ -#define GRPC_MDELEM_LAST_MODIFIED_EMPTY (&grpc_static_mdelem_table[45]) -/* "link": "" */ -#define GRPC_MDELEM_LINK_EMPTY (&grpc_static_mdelem_table[46]) -/* "location": "" */ -#define GRPC_MDELEM_LOCATION_EMPTY (&grpc_static_mdelem_table[47]) -/* "max-forwards": "" */ -#define GRPC_MDELEM_MAX_FORWARDS_EMPTY (&grpc_static_mdelem_table[48]) -/* ":method": "GET" */ -#define GRPC_MDELEM_METHOD_GET (&grpc_static_mdelem_table[49]) -/* ":method": "POST" */ -#define GRPC_MDELEM_METHOD_POST (&grpc_static_mdelem_table[50]) -/* ":path": "/" */ -#define GRPC_MDELEM_PATH_SLASH (&grpc_static_mdelem_table[51]) -/* ":path": "/index.html" */ -#define GRPC_MDELEM_PATH_SLASH_INDEX_DOT_HTML (&grpc_static_mdelem_table[52]) -/* "proxy-authenticate": "" */ -#define GRPC_MDELEM_PROXY_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[53]) -/* "proxy-authorization": "" */ -#define GRPC_MDELEM_PROXY_AUTHORIZATION_EMPTY (&grpc_static_mdelem_table[54]) -/* "range": "" */ -#define GRPC_MDELEM_RANGE_EMPTY (&grpc_static_mdelem_table[55]) -/* "referer": "" */ -#define GRPC_MDELEM_REFERER_EMPTY (&grpc_static_mdelem_table[56]) -/* "refresh": "" */ -#define GRPC_MDELEM_REFRESH_EMPTY (&grpc_static_mdelem_table[57]) -/* "retry-after": "" */ -#define GRPC_MDELEM_RETRY_AFTER_EMPTY (&grpc_static_mdelem_table[58]) -/* ":scheme": "grpc" */ -#define GRPC_MDELEM_SCHEME_GRPC (&grpc_static_mdelem_table[59]) -/* ":scheme": "http" */ -#define GRPC_MDELEM_SCHEME_HTTP (&grpc_static_mdelem_table[60]) -/* ":scheme": "https" */ -#define GRPC_MDELEM_SCHEME_HTTPS (&grpc_static_mdelem_table[61]) -/* "server": "" */ -#define GRPC_MDELEM_SERVER_EMPTY (&grpc_static_mdelem_table[62]) -/* "set-cookie": "" */ -#define GRPC_MDELEM_SET_COOKIE_EMPTY (&grpc_static_mdelem_table[63]) -/* ":status": "200" */ -#define GRPC_MDELEM_STATUS_200 (&grpc_static_mdelem_table[64]) -/* ":status": "204" */ -#define GRPC_MDELEM_STATUS_204 (&grpc_static_mdelem_table[65]) -/* ":status": "206" */ -#define GRPC_MDELEM_STATUS_206 (&grpc_static_mdelem_table[66]) -/* ":status": "304" */ -#define GRPC_MDELEM_STATUS_304 (&grpc_static_mdelem_table[67]) -/* ":status": "400" */ -#define GRPC_MDELEM_STATUS_400 (&grpc_static_mdelem_table[68]) -/* ":status": "404" */ -#define GRPC_MDELEM_STATUS_404 (&grpc_static_mdelem_table[69]) -/* ":status": "500" */ -#define GRPC_MDELEM_STATUS_500 (&grpc_static_mdelem_table[70]) -/* "strict-transport-security": "" */ -#define GRPC_MDELEM_STRICT_TRANSPORT_SECURITY_EMPTY \ - (&grpc_static_mdelem_table[71]) -/* "te": "trailers" */ -#define GRPC_MDELEM_TE_TRAILERS (&grpc_static_mdelem_table[72]) -/* "transfer-encoding": "" */ -#define GRPC_MDELEM_TRANSFER_ENCODING_EMPTY (&grpc_static_mdelem_table[73]) -/* "user-agent": "" */ -#define GRPC_MDELEM_USER_AGENT_EMPTY (&grpc_static_mdelem_table[74]) -/* "vary": "" */ -#define GRPC_MDELEM_VARY_EMPTY (&grpc_static_mdelem_table[75]) -/* "via": "" */ -#define GRPC_MDELEM_VIA_EMPTY (&grpc_static_mdelem_table[76]) -/* "www-authenticate": "" */ -#define GRPC_MDELEM_WWW_AUTHENTICATE_EMPTY (&grpc_static_mdelem_table[77]) - -extern const uint8_t - grpc_static_metadata_elem_indices[GRPC_STATIC_MDELEM_COUNT * 2]; -extern const char *const grpc_static_metadata_strings[GRPC_STATIC_MDSTR_COUNT]; -extern const uint8_t grpc_static_accept_encoding_metadata[8]; -#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) \ - (&grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]]) -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_STATIC_METADATA_H */ diff --git a/third_party/grpc/src/core/transport/transport.c b/third_party/grpc/src/core/transport/transport.c deleted file mode 100644 index 08d685668ccb73..00000000000000 --- a/third_party/grpc/src/core/transport/transport.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include "src/core/transport/transport.h" -#include -#include -#include -#include "src/core/transport/transport_impl.h" - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_stream_ref(grpc_stream_refcount *refcount, const char *reason) { - gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); - gpr_log(GPR_DEBUG, "%s %p:%p REF %d->%d %s", refcount->object_type, - refcount, refcount->destroy.cb_arg, val, val + 1, reason); -#else -void grpc_stream_ref(grpc_stream_refcount *refcount) { -#endif - gpr_ref(&refcount->refs); -} - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount, - const char *reason) { - gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); - gpr_log(GPR_DEBUG, "%s %p:%p UNREF %d->%d %s", refcount->object_type, - refcount, refcount->destroy.cb_arg, val, val - 1, reason); -#else -void grpc_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_stream_refcount *refcount) { -#endif - if (gpr_unref(&refcount->refs)) { - grpc_exec_ctx_enqueue(exec_ctx, &refcount->destroy, true, NULL); - } -} - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, - grpc_iomgr_cb_func cb, void *cb_arg, - const char *object_type) { - refcount->object_type = object_type; -#else -void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, - grpc_iomgr_cb_func cb, void *cb_arg) { -#endif - gpr_ref_init(&refcount->refs, initial_refs); - grpc_closure_init(&refcount->destroy, cb, cb_arg); -} - -size_t grpc_transport_stream_size(grpc_transport *transport) { - return transport->vtable->sizeof_stream; -} - -void grpc_transport_destroy(grpc_exec_ctx *exec_ctx, - grpc_transport *transport) { - transport->vtable->destroy(exec_ctx, transport); -} - -int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, grpc_stream *stream, - grpc_stream_refcount *refcount, - const void *server_data) { - return transport->vtable->init_stream(exec_ctx, transport, stream, refcount, - server_data); -} - -void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_stream *stream, - grpc_transport_stream_op *op) { - transport->vtable->perform_stream_op(exec_ctx, transport, stream, op); -} - -void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_transport_op *op) { - transport->vtable->perform_op(exec_ctx, transport, op); -} - -void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, grpc_stream *stream, - grpc_pollset *pollset) { - transport->vtable->set_pollset(exec_ctx, transport, stream, pollset); -} - -void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_stream *stream) { - transport->vtable->destroy_stream(exec_ctx, transport, stream); -} - -char *grpc_transport_get_peer(grpc_exec_ctx *exec_ctx, - grpc_transport *transport) { - return transport->vtable->get_peer(exec_ctx, transport); -} - -void grpc_transport_stream_op_finish_with_failure( - grpc_exec_ctx *exec_ctx, grpc_transport_stream_op *op) { - grpc_exec_ctx_enqueue(exec_ctx, op->recv_message_ready, false, NULL); - grpc_exec_ctx_enqueue(exec_ctx, op->on_complete, false, NULL); -} - -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_closure *then_call; - grpc_closure closure; -} close_message_data; - -static void free_message(grpc_exec_ctx *exec_ctx, void *p, bool iomgr_success) { - close_message_data *cmd = p; - gpr_slice_unref(cmd->message); - if (cmd->then_call != NULL) { - cmd->then_call->cb(exec_ctx, 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_complete; - grpc_closure_init(&cmd->closure, free_message, cmd); - op->on_complete = &cmd->closure; - op->optional_close_message = &cmd->message; - } - op->close_with_status = status; -} diff --git a/third_party/grpc/src/core/transport/transport.h b/third_party/grpc/src/core/transport/transport.h deleted file mode 100644 index f5cac77adcc51b..00000000000000 --- a/third_party/grpc/src/core/transport/transport.h +++ /dev/null @@ -1,235 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#ifndef 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/metadata_batch.h" -#include "src/core/transport/byte_stream.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; - -/*#define GRPC_STREAM_REFCOUNT_DEBUG*/ - -typedef struct grpc_stream_refcount { - gpr_refcount refs; - grpc_closure destroy; -#ifdef GRPC_STREAM_REFCOUNT_DEBUG - const char *object_type; -#endif -} grpc_stream_refcount; - -#ifdef GRPC_STREAM_REFCOUNT_DEBUG -void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, - grpc_iomgr_cb_func cb, void *cb_arg, - const char *object_type); -void grpc_stream_ref(grpc_stream_refcount *refcount, const char *reason); -void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount, - const char *reason); -#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \ - grpc_stream_ref_init(rc, ir, cb, cb_arg, objtype) -#else -void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, - grpc_iomgr_cb_func cb, void *cb_arg); -void grpc_stream_ref(grpc_stream_refcount *refcount); -void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount); -#define GRPC_STREAM_REF_INIT(rc, ir, cb, cb_arg, objtype) \ - grpc_stream_ref_init(rc, ir, cb, cb_arg) -#endif - -/* Transport stream op: a set of operations to perform on a transport - against a single stream */ -typedef struct grpc_transport_stream_op { - /** Send initial metadata to the peer, from the provided metadata batch. */ - grpc_metadata_batch *send_initial_metadata; - - /** Send trailing metadata to the peer, from the provided metadata batch. */ - grpc_metadata_batch *send_trailing_metadata; - - /** Send message data to the peer, from the provided byte stream. */ - grpc_byte_stream *send_message; - - /** Receive initial metadata from the stream, into provided metadata batch. */ - grpc_metadata_batch *recv_initial_metadata; - - /** Receive message data from the stream, into provided byte stream. */ - grpc_byte_stream **recv_message; - /** Should be enqueued when one message is ready to be processed. */ - grpc_closure *recv_message_ready; - - /** Receive trailing metadata from the stream, into provided metadata batch. - */ - grpc_metadata_batch *recv_trailing_metadata; - - /** Should be enqueued when all requested operations (excluding recv_message - which has its own closure) in a given batch have been completed. */ - grpc_closure *on_complete; - - /** 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_closure *on_consumed; - /** connectivity monitoring - set connectivity_state to NULL to unsubscribe */ - grpc_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)(grpc_exec_ctx *exec_ctx, 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_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_exec_ctx *exec_ctx, - grpc_transport *transport, grpc_stream *stream, - grpc_stream_refcount *refcount, - const void *server_data); - -void grpc_transport_set_pollset(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, grpc_stream *stream, - grpc_pollset *pollset); - -/* 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_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_stream *stream); - -void grpc_transport_stream_op_finish_with_failure(grpc_exec_ctx *exec_ctx, - 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_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_stream *stream, - grpc_transport_stream_op *op); - -void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx, - 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_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_exec_ctx *exec_ctx, grpc_transport *transport); - -/* Get the transports peer */ -char *grpc_transport_get_peer(grpc_exec_ctx *exec_ctx, - grpc_transport *transport); - -#endif /* GRPC_INTERNAL_CORE_TRANSPORT_TRANSPORT_H */ diff --git a/third_party/grpc/src/core/transport/transport_impl.h b/third_party/grpc/src/core/transport/transport_impl.h deleted file mode 100644 index 40bfb4b13ac14a..00000000000000 --- a/third_party/grpc/src/core/transport/transport_impl.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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_exec_ctx *exec_ctx, grpc_transport *self, - grpc_stream *stream, grpc_stream_refcount *refcount, - const void *server_data); - - /* implementation of grpc_transport_set_pollset */ - void (*set_pollset)(grpc_exec_ctx *exec_ctx, grpc_transport *self, - grpc_stream *stream, grpc_pollset *pollset); - - /* implementation of grpc_transport_perform_stream_op */ - void (*perform_stream_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self, - grpc_stream *stream, grpc_transport_stream_op *op); - - /* implementation of grpc_transport_perform_op */ - void (*perform_op)(grpc_exec_ctx *exec_ctx, grpc_transport *self, - grpc_transport_op *op); - - /* implementation of grpc_transport_destroy_stream */ - void (*destroy_stream)(grpc_exec_ctx *exec_ctx, grpc_transport *self, - grpc_stream *stream); - - /* implementation of grpc_transport_destroy */ - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_transport *self); - - /* implementation of grpc_transport_get_peer */ - char *(*get_peer)(grpc_exec_ctx *exec_ctx, 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/third_party/grpc/src/core/transport/transport_op_string.c b/third_party/grpc/src/core/transport/transport_op_string.c deleted file mode 100644 index 98b51afc8819a6..00000000000000 --- a/third_party/grpc/src/core/transport/transport_op_string.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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=%lld.%09d", (long long)md.deadline.tv_sec, - (int)md.deadline.tv_nsec); - gpr_strvec_add(b, tmp); - } -} - -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_initial_metadata != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = 0; - gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA{")); - put_metadata_list(&b, *op->send_initial_metadata); - gpr_strvec_add(&b, gpr_strdup("}")); - } - - if (op->send_message != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = 0; - gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", - op->send_message->flags, op->send_message->length); - gpr_strvec_add(&b, tmp); - } - - if (op->send_trailing_metadata != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = 0; - gpr_strvec_add(&b, gpr_strdup("SEND_TRAILING_METADATA{")); - put_metadata_list(&b, *op->send_trailing_metadata); - gpr_strvec_add(&b, gpr_strdup("}")); - } - - if (op->recv_initial_metadata != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = 0; - gpr_strvec_add(&b, gpr_strdup("RECV_INITIAL_METADATA")); - } - - if (op->recv_message != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = 0; - gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE")); - } - - if (op->recv_trailing_metadata != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = 0; - gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA")); - } - - 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); - } - - 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/third_party/grpc/src/core/tsi/README.md b/third_party/grpc/src/core/tsi/README.md new file mode 100644 index 00000000000000..3ca3c1ef38cc53 --- /dev/null +++ b/third_party/grpc/src/core/tsi/README.md @@ -0,0 +1,2 @@ +# Transport Security Interface +An abstraction library over crypto and auth modules (typically OpenSSL) diff --git a/third_party/grpc/src/core/tsi/alts/crypt/aes_gcm.cc b/third_party/grpc/src/core/tsi/alts/crypt/aes_gcm.cc new file mode 100644 index 00000000000000..c638ce76eedbde --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/crypt/aes_gcm.cc @@ -0,0 +1,689 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/grpc_shadow_boringssl.h" + +#include "src/core/tsi/alts/crypt/gsec.h" + +#include +#include +#include +#include +#include +#include + +#include + +constexpr size_t kKdfKeyLen = 32; +constexpr size_t kKdfCounterLen = 6; +constexpr size_t kKdfCounterOffset = 2; +constexpr size_t kRekeyAeadKeyLen = kAes128GcmKeyLength; + +/* Struct for additional data required if rekeying is enabled. */ +struct gsec_aes_gcm_aead_rekey_data { + uint8_t kdf_counter[kKdfCounterLen]; + uint8_t nonce_mask[kAesGcmNonceLength]; +}; + +/* Main struct for AES_GCM crypter interface. */ +struct gsec_aes_gcm_aead_crypter { + gsec_aead_crypter crypter; + size_t key_length; + size_t nonce_length; + size_t tag_length; + uint8_t* key; + gsec_aes_gcm_aead_rekey_data* rekey_data; + EVP_CIPHER_CTX* ctx; +}; + +static char* aes_gcm_get_openssl_errors() { + BIO* bio = BIO_new(BIO_s_mem()); + ERR_print_errors(bio); + BUF_MEM* mem = nullptr; + char* error_msg = nullptr; + BIO_get_mem_ptr(bio, &mem); + if (mem != nullptr) { + error_msg = static_cast(gpr_malloc(mem->length + 1)); + memcpy(error_msg, mem->data, mem->length); + error_msg[mem->length] = '\0'; + } + BIO_free_all(bio); + return error_msg; +} + +static void aes_gcm_format_errors(const char* error_msg, char** error_details) { + if (error_details == nullptr) { + return; + } + unsigned long error = ERR_get_error(); + if (error == 0 && error_msg != nullptr) { + *error_details = static_cast(gpr_malloc(strlen(error_msg) + 1)); + memcpy(*error_details, error_msg, strlen(error_msg) + 1); + return; + } + char* openssl_errors = aes_gcm_get_openssl_errors(); + if (openssl_errors != nullptr && error_msg != nullptr) { + size_t len = strlen(error_msg) + strlen(openssl_errors) + 2; /* ", " */ + *error_details = static_cast(gpr_malloc(len + 1)); + snprintf(*error_details, len + 1, "%s, %s", error_msg, openssl_errors); + gpr_free(openssl_errors); + } +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length, char** error_details) { + if (max_ciphertext_and_tag_length == nullptr) { + aes_gcm_format_errors("max_ciphertext_and_tag_length is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast( + const_cast(crypter)); + *max_ciphertext_and_tag_length = + plaintext_length + aes_gcm_crypter->tag_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_max_plaintext_length( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length, char** error_details) { + if (max_plaintext_length == nullptr) { + aes_gcm_format_errors("max_plaintext_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast( + const_cast(crypter)); + if (ciphertext_and_tag_length < aes_gcm_crypter->tag_length) { + *max_plaintext_length = 0; + aes_gcm_format_errors( + "ciphertext_and_tag_length is smaller than tag_length.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *max_plaintext_length = + ciphertext_and_tag_length - aes_gcm_crypter->tag_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_nonce_length( + const gsec_aead_crypter* crypter, size_t* nonce_length, + char** error_details) { + if (nonce_length == nullptr) { + aes_gcm_format_errors("nonce_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast( + const_cast(crypter)); + *nonce_length = aes_gcm_crypter->nonce_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_key_length( + const gsec_aead_crypter* crypter, size_t* key_length, + char** error_details) { + if (key_length == nullptr) { + aes_gcm_format_errors("key_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast( + const_cast(crypter)); + *key_length = aes_gcm_crypter->key_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_tag_length( + const gsec_aead_crypter* crypter, size_t* tag_length, + char** error_details) { + if (tag_length == nullptr) { + aes_gcm_format_errors("tag_length is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast( + const_cast(crypter)); + *tag_length = aes_gcm_crypter->tag_length; + return GRPC_STATUS_OK; +} + +static void aes_gcm_mask_nonce(uint8_t* dst, const uint8_t* nonce, + const uint8_t* mask) { + uint64_t mask1; + uint32_t mask2; + memcpy(&mask1, mask, sizeof(mask1)); + memcpy(&mask2, mask + sizeof(mask1), sizeof(mask2)); + uint64_t nonce1; + uint32_t nonce2; + memcpy(&nonce1, nonce, sizeof(nonce1)); + memcpy(&nonce2, nonce + sizeof(nonce1), sizeof(nonce2)); + nonce1 ^= mask1; + nonce2 ^= mask2; + memcpy(dst, &nonce1, sizeof(nonce1)); + memcpy(dst + sizeof(nonce1), &nonce2, sizeof(nonce2)); +} + +static grpc_status_code aes_gcm_derive_aead_key(uint8_t* dst, + const uint8_t* kdf_key, + const uint8_t* kdf_counter) { + unsigned char buf[EVP_MAX_MD_SIZE]; + unsigned char ctr = 1; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + HMAC_CTX hmac; + HMAC_CTX_init(&hmac); + if (!HMAC_Init_ex(&hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) || + !HMAC_Update(&hmac, kdf_counter, kKdfCounterLen) || + !HMAC_Update(&hmac, &ctr, 1) || !HMAC_Final(&hmac, buf, nullptr)) { + HMAC_CTX_cleanup(&hmac); + return GRPC_STATUS_INTERNAL; + } + HMAC_CTX_cleanup(&hmac); +#else + HMAC_CTX* hmac = HMAC_CTX_new(); + if (hmac == nullptr) { + return GRPC_STATUS_INTERNAL; + } + if (!HMAC_Init_ex(hmac, kdf_key, kKdfKeyLen, EVP_sha256(), nullptr) || + !HMAC_Update(hmac, kdf_counter, kKdfCounterLen) || + !HMAC_Update(hmac, &ctr, 1) || !HMAC_Final(hmac, buf, nullptr)) { + HMAC_CTX_free(hmac); + return GRPC_STATUS_INTERNAL; + } + HMAC_CTX_free(hmac); +#endif + memcpy(dst, buf, kRekeyAeadKeyLen); + return GRPC_STATUS_OK; +} + +static grpc_status_code aes_gcm_rekey_if_required( + gsec_aes_gcm_aead_crypter* aes_gcm_crypter, const uint8_t* nonce, + char** error_details) { + // If rekey_data is nullptr, then rekeying is not supported and not required. + // If bytes 2-7 of kdf_counter differ from the (per message) nonce, then the + // encryption key is recomputed from a new kdf_counter to ensure that we don't + // encrypt more than 2^16 messages per encryption key (in each direction). + if (aes_gcm_crypter->rekey_data == nullptr || + memcmp(aes_gcm_crypter->rekey_data->kdf_counter, + nonce + kKdfCounterOffset, kKdfCounterLen) == 0) { + return GRPC_STATUS_OK; + } + memcpy(aes_gcm_crypter->rekey_data->kdf_counter, nonce + kKdfCounterOffset, + kKdfCounterLen); + uint8_t aead_key[kRekeyAeadKeyLen]; + if (aes_gcm_derive_aead_key(aead_key, aes_gcm_crypter->key, + aes_gcm_crypter->rekey_data->kdf_counter) != + GRPC_STATUS_OK) { + aes_gcm_format_errors("Rekeying failed in key derivation.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, aead_key, + nullptr)) { + aes_gcm_format_errors("Rekeying failed in context update.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_encrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details) { + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast(crypter); + // Input checks + if (nonce == nullptr) { + aes_gcm_format_errors("Nonce buffer is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (kAesGcmNonceLength != nonce_length) { + aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (aad_vec_length > 0 && aad_vec == nullptr) { + aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (plaintext_vec_length > 0 && plaintext_vec == nullptr) { + aes_gcm_format_errors( + "Non-zero plaintext_vec_length but plaintext_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (ciphertext_bytes_written == nullptr) { + aes_gcm_format_errors("bytes_written is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *ciphertext_bytes_written = 0; + // rekey if required + if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) != + GRPC_STATUS_OK) { + return GRPC_STATUS_INTERNAL; + } + // mask nonce if required + const uint8_t* nonce_aead = nonce; + uint8_t nonce_masked[kAesGcmNonceLength]; + if (aes_gcm_crypter->rekey_data != nullptr) { + aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask, + nonce); + nonce_aead = nonce_masked; + } + // init openssl context + if (!EVP_EncryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr, + nonce_aead)) { + aes_gcm_format_errors("Initializing nonce failed", error_details); + return GRPC_STATUS_INTERNAL; + } + // process aad + size_t i; + for (i = 0; i < aad_vec_length; i++) { + const uint8_t* aad = static_cast(aad_vec[i].iov_base); + size_t aad_length = aad_vec[i].iov_len; + if (aad_length == 0) { + continue; + } + size_t aad_bytes_read = 0; + if (aad == nullptr) { + aes_gcm_format_errors("aad is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, nullptr, + reinterpret_cast(&aad_bytes_read), aad, + static_cast(aad_length)) || + aad_bytes_read != aad_length) { + aes_gcm_format_errors("Setting authenticated associated data failed", + error_details); + return GRPC_STATUS_INTERNAL; + } + } + uint8_t* ciphertext = static_cast(ciphertext_vec.iov_base); + size_t ciphertext_length = ciphertext_vec.iov_len; + if (ciphertext == nullptr) { + aes_gcm_format_errors("ciphertext is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + // process plaintext + for (i = 0; i < plaintext_vec_length; i++) { + const uint8_t* plaintext = static_cast(plaintext_vec[i].iov_base); + size_t plaintext_length = plaintext_vec[i].iov_len; + if (plaintext == nullptr) { + if (plaintext_length == 0) { + continue; + } + aes_gcm_format_errors("plaintext is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (ciphertext_length < plaintext_length) { + aes_gcm_format_errors( + "ciphertext is not large enough to hold the result.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + int bytes_written = 0; + int bytes_to_write = static_cast(plaintext_length); + if (!EVP_EncryptUpdate(aes_gcm_crypter->ctx, ciphertext, &bytes_written, + plaintext, bytes_to_write)) { + aes_gcm_format_errors("Encrypting plaintext failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written > bytes_to_write) { + aes_gcm_format_errors("More bytes written than expected.", error_details); + return GRPC_STATUS_INTERNAL; + } + ciphertext += bytes_written; + ciphertext_length -= bytes_written; + } + int bytes_written_temp = 0; + if (!EVP_EncryptFinal_ex(aes_gcm_crypter->ctx, nullptr, + &bytes_written_temp)) { + aes_gcm_format_errors("Finalizing encryption failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written_temp != 0) { + aes_gcm_format_errors("Openssl wrote some unexpected bytes.", + error_details); + return GRPC_STATUS_INTERNAL; + } + if (ciphertext_length < kAesGcmTagLength) { + aes_gcm_format_errors("ciphertext is too small to hold a tag.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + + if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_GET_TAG, + kAesGcmTagLength, ciphertext)) { + aes_gcm_format_errors("Writing tag failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + ciphertext += kAesGcmTagLength; + ciphertext_length -= kAesGcmTagLength; + *ciphertext_bytes_written = ciphertext_vec.iov_len - ciphertext_length; + return GRPC_STATUS_OK; +} + +static grpc_status_code gsec_aes_gcm_aead_crypter_decrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details) { + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast( + const_cast(crypter)); + if (nonce == nullptr) { + aes_gcm_format_errors("Nonce buffer is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (kAesGcmNonceLength != nonce_length) { + aes_gcm_format_errors("Nonce buffer has the wrong length.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (aad_vec_length > 0 && aad_vec == nullptr) { + aes_gcm_format_errors("Non-zero aad_vec_length but aad_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (ciphertext_vec_length > 0 && ciphertext_vec == nullptr) { + aes_gcm_format_errors( + "Non-zero plaintext_vec_length but plaintext_vec is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + // Compute the total length so we can ensure we don't pass the tag into + // EVP_decrypt. + size_t total_ciphertext_length = 0; + size_t i; + for (i = 0; i < ciphertext_vec_length; i++) { + total_ciphertext_length += ciphertext_vec[i].iov_len; + } + if (total_ciphertext_length < kAesGcmTagLength) { + aes_gcm_format_errors("ciphertext is too small to hold a tag.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (plaintext_bytes_written == nullptr) { + aes_gcm_format_errors("bytes_written is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *plaintext_bytes_written = 0; + // rekey if required + if (aes_gcm_rekey_if_required(aes_gcm_crypter, nonce, error_details) != + GRPC_STATUS_OK) { + aes_gcm_format_errors("Rekeying failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + // mask nonce if required + const uint8_t* nonce_aead = nonce; + uint8_t nonce_masked[kAesGcmNonceLength]; + if (aes_gcm_crypter->rekey_data != nullptr) { + aes_gcm_mask_nonce(nonce_masked, aes_gcm_crypter->rekey_data->nonce_mask, + nonce); + nonce_aead = nonce_masked; + } + // init openssl context + if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, nullptr, nullptr, nullptr, + nonce_aead)) { + aes_gcm_format_errors("Initializing nonce failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + // process aad + for (i = 0; i < aad_vec_length; i++) { + const uint8_t* aad = static_cast(aad_vec[i].iov_base); + size_t aad_length = aad_vec[i].iov_len; + if (aad_length == 0) { + continue; + } + size_t aad_bytes_read = 0; + if (aad == nullptr) { + aes_gcm_format_errors("aad is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, nullptr, + reinterpret_cast(&aad_bytes_read), aad, + static_cast(aad_length)) || + aad_bytes_read != aad_length) { + aes_gcm_format_errors("Setting authenticated associated data failed.", + error_details); + return GRPC_STATUS_INTERNAL; + } + } + // process ciphertext + uint8_t* plaintext = static_cast(plaintext_vec.iov_base); + size_t plaintext_length = plaintext_vec.iov_len; + if (plaintext_length > 0 && plaintext == nullptr) { + aes_gcm_format_errors( + "plaintext is nullptr, but plaintext_length is positive.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + const uint8_t* ciphertext = nullptr; + size_t ciphertext_length = 0; + for (i = 0; + i < ciphertext_vec_length && total_ciphertext_length > kAesGcmTagLength; + i++) { + ciphertext = static_cast(ciphertext_vec[i].iov_base); + ciphertext_length = ciphertext_vec[i].iov_len; + if (ciphertext == nullptr) { + if (ciphertext_length == 0) { + continue; + } + aes_gcm_format_errors("ciphertext is nullptr.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INVALID_ARGUMENT; + } + size_t bytes_written = 0; + size_t bytes_to_write = ciphertext_length; + // Don't include the tag + if (bytes_to_write > total_ciphertext_length - kAesGcmTagLength) { + bytes_to_write = total_ciphertext_length - kAesGcmTagLength; + } + if (plaintext_length < bytes_to_write) { + aes_gcm_format_errors( + "Not enough plaintext buffer to hold encrypted ciphertext.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!EVP_DecryptUpdate(aes_gcm_crypter->ctx, plaintext, + reinterpret_cast(&bytes_written), ciphertext, + static_cast(bytes_to_write))) { + aes_gcm_format_errors("Decrypting ciphertext failed.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written > ciphertext_length) { + aes_gcm_format_errors("More bytes written than expected.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + ciphertext += bytes_written; + ciphertext_length -= bytes_written; + total_ciphertext_length -= bytes_written; + plaintext += bytes_written; + plaintext_length -= bytes_written; + } + if (total_ciphertext_length > kAesGcmTagLength) { + aes_gcm_format_errors( + "Not enough plaintext buffer to hold encrypted ciphertext.", + error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INVALID_ARGUMENT; + } + uint8_t tag[kAesGcmTagLength]; + uint8_t* tag_tmp = tag; + if (ciphertext_length > 0) { + memcpy(tag_tmp, ciphertext, ciphertext_length); + tag_tmp += ciphertext_length; + total_ciphertext_length -= ciphertext_length; + } + for (; i < ciphertext_vec_length; i++) { + ciphertext = static_cast(ciphertext_vec[i].iov_base); + ciphertext_length = ciphertext_vec[i].iov_len; + if (ciphertext == nullptr) { + if (ciphertext_length == 0) { + continue; + } + aes_gcm_format_errors("ciphertext is nullptr.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INVALID_ARGUMENT; + } + memcpy(tag_tmp, ciphertext, ciphertext_length); + tag_tmp += ciphertext_length; + total_ciphertext_length -= ciphertext_length; + } + if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_TAG, + kAesGcmTagLength, reinterpret_cast(tag))) { + aes_gcm_format_errors("Setting tag failed.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + int bytes_written_temp = 0; + if (!EVP_DecryptFinal_ex(aes_gcm_crypter->ctx, nullptr, + &bytes_written_temp)) { + aes_gcm_format_errors("Checking tag failed.", error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (bytes_written_temp != 0) { + aes_gcm_format_errors("Openssl wrote some unexpected bytes.", + error_details); + memset(plaintext_vec.iov_base, 0x00, plaintext_vec.iov_len); + return GRPC_STATUS_INTERNAL; + } + *plaintext_bytes_written = plaintext_vec.iov_len - plaintext_length; + return GRPC_STATUS_OK; +} + +static void gsec_aes_gcm_aead_crypter_destroy(gsec_aead_crypter* crypter) { + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + reinterpret_cast( + const_cast(crypter)); + gpr_free(aes_gcm_crypter->key); + gpr_free(aes_gcm_crypter->rekey_data); + EVP_CIPHER_CTX_free(aes_gcm_crypter->ctx); +} + +static const gsec_aead_crypter_vtable vtable = { + gsec_aes_gcm_aead_crypter_encrypt_iovec, + gsec_aes_gcm_aead_crypter_decrypt_iovec, + gsec_aes_gcm_aead_crypter_max_ciphertext_and_tag_length, + gsec_aes_gcm_aead_crypter_max_plaintext_length, + gsec_aes_gcm_aead_crypter_nonce_length, + gsec_aes_gcm_aead_crypter_key_length, + gsec_aes_gcm_aead_crypter_tag_length, + gsec_aes_gcm_aead_crypter_destroy}; + +static grpc_status_code aes_gcm_new_evp_cipher_ctx( + gsec_aes_gcm_aead_crypter* aes_gcm_crypter, char** error_details) { + const EVP_CIPHER* cipher = nullptr; + bool is_rekey = aes_gcm_crypter->rekey_data != nullptr; + switch (is_rekey ? kRekeyAeadKeyLen : aes_gcm_crypter->key_length) { + case kAes128GcmKeyLength: + cipher = EVP_aes_128_gcm(); + break; + case kAes256GcmKeyLength: + cipher = EVP_aes_256_gcm(); + break; + } + const uint8_t* aead_key = aes_gcm_crypter->key; + uint8_t aead_key_rekey[kRekeyAeadKeyLen]; + if (is_rekey) { + if (aes_gcm_derive_aead_key(aead_key_rekey, aes_gcm_crypter->key, + aes_gcm_crypter->rekey_data->kdf_counter) != + GRPC_STATUS_OK) { + aes_gcm_format_errors("Deriving key failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + aead_key = aead_key_rekey; + } + if (!EVP_DecryptInit_ex(aes_gcm_crypter->ctx, cipher, nullptr, aead_key, + nullptr)) { + aes_gcm_format_errors("Setting key failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (!EVP_CIPHER_CTX_ctrl(aes_gcm_crypter->ctx, EVP_CTRL_GCM_SET_IVLEN, + static_cast(aes_gcm_crypter->nonce_length), + nullptr)) { + aes_gcm_format_errors("Setting nonce length failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key, + size_t key_length, + size_t nonce_length, + size_t tag_length, bool rekey, + gsec_aead_crypter** crypter, + char** error_details) { + if (key == nullptr) { + aes_gcm_format_errors("key is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (crypter == nullptr) { + aes_gcm_format_errors("crypter is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + *crypter = nullptr; + if ((rekey && key_length != kAes128GcmRekeyKeyLength) || + (!rekey && key_length != kAes128GcmKeyLength && + key_length != kAes256GcmKeyLength) || + (tag_length != kAesGcmTagLength) || + (nonce_length != kAesGcmNonceLength)) { + aes_gcm_format_errors( + "Invalid key and/or nonce and/or tag length are provided at AEAD " + "crypter instance construction time.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + gsec_aes_gcm_aead_crypter* aes_gcm_crypter = + static_cast( + gpr_malloc(sizeof(gsec_aes_gcm_aead_crypter))); + aes_gcm_crypter->crypter.vtable = &vtable; + aes_gcm_crypter->nonce_length = nonce_length; + aes_gcm_crypter->tag_length = tag_length; + if (rekey) { + aes_gcm_crypter->key_length = kKdfKeyLen; + aes_gcm_crypter->rekey_data = static_cast( + gpr_malloc(sizeof(gsec_aes_gcm_aead_rekey_data))); + memcpy(aes_gcm_crypter->rekey_data->nonce_mask, key + kKdfKeyLen, + kAesGcmNonceLength); + // Set kdf_counter to all-zero for initial key derivation. + memset(aes_gcm_crypter->rekey_data->kdf_counter, 0, kKdfCounterLen); + } else { + aes_gcm_crypter->key_length = key_length; + aes_gcm_crypter->rekey_data = nullptr; + } + aes_gcm_crypter->key = + static_cast(gpr_malloc(aes_gcm_crypter->key_length)); + memcpy(aes_gcm_crypter->key, key, aes_gcm_crypter->key_length); + aes_gcm_crypter->ctx = EVP_CIPHER_CTX_new(); + grpc_status_code status = + aes_gcm_new_evp_cipher_ctx(aes_gcm_crypter, error_details); + if (status != GRPC_STATUS_OK) { + gsec_aes_gcm_aead_crypter_destroy(&aes_gcm_crypter->crypter); + gpr_free(aes_gcm_crypter); + return status; + } + *crypter = &aes_gcm_crypter->crypter; + return GRPC_STATUS_OK; +} diff --git a/third_party/grpc/src/core/tsi/alts/crypt/gsec.cc b/third_party/grpc/src/core/tsi/alts/crypt/gsec.cc new file mode 100644 index 00000000000000..6236591a97c968 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/crypt/gsec.cc @@ -0,0 +1,189 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/crypt/gsec.h" + +#include +#include + +#include + +static const char vtable_error_msg[] = + "crypter or crypter->vtable has not been initialized properly"; + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code gsec_aead_crypter_encrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* plaintext, + size_t plaintext_length, uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, size_t* bytes_written, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->encrypt_iovec != nullptr) { + struct iovec aad_vec = {(void*)aad, aad_length}; + struct iovec plaintext_vec = {(void*)plaintext, plaintext_length}; + struct iovec ciphertext_vec = {ciphertext_and_tag, + ciphertext_and_tag_length}; + return crypter->vtable->encrypt_iovec( + crypter, nonce, nonce_length, &aad_vec, 1, &plaintext_vec, 1, + ciphertext_vec, bytes_written, error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_encrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->encrypt_iovec != nullptr) { + return crypter->vtable->encrypt_iovec( + crypter, nonce, nonce_length, aad_vec, aad_vec_length, plaintext_vec, + plaintext_vec_length, ciphertext_vec, ciphertext_bytes_written, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_decrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, uint8_t* plaintext, + size_t plaintext_length, size_t* bytes_written, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->decrypt_iovec != nullptr) { + struct iovec aad_vec = {(void*)aad, aad_length}; + struct iovec ciphertext_vec = {(void*)ciphertext_and_tag, + ciphertext_and_tag_length}; + struct iovec plaintext_vec = {plaintext, plaintext_length}; + return crypter->vtable->decrypt_iovec( + crypter, nonce, nonce_length, &aad_vec, 1, &ciphertext_vec, 1, + plaintext_vec, bytes_written, error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_decrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->encrypt_iovec != nullptr) { + return crypter->vtable->decrypt_iovec( + crypter, nonce, nonce_length, aad_vec, aad_vec_length, ciphertext_vec, + ciphertext_vec_length, plaintext_vec, plaintext_bytes_written, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length_to_return, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->max_ciphertext_and_tag_length != nullptr) { + return crypter->vtable->max_ciphertext_and_tag_length( + crypter, plaintext_length, max_ciphertext_and_tag_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_max_plaintext_length( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length_to_return, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->max_plaintext_length != nullptr) { + return crypter->vtable->max_plaintext_length( + crypter, ciphertext_and_tag_length, max_plaintext_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_nonce_length( + const gsec_aead_crypter* crypter, size_t* nonce_length_to_return, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->nonce_length != nullptr) { + return crypter->vtable->nonce_length(crypter, nonce_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter, + size_t* key_length_to_return, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->key_length != nullptr) { + return crypter->vtable->key_length(crypter, key_length_to_return, + error_details); + } + /* An error occurred */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter, + size_t* tag_length_to_return, + char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->tag_length != nullptr) { + return crypter->vtable->tag_length(crypter, tag_length_to_return, + error_details); + } + /* An error occurred. */ + maybe_copy_error_msg(vtable_error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter) { + if (crypter != nullptr) { + if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) { + crypter->vtable->destruct(crypter); + } + gpr_free(crypter); + } +} diff --git a/third_party/grpc/src/core/tsi/alts/crypt/gsec.h b/third_party/grpc/src/core/tsi/alts/crypt/gsec.h new file mode 100644 index 00000000000000..4d65caa9447479 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/crypt/gsec.h @@ -0,0 +1,454 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H +#define GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H + +#include + +#include +#include +#include + +#include + +struct iovec { + void* iov_base; + size_t iov_len; +}; + +/** + * A gsec interface for AEAD encryption schemes. The API is thread-compatible. + * Each implementation of this interface should specify supported values for + * key, nonce, and tag lengths. + */ + +/* Key, nonce, and tag length in bytes */ +const size_t kAesGcmNonceLength = 12; +const size_t kAesGcmTagLength = 16; +const size_t kAes128GcmKeyLength = 16; +const size_t kAes256GcmKeyLength = 32; + +// The first 32 bytes are used as a KDF key and the remaining 12 bytes are used +// to mask the nonce. +const size_t kAes128GcmRekeyKeyLength = 44; + +typedef struct gsec_aead_crypter gsec_aead_crypter; + +/** + * The gsec_aead_crypter is an API for different AEAD implementations such as + * AES_GCM. It encapsulates all AEAD-related operations in the format of + * V-table that stores pointers to functions implementing those operations. + * It also provides helper functions to wrap each of those function pointers. + * + * A typical usage of this object would be: + * + *------------------------------------------------------------------------------ + * // Declare a gsec_aead_crypter object, and create and assign an instance + * // of specific AEAD implementation e.g., AES_GCM to it. We assume both + * // key and nonce contain cryptographically secure random bytes, and the key + * // can be derived from an upper-layer application. + * gsec_aead_crypter* crypter; + * char* error_in_creation; + * // User can populate the message with any 100 bytes data. + * uint8_t* message = gpr_malloc(100); + * grpc_status_code creation_status = gsec_aes_gcm_aead_crypter_create(key, + * kAes128GcmKeyLength, + * kAesGcmNonceLength, + * kAesGcmTagLength, + * &crypter, + * false, + * 0 + * &error_in_creation); + * + * if (creation_status == GRPC_STATUS_OK) { + * // Allocate a correct amount of memory to hold a ciphertext. + * size_t clength = 0; + * gsec_aead_crypter_max_ciphertext_and_tag_length(crypter, 100, &clength, + * nullptr); + * uint8_t* ciphertext = gpr_malloc(clength); + * + * // Perform encryption + * size_t num_encrypted_bytes = 0; + * char* error_in_encryption = nullptr; + * grpc_status_code status = gsec_aead_crypter_encrypt(crypter, nonce, + * kAesGcmNonceLength, + * nullptr, 0, message, + * 100, ciphertext, + * clength, + * &num_encrypted_bytes, + * &error_in_encryption); + * if (status == GRPC_STATUS_OK) { + * // Allocate a correct amount of memory to hold a plaintext. + * size_t plength = 0; + * gsec_aead_crypter_max_plaintext_length(crypter, num_encrypted_bytes, + * &plength, nullptr); + * uint8_t* plaintext = gpr_malloc(plength); + * + * // Perform decryption. + * size_t num_decrypted_bytes = 0; + * char* error_in_decryption = nullptr; + * status = gsec_aead_crypter_decrypt(crypter, nonce, + * kAesGcmNonceLength, nullptr, 0, + * ciphertext, num_encrypted_bytes, + * plaintext, plength, + * &num_decrypted_bytes, + * &error_in_decryption); + * if (status != GRPC_STATUS_OK) { + * fprintf(stderr, "AEAD decrypt operation failed with error code:" + * "%d, message: %s\n", status, error_in_decryption); + * } + * ... + * gpr_free(plaintext); + * gpr_free(error_in_decryption); + * } else { + * fprintf(stderr, "AEAD encrypt operation failed with error code:" + * "%d, message: %s\n", status, error_in_encryption); + * } + * ... + * gpr_free(ciphertext); + * gpr_free(error_in_encryption); + * } else { + * fprintf(stderr, "Creation of AEAD crypter instance failed with error code:" + * "%d, message: %s\n", creation_status, error_in_creation); + * } + * + * // Destruct AEAD crypter instance. + * if (creation_status == GRPC_STATUS_OK) { + * gsec_aead_crypter_destroy(crypter); + * } + * gpr_free(error_in_creation); + * gpr_free(message); + * ----------------------------------------------------------------------------- + */ + +/* V-table for gsec AEAD operations */ +typedef struct gsec_aead_crypter_vtable { + grpc_status_code (*encrypt_iovec)( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details); + grpc_status_code (*decrypt_iovec)( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details); + grpc_status_code (*max_ciphertext_and_tag_length)( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length_to_return, char** error_details); + grpc_status_code (*max_plaintext_length)( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length_to_return, char** error_details); + grpc_status_code (*nonce_length)(const gsec_aead_crypter* crypter, + size_t* nonce_length_to_return, + char** error_details); + grpc_status_code (*key_length)(const gsec_aead_crypter* crypter, + size_t* key_length_to_return, + char** error_details); + grpc_status_code (*tag_length)(const gsec_aead_crypter* crypter, + size_t* tag_length_to_return, + char** error_details); + void (*destruct)(gsec_aead_crypter* crypter); +} gsec_aead_crypter_vtable; + +/* Main struct for gsec interface */ +struct gsec_aead_crypter { + const struct gsec_aead_crypter_vtable* vtable; +}; + +/** + * This method performs an AEAD encrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad: buffer containing data that needs to be authenticated but not + * encrypted with its size equal to aad_length. + * - aad_length: size of aad buffer, which should be zero if the buffer is + * nullptr. + * - plaintext: buffer containing data that needs to be both encrypted and + * authenticated with its size equal to plaintext_length. + * - plaintext_length: size of plaintext buffer, which should be zero if + * plaintext is nullptr. + * - ciphertext_and_tag: buffer that will contain ciphertext and tags the method + * produced. The buffer should not overlap the plaintext buffer, and pointers + * to those buffers should not be equal. Also if the ciphertext+tag buffer is + * nullptr, the plaintext_length should be zero. + * - ciphertext_and_tag_length: size of ciphertext+tag buffer, which should be + * at least as long as the one returned from method + * gsec_aead_crypter_max_ciphertext_and_tag_length. + * - bytes_written: the actual number of bytes written to the ciphertext+tag + * buffer. If bytes_written is nullptr, the plaintext_length should be zero. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + * + */ +grpc_status_code gsec_aead_crypter_encrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* plaintext, + size_t plaintext_length, uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, size_t* bytes_written, + char** error_details); + +/** + * This method performs an AEAD encrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad_vec: an iovec array containing data that needs to be authenticated but + * not encrypted. + * - aad_vec_length: the array length of aad_vec. + * - plaintext_vec: an iovec array containing data that needs to be both + * encrypted and authenticated. + * - plaintext_vec_length: the array length of plaintext_vec. + * - ciphertext_vec: an iovec containing a ciphertext buffer. The buffer should + * not overlap the plaintext buffer. + * - ciphertext_bytes_written: the actual number of bytes written to + * ciphertext_vec. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of encryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + * + */ +grpc_status_code gsec_aead_crypter_encrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* plaintext_vec, size_t plaintext_vec_length, + struct iovec ciphertext_vec, size_t* ciphertext_bytes_written, + char** error_details); + +/** + * This method performs an AEAD decrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad: buffer containing data that needs to be authenticated only. + * - aad_length: size of aad buffer, which should be zero if the buffer is + * nullptr. + * - ciphertext_and_tag: buffer containing ciphertext and tag. + * - ciphertext_and_tag_length: length of ciphertext and tag. It should be zero + * if any of plaintext, ciphertext_and_tag, or bytes_written is nullptr. Also, + * ciphertext_and_tag_length should be at least as large as the tag length set + * at AEAD crypter instance construction time. + * - plaintext: buffer containing decrypted and authenticated data the method + * produced. The buffer should not overlap with the ciphertext+tag buffer, and + * pointers to those buffers should not be equal. + * - plaintext_length: size of plaintext buffer, which should be at least as + * long as the one returned from gsec_aead_crypter_max_plaintext_length + * method. + * - bytes_written: the actual number of bytes written to the plaintext + * buffer. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_decrypt( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const uint8_t* aad, size_t aad_length, const uint8_t* ciphertext_and_tag, + size_t ciphertext_and_tag_length, uint8_t* plaintext, + size_t plaintext_length, size_t* bytes_written, char** error_details); + +/** + * This method performs an AEAD decrypt operation. + * + * - crypter: AEAD crypter instance. + * - nonce: buffer containing a nonce with its size equal to nonce_length. + * - nonce_length: size of nonce buffer, and must be equal to the value returned + * from method gsec_aead_crypter_nonce_length. + * - aad_vec: an iovec array containing data that needs to be authenticated but + * not encrypted. + * - aad_vec_length: the array length of aad_vec. + * - ciphertext_vec: an iovec array containing the ciphertext and tag. + * - ciphertext_vec_length: the array length of ciphertext_vec. + * - plaintext_vec: an iovec containing a plaintext buffer. The buffer should + * not overlap the ciphertext buffer. + * - plaintext_bytes_written: the actual number of bytes written to + * plaintext_vec. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of decryption, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_decrypt_iovec( + gsec_aead_crypter* crypter, const uint8_t* nonce, size_t nonce_length, + const struct iovec* aad_vec, size_t aad_vec_length, + const struct iovec* ciphertext_vec, size_t ciphertext_vec_length, + struct iovec plaintext_vec, size_t* plaintext_bytes_written, + char** error_details); + +/** + * This method computes the size of ciphertext+tag buffer that must be passed to + * gsec_aead_crypter_encrypt function to ensure correct encryption of a + * plaintext. The actual size of ciphertext+tag written to the buffer could be + * smaller. + * + * - crypter: AEAD crypter instance. + * - plaintext_length: length of plaintext. + * - max_ciphertext_and_tag_length_to_return: the size of ciphertext+tag buffer + * the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_max_ciphertext_and_tag_length( + const gsec_aead_crypter* crypter, size_t plaintext_length, + size_t* max_ciphertext_and_tag_length_to_return, char** error_details); + +/** + * This method computes the size of plaintext buffer that must be passed to + * gsec_aead_crypter_decrypt function to ensure correct decryption of a + * ciphertext. The actual size of plaintext written to the buffer could be + * smaller. + * + * - crypter: AEAD crypter instance. + * - ciphertext_and_tag_length: length of ciphertext and tag. + * - max_plaintext_length_to_return: the size of plaintext buffer the method + * returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_max_plaintext_length( + const gsec_aead_crypter* crypter, size_t ciphertext_and_tag_length, + size_t* max_plaintext_length_to_return, char** error_details); + +/** + * This method returns a valid size of nonce array used at the construction of + * AEAD crypter instance. It is also the size that should be passed to encrypt + * and decrypt methods executed on the instance. + * + * - crypter: AEAD crypter instance. + * - nonce_length_to_return: the length of nonce array the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_nonce_length( + const gsec_aead_crypter* crypter, size_t* nonce_length_to_return, + char** error_details); + +/** + * This method returns a valid size of key array used at the construction of + * AEAD crypter instance. It is also the size that should be passed to encrypt + * and decrypt methods executed on the instance. + * + * - crypter: AEAD crypter instance. + * - key_length_to_return: the length of key array the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_key_length(const gsec_aead_crypter* crypter, + size_t* key_length_to_return, + char** error_details); +/** + * This method returns a valid size of tag array used at the construction of + * AEAD crypter instance. It is also the size that should be passed to encrypt + * and decrypt methods executed on the instance. + * + * - crypter: AEAD crypter instance. + * - tag_length_to_return: the length of tag array the method returns. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On the success of execution, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code gsec_aead_crypter_tag_length(const gsec_aead_crypter* crypter, + size_t* tag_length_to_return, + char** error_details); + +/** + * This method destroys an AEAD crypter instance by de-allocating all of its + * occupied memory. + * + * - crypter: AEAD crypter instance that needs to be destroyed. + */ +void gsec_aead_crypter_destroy(gsec_aead_crypter* crypter); + +/** + * This method creates an AEAD crypter instance of AES-GCM encryption scheme + * which supports 16 and 32 bytes long keys, 12 and 16 bytes long nonces, and + * 16 bytes long tags. It should be noted that once the lengths of key, nonce, + * and tag are determined at construction time, they cannot be modified later. + * + * - key: buffer containing a key which is binded with AEAD crypter instance. + * - key_length: length of a key in bytes, which should be 44 if rekeying is + * enabled and 16 or 32 otherwise. + * - nonce_length: length of a nonce in bytes, which should be either 12 or 16. + * - tag_length: length of a tag in bytes, which should be always 16. + * - rekey: enable nonce-based rekeying and nonce-masking. + * - crypter: address of AES_GCM crypter instance returned from the method. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of instance creation, it stores the address of instance at + * crypter. Otherwise, it returns an error status code together with its details + * specified in error_details. + */ +grpc_status_code gsec_aes_gcm_aead_crypter_create(const uint8_t* key, + size_t key_length, + size_t nonce_length, + size_t tag_length, bool rekey, + gsec_aead_crypter** crypter, + char** error_details); + +#endif /* GRPC_CORE_TSI_ALTS_CRYPT_GSEC_H */ diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.cc new file mode 100644 index 00000000000000..de163e3e08c617 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" + +#include + +#include + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code alts_counter_create(bool is_client, size_t counter_size, + size_t overflow_size, + alts_counter** crypter_counter, + char** error_details) { + /* Perform input sanity check. */ + if (counter_size == 0) { + const char error_msg[] = "counter_size is invalid."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (overflow_size == 0 || overflow_size >= counter_size) { + const char error_msg[] = "overflow_size is invalid."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (crypter_counter == nullptr) { + const char error_msg[] = "crypter_counter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + *crypter_counter = + static_cast(gpr_malloc(sizeof(**crypter_counter))); + (*crypter_counter)->size = counter_size; + (*crypter_counter)->overflow_size = overflow_size; + (*crypter_counter)->counter = + static_cast(gpr_zalloc(counter_size)); + if (is_client) { + ((*crypter_counter)->counter)[counter_size - 1] = 0x80; + } + return GRPC_STATUS_OK; +} + +grpc_status_code alts_counter_increment(alts_counter* crypter_counter, + bool* is_overflow, + char** error_details) { + /* Perform input sanity check. */ + if (crypter_counter == nullptr) { + const char error_msg[] = "crypter_counter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (is_overflow == nullptr) { + const char error_msg[] = "is_overflow is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Increment the internal counter. */ + size_t i = 0; + for (; i < crypter_counter->overflow_size; i++) { + (crypter_counter->counter)[i]++; + if ((crypter_counter->counter)[i] != 0x00) { + break; + } + } + /** + * If the lower overflow_size bytes are all zero, the counter has overflowed. + */ + if (i == crypter_counter->overflow_size) { + *is_overflow = true; + return GRPC_STATUS_FAILED_PRECONDITION; + } + *is_overflow = false; + return GRPC_STATUS_OK; +} + +size_t alts_counter_get_size(alts_counter* crypter_counter) { + if (crypter_counter == nullptr) { + return 0; + } + return crypter_counter->size; +} + +unsigned char* alts_counter_get_counter(alts_counter* crypter_counter) { + if (crypter_counter == nullptr) { + return nullptr; + } + return crypter_counter->counter; +} + +void alts_counter_destroy(alts_counter* crypter_counter) { + if (crypter_counter != nullptr) { + gpr_free(crypter_counter->counter); + gpr_free(crypter_counter); + } +} diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.h new file mode 100644 index 00000000000000..d705638fa8c3e5 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_counter.h @@ -0,0 +1,98 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H + +#include + +#include +#include + +#include + +/* Main struct for a crypter counter managed within seal/unseal operations. */ +typedef struct alts_counter { + size_t size; + size_t overflow_size; + unsigned char* counter; +} alts_counter; + +/** + * This method creates and initializes an alts_counter instance. + * + * - is_client: a flag indicating if the alts_counter instance will be used + * at client (is_client = true) or server (is_client = false) side. + * - counter_size: size of buffer holding the counter value. + * - overflow_size: overflow size in bytes. The counter instance can be used + * to produce at most 2^(overflow_size*8) frames. + * - crypter_counter: an alts_counter instance to be returned from the method. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code alts_counter_create(bool is_client, size_t counter_size, + size_t overflow_size, + alts_counter** crypter_counter, + char** error_details); + +/** + * This method increments the internal counter. + * + * - crypter_counter: an alts_counter instance. + * - is_overflow: after incrementing the internal counter, if an overflow + * occurs, is_overflow is set to true, and no further calls to + * alts_counter_increment() should be made. Otherwise, is_overflow is set to + * false. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code alts_counter_increment(alts_counter* crypter_counter, + bool* is_overflow, + char** error_details); + +/** + * This method returns the size of counter buffer. + * + * - crypter_counter: an alts_counter instance. + */ +size_t alts_counter_get_size(alts_counter* crypter_counter); + +/** + * This method returns the counter buffer. + * + * - crypter_counter: an alts_counter instance. + */ +unsigned char* alts_counter_get_counter(alts_counter* crypter_counter); + +/** + * This method de-allocates all memory allocated to an alts_coutner instance. + * - crypter_counter: an alts_counter instance. + */ +void alts_counter_destroy(alts_counter* crypter_counter); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_COUNTER_H */ diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.cc new file mode 100644 index 00000000000000..56f051218637cd --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.cc @@ -0,0 +1,66 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" + +#include + +#include + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code alts_crypter_process_in_place( + alts_crypter* crypter, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->process_in_place != nullptr) { + return crypter->vtable->process_in_place(crypter, data, data_allocated_size, + data_size, output_size, + error_details); + } + /* An error occurred. */ + const char error_msg[] = + "crypter or crypter->vtable has not been initialized properly."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; +} + +size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter) { + if (crypter != nullptr && crypter->vtable != nullptr && + crypter->vtable->num_overhead_bytes != nullptr) { + return crypter->vtable->num_overhead_bytes(crypter); + } + /* An error occurred. */ + return 0; +} + +void alts_crypter_destroy(alts_crypter* crypter) { + if (crypter != nullptr) { + if (crypter->vtable != nullptr && crypter->vtable->destruct != nullptr) { + crypter->vtable->destruct(crypter); + } + gpr_free(crypter); + } +} diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.h new file mode 100644 index 00000000000000..3140778f4fc594 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_crypter.h @@ -0,0 +1,255 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H + +#include + +#include +#include + +#include + +#include "src/core/tsi/alts/crypt/gsec.h" + +/** + * An alts_crypter interface for an ALTS record protocol providing + * seal/unseal functionality. The interface is thread-compatible. + */ + +typedef struct alts_crypter alts_crypter; + +/** + * A typical usage of the interface would be + *------------------------------------------------------------------------------ + * // Perform a seal operation. We assume the gsec_aead_crypter instance - + * // client_aead_crypter is created beforehand with a 16-byte key and 12-byte + * // nonce length. + * + * alts_crypter* client = nullptr; + * char* client_error_in_creation = nullptr; + * unsigned char* data = nullptr; + * grpc_status_code client_status = + * alts_seal_crypter_create(client_aead_crypter, 1, 5, &client, + * &client_error_in_creation); + * if (client_status == GRPC_STATUS_OK) { + * size_t data_size = 100; + * size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(client); + * size_t data_allocated_size = data_size + num_overhead_bytes; + * data = gpr_malloc(data_allocated_size); + * char* client_error_in_seal = nullptr; + * // Client performs a seal operation. + * client_status = alts_crypter_process_in_place(client, data, + * data_allocated_size, + * &data_size, + * &client_error_in_seal); + * if (client_status != GRPC_STATUS_OK) { + * fprintf(stderr, "seal operation failed with error code:" + * "%d, message: %s\n", client_status, + * client_error_in_seal); + * } + * gpr_free(client_error_in_seal); + * } else { + * fprintf(stderr, "alts_crypter instance creation failed with error" + * "code: %d, message: %s\n", client_status, + * client_error_in_creation); + * } + * + * ... + * + * gpr_free(client_error_in_creation); + * alts_crypter_destroy(client); + * + * ... + * + * // Perform an unseal operation. We assume the gsec_aead_crypter instance - + * // server_aead_crypter is created beforehand with a 16-byte key and 12-byte + * // nonce length. The key used in the creation of gsec_aead_crypter instances + * // at server and client sides should be identical. + * + * alts_crypter* server = nullptr; + * char* server_error_in_creation = nullptr; + * grpc_status_code server_status = + * alts_unseal_crypter_create(server_aead_crypter, 0, 5, &server, + * &server_error_in_creation); + * if (server_status == GRPC_STATUS_OK) { + * size_t num_overhead_bytes = alts_crypter_num_overhead_bytes(server); + * size_t data_size = 100 + num_overhead_bytes; + * size_t data_allocated_size = data_size; + * char* server_error_in_unseal = nullptr; + * // Server performs an unseal operation. + * server_status = alts_crypter_process_in_place(server, data, + * data_allocated_size, + * &data_size, + * &server_error_in_unseal); + * if (server_status != GRPC_STATUS_OK) { + * fprintf(stderr, "unseal operation failed with error code:" + * "%d, message: %s\n", server_status, + * server_error_in_unseal); + * } + * gpr_free(server_error_in_unseal); + * } else { + * fprintf(stderr, "alts_crypter instance creation failed with error" + * "code: %d, message: %s\n", server_status, + * server_error_in_creation); + * } + * + * ... + * + * gpr_free(data); + * gpr_free(server_error_in_creation); + * alts_crypter_destroy(server); + * + * ... + *------------------------------------------------------------------------------ + */ + +/* V-table for alts_crypter operations */ +typedef struct alts_crypter_vtable { + size_t (*num_overhead_bytes)(const alts_crypter* crypter); + grpc_status_code (*process_in_place)(alts_crypter* crypter, + unsigned char* data, + size_t data_allocated_size, + size_t data_size, size_t* output_size, + char** error_details); + void (*destruct)(alts_crypter* crypter); +} alts_crypter_vtable; + +/* Main struct for alts_crypter interface */ +struct alts_crypter { + const alts_crypter_vtable* vtable; +}; + +/** + * This method gets the number of overhead bytes needed for sealing data that + * is the difference in size between the protected and raw data. The counter + * value used in a seal or unseal operation is locally maintained (not sent or + * received from the other peer) and therefore, will not be counted as part of + * overhead bytes. + * + * - crypter: an alts_crypter instance. + * + * On success, the method returns the number of overhead bytes. Otherwise, it + * returns zero. + * + */ +size_t alts_crypter_num_overhead_bytes(const alts_crypter* crypter); + +/** + * This method performs either a seal or an unseal operation depending on the + * alts_crypter instance - crypter passed to the method. If the crypter is + * an instance implementing a seal operation, the method will perform a seal + * operation. That is, it seals raw data and stores the result in-place, and the + * memory allocated for data must be at least data_length + + * alts_crypter_num_overhead_bytes(). If the crypter is an instance + * implementing an unseal operation, the method will perform an unseal + * operation. That is, it unseals protected data and stores the result in-place. + * The size of unsealed data will be data_length - + * alts_crypter_num_overhead_bytes(). Integrity tag will be verified during + * the unseal operation, and if verification fails, the data will be wiped. + * The counters used in both seal and unseal operations are managed internally. + * + * - crypter: an alts_crypter instance. + * - data: if the method performs a seal operation, the data represents raw data + * that needs to be sealed. It also plays the role of buffer to hold the + * protected data as a result of seal. If the method performs an unseal + * operation, the data represents protected data that needs to be unsealed. It + * also plays the role of buffer to hold raw data as a result of unseal. + * - data_allocated_size: the size of data buffer. The parameter is used to + * check whether the result of either seal or unseal can be safely written to + * the data buffer. + * - data_size: if the method performs a seal operation, data_size + * represents the size of raw data that needs to be sealed, and if the method + * performs an unseal operation, data_size represents the size of protected + * data that needs to be unsealed. + * - output_size: size of data written to the data buffer after a seal or an + * unseal operation. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code alts_crypter_process_in_place( + alts_crypter* crypter, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details); + +/** + * This method creates an alts_crypter instance to be used to perform a seal + * operation, given a gsec_aead_crypter instance and a flag indicating if the + * created instance will be used at the client or server side. It takes + * ownership of gsec_aead_crypter instance. + * + * - gc: a gsec_aead_crypter instance used to perform AEAD encryption. + * - is_client: a flag indicating if the alts_crypter instance will be + * used at the client (is_client = true) or server (is_client = + * false) side. + * - overflow_size: overflow size of counter in bytes. + * - crypter: an alts_crypter instance to be returned from the method. + * - error_details: a buffer containing an error message if the method does + * not function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of creation, the method returns GRPC_STATUS_OK. + * Otherwise, it returns an error status code along with its details specified + * in error_details (if error_details is not nullptr). + */ +grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details); + +/** + * This method creates an alts_crypter instance used to perform an unseal + * operation, given a gsec_aead_crypter instance and a flag indicating if the + * created instance will be used at the client or server side. It takes + * ownership of gsec_aead_crypter instance. + * + * - gc: a gsec_aead_crypter instance used to perform AEAD decryption. + * - is_client: a flag indicating if the alts_crypter instance will be + * used at the client (is_client = true) or server (is_client = + * false) side. + * - overflow_size: overflow size of counter in bytes. + * - crypter: an alts_crypter instance to be returned from the method. + * - error_details: a buffer containing an error message if the method does + * not function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of creation, the method returns GRPC_STATUS_OK. + * Otherwise, it returns an error status code along with its details specified + * in error_details (if error_details is not nullptr). + */ +grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc, + bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details); + +/** + * This method destroys an alts_crypter instance by de-allocating all of its + * occupied memory. A gsec_aead_crypter instance passed in at alts_crypter + * instance creation time will be destroyed in this method. + * + * - crypter: an alts_crypter instance. + */ +void alts_crypter_destroy(alts_crypter* crypter); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_CRYPTER_H */ diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.cc new file mode 100644 index 00000000000000..bfa0b7a720f668 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.cc @@ -0,0 +1,407 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h" + +#include +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" +#include "src/core/tsi/alts/frame_protector/frame_handler.h" +#include "src/core/tsi/transport_security.h" + +constexpr size_t kMinFrameLength = 1024; +constexpr size_t kDefaultFrameLength = 16 * 1024; +constexpr size_t kMaxFrameLength = 1024 * 1024; + +// Limit k on number of frames such that at most 2^(8 * k) frames can be sent. +constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8; +constexpr size_t kAltsRecordProtocolFrameLimit = 5; + +/* Main struct for alts_frame_protector. */ +struct alts_frame_protector { + tsi_frame_protector base; + alts_crypter* seal_crypter; + alts_crypter* unseal_crypter; + alts_frame_writer* writer; + alts_frame_reader* reader; + unsigned char* in_place_protect_buffer; + unsigned char* in_place_unprotect_buffer; + size_t in_place_protect_bytes_buffered; + size_t in_place_unprotect_bytes_processed; + size_t max_protected_frame_size; + size_t max_unprotected_frame_size; + size_t overhead_length; + size_t counter_overflow; +}; + +static tsi_result seal(alts_frame_protector* impl) { + char* error_details = nullptr; + size_t output_size = 0; + grpc_status_code status = alts_crypter_process_in_place( + impl->seal_crypter, impl->in_place_protect_buffer, + impl->max_protected_frame_size, impl->in_place_protect_bytes_buffered, + &output_size, &error_details); + impl->in_place_protect_bytes_buffered = output_size; + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "%s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + return TSI_OK; +} + +static size_t max_encrypted_payload_bytes(alts_frame_protector* impl) { + return impl->max_protected_frame_size - kFrameHeaderSize; +} + +static tsi_result alts_protect_flush(tsi_frame_protector* self, + unsigned char* protected_output_frames, + size_t* protected_output_frames_size, + size_t* still_pending_size) { + if (self == nullptr || protected_output_frames == nullptr || + protected_output_frames_size == nullptr || + still_pending_size == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect_flush()."); + return TSI_INVALID_ARGUMENT; + } + alts_frame_protector* impl = reinterpret_cast(self); + /** + * If there's nothing to flush (i.e., in_place_protect_buffer is empty), + * we're done. + */ + if (impl->in_place_protect_bytes_buffered == 0) { + *protected_output_frames_size = 0; + *still_pending_size = 0; + return TSI_OK; + } + /** + * If a new frame can start being processed, we encrypt the payload and reset + * the frame writer to point to in_place_protect_buffer that holds the newly + * sealed frame. + */ + if (alts_is_frame_writer_done(impl->writer)) { + tsi_result result = seal(impl); + if (result != TSI_OK) { + return result; + } + if (!alts_reset_frame_writer(impl->writer, impl->in_place_protect_buffer, + impl->in_place_protect_bytes_buffered)) { + gpr_log(GPR_ERROR, "Couldn't reset frame writer."); + return TSI_INTERNAL_ERROR; + } + } + /** + * Write the sealed frame as much as possible to protected_output_frames. It's + * possible a frame will not be written out completely by a single flush + * (i.e., still_pending_size != 0), in which case the flush should be called + * iteratively until a complete frame has been written out. + */ + size_t written_frame_bytes = *protected_output_frames_size; + if (!alts_write_frame_bytes(impl->writer, protected_output_frames, + &written_frame_bytes)) { + gpr_log(GPR_ERROR, "Couldn't write frame bytes."); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = written_frame_bytes; + *still_pending_size = alts_get_num_writer_bytes_remaining(impl->writer); + /** + * If the current frame has been finished processing (i.e., sealed and written + * out completely), we empty in_place_protect_buffer. + */ + if (alts_is_frame_writer_done(impl->writer)) { + impl->in_place_protect_bytes_buffered = 0; + } + return TSI_OK; +} + +static tsi_result alts_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 == nullptr || unprotected_bytes == nullptr || + unprotected_bytes_size == nullptr || protected_output_frames == nullptr || + protected_output_frames_size == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_protect()."); + return TSI_INVALID_ARGUMENT; + } + alts_frame_protector* impl = reinterpret_cast(self); + + /** + * If more payload can be buffered, we buffer it as much as possible to + * in_place_protect_buffer. + */ + if (impl->in_place_protect_bytes_buffered + impl->overhead_length < + max_encrypted_payload_bytes(impl)) { + size_t bytes_to_buffer = GPR_MIN(*unprotected_bytes_size, + max_encrypted_payload_bytes(impl) - + impl->in_place_protect_bytes_buffered - + impl->overhead_length); + *unprotected_bytes_size = bytes_to_buffer; + if (bytes_to_buffer > 0) { + memcpy( + impl->in_place_protect_buffer + impl->in_place_protect_bytes_buffered, + unprotected_bytes, bytes_to_buffer); + impl->in_place_protect_bytes_buffered += bytes_to_buffer; + } + } else { + *unprotected_bytes_size = 0; + } + /** + * If a full frame has been buffered, we output it. If the first condition + * holds, then there exists an unencrypted full frame. If the second + * condition holds, then there exists a full frame that has already been + * encrypted. + */ + if (max_encrypted_payload_bytes(impl) == + impl->in_place_protect_bytes_buffered + impl->overhead_length || + max_encrypted_payload_bytes(impl) == + impl->in_place_protect_bytes_buffered) { + size_t still_pending_size = 0; + return alts_protect_flush(self, protected_output_frames, + protected_output_frames_size, + &still_pending_size); + } else { + *protected_output_frames_size = 0; + return TSI_OK; + } +} + +static tsi_result unseal(alts_frame_protector* impl) { + char* error_details = nullptr; + size_t output_size = 0; + grpc_status_code status = alts_crypter_process_in_place( + impl->unseal_crypter, impl->in_place_unprotect_buffer, + impl->max_unprotected_frame_size, + alts_get_output_bytes_read(impl->reader), &output_size, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "%s", error_details); + gpr_free(error_details); + return TSI_DATA_CORRUPTED; + } + return TSI_OK; +} + +static void ensure_buffer_size(alts_frame_protector* impl) { + if (!alts_has_read_frame_length(impl->reader)) { + return; + } + size_t buffer_space_remaining = impl->max_unprotected_frame_size - + alts_get_output_bytes_read(impl->reader); + /** + * Check if we need to resize in_place_unprotect_buffer in order to hold + * remaining bytes of a full frame. + */ + if (buffer_space_remaining < alts_get_reader_bytes_remaining(impl->reader)) { + size_t buffer_len = alts_get_output_bytes_read(impl->reader) + + alts_get_reader_bytes_remaining(impl->reader); + unsigned char* buffer = static_cast(gpr_malloc(buffer_len)); + memcpy(buffer, impl->in_place_unprotect_buffer, + alts_get_output_bytes_read(impl->reader)); + impl->max_unprotected_frame_size = buffer_len; + gpr_free(impl->in_place_unprotect_buffer); + impl->in_place_unprotect_buffer = buffer; + alts_reset_reader_output_buffer( + impl->reader, buffer + alts_get_output_bytes_read(impl->reader)); + } +} + +static tsi_result alts_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 == nullptr || protected_frames_bytes == nullptr || + protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr || + unprotected_bytes_size == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to alts_unprotect()."); + return TSI_INVALID_ARGUMENT; + } + alts_frame_protector* impl = reinterpret_cast(self); + /** + * If a new frame can start being processed, we reset the frame reader to + * point to in_place_unprotect_buffer that will be used to hold deframed + * result. + */ + if (alts_is_frame_reader_done(impl->reader) && + ((alts_get_output_buffer(impl->reader) == nullptr) || + (alts_get_output_bytes_read(impl->reader) == + impl->in_place_unprotect_bytes_processed + impl->overhead_length))) { + if (!alts_reset_frame_reader(impl->reader, + impl->in_place_unprotect_buffer)) { + gpr_log(GPR_ERROR, "Couldn't reset frame reader."); + return TSI_INTERNAL_ERROR; + } + impl->in_place_unprotect_bytes_processed = 0; + } + /** + * If a full frame has not yet been read, we read more bytes from + * protected_frames_bytes until a full frame has been read. We also need to + * make sure in_place_unprotect_buffer is large enough to hold a complete + * frame. + */ + if (!alts_is_frame_reader_done(impl->reader)) { + ensure_buffer_size(impl); + *protected_frames_bytes_size = + GPR_MIN(impl->max_unprotected_frame_size - + alts_get_output_bytes_read(impl->reader), + *protected_frames_bytes_size); + size_t read_frames_bytes_size = *protected_frames_bytes_size; + if (!alts_read_frame_bytes(impl->reader, protected_frames_bytes, + &read_frames_bytes_size)) { + gpr_log(GPR_ERROR, "Failed to process frame."); + return TSI_INTERNAL_ERROR; + } + *protected_frames_bytes_size = read_frames_bytes_size; + } else { + *protected_frames_bytes_size = 0; + } + /** + * If a full frame has been read, we unseal it, and write out the + * deframed result to unprotected_bytes. + */ + if (alts_is_frame_reader_done(impl->reader)) { + if (impl->in_place_unprotect_bytes_processed == 0) { + tsi_result result = unseal(impl); + if (result != TSI_OK) { + return result; + } + } + size_t bytes_to_write = GPR_MIN( + *unprotected_bytes_size, alts_get_output_bytes_read(impl->reader) - + impl->in_place_unprotect_bytes_processed - + impl->overhead_length); + if (bytes_to_write > 0) { + memcpy(unprotected_bytes, + impl->in_place_unprotect_buffer + + impl->in_place_unprotect_bytes_processed, + bytes_to_write); + } + *unprotected_bytes_size = bytes_to_write; + impl->in_place_unprotect_bytes_processed += bytes_to_write; + return TSI_OK; + } else { + *unprotected_bytes_size = 0; + return TSI_OK; + } +} + +static void alts_destroy(tsi_frame_protector* self) { + alts_frame_protector* impl = reinterpret_cast(self); + if (impl != nullptr) { + alts_crypter_destroy(impl->seal_crypter); + alts_crypter_destroy(impl->unseal_crypter); + gpr_free(impl->in_place_protect_buffer); + gpr_free(impl->in_place_unprotect_buffer); + alts_destroy_frame_writer(impl->writer); + alts_destroy_frame_reader(impl->reader); + gpr_free(impl); + } +} + +static const tsi_frame_protector_vtable alts_frame_protector_vtable = { + alts_protect, alts_protect_flush, alts_unprotect, alts_destroy}; + +static grpc_status_code create_alts_crypters(const uint8_t* key, + size_t key_size, bool is_client, + bool is_rekey, + alts_frame_protector* impl, + char** error_details) { + grpc_status_code status; + gsec_aead_crypter* aead_crypter_seal = nullptr; + gsec_aead_crypter* aead_crypter_unseal = nullptr; + status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength, + kAesGcmTagLength, is_rekey, + &aead_crypter_seal, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + status = gsec_aes_gcm_aead_crypter_create( + key, key_size, kAesGcmNonceLength, kAesGcmTagLength, is_rekey, + &aead_crypter_unseal, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + size_t overflow_size = is_rekey ? kAltsRecordProtocolRekeyFrameLimit + : kAltsRecordProtocolFrameLimit; + status = alts_seal_crypter_create(aead_crypter_seal, is_client, overflow_size, + &impl->seal_crypter, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + status = + alts_unseal_crypter_create(aead_crypter_unseal, is_client, overflow_size, + &impl->unseal_crypter, error_details); + return status; +} + +tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size, + bool is_client, bool is_rekey, + size_t* max_protected_frame_size, + tsi_frame_protector** self) { + if (key == nullptr || self == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_create_frame_protector()."); + return TSI_INTERNAL_ERROR; + } + char* error_details = nullptr; + alts_frame_protector* impl = + static_cast(gpr_zalloc(sizeof(*impl))); + grpc_status_code status = create_alts_crypters( + key, key_size, is_client, is_rekey, impl, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to create ALTS crypters, %s.", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + /** + * Set maximum frame size to be used by a frame protector. If it is nullptr, a + * default frame size will be used. Otherwise, the provided frame size will be + * adjusted (if not falling into a valid frame range) and used. + */ + size_t max_protected_frame_size_to_set = kDefaultFrameLength; + if (max_protected_frame_size != nullptr) { + *max_protected_frame_size = + GPR_MIN(*max_protected_frame_size, kMaxFrameLength); + *max_protected_frame_size = + GPR_MAX(*max_protected_frame_size, kMinFrameLength); + max_protected_frame_size_to_set = *max_protected_frame_size; + } + impl->max_protected_frame_size = max_protected_frame_size_to_set; + impl->max_unprotected_frame_size = max_protected_frame_size_to_set; + impl->in_place_protect_bytes_buffered = 0; + impl->in_place_unprotect_bytes_processed = 0; + impl->in_place_protect_buffer = static_cast( + gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set)); + impl->in_place_unprotect_buffer = static_cast( + gpr_malloc(sizeof(unsigned char) * max_protected_frame_size_to_set)); + impl->overhead_length = alts_crypter_num_overhead_bytes(impl->seal_crypter); + impl->writer = alts_create_frame_writer(); + impl->reader = alts_create_frame_reader(); + impl->base.vtable = &alts_frame_protector_vtable; + *self = &impl->base; + return TSI_OK; +} diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.h new file mode 100644 index 00000000000000..321bffaed84acd --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_frame_protector.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H + +#include + +#include + +#include "src/core/tsi/transport_security_interface.h" + +typedef struct alts_frame_protector alts_frame_protector; + +/** + * TODO: Add a parameter to the interface to support the use of + * different record protocols within a frame protector. + * + * This method creates a frame protector. + * + * - key: a symmetric key used to seal/unseal frames. + * - key_size: the size of symmetric key. + * - is_client: a flag indicating if the frame protector will be used at client + * (is_client = true) or server (is_client = false) side. + * - is_rekey: a flag indicating if the frame protector will use an AEAD with + * rekeying. + * - max_protected_frame_size: an in/out parameter indicating max frame size + * to be used by the frame protector. If it is nullptr, the default frame + * size will be used. Otherwise, the provided frame size will be adjusted (if + * not falling into a valid frame range) and used. + * - self: a pointer to the frame protector returned from the method. + * + * This method returns TSI_OK on success and TSI_INTERNAL_ERROR otherwise. + */ +tsi_result alts_create_frame_protector(const uint8_t* key, size_t key_size, + bool is_client, bool is_rekey, + size_t* max_protected_frame_size, + tsi_frame_protector** self); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_FRAME_PROTECTOR_H */ diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc new file mode 100644 index 00000000000000..0574ed5012e719 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.cc @@ -0,0 +1,114 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" + +#include + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +grpc_status_code input_sanity_check( + const alts_record_protocol_crypter* rp_crypter, const unsigned char* data, + size_t* output_size, char** error_details) { + if (rp_crypter == nullptr) { + maybe_copy_error_msg("alts_crypter instance is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } else if (data == nullptr) { + maybe_copy_error_msg("data is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } else if (output_size == nullptr) { + maybe_copy_error_msg("output_size is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter, + char** error_details) { + bool is_overflow = false; + grpc_status_code status = + alts_counter_increment(rp_crypter->ctr, &is_overflow, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (is_overflow) { + const char error_msg[] = + "crypter counter is wrapped. The connection" + "should be closed and the key should be deleted."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c) { + if (c != nullptr) { + size_t num_overhead_bytes = 0; + char* error_details = nullptr; + const alts_record_protocol_crypter* rp_crypter = + reinterpret_cast(c); + grpc_status_code status = gsec_aead_crypter_tag_length( + rp_crypter->crypter, &num_overhead_bytes, &error_details); + if (status == GRPC_STATUS_OK) { + return num_overhead_bytes; + } + } + return 0; +} + +void alts_record_protocol_crypter_destruct(alts_crypter* c) { + if (c != nullptr) { + alts_record_protocol_crypter* rp_crypter = + reinterpret_cast(c); + alts_counter_destroy(rp_crypter->ctr); + gsec_aead_crypter_destroy(rp_crypter->crypter); + } +} + +alts_record_protocol_crypter* alts_crypter_create_common( + gsec_aead_crypter* crypter, bool is_client, size_t overflow_size, + char** error_details) { + if (crypter != nullptr) { + auto* rp_crypter = static_cast( + gpr_malloc(sizeof(alts_record_protocol_crypter))); + size_t counter_size = 0; + grpc_status_code status = + gsec_aead_crypter_nonce_length(crypter, &counter_size, error_details); + if (status != GRPC_STATUS_OK) { + return nullptr; + } + /* Create a counter. */ + status = alts_counter_create(is_client, counter_size, overflow_size, + &rp_crypter->ctr, error_details); + if (status != GRPC_STATUS_OK) { + return nullptr; + } + rp_crypter->crypter = crypter; + return rp_crypter; + } + const char error_msg[] = "crypter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return nullptr; +} diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h new file mode 100644 index 00000000000000..682a8f7e7a939a --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h @@ -0,0 +1,114 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H + +#include + +#include + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" + +/** + * This file contains common implementation that will be used in both seal and + * unseal operations. + */ + +/** + * Main struct for alts_record_protocol_crypter that will be used in both + * seal and unseal operations. + */ +typedef struct alts_record_protocol_crypter { + alts_crypter base; + gsec_aead_crypter* crypter; + alts_counter* ctr; +} alts_record_protocol_crypter; + +/** + * This method performs input sanity checks on a subset of inputs to + * alts_crypter_process_in_place() for both seal and unseal operations. + * + * - rp_crypter: an alts_record_protocol_crypter instance. + * - data: it represents raw data that needs to be sealed in a seal operation or + * protected data that needs to be unsealed in an unseal operation. + * - output_size: size of data written to the data buffer after a seal or + * unseal operation. + * - error_details: a buffer containing an error message if any of checked + * inputs is nullptr. It is legal to pass nullptr into error_details and + * otherwise, the parameter should be freed with gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code input_sanity_check( + const alts_record_protocol_crypter* rp_crypter, const unsigned char* data, + size_t* output_size, char** error_details); + +/** + * This method increments the counter within an alts_record_protocol_crypter + * instance. + * + * - rp_crypter: an alts_record_protocol_crypter instance. + * - error_details: a buffer containing an error message if the method does not + * function correctly or the counter is wrapped. It is legal to pass nullptr + * into error_details and otherwise, the parameter should be freed with + * gpr_free. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, + * it returns an error status code along with its details specified in + * error_details (if error_details is not nullptr). + */ +grpc_status_code increment_counter(alts_record_protocol_crypter* rp_crypter, + char** error_details); + +/** + * This method creates an alts_crypter instance, and populates the fields + * that are common to both seal and unseal operations. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. The + * function does not take ownership of crypter. + * - is_client: a flag indicating if the alts_crypter instance will be + * used at the client (is_client = true) or server (is_client = + * false) side. + * - overflow_size: overflow size of counter in bytes. + * - error_details: a buffer containing an error message if the method does + * not function correctly. It is legal to pass nullptr into error_details, and + * otherwise, the parameter should be freed with gpr_free. + * + * On success of creation, the method returns alts_record_protocol_crypter + * instance. Otherwise, it returns nullptr with its details specified in + * error_details (if error_details is not nullptr). + * + */ +alts_record_protocol_crypter* alts_crypter_create_common( + gsec_aead_crypter* crypter, bool is_client, size_t overflow_size, + char** error_details); + +/** + * For the following two methods, please refer to the corresponding API in + * alts_crypter.h for detailed specifications. + */ +size_t alts_record_protocol_crypter_num_overhead_bytes(const alts_crypter* c); + +void alts_record_protocol_crypter_destruct(alts_crypter* c); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_ALTS_RECORD_PROTOCOL_CRYPTER_COMMON_H \ + */ diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc new file mode 100644 index 00000000000000..f407831613ad0f --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_seal_privacy_integrity_crypter.cc @@ -0,0 +1,105 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" +#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +/* Perform input santity check for a seal operation. */ +static grpc_status_code seal_check(alts_crypter* c, const unsigned char* data, + size_t data_allocated_size, size_t data_size, + size_t* output_size, char** error_details) { + /* Do common input sanity check. */ + grpc_status_code status = input_sanity_check( + reinterpret_cast(c), data, + output_size, error_details); + if (status != GRPC_STATUS_OK) return status; + /* Do seal-specific check. */ + size_t num_overhead_bytes = + alts_crypter_num_overhead_bytes(reinterpret_cast(c)); + if (data_size == 0) { + const char error_msg[] = "data_size is zero."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (data_size + num_overhead_bytes > data_allocated_size) { + const char error_msg[] = + "data_allocated_size is smaller than sum of data_size and " + "num_overhead_bytes."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +static grpc_status_code alts_seal_crypter_process_in_place( + alts_crypter* c, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details) { + grpc_status_code status = seal_check(c, data, data_allocated_size, data_size, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Do AEAD encryption. */ + alts_record_protocol_crypter* rp_crypter = + reinterpret_cast(c); + status = gsec_aead_crypter_encrypt( + rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr), + alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */, + 0 /* aad_length */, data, data_size, data, data_allocated_size, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Increment the crypter counter. */ + return increment_counter(rp_crypter, error_details); +} + +static const alts_crypter_vtable vtable = { + alts_record_protocol_crypter_num_overhead_bytes, + alts_seal_crypter_process_in_place, alts_record_protocol_crypter_destruct}; + +grpc_status_code alts_seal_crypter_create(gsec_aead_crypter* gc, bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details) { + if (crypter == nullptr) { + const char error_msg[] = "crypter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + alts_record_protocol_crypter* rp_crypter = + alts_crypter_create_common(gc, !is_client, overflow_size, error_details); + if (rp_crypter == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + rp_crypter->base.vtable = &vtable; + *crypter = &rp_crypter->base; + return GRPC_STATUS_OK; +} diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc new file mode 100644 index 00000000000000..51bea24f1fa618 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/alts_unseal_privacy_integrity_crypter.cc @@ -0,0 +1,103 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" +#include "src/core/tsi/alts/frame_protector/alts_crypter.h" +#include "src/core/tsi/alts/frame_protector/alts_record_protocol_crypter_common.h" + +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +/* Perform input santity check. */ +static grpc_status_code unseal_check(alts_crypter* c, const unsigned char* data, + size_t data_allocated_size, + size_t data_size, size_t* output_size, + char** error_details) { + /* Do common input sanity check. */ + grpc_status_code status = input_sanity_check( + reinterpret_cast(c), data, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Do unseal-specific input check. */ + size_t num_overhead_bytes = + alts_crypter_num_overhead_bytes(reinterpret_cast(c)); + if (num_overhead_bytes > data_size) { + const char error_msg[] = "data_size is smaller than num_overhead_bytes."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +static grpc_status_code alts_unseal_crypter_process_in_place( + alts_crypter* c, unsigned char* data, size_t data_allocated_size, + size_t data_size, size_t* output_size, char** error_details) { + grpc_status_code status = unseal_check(c, data, data_allocated_size, + data_size, output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Do AEAD decryption. */ + alts_record_protocol_crypter* rp_crypter = + reinterpret_cast(c); + status = gsec_aead_crypter_decrypt( + rp_crypter->crypter, alts_counter_get_counter(rp_crypter->ctr), + alts_counter_get_size(rp_crypter->ctr), nullptr /* aad */, + 0 /* aad_length */, data, data_size, data, data_allocated_size, + output_size, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Increment the crypter counter. */ + return increment_counter(rp_crypter, error_details); +} + +static const alts_crypter_vtable vtable = { + alts_record_protocol_crypter_num_overhead_bytes, + alts_unseal_crypter_process_in_place, + alts_record_protocol_crypter_destruct}; + +grpc_status_code alts_unseal_crypter_create(gsec_aead_crypter* gc, + bool is_client, + size_t overflow_size, + alts_crypter** crypter, + char** error_details) { + if (crypter == nullptr) { + const char error_msg[] = "crypter is nullptr."; + maybe_copy_error_msg(error_msg, error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + alts_record_protocol_crypter* rp_crypter = + alts_crypter_create_common(gc, is_client, overflow_size, error_details); + if (rp_crypter == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + rp_crypter->base.vtable = &vtable; + *crypter = &rp_crypter->base; + return GRPC_STATUS_OK; +} diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.cc b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.cc new file mode 100644 index 00000000000000..d3fda63b3dd9cf --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.cc @@ -0,0 +1,218 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/frame_protector/frame_handler.h" + +#include +#include +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" + +/* Use little endian to interpret a string of bytes as uint32_t. */ +static uint32_t load_32_le(const unsigned char* buffer) { + return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) | + (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]); +} + +/* Store uint32_t as a string of little endian bytes. */ +static void store_32_le(uint32_t value, unsigned char* buffer) { + buffer[3] = (unsigned char)(value >> 24) & 0xFF; + buffer[2] = (unsigned char)(value >> 16) & 0xFF; + buffer[1] = (unsigned char)(value >> 8) & 0xFF; + buffer[0] = (unsigned char)(value)&0xFF; +} + +/* Frame writer implementation. */ +alts_frame_writer* alts_create_frame_writer() { + alts_frame_writer* writer = + static_cast(gpr_zalloc(sizeof(*writer))); + return writer; +} + +bool alts_reset_frame_writer(alts_frame_writer* writer, + const unsigned char* buffer, size_t length) { + if (buffer == nullptr) return false; + size_t max_input_size = SIZE_MAX - kFrameLengthFieldSize; + if (length > max_input_size) { + gpr_log(GPR_ERROR, "length must be at most %zu", max_input_size); + return false; + } + writer->input_buffer = buffer; + writer->input_size = length; + writer->input_bytes_written = 0; + writer->header_bytes_written = 0; + store_32_le( + static_cast(writer->input_size + kFrameMessageTypeFieldSize), + writer->header_buffer); + store_32_le(kFrameMessageType, writer->header_buffer + kFrameLengthFieldSize); + return true; +} + +bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output, + size_t* bytes_size) { + if (bytes_size == nullptr || output == nullptr) return false; + if (alts_is_frame_writer_done(writer)) { + *bytes_size = 0; + return true; + } + size_t bytes_written = 0; + /* Write some header bytes, if needed. */ + if (writer->header_bytes_written != sizeof(writer->header_buffer)) { + size_t bytes_to_write = + GPR_MIN(*bytes_size, + sizeof(writer->header_buffer) - writer->header_bytes_written); + memcpy(output, writer->header_buffer + writer->header_bytes_written, + bytes_to_write); + bytes_written += bytes_to_write; + *bytes_size -= bytes_to_write; + writer->header_bytes_written += bytes_to_write; + output += bytes_to_write; + if (writer->header_bytes_written != sizeof(writer->header_buffer)) { + *bytes_size = bytes_written; + return true; + } + } + /* Write some non-header bytes. */ + size_t bytes_to_write = + GPR_MIN(writer->input_size - writer->input_bytes_written, *bytes_size); + memcpy(output, writer->input_buffer, bytes_to_write); + writer->input_buffer += bytes_to_write; + bytes_written += bytes_to_write; + writer->input_bytes_written += bytes_to_write; + *bytes_size = bytes_written; + return true; +} + +bool alts_is_frame_writer_done(alts_frame_writer* writer) { + return writer->input_buffer == nullptr || + writer->input_size == writer->input_bytes_written; +} + +size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer) { + return (sizeof(writer->header_buffer) - writer->header_bytes_written) + + (writer->input_size - writer->input_bytes_written); +} + +void alts_destroy_frame_writer(alts_frame_writer* writer) { gpr_free(writer); } + +/* Frame reader implementation. */ +alts_frame_reader* alts_create_frame_reader() { + alts_frame_reader* reader = + static_cast(gpr_zalloc(sizeof(*reader))); + return reader; +} + +bool alts_is_frame_reader_done(alts_frame_reader* reader) { + return reader->output_buffer == nullptr || + (reader->header_bytes_read == sizeof(reader->header_buffer) && + reader->bytes_remaining == 0); +} + +bool alts_has_read_frame_length(alts_frame_reader* reader) { + return sizeof(reader->header_buffer) == reader->header_bytes_read; +} + +size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader) { + return alts_has_read_frame_length(reader) ? reader->bytes_remaining : 0; +} + +void alts_reset_reader_output_buffer(alts_frame_reader* reader, + unsigned char* buffer) { + reader->output_buffer = buffer; +} + +bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer) { + if (buffer == nullptr) return false; + reader->output_buffer = buffer; + reader->bytes_remaining = 0; + reader->header_bytes_read = 0; + reader->output_bytes_read = 0; + return true; +} + +bool alts_read_frame_bytes(alts_frame_reader* reader, + const unsigned char* bytes, size_t* bytes_size) { + if (bytes_size == nullptr) return false; + if (bytes == nullptr) { + *bytes_size = 0; + return false; + } + if (alts_is_frame_reader_done(reader)) { + *bytes_size = 0; + return true; + } + size_t bytes_processed = 0; + /* Process the header, if needed. */ + if (reader->header_bytes_read != sizeof(reader->header_buffer)) { + size_t bytes_to_write = GPR_MIN( + *bytes_size, sizeof(reader->header_buffer) - reader->header_bytes_read); + memcpy(reader->header_buffer + reader->header_bytes_read, bytes, + bytes_to_write); + reader->header_bytes_read += bytes_to_write; + bytes_processed += bytes_to_write; + bytes += bytes_to_write; + *bytes_size -= bytes_to_write; + if (reader->header_bytes_read != sizeof(reader->header_buffer)) { + *bytes_size = bytes_processed; + return true; + } + size_t frame_length = load_32_le(reader->header_buffer); + if (frame_length < kFrameMessageTypeFieldSize || + frame_length > kFrameMaxSize) { + gpr_log(GPR_ERROR, + "Bad frame length (should be at least %zu, and at most %zu)", + kFrameMessageTypeFieldSize, kFrameMaxSize); + *bytes_size = 0; + return false; + } + size_t message_type = + load_32_le(reader->header_buffer + kFrameLengthFieldSize); + if (message_type != kFrameMessageType) { + gpr_log(GPR_ERROR, "Unsupported message type %zu (should be %zu)", + message_type, kFrameMessageType); + *bytes_size = 0; + return false; + } + reader->bytes_remaining = frame_length - kFrameMessageTypeFieldSize; + } + /* Process the non-header bytes. */ + size_t bytes_to_write = GPR_MIN(*bytes_size, reader->bytes_remaining); + memcpy(reader->output_buffer, bytes, bytes_to_write); + reader->output_buffer += bytes_to_write; + bytes_processed += bytes_to_write; + reader->bytes_remaining -= bytes_to_write; + reader->output_bytes_read += bytes_to_write; + *bytes_size = bytes_processed; + return true; +} + +size_t alts_get_output_bytes_read(alts_frame_reader* reader) { + return reader->output_bytes_read; +} + +unsigned char* alts_get_output_buffer(alts_frame_reader* reader) { + return reader->output_buffer; +} + +void alts_destroy_frame_reader(alts_frame_reader* reader) { gpr_free(reader); } diff --git a/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.h b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.h new file mode 100644 index 00000000000000..a703ff40d308f8 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/frame_protector/frame_handler.h @@ -0,0 +1,236 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H +#define GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H + +#include + +#include +#include + +const size_t kFrameMessageType = 0x06; +const size_t kFrameLengthFieldSize = 4; +const size_t kFrameMessageTypeFieldSize = 4; +const size_t kFrameMaxSize = 1024 * 1024; +const size_t kFrameHeaderSize = + kFrameLengthFieldSize + kFrameMessageTypeFieldSize; + +/** + * Implementation of frame reader and frame writer. All APIs in the + * header are thread-compatible. + */ + +/** + * Main struct for a frame writer. It reads frames from an input buffer, and + * writes the contents as raw bytes. It does not own the input buffer. + */ +typedef struct alts_frame_writer { + const unsigned char* input_buffer; + unsigned char header_buffer[kFrameHeaderSize]; + size_t input_bytes_written; + size_t header_bytes_written; + size_t input_size; +} alts_frame_writer; + +/** + * Main struct for a frame reader. It reads raw bytes and puts the framed + * result into an output buffer. It does not own the output buffer. + */ +typedef struct alts_frame_reader { + unsigned char* output_buffer; + unsigned char header_buffer[kFrameHeaderSize]; + size_t header_bytes_read; + size_t output_bytes_read; + size_t bytes_remaining; +} alts_frame_reader; + +/** + * This method creates a frame writer instance and initializes its internal + * states. + */ +alts_frame_writer* alts_create_frame_writer(); + +/** + * This method resets internal states of a frame writer and prepares to write + * a single frame. It does not take ownership of payload_buffer. + * The payload_buffer must outlive the writer. + * + * - writer: a frame writer instance. + * - buffer: a buffer storing full payload data to be framed. + * - length: size of payload data. + * + * The method returns true on success and false otherwise. + */ +bool alts_reset_frame_writer(alts_frame_writer* writer, + const unsigned char* buffer, size_t length); + +/** + * This method writes up to bytes_size bytes of a frame to output. + * + * - writer: a frame writer instance. + * - output: an output buffer used to store the frame. + * - bytes_size: an in/out parameter that stores the size of output buffer + * before the call, and gets written the number of frame bytes written to the + * buffer. + * + * The method returns true on success and false otherwise. + */ +bool alts_write_frame_bytes(alts_frame_writer* writer, unsigned char* output, + size_t* bytes_size); + +/** + * This method checks if a reset can be called to write a new frame. It returns + * true if it's the first time to frame a payload, or the current frame has + * been finished processing. It returns false if it's not ready yet to start a + * new frame (e.g., more payload data needs to be accumulated to process the + * current frame). + * + * if (alts_is_frame_writer_done(writer)) { + * // a new frame can be written, call reset. + * alts_reset_frame_writer(writer, payload_buffer, payload_size); + * } else { + * // accumulate more payload data until a full frame can be written. + * } + * + * - writer: a frame writer instance. + */ +bool alts_is_frame_writer_done(alts_frame_writer* writer); + +/** + * This method returns the number of bytes left to write before a complete frame + * is formed. + * + * - writer: a frame writer instance. + */ +size_t alts_get_num_writer_bytes_remaining(alts_frame_writer* writer); + +/** + * This method destroys a frame writer instance. + * + * - writer: a frame writer instance. + */ +void alts_destroy_frame_writer(alts_frame_writer* writer); + +/** + * This method creates a frame reader instance and initializes its internal + * states. + */ +alts_frame_reader* alts_create_frame_reader(); + +/** + * This method resets internal states of a frame reader (including setting its + * output_buffer with buffer), and prepares to write processed bytes to + * an output_buffer. It does not take ownership of buffer. The buffer must + * outlive reader. + * + * - reader: a frame reader instance. + * - buffer: an output buffer used to store deframed results. + * + * The method returns true on success and false otherwise. + */ +bool alts_reset_frame_reader(alts_frame_reader* reader, unsigned char* buffer); + +/** + * This method processes up to the number of bytes given in bytes_size. It may + * choose not to process all the bytes, if, for instance, more bytes are + * given to the method than required to complete the current frame. + * + * - reader: a frame reader instance. + * - bytes: a buffer that stores data to be processed. + * - bytes_size: an in/out parameter that stores the size of bytes before the + * call and gets written the number of bytes processed. + * + * The method returns true on success and false otherwise. + */ +bool alts_read_frame_bytes(alts_frame_reader* reader, + const unsigned char* bytes, size_t* bytes_size); + +/** + * This method checks if a frame length has been read. + * + * - reader: a frame reader instance. + * + * The method returns true if a frame length has been read and false otherwise. + */ +bool alts_has_read_frame_length(alts_frame_reader* reader); + +/** + * This method returns the number of bytes the frame reader intends to write. + * It may only be called if alts_has_read_frame_length() returns true. + * + * - reader: a frame reader instance. + */ +size_t alts_get_reader_bytes_remaining(alts_frame_reader* reader); + +/** + * This method resets output_buffer but does not otherwise modify other internal + * states of a frame reader instance. After being set, the new output_buffer + * will hold the deframed payload held by the original output_buffer. It does + * not take ownership of buffer. The buffer must outlive the reader. + * To distinguish between two reset methods on a frame reader, + * + * if (alts_fh_is_frame_reader_done(reader)) { + * // if buffer contains a full payload to be deframed, call reset. + * alts_reset_frame_reader(reader, buffer); + * } + * + * // if remaining buffer space is not enough to hold a full payload + * if (buffer_space_remaining < alts_get_reader_bytes_remaining(reader)) { + * // allocate enough space for a new buffer, copy back data processed so far, + * // and call reset. + * alts_reset_reader_output_buffer(reader, new_buffer). + * } + * + * - reader: a frame reader instance. + * - buffer: a buffer used to set reader's output_buffer. + */ +void alts_reset_reader_output_buffer(alts_frame_reader* reader, + unsigned char* buffer); + +/** + * This method checks if reset can be called to start processing a new frame. + * If true and reset was previously called, a full frame has been processed and + * the content of the frame is available in output_buffer. + + * - reader: a frame reader instance. + */ +bool alts_is_frame_reader_done(alts_frame_reader* reader); + +/** + * This method returns output_bytes_read of a frame reader instance. + * + * - reader: a frame reader instance. + */ +size_t alts_get_output_bytes_read(alts_frame_reader* reader); + +/** + * This method returns output_buffer of a frame reader instance. + * + * - reader: a frame reader instance. + */ +unsigned char* alts_get_output_buffer(alts_frame_reader* reader); + +/** + * This method destroys a frame reader instance. + * + * - reader: a frame reader instance. + */ +void alts_destroy_frame_reader(alts_frame_reader* reader); + +#endif /* GRPC_CORE_TSI_ALTS_FRAME_PROTECTOR_FRAME_HANDLER_H */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.cc new file mode 100644 index 00000000000000..43d0979f4b90f4 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.cc @@ -0,0 +1,615 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" + +#include +#include +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h" +#include "src/core/tsi/alts/handshaker/alts_shared_resource.h" +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h" +#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h" + +#define TSI_ALTS_INITIAL_BUFFER_SIZE 256 + +const int kHandshakerClientOpNum = 4; + +struct alts_handshaker_client { + const alts_handshaker_client_vtable* vtable; +}; + +typedef struct alts_grpc_handshaker_client { + alts_handshaker_client base; + alts_tsi_handshaker* handshaker; + grpc_call* call; + /* A pointer to a function handling the interaction with handshaker service. + * That is, it points to grpc_call_start_batch_and_execute when the handshaker + * client is used in a non-testing use case and points to a custom function + * that validates the data to be sent to handshaker service in a testing use + * case. */ + alts_grpc_caller grpc_caller; + /* A callback function provided by gRPC to handle the response returned from + * handshaker service. It also serves to bring the control safely back to + * application when dedicated CQ and thread are used. */ + grpc_iomgr_cb_func grpc_cb; + /* A gRPC closure to be scheduled when the response from handshaker service + * is received. It will be initialized with grpc_cb. */ + grpc_closure on_handshaker_service_resp_recv; + /* Buffers containing information to be sent (or received) to (or from) the + * handshaker service. */ + grpc_byte_buffer* send_buffer; + grpc_byte_buffer* recv_buffer; + grpc_status_code status; + /* Initial metadata to be received from handshaker service. */ + grpc_metadata_array recv_initial_metadata; + /* A callback function provided by an application to be invoked when response + * is received from handshaker service. */ + tsi_handshaker_on_next_done_cb cb; + void* user_data; + /* ALTS credential options passed in from the caller. */ + grpc_alts_credentials_options* options; + /* target name information to be passed to handshaker service for server + * authorization check. */ + grpc_slice target_name; + /* boolean flag indicating if the handshaker client is used at client + * (is_client = true) or server (is_client = false) side. */ + bool is_client; + /* a temporary store for data received from handshaker service used to extract + * unused data. */ + grpc_slice recv_bytes; + /* a buffer containing data to be sent to the grpc client or server's peer. */ + unsigned char* buffer; + size_t buffer_size; +} alts_grpc_handshaker_client; + +static void handshaker_client_send_buffer_destroy( + alts_grpc_handshaker_client* client) { + GPR_ASSERT(client != nullptr); + grpc_byte_buffer_destroy(client->send_buffer); + client->send_buffer = nullptr; +} + +static bool is_handshake_finished_properly(grpc_gcp_handshaker_resp* resp) { + GPR_ASSERT(resp != nullptr); + if (resp->has_result) { + return true; + } + return false; +} + +void alts_handshaker_client_handle_response(alts_handshaker_client* c, + bool is_ok) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + grpc_byte_buffer* recv_buffer = client->recv_buffer; + grpc_status_code status = client->status; + tsi_handshaker_on_next_done_cb cb = client->cb; + void* user_data = client->user_data; + alts_tsi_handshaker* handshaker = client->handshaker; + + /* Invalid input check. */ + if (cb == nullptr) { + gpr_log(GPR_ERROR, + "cb is nullptr in alts_tsi_handshaker_handle_response()"); + return; + } + if (handshaker == nullptr) { + gpr_log(GPR_ERROR, + "handshaker is nullptr in alts_tsi_handshaker_handle_response()"); + cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr); + return; + } + /* TSI handshake has been shutdown. */ + if (alts_tsi_handshaker_has_shutdown(handshaker)) { + gpr_log(GPR_ERROR, "TSI handshake shutdown"); + cb(TSI_HANDSHAKE_SHUTDOWN, user_data, nullptr, 0, nullptr); + return; + } + /* Failed grpc call check. */ + if (!is_ok || status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "grpc call made to handshaker service failed"); + cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr); + return; + } + if (recv_buffer == nullptr) { + gpr_log(GPR_ERROR, + "recv_buffer is nullptr in alts_tsi_handshaker_handle_response()"); + cb(TSI_INTERNAL_ERROR, user_data, nullptr, 0, nullptr); + return; + } + grpc_gcp_handshaker_resp* resp = + alts_tsi_utils_deserialize_response(recv_buffer); + grpc_byte_buffer_destroy(client->recv_buffer); + client->recv_buffer = nullptr; + /* Invalid handshaker response check. */ + if (resp == nullptr) { + gpr_log(GPR_ERROR, "alts_tsi_utils_deserialize_response() failed"); + cb(TSI_DATA_CORRUPTED, user_data, nullptr, 0, nullptr); + return; + } + grpc_slice* slice = static_cast(resp->out_frames.arg); + unsigned char* bytes_to_send = nullptr; + size_t bytes_to_send_size = 0; + if (slice != nullptr) { + bytes_to_send_size = GRPC_SLICE_LENGTH(*slice); + while (bytes_to_send_size > client->buffer_size) { + client->buffer_size *= 2; + client->buffer = static_cast( + gpr_realloc(client->buffer, client->buffer_size)); + } + memcpy(client->buffer, GRPC_SLICE_START_PTR(*slice), bytes_to_send_size); + bytes_to_send = client->buffer; + } + tsi_handshaker_result* result = nullptr; + if (is_handshake_finished_properly(resp)) { + alts_tsi_handshaker_result_create(resp, client->is_client, &result); + alts_tsi_handshaker_result_set_unused_bytes(result, &client->recv_bytes, + resp->bytes_consumed); + } + grpc_status_code code = static_cast(resp->status.code); + if (code != GRPC_STATUS_OK) { + grpc_slice* details = static_cast(resp->status.details.arg); + if (details != nullptr) { + char* error_details = grpc_slice_to_c_string(*details); + gpr_log(GPR_ERROR, "Error from handshaker service:%s", error_details); + gpr_free(error_details); + } + } + grpc_gcp_handshaker_resp_destroy(resp); + cb(alts_tsi_utils_convert_to_tsi_result(code), user_data, bytes_to_send, + bytes_to_send_size, result); +} + +/** + * Populate grpc operation data with the fields of ALTS handshaker client and + * make a grpc call. + */ +static tsi_result make_grpc_call(alts_handshaker_client* c, bool is_start) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + grpc_op ops[kHandshakerClientOpNum]; + memset(ops, 0, sizeof(ops)); + grpc_op* op = ops; + if (is_start) { + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &client->recv_initial_metadata; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + } + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = client->send_buffer; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &client->recv_buffer; + op++; + GPR_ASSERT(op - ops <= kHandshakerClientOpNum); + GPR_ASSERT(client->grpc_caller != nullptr); + if (client->grpc_caller(client->call, ops, static_cast(op - ops), + &client->on_handshaker_service_resp_recv) != + GRPC_CALL_OK) { + gpr_log(GPR_ERROR, "Start batch operation failed"); + return TSI_INTERNAL_ERROR; + } + return TSI_OK; +} + +/* Create and populate a client_start handshaker request, then serialize it. */ +static grpc_byte_buffer* get_serialized_start_client( + alts_handshaker_client* c) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + bool ok = true; + grpc_gcp_handshaker_req* req = + grpc_gcp_handshaker_req_create(CLIENT_START_REQ); + ok &= grpc_gcp_handshaker_req_set_handshake_protocol( + req, grpc_gcp_HandshakeProtocol_ALTS); + ok &= grpc_gcp_handshaker_req_add_application_protocol( + req, ALTS_APPLICATION_PROTOCOL); + ok &= grpc_gcp_handshaker_req_add_record_protocol(req, ALTS_RECORD_PROTOCOL); + grpc_gcp_rpc_protocol_versions* versions = &client->options->rpc_versions; + ok &= grpc_gcp_handshaker_req_set_rpc_versions( + req, versions->max_rpc_version.major, versions->max_rpc_version.minor, + versions->min_rpc_version.major, versions->min_rpc_version.minor); + char* target_name = grpc_slice_to_c_string(client->target_name); + ok &= grpc_gcp_handshaker_req_set_target_name(req, target_name); + target_service_account* ptr = + (reinterpret_cast(client->options)) + ->target_account_list_head; + while (ptr != nullptr) { + grpc_gcp_handshaker_req_add_target_identity_service_account(req, ptr->data); + ptr = ptr->next; + } + grpc_slice slice; + ok &= grpc_gcp_handshaker_req_encode(req, &slice); + grpc_byte_buffer* buffer = nullptr; + if (ok) { + buffer = grpc_raw_byte_buffer_create(&slice, 1 /* number of slices */); + } + grpc_slice_unref_internal(slice); + gpr_free(target_name); + grpc_gcp_handshaker_req_destroy(req); + return buffer; +} + +static tsi_result handshaker_client_start_client(alts_handshaker_client* c) { + if (c == nullptr) { + gpr_log(GPR_ERROR, "client is nullptr in handshaker_client_start_client()"); + return TSI_INVALID_ARGUMENT; + } + grpc_byte_buffer* buffer = get_serialized_start_client(c); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + if (buffer == nullptr) { + gpr_log(GPR_ERROR, "get_serialized_start_client() failed"); + return TSI_INTERNAL_ERROR; + } + handshaker_client_send_buffer_destroy(client); + client->send_buffer = buffer; + tsi_result result = make_grpc_call(&client->base, true /* is_start */); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "make_grpc_call() failed"); + } + return result; +} + +/* Create and populate a start_server handshaker request, then serialize it. */ +static grpc_byte_buffer* get_serialized_start_server( + alts_handshaker_client* c, grpc_slice* bytes_received) { + GPR_ASSERT(c != nullptr); + GPR_ASSERT(bytes_received != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + grpc_gcp_handshaker_req* req = + grpc_gcp_handshaker_req_create(SERVER_START_REQ); + bool ok = grpc_gcp_handshaker_req_add_application_protocol( + req, ALTS_APPLICATION_PROTOCOL); + ok &= grpc_gcp_handshaker_req_param_add_record_protocol( + req, grpc_gcp_HandshakeProtocol_ALTS, ALTS_RECORD_PROTOCOL); + ok &= grpc_gcp_handshaker_req_set_in_bytes( + req, reinterpret_cast GRPC_SLICE_START_PTR(*bytes_received), + GRPC_SLICE_LENGTH(*bytes_received)); + grpc_gcp_rpc_protocol_versions* versions = &client->options->rpc_versions; + ok &= grpc_gcp_handshaker_req_set_rpc_versions( + req, versions->max_rpc_version.major, versions->max_rpc_version.minor, + versions->min_rpc_version.major, versions->min_rpc_version.minor); + grpc_slice req_slice; + ok &= grpc_gcp_handshaker_req_encode(req, &req_slice); + grpc_byte_buffer* buffer = nullptr; + if (ok) { + buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */); + } + grpc_slice_unref_internal(req_slice); + grpc_gcp_handshaker_req_destroy(req); + return buffer; +} + +static tsi_result handshaker_client_start_server(alts_handshaker_client* c, + grpc_slice* bytes_received) { + if (c == nullptr || bytes_received == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to handshaker_client_start_server()"); + return TSI_INVALID_ARGUMENT; + } + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + grpc_byte_buffer* buffer = get_serialized_start_server(c, bytes_received); + if (buffer == nullptr) { + gpr_log(GPR_ERROR, "get_serialized_start_server() failed"); + return TSI_INTERNAL_ERROR; + } + handshaker_client_send_buffer_destroy(client); + client->send_buffer = buffer; + tsi_result result = make_grpc_call(&client->base, true /* is_start */); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "make_grpc_call() failed"); + } + return result; +} + +/* Create and populate a next handshaker request, then serialize it. */ +static grpc_byte_buffer* get_serialized_next(grpc_slice* bytes_received) { + GPR_ASSERT(bytes_received != nullptr); + grpc_gcp_handshaker_req* req = grpc_gcp_handshaker_req_create(NEXT_REQ); + bool ok = grpc_gcp_handshaker_req_set_in_bytes( + req, reinterpret_cast GRPC_SLICE_START_PTR(*bytes_received), + GRPC_SLICE_LENGTH(*bytes_received)); + grpc_slice req_slice; + ok &= grpc_gcp_handshaker_req_encode(req, &req_slice); + grpc_byte_buffer* buffer = nullptr; + if (ok) { + buffer = grpc_raw_byte_buffer_create(&req_slice, 1 /* number of slices */); + } + grpc_slice_unref_internal(req_slice); + grpc_gcp_handshaker_req_destroy(req); + return buffer; +} + +static tsi_result handshaker_client_next(alts_handshaker_client* c, + grpc_slice* bytes_received) { + if (c == nullptr || bytes_received == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to handshaker_client_next()"); + return TSI_INVALID_ARGUMENT; + } + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + grpc_slice_unref_internal(client->recv_bytes); + client->recv_bytes = grpc_slice_ref(*bytes_received); + grpc_byte_buffer* buffer = get_serialized_next(bytes_received); + if (buffer == nullptr) { + gpr_log(GPR_ERROR, "get_serialized_next() failed"); + return TSI_INTERNAL_ERROR; + } + handshaker_client_send_buffer_destroy(client); + client->send_buffer = buffer; + tsi_result result = make_grpc_call(&client->base, false /* is_start */); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "make_grpc_call() failed"); + } + return result; +} + +static void handshaker_client_shutdown(alts_handshaker_client* c) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + if (client->call != nullptr) { + grpc_call_cancel_internal(client->call); + } +} + +static void handshaker_client_destruct(alts_handshaker_client* c) { + if (c == nullptr) { + return; + } + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + if (client->call != nullptr) { + grpc_call_unref(client->call); + } +} + +static const alts_handshaker_client_vtable vtable = { + handshaker_client_start_client, handshaker_client_start_server, + handshaker_client_next, handshaker_client_shutdown, + handshaker_client_destruct}; + +alts_handshaker_client* alts_grpc_handshaker_client_create( + alts_tsi_handshaker* handshaker, grpc_channel* channel, + const char* handshaker_service_url, grpc_pollset_set* interested_parties, + grpc_alts_credentials_options* options, grpc_slice target_name, + grpc_iomgr_cb_func grpc_cb, tsi_handshaker_on_next_done_cb cb, + void* user_data, alts_handshaker_client_vtable* vtable_for_testing, + bool is_client) { + if (channel == nullptr || handshaker_service_url == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to alts_handshaker_client_create()"); + return nullptr; + } + alts_grpc_handshaker_client* client = + static_cast(gpr_zalloc(sizeof(*client))); + client->grpc_caller = grpc_call_start_batch_and_execute; + client->handshaker = handshaker; + client->cb = cb; + client->user_data = user_data; + client->send_buffer = nullptr; + client->recv_buffer = nullptr; + client->options = grpc_alts_credentials_options_copy(options); + client->target_name = grpc_slice_copy(target_name); + client->recv_bytes = grpc_empty_slice(); + grpc_metadata_array_init(&client->recv_initial_metadata); + client->grpc_cb = grpc_cb; + client->is_client = is_client; + client->buffer_size = TSI_ALTS_INITIAL_BUFFER_SIZE; + client->buffer = static_cast(gpr_zalloc(client->buffer_size)); + grpc_slice slice = grpc_slice_from_copied_string(handshaker_service_url); + client->call = + strcmp(handshaker_service_url, ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING) == + 0 + ? nullptr + : grpc_channel_create_pollset_set_call( + channel, nullptr, GRPC_PROPAGATE_DEFAULTS, interested_parties, + grpc_slice_from_static_string(ALTS_SERVICE_METHOD), &slice, + GRPC_MILLIS_INF_FUTURE, nullptr); + client->base.vtable = + vtable_for_testing == nullptr ? &vtable : vtable_for_testing; + GRPC_CLOSURE_INIT(&client->on_handshaker_service_resp_recv, client->grpc_cb, + client, grpc_schedule_on_exec_ctx); + grpc_slice_unref_internal(slice); + return &client->base; +} + +namespace grpc_core { +namespace internal { + +void alts_handshaker_client_set_grpc_caller_for_testing( + alts_handshaker_client* c, alts_grpc_caller caller) { + GPR_ASSERT(c != nullptr && caller != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + client->grpc_caller = caller; +} + +grpc_byte_buffer* alts_handshaker_client_get_send_buffer_for_testing( + alts_handshaker_client* c) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + return client->send_buffer; +} + +grpc_byte_buffer** alts_handshaker_client_get_recv_buffer_addr_for_testing( + alts_handshaker_client* c) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + return &client->recv_buffer; +} + +grpc_metadata_array* alts_handshaker_client_get_initial_metadata_for_testing( + alts_handshaker_client* c) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + return &client->recv_initial_metadata; +} + +void alts_handshaker_client_set_recv_bytes_for_testing( + alts_handshaker_client* c, grpc_slice* recv_bytes) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + client->recv_bytes = grpc_slice_ref(*recv_bytes); +} + +void alts_handshaker_client_set_fields_for_testing( + alts_handshaker_client* c, alts_tsi_handshaker* handshaker, + tsi_handshaker_on_next_done_cb cb, void* user_data, + grpc_byte_buffer* recv_buffer, grpc_status_code status) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + client->handshaker = handshaker; + client->cb = cb; + client->user_data = user_data; + client->recv_buffer = recv_buffer; + client->status = status; +} + +void alts_handshaker_client_check_fields_for_testing( + alts_handshaker_client* c, tsi_handshaker_on_next_done_cb cb, + void* user_data, bool has_sent_start_message, grpc_slice* recv_bytes) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + GPR_ASSERT(client->cb == cb); + GPR_ASSERT(client->user_data == user_data); + if (recv_bytes != nullptr) { + GPR_ASSERT(grpc_slice_cmp(client->recv_bytes, *recv_bytes) == 0); + } + GPR_ASSERT(alts_tsi_handshaker_get_has_sent_start_message_for_testing( + client->handshaker) == has_sent_start_message); +} + +void alts_handshaker_client_set_vtable_for_testing( + alts_handshaker_client* c, alts_handshaker_client_vtable* vtable) { + GPR_ASSERT(c != nullptr); + GPR_ASSERT(vtable != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + client->base.vtable = vtable; +} + +alts_tsi_handshaker* alts_handshaker_client_get_handshaker_for_testing( + alts_handshaker_client* c) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + return client->handshaker; +} + +void alts_handshaker_client_set_cb_for_testing( + alts_handshaker_client* c, tsi_handshaker_on_next_done_cb cb) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + client->cb = cb; +} + +grpc_closure* alts_handshaker_client_get_closure_for_testing( + alts_handshaker_client* c) { + GPR_ASSERT(c != nullptr); + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + return &client->on_handshaker_service_resp_recv; +} + +} // namespace internal +} // namespace grpc_core + +tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client) { + if (client != nullptr && client->vtable != nullptr && + client->vtable->client_start != nullptr) { + return client->vtable->client_start(client); + } + gpr_log(GPR_ERROR, + "client or client->vtable has not been initialized properly"); + return TSI_INVALID_ARGUMENT; +} + +tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client, + grpc_slice* bytes_received) { + if (client != nullptr && client->vtable != nullptr && + client->vtable->server_start != nullptr) { + return client->vtable->server_start(client, bytes_received); + } + gpr_log(GPR_ERROR, + "client or client->vtable has not been initialized properly"); + return TSI_INVALID_ARGUMENT; +} + +tsi_result alts_handshaker_client_next(alts_handshaker_client* client, + grpc_slice* bytes_received) { + if (client != nullptr && client->vtable != nullptr && + client->vtable->next != nullptr) { + return client->vtable->next(client, bytes_received); + } + gpr_log(GPR_ERROR, + "client or client->vtable has not been initialized properly"); + return TSI_INVALID_ARGUMENT; +} + +void alts_handshaker_client_shutdown(alts_handshaker_client* client) { + if (client != nullptr && client->vtable != nullptr && + client->vtable->shutdown != nullptr) { + client->vtable->shutdown(client); + } +} + +void alts_handshaker_client_destroy(alts_handshaker_client* c) { + if (c != nullptr) { + if (c->vtable != nullptr && c->vtable->destruct != nullptr) { + c->vtable->destruct(c); + } + alts_grpc_handshaker_client* client = + reinterpret_cast(c); + grpc_byte_buffer_destroy(client->send_buffer); + grpc_byte_buffer_destroy(client->recv_buffer); + client->send_buffer = nullptr; + client->recv_buffer = nullptr; + grpc_metadata_array_destroy(&client->recv_initial_metadata); + grpc_slice_unref_internal(client->recv_bytes); + grpc_slice_unref_internal(client->target_name); + grpc_alts_credentials_options_destroy(client->options); + gpr_free(client->buffer); + gpr_free(client); + } +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.h new file mode 100644 index 00000000000000..4b489875f3c4fb --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_client.h @@ -0,0 +1,157 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H + +#include + +#include +#include +#include + +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" +#include "src/core/tsi/transport_security_interface.h" + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/pollset_set.h" + +#define ALTS_SERVICE_METHOD "/grpc.gcp.HandshakerService/DoHandshake" +#define ALTS_APPLICATION_PROTOCOL "grpc" +#define ALTS_RECORD_PROTOCOL "ALTSRP_GCM_AES128_REKEY" +#define ALTS_HANDSHAKER_SERVICE_URL_FOR_TESTING "lame" + +const size_t kAltsAes128GcmRekeyKeyLength = 44; + +typedef struct alts_tsi_handshaker alts_tsi_handshaker; +/** + * A ALTS handshaker client interface. It is used to communicate with + * ALTS handshaker service by scheduling a handshaker request that could be one + * of client_start, server_start, and next handshaker requests. All APIs in the + * header are thread-compatible. + */ +typedef struct alts_handshaker_client alts_handshaker_client; + +/* A function that makes the grpc call to the handshaker service. */ +typedef grpc_call_error (*alts_grpc_caller)(grpc_call* call, const grpc_op* ops, + size_t nops, grpc_closure* tag); + +/* V-table for ALTS handshaker client operations. */ +typedef struct alts_handshaker_client_vtable { + tsi_result (*client_start)(alts_handshaker_client* client); + tsi_result (*server_start)(alts_handshaker_client* client, + grpc_slice* bytes_received); + tsi_result (*next)(alts_handshaker_client* client, + grpc_slice* bytes_received); + void (*shutdown)(alts_handshaker_client* client); + void (*destruct)(alts_handshaker_client* client); +} alts_handshaker_client_vtable; + +/** + * This method schedules a client_start handshaker request to ALTS handshaker + * service. + * + * - client: ALTS handshaker client instance. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_handshaker_client_start_client(alts_handshaker_client* client); + +/** + * This method schedules a server_start handshaker request to ALTS handshaker + * service. + * + * - client: ALTS handshaker client instance. + * - bytes_received: bytes in out_frames returned from the peer's handshaker + * response. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_handshaker_client_start_server(alts_handshaker_client* client, + grpc_slice* bytes_received); + +/** + * This method schedules a next handshaker request to ALTS handshaker service. + * + * - client: ALTS handshaker client instance. + * - bytes_received: bytes in out_frames returned from the peer's handshaker + * response. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result alts_handshaker_client_next(alts_handshaker_client* client, + grpc_slice* bytes_received); + +/** + * This method cancels previously scheduled, but yet executed handshaker + * requests to ALTS handshaker service. After this operation, the handshake + * will be shutdown, and no more handshaker requests will get scheduled. + * + * - client: ALTS handshaker client instance. + */ +void alts_handshaker_client_shutdown(alts_handshaker_client* client); + +/** + * This method destroys an ALTS handshaker client. + * + * - client: an ALTS handshaker client instance. + */ +void alts_handshaker_client_destroy(alts_handshaker_client* client); + +/** + * This method creates an ALTS handshaker client. + * + * - handshaker: ALTS TSI handshaker to which the created handshaker client + * belongs to. + * - channel: grpc channel to ALTS handshaker service. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". + * - interested_parties: set of pollsets interested in this connection. + * - options: ALTS credentials options containing information passed from TSI + * caller (e.g., rpc protocol versions) + * - target_name: the name of the endpoint that the channel is connecting to, + * and will be used for secure naming check + * - grpc_cb: gRPC provided callbacks passed from TSI handshaker. + * - cb: callback to be executed when tsi_handshaker_next API compltes. + * - user_data: argument passed to cb. + * - vtable_for_testing: ALTS handshaker client vtable instance used for + * testing purpose. + * - is_client: a boolean value indicating if the created handshaker client is + * used at the client (is_client = true) or server (is_client = false) side. It + * returns the created ALTS handshaker client on success, and NULL on failure. + */ +alts_handshaker_client* alts_grpc_handshaker_client_create( + alts_tsi_handshaker* handshaker, grpc_channel* channel, + const char* handshaker_service_url, grpc_pollset_set* interested_parties, + grpc_alts_credentials_options* options, grpc_slice target_name, + grpc_iomgr_cb_func grpc_cb, tsi_handshaker_on_next_done_cb cb, + void* user_data, alts_handshaker_client_vtable* vtable_for_testing, + bool is_client); + +/** + * This method handles handshaker response returned from ALTS handshaker + * service. Note that the only reason the API is exposed is that it is used in + * alts_shared_resources.cc. + * + * - client: an ALTS handshaker client instance. + * - is_ok: a boolean value indicating if the handshaker response is ok to read. + */ +void alts_handshaker_client_handle_response(alts_handshaker_client* client, + bool is_ok); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_CLIENT_H */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc new file mode 100644 index 00000000000000..256e414ae435b9 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.cc @@ -0,0 +1,520 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h" + +#include +#include + +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +/* HandshakerReq */ +grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create( + grpc_gcp_handshaker_req_type type) { + grpc_gcp_handshaker_req* req = + static_cast(gpr_zalloc(sizeof(*req))); + switch (type) { + case CLIENT_START_REQ: + req->has_client_start = true; + break; + case SERVER_START_REQ: + req->has_server_start = true; + break; + case NEXT_REQ: + req->has_next = true; + break; + } + return req; +} + +void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req) { + if (req == nullptr) { + return; + } + if (req->has_client_start) { + /* Destroy client_start request. */ + destroy_repeated_field_list_identity( + static_cast(req->client_start.target_identities.arg)); + destroy_repeated_field_list_string(static_cast( + req->client_start.application_protocols.arg)); + destroy_repeated_field_list_string( + static_cast(req->client_start.record_protocols.arg)); + if (req->client_start.has_local_identity) { + destroy_slice(static_cast( + req->client_start.local_identity.hostname.arg)); + destroy_slice(static_cast( + req->client_start.local_identity.service_account.arg)); + } + if (req->client_start.has_local_endpoint) { + destroy_slice(static_cast( + req->client_start.local_endpoint.ip_address.arg)); + } + if (req->client_start.has_remote_endpoint) { + destroy_slice(static_cast( + req->client_start.remote_endpoint.ip_address.arg)); + } + destroy_slice(static_cast(req->client_start.target_name.arg)); + } else if (req->has_server_start) { + /* Destroy server_start request. */ + size_t i = 0; + for (i = 0; i < req->server_start.handshake_parameters_count; i++) { + destroy_repeated_field_list_identity( + static_cast(req->server_start.handshake_parameters[i] + .value.local_identities.arg)); + destroy_repeated_field_list_string( + static_cast(req->server_start.handshake_parameters[i] + .value.record_protocols.arg)); + } + destroy_repeated_field_list_string(static_cast( + req->server_start.application_protocols.arg)); + if (req->server_start.has_local_endpoint) { + destroy_slice(static_cast( + req->server_start.local_endpoint.ip_address.arg)); + } + if (req->server_start.has_remote_endpoint) { + destroy_slice(static_cast( + req->server_start.remote_endpoint.ip_address.arg)); + } + destroy_slice(static_cast(req->server_start.in_bytes.arg)); + } else { + /* Destroy next request. */ + destroy_slice(static_cast(req->next.in_bytes.arg)); + } + gpr_free(req); +} + +bool grpc_gcp_handshaker_req_set_handshake_protocol( + grpc_gcp_handshaker_req* req, + grpc_gcp_handshake_protocol handshake_protocol) { + if (req == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_handshake_protocol()."); + return false; + } + req->client_start.has_handshake_security_protocol = true; + req->client_start.handshake_security_protocol = handshake_protocol; + return true; +} + +bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req, + const char* target_name) { + if (req == nullptr || target_name == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_target_name()."); + return false; + } + grpc_slice* slice = create_slice(target_name, strlen(target_name)); + req->client_start.target_name.arg = slice; + req->client_start.target_name.funcs.encode = encode_string_or_bytes_cb; + return true; +} + +bool grpc_gcp_handshaker_req_add_application_protocol( + grpc_gcp_handshaker_req* req, const char* application_protocol) { + if (req == nullptr || application_protocol == nullptr || req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_add_application_protocol()."); + return false; + } + grpc_slice* slice = + create_slice(application_protocol, strlen(application_protocol)); + if (req->has_client_start) { + add_repeated_field(reinterpret_cast( + &req->client_start.application_protocols.arg), + slice); + req->client_start.application_protocols.funcs.encode = + encode_repeated_string_cb; + } else { + add_repeated_field(reinterpret_cast( + &req->server_start.application_protocols.arg), + slice); + req->server_start.application_protocols.funcs.encode = + encode_repeated_string_cb; + } + return true; +} + +bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req, + const char* record_protocol) { + if (req == nullptr || record_protocol == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_add_record_protocol()."); + return false; + } + grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol)); + add_repeated_field(reinterpret_cast( + &req->client_start.record_protocols.arg), + slice); + req->client_start.record_protocols.funcs.encode = encode_repeated_string_cb; + return true; +} + +static void set_identity_hostname(grpc_gcp_identity* identity, + const char* hostname) { + grpc_slice* slice = create_slice(hostname, strlen(hostname)); + identity->hostname.arg = slice; + identity->hostname.funcs.encode = encode_string_or_bytes_cb; +} + +static void set_identity_service_account(grpc_gcp_identity* identity, + const char* service_account) { + grpc_slice* slice = create_slice(service_account, strlen(service_account)); + identity->service_account.arg = slice; + identity->service_account.funcs.encode = encode_string_or_bytes_cb; +} + +bool grpc_gcp_handshaker_req_add_target_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname) { + if (req == nullptr || hostname == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_add_target_identity_hostname()."); + return false; + } + grpc_gcp_identity* target_identity = + static_cast(gpr_zalloc(sizeof(*target_identity))); + set_identity_hostname(target_identity, hostname); + req->client_start.target_identities.funcs.encode = + encode_repeated_identity_cb; + add_repeated_field(reinterpret_cast( + &req->client_start.target_identities.arg), + target_identity); + return true; +} + +bool grpc_gcp_handshaker_req_add_target_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account) { + if (req == nullptr || service_account == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_add_target_identity_service_account()."); + return false; + } + grpc_gcp_identity* target_identity = + static_cast(gpr_zalloc(sizeof(*target_identity))); + set_identity_service_account(target_identity, service_account); + req->client_start.target_identities.funcs.encode = + encode_repeated_identity_cb; + add_repeated_field(reinterpret_cast( + &req->client_start.target_identities.arg), + target_identity); + return true; +} + +bool grpc_gcp_handshaker_req_set_local_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname) { + if (req == nullptr || hostname == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_set_local_identity_hostname()."); + return false; + } + req->client_start.has_local_identity = true; + set_identity_hostname(&req->client_start.local_identity, hostname); + return true; +} + +bool grpc_gcp_handshaker_req_set_local_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account) { + if (req == nullptr || service_account == nullptr || !req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_handshaker_req_set_local_identity_service_account()."); + return false; + } + req->client_start.has_local_identity = true; + set_identity_service_account(&req->client_start.local_identity, + service_account); + return true; +} + +static void set_endpoint(grpc_gcp_endpoint* endpoint, const char* ip_address, + size_t port, grpc_gcp_network_protocol protocol) { + grpc_slice* slice = create_slice(ip_address, strlen(ip_address)); + endpoint->ip_address.arg = slice; + endpoint->ip_address.funcs.encode = encode_string_or_bytes_cb; + endpoint->has_port = true; + endpoint->port = static_cast(port); + endpoint->has_protocol = true; + endpoint->protocol = protocol; +} + +bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req, + uint32_t max_major, + uint32_t max_minor, + uint32_t min_major, + uint32_t min_minor) { + if (req == nullptr || req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_rpc_versions()."); + return false; + } + if (req->has_client_start) { + req->client_start.has_rpc_versions = true; + grpc_gcp_rpc_protocol_versions_set_max(&req->client_start.rpc_versions, + max_major, max_minor); + grpc_gcp_rpc_protocol_versions_set_min(&req->client_start.rpc_versions, + min_major, min_minor); + } else { + req->server_start.has_rpc_versions = true; + grpc_gcp_rpc_protocol_versions_set_max(&req->server_start.rpc_versions, + max_major, max_minor); + grpc_gcp_rpc_protocol_versions_set_min(&req->server_start.rpc_versions, + min_major, min_minor); + } + return true; +} + +bool grpc_gcp_handshaker_req_set_local_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol) { + if (req == nullptr || ip_address == nullptr || port > 65535 || + req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_local_endpoint()."); + return false; + } + if (req->has_client_start) { + req->client_start.has_local_endpoint = true; + set_endpoint(&req->client_start.local_endpoint, ip_address, port, protocol); + } else { + req->server_start.has_local_endpoint = true; + set_endpoint(&req->server_start.local_endpoint, ip_address, port, protocol); + } + return true; +} + +bool grpc_gcp_handshaker_req_set_remote_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol) { + if (req == nullptr || ip_address == nullptr || port > 65535 || + req->has_next) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_remote_endpoint()."); + return false; + } + if (req->has_client_start) { + req->client_start.has_remote_endpoint = true; + set_endpoint(&req->client_start.remote_endpoint, ip_address, port, + protocol); + } else { + req->server_start.has_remote_endpoint = true; + set_endpoint(&req->server_start.remote_endpoint, ip_address, port, + protocol); + } + return true; +} + +bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req, + const char* in_bytes, size_t size) { + if (req == nullptr || in_bytes == nullptr || req->has_client_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_set_in_bytes()."); + return false; + } + grpc_slice* slice = create_slice(in_bytes, size); + if (req->has_next) { + req->next.in_bytes.arg = slice; + req->next.in_bytes.funcs.encode = &encode_string_or_bytes_cb; + } else { + req->server_start.in_bytes.arg = slice; + req->server_start.in_bytes.funcs.encode = &encode_string_or_bytes_cb; + } + return true; +} + +static grpc_gcp_server_handshake_parameters* server_start_find_param( + grpc_gcp_handshaker_req* req, int32_t key) { + size_t i = 0; + for (i = 0; i < req->server_start.handshake_parameters_count; i++) { + if (req->server_start.handshake_parameters[i].key == key) { + return &req->server_start.handshake_parameters[i].value; + } + } + req->server_start + .handshake_parameters[req->server_start.handshake_parameters_count] + .has_key = true; + req->server_start + .handshake_parameters[req->server_start.handshake_parameters_count] + .has_value = true; + req->server_start + .handshake_parameters[req->server_start.handshake_parameters_count++] + .key = key; + return &req->server_start + .handshake_parameters + [req->server_start.handshake_parameters_count - 1] + .value; +} + +bool grpc_gcp_handshaker_req_param_add_record_protocol( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* record_protocol) { + if (req == nullptr || record_protocol == nullptr || !req->has_server_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_param_add_record_protocol()."); + return false; + } + grpc_gcp_server_handshake_parameters* param = + server_start_find_param(req, key); + grpc_slice* slice = create_slice(record_protocol, strlen(record_protocol)); + add_repeated_field( + reinterpret_cast(¶m->record_protocols.arg), slice); + param->record_protocols.funcs.encode = &encode_repeated_string_cb; + return true; +} + +bool grpc_gcp_handshaker_req_param_add_local_identity_hostname( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* hostname) { + if (req == nullptr || hostname == nullptr || !req->has_server_start) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_param_add_local_identity_hostname()."); + return false; + } + grpc_gcp_server_handshake_parameters* param = + server_start_find_param(req, key); + grpc_gcp_identity* local_identity = + static_cast(gpr_zalloc(sizeof(*local_identity))); + set_identity_hostname(local_identity, hostname); + add_repeated_field( + reinterpret_cast(¶m->local_identities.arg), + local_identity); + param->local_identities.funcs.encode = &encode_repeated_identity_cb; + return true; +} + +bool grpc_gcp_handshaker_req_param_add_local_identity_service_account( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* service_account) { + if (req == nullptr || service_account == nullptr || !req->has_server_start) { + gpr_log( + GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_handshaker_req_param_add_local_identity_service_account()."); + return false; + } + grpc_gcp_server_handshake_parameters* param = + server_start_find_param(req, key); + grpc_gcp_identity* local_identity = + static_cast(gpr_zalloc(sizeof(*local_identity))); + set_identity_service_account(local_identity, service_account); + add_repeated_field( + reinterpret_cast(¶m->local_identities.arg), + local_identity); + param->local_identities.funcs.encode = &encode_repeated_identity_cb; + return true; +} + +bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req, + grpc_slice* slice) { + if (req == nullptr || slice == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to grpc_gcp_handshaker_req_encode()."); + return false; + } + pb_ostream_t size_stream; + memset(&size_stream, 0, sizeof(pb_ostream_t)); + if (!pb_encode(&size_stream, grpc_gcp_HandshakerReq_fields, req)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream)); + return false; + } + size_t encoded_length = size_stream.bytes_written; + *slice = grpc_slice_malloc(encoded_length); + pb_ostream_t output_stream = + pb_ostream_from_buffer(GRPC_SLICE_START_PTR(*slice), encoded_length); + if (!pb_encode(&output_stream, grpc_gcp_HandshakerReq_fields, req) != 0) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream)); + return false; + } + return true; +} + +/* HandshakerResp. */ +grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void) { + grpc_gcp_handshaker_resp* resp = + static_cast(gpr_zalloc(sizeof(*resp))); + return resp; +} + +void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp) { + if (resp != nullptr) { + destroy_slice(static_cast(resp->out_frames.arg)); + if (resp->has_status) { + destroy_slice(static_cast(resp->status.details.arg)); + } + if (resp->has_result) { + destroy_slice( + static_cast(resp->result.application_protocol.arg)); + destroy_slice(static_cast(resp->result.record_protocol.arg)); + destroy_slice(static_cast(resp->result.key_data.arg)); + if (resp->result.has_local_identity) { + destroy_slice( + static_cast(resp->result.local_identity.hostname.arg)); + destroy_slice(static_cast( + resp->result.local_identity.service_account.arg)); + } + if (resp->result.has_peer_identity) { + destroy_slice( + static_cast(resp->result.peer_identity.hostname.arg)); + destroy_slice(static_cast( + resp->result.peer_identity.service_account.arg)); + } + } + gpr_free(resp); + } +} + +bool grpc_gcp_handshaker_resp_decode(grpc_slice encoded_handshaker_resp, + grpc_gcp_handshaker_resp* resp) { + if (resp == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr argument to grpc_gcp_handshaker_resp_decode()."); + return false; + } + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_handshaker_resp), + GRPC_SLICE_LENGTH(encoded_handshaker_resp)); + resp->out_frames.funcs.decode = decode_string_or_bytes_cb; + resp->status.details.funcs.decode = decode_string_or_bytes_cb; + resp->result.application_protocol.funcs.decode = decode_string_or_bytes_cb; + resp->result.record_protocol.funcs.decode = decode_string_or_bytes_cb; + resp->result.key_data.funcs.decode = decode_string_or_bytes_cb; + resp->result.peer_identity.hostname.funcs.decode = decode_string_or_bytes_cb; + resp->result.peer_identity.service_account.funcs.decode = + decode_string_or_bytes_cb; + resp->result.local_identity.hostname.funcs.decode = decode_string_or_bytes_cb; + resp->result.local_identity.service_account.funcs.decode = + decode_string_or_bytes_cb; + if (!pb_decode(&stream, grpc_gcp_HandshakerResp_fields, resp)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return false; + } + return true; +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h new file mode 100644 index 00000000000000..5df56a86fae8ca --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api.h @@ -0,0 +1,323 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H + +#include + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h" + +/** + * An implementation of nanopb thin wrapper used to set/get and + * serialize/de-serialize of ALTS handshake requests and responses. + * + * All APIs in the header are thread-compatible. A typical usage of this API at + * the client side is as follows: + * + * ----------------------------------------------------------------------------- + * // Create, populate, and serialize an ALTS client_start handshake request to + * // send to the server. + * grpc_gcp_handshaker_req* req = + * grpc_gcp_handshaker_req_create(CLIENT_START_REQ); + * grpc_gcp_handshaker_req_set_handshake_protocol( + req, grpc_gcp_HandshakeProtocol_ALTS); + * grpc_gcp_handshaker_req_add_application_protocol(req, "grpc"); + * grpc_gcp_handshaker_req_add_record_protocol(req, "ALTSRP_GCM_AES128"); + * grpc_slice client_slice; + * if (!grpc_gcp_handshaker_req_encode(req, &client_slice)) { + * fprintf(stderr, "ALTS handshake request encoding failed."; + * } + * + * // De-serialize a data stream received from the server, and store the result + * // at ALTS handshake response. + * grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create(); + * if (!grpc_gcp_handshaker_resp_decode(server_slice, resp)) { + * fprintf(stderr, "ALTS handshake response decoding failed."); + * } + * // To access a variable-length datatype field (i.e., pb_callback_t), + * // access its "arg" subfield (if it has been set). + * if (resp->out_frames.arg != nullptr) { + * grpc_slice* slice = resp->out_frames.arg; + * } + * // To access a fixed-length datatype field (i.e., not pb_calback_t), + * // access the field directly (if it has been set). + * if (resp->has_status && resp->status->has_code) { + * uint32_t code = resp->status->code; + * } + *------------------------------------------------------------------------------ + */ + +/** + * This method creates an ALTS handshake request. + * + * - type: an enum type value that can be either CLIENT_START_REQ, + * SERVER_START_REQ, or NEXT_REQ to indicate the created instance will be + * client_start, server_start, and next handshake request message + * respectively. + * + * The method returns a pointer to the created instance. + */ +grpc_gcp_handshaker_req* grpc_gcp_handshaker_req_create( + grpc_gcp_handshaker_req_type type); + +/** + * This method sets the value for handshake_security_protocol field of ALTS + * client_start handshake request. + * + * - req: an ALTS handshake request. + * - handshake_protocol: a enum type value representing the handshake security + * protocol. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_handshake_protocol( + grpc_gcp_handshaker_req* req, + grpc_gcp_handshake_protocol handshake_protocol); + +/** + * This method sets the value for target_name field of ALTS client_start + * handshake request. + * + * - req: an ALTS handshake request. + * - target_name: a target name to be set. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_target_name(grpc_gcp_handshaker_req* req, + const char* target_name); + +/** + * This method adds an application protocol supported by the server (or + * client) to ALTS server_start (or client_start) handshake request. + * + * - req: an ALTS handshake request. + * - application_protocol: an application protocol (e.g., grpc) to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_application_protocol( + grpc_gcp_handshaker_req* req, const char* application_protocol); + +/** + * This method adds a record protocol supported by the client to ALTS + * client_start handshake request. + * + * - req: an ALTS handshake request. + * - record_protocol: a record protocol (e.g., ALTSRP_GCM_AES128) to be + * added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_record_protocol(grpc_gcp_handshaker_req* req, + const char* record_protocol); + +/** + * This method adds a target server identity represented as hostname and + * acceptable by a client to ALTS client_start handshake request. + * + * - req: an ALTS handshake request. + * - hostname: a string representation of hostname at the connection + * endpoint to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_target_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname); + +/** + * This method adds a target server identity represented as service account and + * acceptable by a client to ALTS client_start handshake request. + * + * - req: an ALTS handshake request. + * - service_account: a string representation of service account at the + * connection endpoint to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_add_target_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account); + +/** + * This method sets the hostname for local_identity field of ALTS client_start + * handshake request. + * + * - req: an ALTS handshake request. + * - hostname: a string representation of hostname. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_local_identity_hostname( + grpc_gcp_handshaker_req* req, const char* hostname); + +/** + * This method sets the service account for local_identity field of ALTS + * client_start handshake request. + * + * - req: an ALTS handshake request. + * - service_account: a string representation of service account. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_local_identity_service_account( + grpc_gcp_handshaker_req* req, const char* service_account); + +/** + * This method sets the value for local_endpoint field of either ALTS + * client_start or server_start handshake request. + * + * - req: an ALTS handshake request. + * - ip_address: a string representation of ip address associated with the + * local endpoint, that could be either IPv4 or IPv6. + * - port: a port number associated with the local endpoint. + * - protocol: a network protocol (e.g., TCP or UDP) associated with the + * local endpoint. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_local_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol); + +/** + * This method sets the value for remote_endpoint field of either ALTS + * client_start or server_start handshake request. + * + * - req: an ALTS handshake request. + * - ip_address: a string representation of ip address associated with the + * remote endpoint, that could be either IPv4 or IPv6. + * - port: a port number associated with the remote endpoint. + * - protocol: a network protocol (e.g., TCP or UDP) associated with the + * remote endpoint. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_remote_endpoint( + grpc_gcp_handshaker_req* req, const char* ip_address, size_t port, + grpc_gcp_network_protocol protocol); + +/** + * This method sets the value for in_bytes field of either ALTS server_start or + * next handshake request. + * + * - req: an ALTS handshake request. + * - in_bytes: a buffer containing bytes taken from out_frames of the peer's + * ALTS handshake response. It is possible that the peer's out_frames are + * split into multiple handshake request messages. + * - size: size of in_bytes buffer. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_in_bytes(grpc_gcp_handshaker_req* req, + const char* in_bytes, size_t size); + +/** + * This method adds a record protocol to handshake parameters mapped by the + * handshake protocol for ALTS server_start handshake request. + * + * - req: an ALTS handshake request. + * - key: an enum type value representing a handshake security protocol. + * - record_protocol: a record protocol to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_param_add_record_protocol( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* record_protocol); + +/** + * This method adds a local identity represented as hostname to handshake + * parameters mapped by the handshake protocol for ALTS server_start handshake + * request. + * + * - req: an ALTS handshake request. + * - key: an enum type value representing a handshake security protocol. + * - hostname: a string representation of hostname to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_param_add_local_identity_hostname( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* hostname); + +/** + * This method adds a local identity represented as service account to handshake + * parameters mapped by the handshake protocol for ALTS server_start handshake + * request. + * + * - req: an ALTS handshake request. + * - key: an enum type value representing a handshake security protocol. + * - service_account: a string representation of service account to be added. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_param_add_local_identity_service_account( + grpc_gcp_handshaker_req* req, grpc_gcp_handshake_protocol key, + const char* service_account); + +/** + * This method sets the value for rpc_versions field of either ALTS + * client_start or server_start handshake request. + * + * - req: an ALTS handshake request. + * - max_major: a major version of maximum supported RPC version. + * - max_minor: a minor version of maximum supported RPC version. + * - min_major: a major version of minimum supported RPC version. + * - min_minor: a minor version of minimum supported RPC version. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_set_rpc_versions(grpc_gcp_handshaker_req* req, + uint32_t max_major, + uint32_t max_minor, + uint32_t min_major, + uint32_t min_minor); + +/** + * This method serializes an ALTS handshake request and returns a data stream. + * + * - req: an ALTS handshake request. + * - slice: a data stream where the serialized result will be written. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_req_encode(grpc_gcp_handshaker_req* req, + grpc_slice* slice); + +/* This method destroys an ALTS handshake request. */ +void grpc_gcp_handshaker_req_destroy(grpc_gcp_handshaker_req* req); + +/* This method creates an ALTS handshake response. */ +grpc_gcp_handshaker_resp* grpc_gcp_handshaker_resp_create(void); + +/** + * This method de-serializes a data stream and stores the result + * in an ALTS handshake response. + * + * - slice: a data stream containing a serialized ALTS handshake response. + * - resp: an ALTS handshake response used to hold de-serialized result. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_handshaker_resp_decode(grpc_slice slice, + grpc_gcp_handshaker_resp* resp); + +/* This method destroys an ALTS handshake response. */ +void grpc_gcp_handshaker_resp_destroy(grpc_gcp_handshaker_resp* resp); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_H */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc new file mode 100644 index 00000000000000..d63d3538c569c0 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.cc @@ -0,0 +1,145 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h" + +#include "src/core/lib/slice/slice_internal.h" + +void add_repeated_field(repeated_field** head, const void* data) { + repeated_field* field = + static_cast(gpr_zalloc(sizeof(*field))); + field->data = data; + if (*head == nullptr) { + *head = field; + (*head)->next = nullptr; + } else { + field->next = *head; + *head = field; + } +} + +void destroy_repeated_field_list_identity(repeated_field* head) { + repeated_field* field = head; + while (field != nullptr) { + repeated_field* next_field = field->next; + const grpc_gcp_identity* identity = + static_cast(field->data); + destroy_slice(static_cast(identity->hostname.arg)); + destroy_slice(static_cast(identity->service_account.arg)); + gpr_free((void*)identity); + gpr_free(field); + field = next_field; + } +} + +void destroy_repeated_field_list_string(repeated_field* head) { + repeated_field* field = head; + while (field != nullptr) { + repeated_field* next_field = field->next; + destroy_slice((grpc_slice*)field->data); + gpr_free(field); + field = next_field; + } +} + +grpc_slice* create_slice(const char* data, size_t size) { + grpc_slice slice = grpc_slice_from_copied_buffer(data, size); + grpc_slice* cb_slice = + static_cast(gpr_zalloc(sizeof(*cb_slice))); + memcpy(cb_slice, &slice, sizeof(*cb_slice)); + return cb_slice; +} + +void destroy_slice(grpc_slice* slice) { + if (slice != nullptr) { + grpc_slice_unref_internal(*slice); + gpr_free(slice); + } +} + +bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + grpc_slice* slice = static_cast(*arg); + if (!pb_encode_tag_for_field(stream, field)) return false; + return pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice), + GRPC_SLICE_LENGTH(*slice)); +} + +bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + repeated_field* var = static_cast(*arg); + while (var != nullptr) { + if (!pb_encode_tag_for_field(stream, field)) return false; + if (!pb_encode_submessage(stream, grpc_gcp_Identity_fields, + (grpc_gcp_identity*)var->data)) + return false; + var = var->next; + } + return true; +} + +bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg) { + repeated_field* var = static_cast(*arg); + while (var != nullptr) { + if (!pb_encode_tag_for_field(stream, field)) return false; + const grpc_slice* slice = static_cast(var->data); + if (!pb_encode_string(stream, GRPC_SLICE_START_PTR(*slice), + GRPC_SLICE_LENGTH(*slice))) + return false; + var = var->next; + } + return true; +} + +bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + grpc_slice slice = grpc_slice_malloc(stream->bytes_left); + grpc_slice* cb_slice = + static_cast(gpr_zalloc(sizeof(*cb_slice))); + memcpy(cb_slice, &slice, sizeof(*cb_slice)); + if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left)) + return false; + *arg = cb_slice; + return true; +} + +bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + grpc_gcp_identity* identity = + static_cast(gpr_zalloc(sizeof(*identity))); + identity->hostname.funcs.decode = decode_string_or_bytes_cb; + identity->service_account.funcs.decode = decode_string_or_bytes_cb; + add_repeated_field(reinterpret_cast(arg), identity); + if (!pb_decode(stream, grpc_gcp_Identity_fields, identity)) return false; + return true; +} + +bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg) { + grpc_slice slice = grpc_slice_malloc(stream->bytes_left); + grpc_slice* cb_slice = + static_cast(gpr_zalloc(sizeof(*cb_slice))); + memcpy(cb_slice, &slice, sizeof(grpc_slice)); + if (!pb_read(stream, GRPC_SLICE_START_PTR(*cb_slice), stream->bytes_left)) + return false; + add_repeated_field(reinterpret_cast(arg), cb_slice); + return true; +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h new file mode 100644 index 00000000000000..966ea45617eb94 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h @@ -0,0 +1,149 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H + +#include + +#include "pb_decode.h" +#include "pb_encode.h" + +#include +#include +#include +#include + +#include "src/core/tsi/alts/handshaker/handshaker.pb.h" + +/** + * An implementation of utility functions used to serialize/ + * de-serialize ALTS handshake requests/responses. All APIs in the header + * are thread-compatible. + */ + +/* Renaming of message/field structs generated by nanopb compiler. */ +typedef grpc_gcp_HandshakeProtocol grpc_gcp_handshake_protocol; +typedef grpc_gcp_NetworkProtocol grpc_gcp_network_protocol; +typedef grpc_gcp_Identity grpc_gcp_identity; +typedef grpc_gcp_NextHandshakeMessageReq grpc_gcp_next_handshake_message_req; +typedef grpc_gcp_ServerHandshakeParameters grpc_gcp_server_handshake_parameters; +typedef grpc_gcp_Endpoint grpc_gcp_endpoint; +typedef grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry + grpc_gcp_handshake_parameters_entry; +typedef grpc_gcp_StartClientHandshakeReq grpc_gcp_start_client_handshake_req; +typedef grpc_gcp_StartServerHandshakeReq grpc_gcp_start_server_handshake_req; +typedef grpc_gcp_HandshakerReq grpc_gcp_handshaker_req; +typedef grpc_gcp_HandshakerResult grpc_gcp_handshaker_result; +typedef grpc_gcp_HandshakerStatus grpc_gcp_handshaker_status; +typedef grpc_gcp_HandshakerResp grpc_gcp_handshaker_resp; + +typedef enum { + CLIENT_START_REQ = 0, /* StartClientHandshakeReq. */ + SERVER_START_REQ = 1, /* StartServerHandshakeReq. */ + NEXT_REQ = 2, /* NextHandshakeMessageReq. */ +} grpc_gcp_handshaker_req_type; + +/** + * A struct representing a repeated field. The struct is used to organize all + * instances of a specific repeated field into a linked list, which then will + * be used at encode/decode phase. For instance at the encode phase, the encode + * function will iterate through the list, encode each field, and then output + * the result to the stream. + */ +typedef struct repeated_field_ { + struct repeated_field_* next; + const void* data; +} repeated_field; + +/** + * This method adds a repeated field to the head of repeated field list. + * + * - head: a head of repeated field list. + * - field: a repeated field to be added to the list. + */ +void add_repeated_field(repeated_field** head, const void* field); + +/** + * This method destroys a repeated field list that consists of string type + * fields. + * + * - head: a head of repeated field list. + */ +void destroy_repeated_field_list_string(repeated_field* head); + +/** + * This method destroys a repeated field list that consists of + * grpc_gcp_identity type fields. + * + * - head: a head of repeated field list. + */ +void destroy_repeated_field_list_identity(repeated_field* head); + +/** + * This method creates a grpc_slice instance by copying a data buffer. It is + * similar to grpc_slice_from_copied_buffer() except that it returns an instance + * allocated from the heap. + * + * - data: a data buffer to be copied to grpc_slice instance. + * - size: size of data buffer. + */ +grpc_slice* create_slice(const char* data, size_t size); + +/* This method destroys a grpc_slice instance. */ +void destroy_slice(grpc_slice* slice); + +/** + * The following encode/decode functions will be assigned to encode/decode + * function pointers of pb_callback_t struct (defined in + * //third_party/nanopb/pb.h), that represent a repeated field with a dynamic + * length (e.g., a string type or repeated field). + */ + +/* This method is an encode callback function for a string or byte array. */ +bool encode_string_or_bytes_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg); + +/** + * This method is an encode callback function for a repeated grpc_gcp_identity + * field. + */ +bool encode_repeated_identity_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg); + +/* This method is an encode callback function for a repeated string field. */ +bool encode_repeated_string_cb(pb_ostream_t* stream, const pb_field_t* field, + void* const* arg); + +/** + * This method is a decode callback function for a string or byte array field. + */ +bool decode_string_or_bytes_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg); +/** + * This method is a decode callback function for a repeated grpc_gcp_identity + * field. + */ +bool decode_repeated_identity_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg); + +/* This method is a decode callback function for a repeated string field. */ +bool decode_repeated_string_cb(pb_istream_t* stream, const pb_field_t* field, + void** arg); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_HANDSHAKER_SERVICE_API_UTIL_H */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.cc new file mode 100644 index 00000000000000..3501257f0520e8 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.cc @@ -0,0 +1,83 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/handshaker/alts_shared_resource.h" + +#include + +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" + +static alts_shared_resource_dedicated g_alts_resource_dedicated; + +alts_shared_resource_dedicated* grpc_alts_get_shared_resource_dedicated(void) { + return &g_alts_resource_dedicated; +} + +static void thread_worker(void* arg) { + while (true) { + grpc_event event = + grpc_completion_queue_next(g_alts_resource_dedicated.cq, + gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); + GPR_ASSERT(event.type != GRPC_QUEUE_TIMEOUT); + if (event.type == GRPC_QUEUE_SHUTDOWN) { + break; + } + GPR_ASSERT(event.type == GRPC_OP_COMPLETE); + alts_handshaker_client* client = + static_cast(event.tag); + alts_handshaker_client_handle_response(client, event.success); + } +} + +void grpc_alts_shared_resource_dedicated_init() { + g_alts_resource_dedicated.cq = nullptr; + gpr_mu_init(&g_alts_resource_dedicated.mu); +} + +void grpc_alts_shared_resource_dedicated_start( + const char* handshaker_service_url) { + gpr_mu_lock(&g_alts_resource_dedicated.mu); + if (g_alts_resource_dedicated.cq == nullptr) { + g_alts_resource_dedicated.channel = + grpc_insecure_channel_create(handshaker_service_url, nullptr, nullptr); + g_alts_resource_dedicated.cq = + grpc_completion_queue_create_for_next(nullptr); + g_alts_resource_dedicated.thread = + grpc_core::Thread("alts_tsi_handshaker", &thread_worker, nullptr); + g_alts_resource_dedicated.interested_parties = grpc_pollset_set_create(); + grpc_pollset_set_add_pollset(g_alts_resource_dedicated.interested_parties, + grpc_cq_pollset(g_alts_resource_dedicated.cq)); + g_alts_resource_dedicated.thread.Start(); + } + gpr_mu_unlock(&g_alts_resource_dedicated.mu); +} + +void grpc_alts_shared_resource_dedicated_shutdown() { + if (g_alts_resource_dedicated.cq != nullptr) { + grpc_pollset_set_del_pollset(g_alts_resource_dedicated.interested_parties, + grpc_cq_pollset(g_alts_resource_dedicated.cq)); + grpc_completion_queue_shutdown(g_alts_resource_dedicated.cq); + g_alts_resource_dedicated.thread.Join(); + grpc_pollset_set_destroy(g_alts_resource_dedicated.interested_parties); + grpc_completion_queue_destroy(g_alts_resource_dedicated.cq); + grpc_channel_destroy(g_alts_resource_dedicated.channel); + } + gpr_mu_destroy(&g_alts_resource_dedicated.mu); +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.h new file mode 100644 index 00000000000000..8ae0089a11ca49 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_shared_resource.h @@ -0,0 +1,73 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H + +#include + +#include +#include + +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/surface/completion_queue.h" + +/** + * Main struct containing ALTS shared resources used when + * employing the dedicated completion queue and thread. + */ +typedef struct alts_shared_resource_dedicated { + grpc_core::Thread thread; + grpc_completion_queue* cq; + grpc_pollset_set* interested_parties; + grpc_cq_completion storage; + gpr_mu mu; + grpc_channel* channel; +} alts_shared_resource_dedicated; + +/* This method returns the address of alts_shared_resource_dedicated + * object shared by all TSI handshakes. + */ +alts_shared_resource_dedicated* grpc_alts_get_shared_resource_dedicated(void); + +/** + * This method destroys the alts_shared_resource_dedicated object + * shared by all TSI handshakes. The applicaiton is responsible for + * invoking the API before calling grpc_shutdown(). + */ +void grpc_alts_shared_resource_dedicated_shutdown(); + +/** + * This method initializes the alts_shared_resource_dedicated object + * shared by all TSI handshakes. The application is responsible for + * invoking the API after calling grpc_init(); + */ +void grpc_alts_shared_resource_dedicated_init(); + +/** + * This method populates various fields of the alts_shared_resource_dedicated + * object shared by all TSI handshakes and start the dedicated thread. + * The API will be invoked by the caller in a lazy manner. That is, + * it will get invoked when ALTS TSI handshake occurs for the first time. + */ +void grpc_alts_shared_resource_dedicated_start( + const char* handshaker_service_url); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_SHARED_RESOURCE_H \ + */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc new file mode 100644 index 00000000000000..1b7e58d3ce09cb --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.cc @@ -0,0 +1,466 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/handshaker/alts_tsi_handshaker.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/host_port.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/alts/frame_protector/alts_frame_protector.h" +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" +#include "src/core/tsi/alts/handshaker/alts_shared_resource.h" +#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h" + +/* Main struct for ALTS TSI handshaker. */ +struct alts_tsi_handshaker { + tsi_handshaker base; + alts_handshaker_client* client; + grpc_slice target_name; + bool is_client; + bool has_sent_start_message; + bool has_created_handshaker_client; + char* handshaker_service_url; + grpc_pollset_set* interested_parties; + grpc_alts_credentials_options* options; + alts_handshaker_client_vtable* client_vtable_for_testing; + grpc_channel* channel; +}; + +/* Main struct for ALTS TSI handshaker result. */ +typedef struct alts_tsi_handshaker_result { + tsi_handshaker_result base; + char* peer_identity; + char* key_data; + unsigned char* unused_bytes; + size_t unused_bytes_size; + grpc_slice rpc_versions; + bool is_client; +} alts_tsi_handshaker_result; + +static tsi_result handshaker_result_extract_peer( + const tsi_handshaker_result* self, tsi_peer* peer) { + if (self == nullptr || peer == nullptr) { + gpr_log(GPR_ERROR, "Invalid argument to handshaker_result_extract_peer()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast( + const_cast(self)); + GPR_ASSERT(kTsiAltsNumOfPeerProperties == 3); + tsi_result ok = tsi_construct_peer(kTsiAltsNumOfPeerProperties, peer); + int index = 0; + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to construct tsi peer"); + return ok; + } + GPR_ASSERT(&peer->properties[index] != nullptr); + ok = tsi_construct_string_peer_property_from_cstring( + TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_ALTS_CERTIFICATE_TYPE, + &peer->properties[index]); + if (ok != TSI_OK) { + tsi_peer_destruct(peer); + gpr_log(GPR_ERROR, "Failed to set tsi peer property"); + return ok; + } + index++; + GPR_ASSERT(&peer->properties[index] != nullptr); + ok = tsi_construct_string_peer_property_from_cstring( + TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY, result->peer_identity, + &peer->properties[index]); + if (ok != TSI_OK) { + tsi_peer_destruct(peer); + gpr_log(GPR_ERROR, "Failed to set tsi peer property"); + } + index++; + GPR_ASSERT(&peer->properties[index] != nullptr); + ok = tsi_construct_string_peer_property( + TSI_ALTS_RPC_VERSIONS, + reinterpret_cast(GRPC_SLICE_START_PTR(result->rpc_versions)), + GRPC_SLICE_LENGTH(result->rpc_versions), &peer->properties[2]); + if (ok != TSI_OK) { + tsi_peer_destruct(peer); + gpr_log(GPR_ERROR, "Failed to set tsi peer property"); + } + GPR_ASSERT(++index == kTsiAltsNumOfPeerProperties); + return ok; +} + +static tsi_result handshaker_result_create_zero_copy_grpc_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_zero_copy_grpc_protector** protector) { + if (self == nullptr || protector == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to create_zero_copy_grpc_protector()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast( + const_cast(self)); + tsi_result ok = alts_zero_copy_grpc_protector_create( + reinterpret_cast(result->key_data), + kAltsAes128GcmRekeyKeyLength, /*is_rekey=*/true, result->is_client, + /*is_integrity_only=*/false, /*enable_extra_copy=*/false, + max_output_protected_frame_size, protector); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to create zero-copy grpc protector"); + } + return ok; +} + +static tsi_result handshaker_result_create_frame_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_frame_protector** protector) { + if (self == nullptr || protector == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to handshaker_result_create_frame_protector()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast( + const_cast(self)); + tsi_result ok = alts_create_frame_protector( + reinterpret_cast(result->key_data), + kAltsAes128GcmRekeyKeyLength, result->is_client, /*is_rekey=*/true, + max_output_protected_frame_size, protector); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to create frame protector"); + } + return ok; +} + +static tsi_result handshaker_result_get_unused_bytes( + const tsi_handshaker_result* self, const unsigned char** bytes, + size_t* bytes_size) { + if (self == nullptr || bytes == nullptr || bytes_size == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to handshaker_result_get_unused_bytes()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker_result* result = + reinterpret_cast( + const_cast(self)); + *bytes = result->unused_bytes; + *bytes_size = result->unused_bytes_size; + return TSI_OK; +} + +static void handshaker_result_destroy(tsi_handshaker_result* self) { + if (self == nullptr) { + return; + } + alts_tsi_handshaker_result* result = + reinterpret_cast( + const_cast(self)); + gpr_free(result->peer_identity); + gpr_free(result->key_data); + gpr_free(result->unused_bytes); + grpc_slice_unref_internal(result->rpc_versions); + gpr_free(result); +} + +static const tsi_handshaker_result_vtable result_vtable = { + handshaker_result_extract_peer, + handshaker_result_create_zero_copy_grpc_protector, + handshaker_result_create_frame_protector, + handshaker_result_get_unused_bytes, handshaker_result_destroy}; + +tsi_result alts_tsi_handshaker_result_create(grpc_gcp_handshaker_resp* resp, + bool is_client, + tsi_handshaker_result** self) { + if (self == nullptr || resp == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()"); + return TSI_INVALID_ARGUMENT; + } + grpc_slice* key = static_cast(resp->result.key_data.arg); + GPR_ASSERT(key != nullptr); + grpc_slice* identity = + static_cast(resp->result.peer_identity.service_account.arg); + if (identity == nullptr) { + gpr_log(GPR_ERROR, "Invalid service account"); + return TSI_FAILED_PRECONDITION; + } + if (GRPC_SLICE_LENGTH(*key) < kAltsAes128GcmRekeyKeyLength) { + gpr_log(GPR_ERROR, "Bad key length"); + return TSI_FAILED_PRECONDITION; + } + alts_tsi_handshaker_result* result = + static_cast(gpr_zalloc(sizeof(*result))); + result->key_data = + static_cast(gpr_zalloc(kAltsAes128GcmRekeyKeyLength)); + memcpy(result->key_data, GRPC_SLICE_START_PTR(*key), + kAltsAes128GcmRekeyKeyLength); + result->peer_identity = grpc_slice_to_c_string(*identity); + if (!resp->result.has_peer_rpc_versions) { + gpr_log(GPR_ERROR, "Peer does not set RPC protocol versions."); + return TSI_FAILED_PRECONDITION; + } + if (!grpc_gcp_rpc_protocol_versions_encode(&resp->result.peer_rpc_versions, + &result->rpc_versions)) { + gpr_log(GPR_ERROR, "Failed to serialize peer's RPC protocol versions."); + return TSI_FAILED_PRECONDITION; + } + result->is_client = is_client; + result->base.vtable = &result_vtable; + *self = &result->base; + return TSI_OK; +} + +/* gRPC provided callback used when gRPC thread model is applied. */ +static void on_handshaker_service_resp_recv(void* arg, grpc_error* error) { + alts_handshaker_client* client = static_cast(arg); + if (client == nullptr) { + gpr_log(GPR_ERROR, "ALTS handshaker client is nullptr"); + return; + } + alts_handshaker_client_handle_response(client, true); +} + +/* gRPC provided callback used when dedicatd CQ and thread are used. + * It serves to safely bring the control back to application. */ +static void on_handshaker_service_resp_recv_dedicated(void* arg, + grpc_error* error) { + alts_shared_resource_dedicated* resource = + grpc_alts_get_shared_resource_dedicated(); + grpc_cq_end_op(resource->cq, arg, GRPC_ERROR_NONE, + [](void* done_arg, grpc_cq_completion* storage) {}, nullptr, + &resource->storage); +} + +static tsi_result handshaker_next( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** result, + tsi_handshaker_on_next_done_cb cb, void* user_data) { + if (self == nullptr || cb == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to handshaker_next()"); + return TSI_INVALID_ARGUMENT; + } + if (self->handshake_shutdown) { + gpr_log(GPR_ERROR, "TSI handshake shutdown"); + return TSI_HANDSHAKE_SHUTDOWN; + } + alts_tsi_handshaker* handshaker = + reinterpret_cast(self); + tsi_result ok = TSI_OK; + if (!handshaker->has_created_handshaker_client) { + if (handshaker->channel == nullptr) { + grpc_alts_shared_resource_dedicated_start( + handshaker->handshaker_service_url); + handshaker->interested_parties = + grpc_alts_get_shared_resource_dedicated()->interested_parties; + GPR_ASSERT(handshaker->interested_parties != nullptr); + } + grpc_iomgr_cb_func grpc_cb = handshaker->channel == nullptr + ? on_handshaker_service_resp_recv_dedicated + : on_handshaker_service_resp_recv; + grpc_channel* channel = + handshaker->channel == nullptr + ? grpc_alts_get_shared_resource_dedicated()->channel + : handshaker->channel; + handshaker->client = alts_grpc_handshaker_client_create( + handshaker, channel, handshaker->handshaker_service_url, + handshaker->interested_parties, handshaker->options, + handshaker->target_name, grpc_cb, cb, user_data, + handshaker->client_vtable_for_testing, handshaker->is_client); + if (handshaker->client == nullptr) { + gpr_log(GPR_ERROR, "Failed to create ALTS handshaker client"); + return TSI_FAILED_PRECONDITION; + } + handshaker->has_created_handshaker_client = true; + } + if (handshaker->channel == nullptr && + handshaker->client_vtable_for_testing == nullptr) { + GPR_ASSERT(grpc_cq_begin_op(grpc_alts_get_shared_resource_dedicated()->cq, + handshaker->client)); + } + grpc_slice slice = (received_bytes == nullptr || received_bytes_size == 0) + ? grpc_empty_slice() + : grpc_slice_from_copied_buffer( + reinterpret_cast(received_bytes), + received_bytes_size); + if (!handshaker->has_sent_start_message) { + ok = handshaker->is_client + ? alts_handshaker_client_start_client(handshaker->client) + : alts_handshaker_client_start_server(handshaker->client, &slice); + handshaker->has_sent_start_message = true; + } else { + ok = alts_handshaker_client_next(handshaker->client, &slice); + } + grpc_slice_unref_internal(slice); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to schedule ALTS handshaker requests"); + return ok; + } + return TSI_ASYNC; +} + +/* + * This API will be invoked by a non-gRPC application, and an ExecCtx needs + * to be explicitly created in order to invoke ALTS handshaker client API's + * that assumes the caller is inside gRPC core. + */ +static tsi_result handshaker_next_dedicated( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** result, + tsi_handshaker_on_next_done_cb cb, void* user_data) { + grpc_core::ExecCtx exec_ctx; + return handshaker_next(self, received_bytes, received_bytes_size, + bytes_to_send, bytes_to_send_size, result, cb, + user_data); +} + +static void handshaker_shutdown(tsi_handshaker* self) { + GPR_ASSERT(self != nullptr); + if (self->handshake_shutdown) { + return; + } + alts_tsi_handshaker* handshaker = + reinterpret_cast(self); + alts_handshaker_client_shutdown(handshaker->client); +} + +static void handshaker_destroy(tsi_handshaker* self) { + if (self == nullptr) { + return; + } + alts_tsi_handshaker* handshaker = + reinterpret_cast(self); + alts_handshaker_client_destroy(handshaker->client); + grpc_slice_unref_internal(handshaker->target_name); + grpc_alts_credentials_options_destroy(handshaker->options); + if (handshaker->channel != nullptr) { + grpc_channel_destroy(handshaker->channel); + } + gpr_free(handshaker->handshaker_service_url); + gpr_free(handshaker); +} + +static const tsi_handshaker_vtable handshaker_vtable = { + nullptr, nullptr, + nullptr, nullptr, + nullptr, handshaker_destroy, + handshaker_next, handshaker_shutdown}; + +static const tsi_handshaker_vtable handshaker_vtable_dedicated = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + handshaker_destroy, + handshaker_next_dedicated, + handshaker_shutdown}; + +bool alts_tsi_handshaker_has_shutdown(alts_tsi_handshaker* handshaker) { + GPR_ASSERT(handshaker != nullptr); + return handshaker->base.handshake_shutdown; +} + +tsi_result alts_tsi_handshaker_create( + const grpc_alts_credentials_options* options, const char* target_name, + const char* handshaker_service_url, bool is_client, + grpc_pollset_set* interested_parties, tsi_handshaker** self) { + if (handshaker_service_url == nullptr || self == nullptr || + options == nullptr || (is_client && target_name == nullptr)) { + gpr_log(GPR_ERROR, "Invalid arguments to alts_tsi_handshaker_create()"); + return TSI_INVALID_ARGUMENT; + } + alts_tsi_handshaker* handshaker = + static_cast(gpr_zalloc(sizeof(*handshaker))); + bool use_dedicated_cq = interested_parties == nullptr; + handshaker->client = nullptr; + handshaker->is_client = is_client; + handshaker->has_sent_start_message = false; + handshaker->target_name = target_name == nullptr + ? grpc_empty_slice() + : grpc_slice_from_static_string(target_name); + handshaker->interested_parties = interested_parties; + handshaker->has_created_handshaker_client = false; + handshaker->handshaker_service_url = gpr_strdup(handshaker_service_url); + handshaker->options = grpc_alts_credentials_options_copy(options); + handshaker->base.vtable = + use_dedicated_cq ? &handshaker_vtable_dedicated : &handshaker_vtable; + handshaker->channel = + use_dedicated_cq + ? nullptr + : grpc_insecure_channel_create(handshaker->handshaker_service_url, + nullptr, nullptr); + *self = &handshaker->base; + return TSI_OK; +} + +void alts_tsi_handshaker_result_set_unused_bytes(tsi_handshaker_result* self, + grpc_slice* recv_bytes, + size_t bytes_consumed) { + GPR_ASSERT(recv_bytes != nullptr && self != nullptr); + if (GRPC_SLICE_LENGTH(*recv_bytes) == bytes_consumed) { + return; + } + alts_tsi_handshaker_result* result = + reinterpret_cast(self); + result->unused_bytes_size = GRPC_SLICE_LENGTH(*recv_bytes) - bytes_consumed; + result->unused_bytes = + static_cast(gpr_zalloc(result->unused_bytes_size)); + memcpy(result->unused_bytes, + GRPC_SLICE_START_PTR(*recv_bytes) + bytes_consumed, + result->unused_bytes_size); +} + +namespace grpc_core { +namespace internal { + +bool alts_tsi_handshaker_get_has_sent_start_message_for_testing( + alts_tsi_handshaker* handshaker) { + GPR_ASSERT(handshaker != nullptr); + return handshaker->has_sent_start_message; +} + +void alts_tsi_handshaker_set_client_vtable_for_testing( + alts_tsi_handshaker* handshaker, alts_handshaker_client_vtable* vtable) { + GPR_ASSERT(handshaker != nullptr); + handshaker->client_vtable_for_testing = vtable; +} + +bool alts_tsi_handshaker_get_is_client_for_testing( + alts_tsi_handshaker* handshaker) { + GPR_ASSERT(handshaker != nullptr); + return handshaker->is_client; +} + +alts_handshaker_client* alts_tsi_handshaker_get_client_for_testing( + alts_tsi_handshaker* handshaker) { + return handshaker->client; +} + +} // namespace internal +} // namespace grpc_core diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h new file mode 100644 index 00000000000000..32f94bc9d3feda --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker.h @@ -0,0 +1,94 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H + +#include + +#include + +#include "src/core/lib/iomgr/pollset_set.h" +#include "src/core/lib/security/credentials/alts/grpc_alts_credentials_options.h" +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api_util.h" +#include "src/core/tsi/transport_security.h" +#include "src/core/tsi/transport_security_interface.h" + +#define TSI_ALTS_SERVICE_ACCOUNT_PEER_PROPERTY "service_accont" +#define TSI_ALTS_CERTIFICATE_TYPE "ALTS" +#define TSI_ALTS_RPC_VERSIONS "rpc_versions" + +const size_t kTsiAltsNumOfPeerProperties = 3; + +typedef struct alts_tsi_handshaker alts_tsi_handshaker; + +/** + * This method creates a ALTS TSI handshaker instance. + * + * - options: ALTS credentials options containing information passed from TSI + * caller (e.g., rpc protocol versions). + * - target_name: the name of the endpoint that the channel is connecting to, + * and will be used for secure naming check. + * - handshaker_service_url: address of ALTS handshaker service in the format of + * "host:port". + * - is_client: boolean value indicating if the handshaker is used at the client + * (is_client = true) or server (is_client = false) side. + * - interested_parties: set of pollsets interested in this connection. + * - self: address of ALTS TSI handshaker instance to be returned from the + * method. + * + * It returns TSI_OK on success and an error status code on failure. Note that + * if interested_parties is nullptr, a dedicated TSI thread will be created and + * used. + */ +tsi_result alts_tsi_handshaker_create( + const grpc_alts_credentials_options* options, const char* target_name, + const char* handshaker_service_url, bool is_client, + grpc_pollset_set* interested_parties, tsi_handshaker** self); + +/** + * This method creates an ALTS TSI handshaker result instance. + * + * - resp: data received from the handshaker service. + * - is_client: a boolean value indicating if the result belongs to a + * client or not. + * - result: address of ALTS TSI handshaker result instance. + */ +tsi_result alts_tsi_handshaker_result_create(grpc_gcp_handshaker_resp* resp, + bool is_client, + tsi_handshaker_result** result); + +/** + * This method sets unused bytes of ALTS TSI handshaker result instance. + * + * - result: an ALTS TSI handshaker result instance. + * - recv_bytes: data received from the handshaker service. + * - bytes_consumed: size of data consumed by the handshaker service. + */ +void alts_tsi_handshaker_result_set_unused_bytes(tsi_handshaker_result* result, + grpc_slice* recv_bytes, + size_t bytes_consumed); + +/** + * This method returns a boolean value indicating if an ALTS TSI handshaker + * has been shutdown or not. + */ +bool alts_tsi_handshaker_has_shutdown(alts_tsi_handshaker* handshaker); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_H */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h new file mode 100644 index 00000000000000..ec2616e95fc093 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_handshaker_private.h @@ -0,0 +1,83 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H + +#include + +#include "src/core/tsi/alts/handshaker/alts_handshaker_client.h" + +namespace grpc_core { +namespace internal { + +/** + * Unsafe, use for testing only. */ + +alts_handshaker_client* alts_tsi_handshaker_get_client_for_testing( + alts_tsi_handshaker* handshaker); + +bool alts_tsi_handshaker_get_has_sent_start_message_for_testing( + alts_tsi_handshaker* handshaker); + +void alts_tsi_handshaker_set_client_vtable_for_testing( + alts_tsi_handshaker* handshaker, alts_handshaker_client_vtable* vtable); + +bool alts_tsi_handshaker_get_is_client_for_testing( + alts_tsi_handshaker* handshaker); + +void alts_handshaker_client_set_grpc_caller_for_testing( + alts_handshaker_client* client, alts_grpc_caller caller); + +grpc_byte_buffer* alts_handshaker_client_get_send_buffer_for_testing( + alts_handshaker_client* client); + +grpc_byte_buffer** alts_handshaker_client_get_recv_buffer_addr_for_testing( + alts_handshaker_client* client); + +grpc_metadata_array* alts_handshaker_client_get_initial_metadata_for_testing( + alts_handshaker_client* client); + +void alts_handshaker_client_set_recv_bytes_for_testing( + alts_handshaker_client* client, grpc_slice* recv_bytes); + +void alts_handshaker_client_check_fields_for_testing( + alts_handshaker_client* client, tsi_handshaker_on_next_done_cb cb, + void* user_data, bool has_sent_start_message, grpc_slice* recv_bytes); + +void alts_handshaker_client_set_fields_for_testing( + alts_handshaker_client* client, alts_tsi_handshaker* handshaker, + tsi_handshaker_on_next_done_cb cb, void* user_data, + grpc_byte_buffer* recv_buffer, grpc_status_code status); + +void alts_handshaker_client_set_vtable_for_testing( + alts_handshaker_client* client, alts_handshaker_client_vtable* vtable); + +alts_tsi_handshaker* alts_handshaker_client_get_handshaker_for_testing( + alts_handshaker_client* client); + +void alts_handshaker_client_set_cb_for_testing( + alts_handshaker_client* client, tsi_handshaker_on_next_done_cb cb); + +grpc_closure* alts_handshaker_client_get_closure_for_testing( + alts_handshaker_client* client); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_HANDSHAKER_PRIVATE_H */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.cc b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.cc new file mode 100644 index 00000000000000..1747f1ad04f03e --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.cc @@ -0,0 +1,60 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/handshaker/alts_tsi_utils.h" + +#include + +#include "src/core/lib/slice/slice_internal.h" + +tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code) { + switch (code) { + case GRPC_STATUS_OK: + return TSI_OK; + case GRPC_STATUS_UNKNOWN: + return TSI_UNKNOWN_ERROR; + case GRPC_STATUS_INVALID_ARGUMENT: + return TSI_INVALID_ARGUMENT; + case GRPC_STATUS_NOT_FOUND: + return TSI_NOT_FOUND; + case GRPC_STATUS_INTERNAL: + return TSI_INTERNAL_ERROR; + default: + return TSI_UNKNOWN_ERROR; + } +} + +grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response( + grpc_byte_buffer* resp_buffer) { + GPR_ASSERT(resp_buffer != nullptr); + grpc_byte_buffer_reader bbr; + grpc_byte_buffer_reader_init(&bbr, resp_buffer); + grpc_slice slice = grpc_byte_buffer_reader_readall(&bbr); + grpc_gcp_handshaker_resp* resp = grpc_gcp_handshaker_resp_create(); + bool ok = grpc_gcp_handshaker_resp_decode(slice, resp); + grpc_slice_unref_internal(slice); + grpc_byte_buffer_reader_destroy(&bbr); + if (!ok) { + grpc_gcp_handshaker_resp_destroy(resp); + gpr_log(GPR_ERROR, "grpc_gcp_handshaker_resp_decode() failed"); + return nullptr; + } + return resp; +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.h b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.h new file mode 100644 index 00000000000000..9ef649de2b9b55 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/alts_tsi_utils.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H + +#include + +#include +#include + +#include "src/core/tsi/alts/handshaker/alts_handshaker_service_api.h" +#include "src/core/tsi/transport_security_interface.h" + +/** + * This method converts grpc_status_code code to the corresponding tsi_result + * code. + * + * - code: grpc_status_code code. + * + * It returns the converted tsi_result code. + */ +tsi_result alts_tsi_utils_convert_to_tsi_result(grpc_status_code code); + +/** + * This method deserializes a handshaker response returned from ALTS handshaker + * service. + * + * - bytes_received: data returned from ALTS handshaker service. + * + * It returns a deserialized handshaker response on success and nullptr on + * failure. + */ +grpc_gcp_handshaker_resp* alts_tsi_utils_deserialize_response( + grpc_byte_buffer* resp_buffer); + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_ALTS_TSI_UTILS_H */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.c b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.c new file mode 100644 index 00000000000000..5fb152a558e6b4 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.c @@ -0,0 +1,47 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/tsi/alts/handshaker/altscontext.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_gcp_AltsContext_fields[7] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_AltsContext, application_protocol, application_protocol, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, record_protocol, application_protocol, 0), + PB_FIELD( 3, UENUM , OPTIONAL, STATIC , OTHER, grpc_gcp_AltsContext, security_level, record_protocol, 0), + PB_FIELD( 4, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, peer_service_account, security_level, 0), + PB_FIELD( 5, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_AltsContext, local_service_account, peer_service_account, 0), + PB_FIELD( 6, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_AltsContext, peer_rpc_versions, local_service_account, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_AltsContext, peer_rpc_versions) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_AltsContext) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_AltsContext, peer_rpc_versions) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_AltsContext) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.h b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.h new file mode 100644 index 00000000000000..632b20c0e2a003 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/altscontext.pb.h @@ -0,0 +1,63 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED +#define PB_GRPC_GCP_ALTSCONTEXT_PB_H_INCLUDED +#include "pb.h" +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Struct definitions */ +typedef struct _grpc_gcp_AltsContext { + pb_callback_t application_protocol; + pb_callback_t record_protocol; + bool has_security_level; + grpc_gcp_SecurityLevel security_level; + pb_callback_t peer_service_account; + pb_callback_t local_service_account; + bool has_peer_rpc_versions; + grpc_gcp_RpcProtocolVersions peer_rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_AltsContext) */ +} grpc_gcp_AltsContext; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_gcp_AltsContext_init_default {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_AltsContext_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, false, (grpc_gcp_SecurityLevel)0, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_gcp_AltsContext_application_protocol_tag 1 +#define grpc_gcp_AltsContext_record_protocol_tag 2 +#define grpc_gcp_AltsContext_security_level_tag 3 +#define grpc_gcp_AltsContext_peer_service_account_tag 4 +#define grpc_gcp_AltsContext_local_service_account_tag 5 +#define grpc_gcp_AltsContext_peer_rpc_versions_tag 6 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_gcp_AltsContext_fields[7]; + +/* Maximum encoded size of messages (where known) */ +/* grpc_gcp_AltsContext_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define ALTSCONTEXT_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.c b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.c new file mode 100644 index 00000000000000..5450b1602d4695 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.c @@ -0,0 +1,122 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/tsi/alts/handshaker/handshaker.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_gcp_Endpoint_fields[4] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Endpoint, ip_address, ip_address, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_gcp_Endpoint, port, ip_address, 0), + PB_FIELD( 3, UENUM , OPTIONAL, STATIC , OTHER, grpc_gcp_Endpoint, protocol, port, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_Identity_fields[3] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_Identity, service_account, service_account, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_Identity, hostname, service_account, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10] = { + PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, grpc_gcp_StartClientHandshakeReq, handshake_security_protocol, handshake_security_protocol, 0), + PB_FIELD( 2, STRING , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, application_protocols, handshake_security_protocol, 0), + PB_FIELD( 3, STRING , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, record_protocols, application_protocols, 0), + PB_FIELD( 4, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_identities, record_protocols, &grpc_gcp_Identity_fields), + PB_FIELD( 5, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, local_identity, target_identities, &grpc_gcp_Identity_fields), + PB_FIELD( 6, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, local_endpoint, local_identity, &grpc_gcp_Endpoint_fields), + PB_FIELD( 7, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields), + PB_FIELD( 8, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartClientHandshakeReq, target_name, remote_endpoint, 0), + PB_FIELD( 9, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartClientHandshakeReq, rpc_versions, target_name, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3] = { + PB_FIELD( 1, STRING , REPEATED, CALLBACK, FIRST, grpc_gcp_ServerHandshakeParameters, record_protocols, record_protocols, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_gcp_ServerHandshakeParameters, local_identities, record_protocols, &grpc_gcp_Identity_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7] = { + PB_FIELD( 1, STRING , REPEATED, CALLBACK, FIRST, grpc_gcp_StartServerHandshakeReq, application_protocols, application_protocols, 0), + PB_FIELD( 2, MESSAGE , REPEATED, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, handshake_parameters, application_protocols, &grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields), + PB_FIELD( 3, BYTES , OPTIONAL, CALLBACK, OTHER, grpc_gcp_StartServerHandshakeReq, in_bytes, handshake_parameters, 0), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, local_endpoint, in_bytes, &grpc_gcp_Endpoint_fields), + PB_FIELD( 5, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, remote_endpoint, local_endpoint, &grpc_gcp_Endpoint_fields), + PB_FIELD( 6, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq, rpc_versions, remote_endpoint, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3] = { + PB_FIELD( 1, INT32 , OPTIONAL, STATIC , FIRST, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, key, key, 0), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value, key, &grpc_gcp_ServerHandshakeParameters_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2] = { + PB_FIELD( 1, BYTES , OPTIONAL, CALLBACK, FIRST, grpc_gcp_NextHandshakeMessageReq, in_bytes, in_bytes, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerReq_fields[4] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_gcp_HandshakerReq, client_start, client_start, &grpc_gcp_StartClientHandshakeReq_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerReq, server_start, client_start, &grpc_gcp_StartServerHandshakeReq_fields), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerReq, next, server_start, &grpc_gcp_NextHandshakeMessageReq_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerResult_fields[8] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResult, application_protocol, application_protocol, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, record_protocol, application_protocol, 0), + PB_FIELD( 3, BYTES , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerResult, key_data, record_protocol, 0), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, peer_identity, key_data, &grpc_gcp_Identity_fields), + PB_FIELD( 5, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, local_identity, peer_identity, &grpc_gcp_Identity_fields), + PB_FIELD( 6, BOOL , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, keep_channel_open, local_identity, 0), + PB_FIELD( 7, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResult, peer_rpc_versions, keep_channel_open, &grpc_gcp_RpcProtocolVersions_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerStatus_fields[3] = { + PB_FIELD( 1, UINT32 , OPTIONAL, STATIC , FIRST, grpc_gcp_HandshakerStatus, code, code, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, grpc_gcp_HandshakerStatus, details, code, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_HandshakerResp_fields[5] = { + PB_FIELD( 1, BYTES , OPTIONAL, CALLBACK, FIRST, grpc_gcp_HandshakerResp, out_frames, out_frames, 0), + PB_FIELD( 2, UINT32 , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResp, bytes_consumed, out_frames, 0), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResp, result, bytes_consumed, &grpc_gcp_HandshakerResult_fields), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_HandshakerResp, status, result, &grpc_gcp_HandshakerStatus_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_StartClientHandshakeReq, target_identities) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 65536 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 65536 && pb_membersize(grpc_gcp_HandshakerReq, next) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 65536 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, result) < 65536 && pb_membersize(grpc_gcp_HandshakerResp, status) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_StartClientHandshakeReq, target_identities) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_identity) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartClientHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_ServerHandshakeParameters, local_identities) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, handshake_parameters[0]) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, local_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, remote_endpoint) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq, rpc_versions) < 256 && pb_membersize(grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry, value) < 256 && pb_membersize(grpc_gcp_HandshakerReq, client_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, server_start) < 256 && pb_membersize(grpc_gcp_HandshakerReq, next) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, local_identity) < 256 && pb_membersize(grpc_gcp_HandshakerResult, peer_rpc_versions) < 256 && pb_membersize(grpc_gcp_HandshakerResp, result) < 256 && pb_membersize(grpc_gcp_HandshakerResp, status) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_Endpoint_grpc_gcp_Identity_grpc_gcp_StartClientHandshakeReq_grpc_gcp_ServerHandshakeParameters_grpc_gcp_StartServerHandshakeReq_grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_grpc_gcp_NextHandshakeMessageReq_grpc_gcp_HandshakerReq_grpc_gcp_HandshakerResult_grpc_gcp_HandshakerStatus_grpc_gcp_HandshakerResp) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.h b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.h new file mode 100644 index 00000000000000..5ee42a3c69f01b --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/handshaker.pb.h @@ -0,0 +1,254 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED +#define PB_GRPC_GCP_HANDSHAKER_PB_H_INCLUDED +#include "pb.h" +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _grpc_gcp_HandshakeProtocol { + grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED = 0, + grpc_gcp_HandshakeProtocol_TLS = 1, + grpc_gcp_HandshakeProtocol_ALTS = 2 +} grpc_gcp_HandshakeProtocol; +#define _grpc_gcp_HandshakeProtocol_MIN grpc_gcp_HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED +#define _grpc_gcp_HandshakeProtocol_MAX grpc_gcp_HandshakeProtocol_ALTS +#define _grpc_gcp_HandshakeProtocol_ARRAYSIZE ((grpc_gcp_HandshakeProtocol)(grpc_gcp_HandshakeProtocol_ALTS+1)) + +typedef enum _grpc_gcp_NetworkProtocol { + grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED = 0, + grpc_gcp_NetworkProtocol_TCP = 1, + grpc_gcp_NetworkProtocol_UDP = 2 +} grpc_gcp_NetworkProtocol; +#define _grpc_gcp_NetworkProtocol_MIN grpc_gcp_NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED +#define _grpc_gcp_NetworkProtocol_MAX grpc_gcp_NetworkProtocol_UDP +#define _grpc_gcp_NetworkProtocol_ARRAYSIZE ((grpc_gcp_NetworkProtocol)(grpc_gcp_NetworkProtocol_UDP+1)) + +/* Struct definitions */ +typedef struct _grpc_gcp_Identity { + pb_callback_t service_account; + pb_callback_t hostname; +/* @@protoc_insertion_point(struct:grpc_gcp_Identity) */ +} grpc_gcp_Identity; + +typedef struct _grpc_gcp_NextHandshakeMessageReq { + pb_callback_t in_bytes; +/* @@protoc_insertion_point(struct:grpc_gcp_NextHandshakeMessageReq) */ +} grpc_gcp_NextHandshakeMessageReq; + +typedef struct _grpc_gcp_ServerHandshakeParameters { + pb_callback_t record_protocols; + pb_callback_t local_identities; +/* @@protoc_insertion_point(struct:grpc_gcp_ServerHandshakeParameters) */ +} grpc_gcp_ServerHandshakeParameters; + +typedef struct _grpc_gcp_Endpoint { + pb_callback_t ip_address; + bool has_port; + int32_t port; + bool has_protocol; + grpc_gcp_NetworkProtocol protocol; +/* @@protoc_insertion_point(struct:grpc_gcp_Endpoint) */ +} grpc_gcp_Endpoint; + +typedef struct _grpc_gcp_HandshakerResult { + pb_callback_t application_protocol; + pb_callback_t record_protocol; + pb_callback_t key_data; + bool has_peer_identity; + grpc_gcp_Identity peer_identity; + bool has_local_identity; + grpc_gcp_Identity local_identity; + bool has_keep_channel_open; + bool keep_channel_open; + bool has_peer_rpc_versions; + grpc_gcp_RpcProtocolVersions peer_rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResult) */ +} grpc_gcp_HandshakerResult; + +typedef struct _grpc_gcp_HandshakerStatus { + bool has_code; + uint32_t code; + pb_callback_t details; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerStatus) */ +} grpc_gcp_HandshakerStatus; + +typedef struct _grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry { + bool has_key; + int32_t key; + bool has_value; + grpc_gcp_ServerHandshakeParameters value; +/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry) */ +} grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry; + +typedef struct _grpc_gcp_HandshakerResp { + pb_callback_t out_frames; + bool has_bytes_consumed; + uint32_t bytes_consumed; + bool has_result; + grpc_gcp_HandshakerResult result; + bool has_status; + grpc_gcp_HandshakerStatus status; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerResp) */ +} grpc_gcp_HandshakerResp; + +typedef struct _grpc_gcp_StartClientHandshakeReq { + bool has_handshake_security_protocol; + grpc_gcp_HandshakeProtocol handshake_security_protocol; + pb_callback_t application_protocols; + pb_callback_t record_protocols; + pb_callback_t target_identities; + bool has_local_identity; + grpc_gcp_Identity local_identity; + bool has_local_endpoint; + grpc_gcp_Endpoint local_endpoint; + bool has_remote_endpoint; + grpc_gcp_Endpoint remote_endpoint; + pb_callback_t target_name; + bool has_rpc_versions; + grpc_gcp_RpcProtocolVersions rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_StartClientHandshakeReq) */ +} grpc_gcp_StartClientHandshakeReq; + +typedef struct _grpc_gcp_StartServerHandshakeReq { + pb_callback_t application_protocols; + pb_size_t handshake_parameters_count; + grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry handshake_parameters[3]; + pb_callback_t in_bytes; + bool has_local_endpoint; + grpc_gcp_Endpoint local_endpoint; + bool has_remote_endpoint; + grpc_gcp_Endpoint remote_endpoint; + bool has_rpc_versions; + grpc_gcp_RpcProtocolVersions rpc_versions; +/* @@protoc_insertion_point(struct:grpc_gcp_StartServerHandshakeReq) */ +} grpc_gcp_StartServerHandshakeReq; + +typedef struct _grpc_gcp_HandshakerReq { + bool has_client_start; + grpc_gcp_StartClientHandshakeReq client_start; + bool has_server_start; + grpc_gcp_StartServerHandshakeReq server_start; + bool has_next; + grpc_gcp_NextHandshakeMessageReq next; +/* @@protoc_insertion_point(struct:grpc_gcp_HandshakerReq) */ +} grpc_gcp_HandshakerReq; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_gcp_Endpoint_init_default {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0} +#define grpc_gcp_Identity_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartClientHandshakeReq_init_default {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_ServerHandshakeParameters_init_default {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartServerHandshakeReq_init_default {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_Endpoint_init_default, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_default {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_default} +#define grpc_gcp_NextHandshakeMessageReq_init_default {{{NULL}, NULL}} +#define grpc_gcp_HandshakerReq_init_default {false, grpc_gcp_StartClientHandshakeReq_init_default, false, grpc_gcp_StartServerHandshakeReq_init_default, false, grpc_gcp_NextHandshakeMessageReq_init_default} +#define grpc_gcp_HandshakerResult_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_default, false, grpc_gcp_Identity_init_default, false, 0, false, grpc_gcp_RpcProtocolVersions_init_default} +#define grpc_gcp_HandshakerStatus_init_default {false, 0, {{NULL}, NULL}} +#define grpc_gcp_HandshakerResp_init_default {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_default, false, grpc_gcp_HandshakerStatus_init_default} +#define grpc_gcp_Endpoint_init_zero {{{NULL}, NULL}, false, 0, false, (grpc_gcp_NetworkProtocol)0} +#define grpc_gcp_Identity_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartClientHandshakeReq_init_zero {false, (grpc_gcp_HandshakeProtocol)0, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, {{NULL}, NULL}, false, grpc_gcp_RpcProtocolVersions_init_zero} +#define grpc_gcp_ServerHandshakeParameters_init_zero {{{NULL}, NULL}, {{NULL}, NULL}} +#define grpc_gcp_StartServerHandshakeReq_init_zero {{{NULL}, NULL}, 0, {grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero, grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero}, {{NULL}, NULL}, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_Endpoint_init_zero, false, grpc_gcp_RpcProtocolVersions_init_zero} +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_init_zero {false, 0, false, grpc_gcp_ServerHandshakeParameters_init_zero} +#define grpc_gcp_NextHandshakeMessageReq_init_zero {{{NULL}, NULL}} +#define grpc_gcp_HandshakerReq_init_zero {false, grpc_gcp_StartClientHandshakeReq_init_zero, false, grpc_gcp_StartServerHandshakeReq_init_zero, false, grpc_gcp_NextHandshakeMessageReq_init_zero} +#define grpc_gcp_HandshakerResult_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, false, grpc_gcp_Identity_init_zero, false, grpc_gcp_Identity_init_zero, false, 0, false, grpc_gcp_RpcProtocolVersions_init_zero} +#define grpc_gcp_HandshakerStatus_init_zero {false, 0, {{NULL}, NULL}} +#define grpc_gcp_HandshakerResp_init_zero {{{NULL}, NULL}, false, 0, false, grpc_gcp_HandshakerResult_init_zero, false, grpc_gcp_HandshakerStatus_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_gcp_Identity_service_account_tag 1 +#define grpc_gcp_Identity_hostname_tag 2 +#define grpc_gcp_NextHandshakeMessageReq_in_bytes_tag 1 +#define grpc_gcp_ServerHandshakeParameters_record_protocols_tag 1 +#define grpc_gcp_ServerHandshakeParameters_local_identities_tag 2 +#define grpc_gcp_Endpoint_ip_address_tag 1 +#define grpc_gcp_Endpoint_port_tag 2 +#define grpc_gcp_Endpoint_protocol_tag 3 +#define grpc_gcp_HandshakerResult_application_protocol_tag 1 +#define grpc_gcp_HandshakerResult_record_protocol_tag 2 +#define grpc_gcp_HandshakerResult_key_data_tag 3 +#define grpc_gcp_HandshakerResult_peer_identity_tag 4 +#define grpc_gcp_HandshakerResult_local_identity_tag 5 +#define grpc_gcp_HandshakerResult_keep_channel_open_tag 6 +#define grpc_gcp_HandshakerResult_peer_rpc_versions_tag 7 +#define grpc_gcp_HandshakerStatus_code_tag 1 +#define grpc_gcp_HandshakerStatus_details_tag 2 +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_key_tag 1 +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_value_tag 2 +#define grpc_gcp_HandshakerResp_out_frames_tag 1 +#define grpc_gcp_HandshakerResp_bytes_consumed_tag 2 +#define grpc_gcp_HandshakerResp_result_tag 3 +#define grpc_gcp_HandshakerResp_status_tag 4 +#define grpc_gcp_StartClientHandshakeReq_handshake_security_protocol_tag 1 +#define grpc_gcp_StartClientHandshakeReq_application_protocols_tag 2 +#define grpc_gcp_StartClientHandshakeReq_record_protocols_tag 3 +#define grpc_gcp_StartClientHandshakeReq_target_identities_tag 4 +#define grpc_gcp_StartClientHandshakeReq_local_identity_tag 5 +#define grpc_gcp_StartClientHandshakeReq_local_endpoint_tag 6 +#define grpc_gcp_StartClientHandshakeReq_remote_endpoint_tag 7 +#define grpc_gcp_StartClientHandshakeReq_target_name_tag 8 +#define grpc_gcp_StartClientHandshakeReq_rpc_versions_tag 9 +#define grpc_gcp_StartServerHandshakeReq_application_protocols_tag 1 +#define grpc_gcp_StartServerHandshakeReq_handshake_parameters_tag 2 +#define grpc_gcp_StartServerHandshakeReq_in_bytes_tag 3 +#define grpc_gcp_StartServerHandshakeReq_local_endpoint_tag 4 +#define grpc_gcp_StartServerHandshakeReq_remote_endpoint_tag 5 +#define grpc_gcp_StartServerHandshakeReq_rpc_versions_tag 6 +#define grpc_gcp_HandshakerReq_client_start_tag 1 +#define grpc_gcp_HandshakerReq_server_start_tag 2 +#define grpc_gcp_HandshakerReq_next_tag 3 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_gcp_Endpoint_fields[4]; +extern const pb_field_t grpc_gcp_Identity_fields[3]; +extern const pb_field_t grpc_gcp_StartClientHandshakeReq_fields[10]; +extern const pb_field_t grpc_gcp_ServerHandshakeParameters_fields[3]; +extern const pb_field_t grpc_gcp_StartServerHandshakeReq_fields[7]; +extern const pb_field_t grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_fields[3]; +extern const pb_field_t grpc_gcp_NextHandshakeMessageReq_fields[2]; +extern const pb_field_t grpc_gcp_HandshakerReq_fields[4]; +extern const pb_field_t grpc_gcp_HandshakerResult_fields[8]; +extern const pb_field_t grpc_gcp_HandshakerStatus_fields[3]; +extern const pb_field_t grpc_gcp_HandshakerResp_fields[5]; + +/* Maximum encoded size of messages (where known) */ +/* grpc_gcp_Endpoint_size depends on runtime parameters */ +/* grpc_gcp_Identity_size depends on runtime parameters */ +/* grpc_gcp_StartClientHandshakeReq_size depends on runtime parameters */ +/* grpc_gcp_ServerHandshakeParameters_size depends on runtime parameters */ +/* grpc_gcp_StartServerHandshakeReq_size depends on runtime parameters */ +#define grpc_gcp_StartServerHandshakeReq_HandshakeParametersEntry_size (17 + grpc_gcp_ServerHandshakeParameters_size) +/* grpc_gcp_NextHandshakeMessageReq_size depends on runtime parameters */ +#define grpc_gcp_HandshakerReq_size (18 + grpc_gcp_StartClientHandshakeReq_size + grpc_gcp_StartServerHandshakeReq_size + grpc_gcp_NextHandshakeMessageReq_size) +/* grpc_gcp_HandshakerResult_size depends on runtime parameters */ +/* grpc_gcp_HandshakerStatus_size depends on runtime parameters */ +/* grpc_gcp_HandshakerResp_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define HANDSHAKER_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/altscontext.proto b/third_party/grpc/src/core/tsi/alts/handshaker/proto/altscontext.proto new file mode 100644 index 00000000000000..9a1dad5f56a1dd --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/altscontext.proto @@ -0,0 +1,41 @@ +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "transport_security_common.proto"; + +package grpc.gcp; + +option java_package = "io.grpc.alts.internal"; + +message AltsContext { + // The application protocol negotiated for this connection. + string application_protocol = 1; + + // The record protocol negotiated for this connection. + string record_protocol = 2; + + // The security level of the created secure channel. + SecurityLevel security_level = 3; + + // The peer service account. + string peer_service_account = 4; + + // The local service account. + string local_service_account = 5; + + // The RPC protocol versions supported by the peer. + RpcProtocolVersions peer_rpc_versions = 6; +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.options b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.options new file mode 100644 index 00000000000000..702ba3802af57d --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.options @@ -0,0 +1,2 @@ +handshaker.proto no_unions:true +grpc.gcp.StartServerHandshakeReq.handshake_parameters max_count:3 diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.proto b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.proto new file mode 100644 index 00000000000000..84a4153b703771 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/handshaker.proto @@ -0,0 +1,224 @@ +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "transport_security_common.proto"; + +package grpc.gcp; + +option java_package = "io.grpc.alts.internal"; + +enum HandshakeProtocol { + // Default value. + HANDSHAKE_PROTOCOL_UNSPECIFIED = 0; + + // TLS handshake protocol. + TLS = 1; + + // Application Layer Transport Security handshake protocol. + ALTS = 2; +} + +enum NetworkProtocol { + NETWORK_PROTOCOL_UNSPECIFIED = 0; + TCP = 1; + UDP = 2; +} + +message Endpoint { + // IP address. It should contain an IPv4 or IPv6 string literal, e.g. + // "192.168.0.1" or "2001:db8::1". + string ip_address = 1; + + // Port number. + int32 port = 2; + + // Network protocol (e.g., TCP, UDP) associated with this endpoint. + NetworkProtocol protocol = 3; +} + +message Identity { + oneof identity_oneof { + // Service account of a connection endpoint. + string service_account = 1; + + // Hostname of a connection endpoint. + string hostname = 2; + } +} + +message StartClientHandshakeReq { + // Handshake security protocol requested by the client. + HandshakeProtocol handshake_security_protocol = 1; + + // The application protocols supported by the client, e.g., "h2" (for http2), + // "grpc". + repeated string application_protocols = 2; + + // The record protocols supported by the client, e.g., + // "ALTSRP_GCM_AES128". + repeated string record_protocols = 3; + + // (Optional) Describes which server identities are acceptable by the client. + // If target identities are provided and none of them matches the peer + // identity of the server, handshake will fail. + repeated Identity target_identities = 4; + + // (Optional) Application may specify a local identity. Otherwise, the + // handshaker chooses a default local identity. + Identity local_identity = 5; + + // (Optional) Local endpoint information of the connection to the server, + // such as local IP address, port number, and network protocol. + Endpoint local_endpoint = 6; + + // (Optional) Endpoint information of the remote server, such as IP address, + // port number, and network protocol. + Endpoint remote_endpoint = 7; + + // (Optional) If target name is provided, a secure naming check is performed + // to verify that the peer authenticated identity is indeed authorized to run + // the target name. + string target_name = 8; + + // (Optional) RPC protocol versions supported by the client. + RpcProtocolVersions rpc_versions = 9; +} + +message ServerHandshakeParameters { + // The record protocols supported by the server, e.g., + // "ALTSRP_GCM_AES128". + repeated string record_protocols = 1; + + // (Optional) A list of local identities supported by the server, if + // specified. Otherwise, the handshaker chooses a default local identity. + repeated Identity local_identities = 2; +} + +message StartServerHandshakeReq { + // The application protocols supported by the server, e.g., "h2" (for http2), + // "grpc". + repeated string application_protocols = 1; + + // Handshake parameters (record protocols and local identities supported by + // the server) mapped by the handshake protocol. Each handshake security + // protocol (e.g., TLS or ALTS) has its own set of record protocols and local + // identities. Since protobuf does not support enum as key to the map, the key + // to handshake_parameters is the integer value of HandshakeProtocol enum. + map handshake_parameters = 2; + + // Bytes in out_frames returned from the peer's HandshakerResp. It is possible + // that the peer's out_frames are split into multiple HandshakReq messages. + bytes in_bytes = 3; + + // (Optional) Local endpoint information of the connection to the client, + // such as local IP address, port number, and network protocol. + Endpoint local_endpoint = 4; + + // (Optional) Endpoint information of the remote client, such as IP address, + // port number, and network protocol. + Endpoint remote_endpoint = 5; + + // (Optional) RPC protocol versions supported by the server. + RpcProtocolVersions rpc_versions = 6; +} + +message NextHandshakeMessageReq { + // Bytes in out_frames returned from the peer's HandshakerResp. It is possible + // that the peer's out_frames are split into multiple NextHandshakerMessageReq + // messages. + bytes in_bytes = 1; +} + +message HandshakerReq { + oneof req_oneof { + // The start client handshake request message. + StartClientHandshakeReq client_start = 1; + + // The start server handshake request message. + StartServerHandshakeReq server_start = 2; + + // The next handshake request message. + NextHandshakeMessageReq next = 3; + } +} + +message HandshakerResult { + // The application protocol negotiated for this connection. + string application_protocol = 1; + + // The record protocol negotiated for this connection. + string record_protocol = 2; + + // Cryptographic key data. The key data may be more than the key length + // required for the record protocol, thus the client of the handshaker + // service needs to truncate the key data into the right key length. + bytes key_data = 3; + + // The authenticated identity of the peer. + Identity peer_identity = 4; + + // The local identity used in the handshake. + Identity local_identity = 5; + + // Indicate whether the handshaker service client should keep the channel + // between the handshaker service open, e.g., in order to handle + // post-handshake messages in the future. + bool keep_channel_open = 6; + + // The RPC protocol versions supported by the peer. + RpcProtocolVersions peer_rpc_versions = 7; +} + +message HandshakerStatus { + // The status code. This could be the gRPC status code. + uint32 code = 1; + + // The status details. + string details = 2; +} + +message HandshakerResp { + // Frames to be given to the peer for the NextHandshakeMessageReq. May be + // empty if no out_frames have to be sent to the peer or if in_bytes in the + // HandshakerReq are incomplete. All the non-empty out frames must be sent to + // the peer even if the handshaker status is not OK as these frames may + // contain the alert frames. + bytes out_frames = 1; + + // Number of bytes in the in_bytes consumed by the handshaker. It is possible + // that part of in_bytes in HandshakerReq was unrelated to the handshake + // process. + uint32 bytes_consumed = 2; + + // This is set iff the handshake was successful. out_frames may still be set + // to frames that needs to be forwarded to the peer. + HandshakerResult result = 3; + + // Status of the handshaker. + HandshakerStatus status = 4; +} + +service HandshakerService { + // Handshaker service accepts a stream of handshaker request, returning a + // stream of handshaker response. Client is expected to send exactly one + // message with either client_start or server_start followed by one or more + // messages with next. Each time client sends a request, the handshaker + // service expects to respond. Client does not have to wait for service's + // response before sending next request. + rpc DoHandshake(stream HandshakerReq) + returns (stream HandshakerResp) { + } +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/proto/transport_security_common.proto b/third_party/grpc/src/core/tsi/alts/handshaker/proto/transport_security_common.proto new file mode 100644 index 00000000000000..d0f861e6446730 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/proto/transport_security_common.proto @@ -0,0 +1,40 @@ +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package grpc.gcp; + +option java_package = "io.grpc.alts.internal"; + +// The security level of the created channel. The list is sorted in increasing +// level of security. This order must always be maintained. +enum SecurityLevel { + SECURITY_NONE = 0; + INTEGRITY_ONLY = 1; + INTEGRITY_AND_PRIVACY = 2; +} + +// Max and min supported RPC protocol versions. +message RpcProtocolVersions { + // RPC version contains a major version and a minor version. + message Version { + uint32 major = 1; + uint32 minor = 2; + } + // Maximum supported RPC version. + Version max_rpc_version = 1; + // Minimum supported RPC version. + Version min_rpc_version = 2; +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.c b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.c new file mode 100644 index 00000000000000..326b1b10ab78d8 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.c @@ -0,0 +1,49 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_gcp_RpcProtocolVersions, max_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_gcp_RpcProtocolVersions, min_rpc_version, max_rpc_version, &grpc_gcp_RpcProtocolVersions_Version_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3] = { + PB_FIELD( 1, UINT32 , OPTIONAL, STATIC , FIRST, grpc_gcp_RpcProtocolVersions_Version, major, major, 0), + PB_FIELD( 2, UINT32 , OPTIONAL, STATIC , OTHER, grpc_gcp_RpcProtocolVersions_Version, minor, major, 0), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 65536 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_gcp_RpcProtocolVersions, max_rpc_version) < 256 && pb_membersize(grpc_gcp_RpcProtocolVersions, min_rpc_version) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_gcp_RpcProtocolVersions_grpc_gcp_RpcProtocolVersions_Version) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.h b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.h new file mode 100644 index 00000000000000..87d9abf149f191 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common.pb.h @@ -0,0 +1,78 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.7-dev */ + +#ifndef PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED +#define PB_GRPC_GCP_TRANSPORT_SECURITY_COMMON_PB_H_INCLUDED +#include "pb.h" +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _grpc_gcp_SecurityLevel { + grpc_gcp_SecurityLevel_SECURITY_NONE = 0, + grpc_gcp_SecurityLevel_INTEGRITY_ONLY = 1, + grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY = 2 +} grpc_gcp_SecurityLevel; +#define _grpc_gcp_SecurityLevel_MIN grpc_gcp_SecurityLevel_SECURITY_NONE +#define _grpc_gcp_SecurityLevel_MAX grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY +#define _grpc_gcp_SecurityLevel_ARRAYSIZE ((grpc_gcp_SecurityLevel)(grpc_gcp_SecurityLevel_INTEGRITY_AND_PRIVACY+1)) + +/* Struct definitions */ +typedef struct _grpc_gcp_RpcProtocolVersions_Version { + bool has_major; + uint32_t major; + bool has_minor; + uint32_t minor; +/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions_Version) */ +} grpc_gcp_RpcProtocolVersions_Version; + +typedef struct _grpc_gcp_RpcProtocolVersions { + bool has_max_rpc_version; + grpc_gcp_RpcProtocolVersions_Version max_rpc_version; + bool has_min_rpc_version; + grpc_gcp_RpcProtocolVersions_Version min_rpc_version; +/* @@protoc_insertion_point(struct:grpc_gcp_RpcProtocolVersions) */ +} grpc_gcp_RpcProtocolVersions; + +/* Default values for struct fields */ + +/* Initializer values for message structs */ +#define grpc_gcp_RpcProtocolVersions_init_default {false, grpc_gcp_RpcProtocolVersions_Version_init_default, false, grpc_gcp_RpcProtocolVersions_Version_init_default} +#define grpc_gcp_RpcProtocolVersions_Version_init_default {false, 0, false, 0} +#define grpc_gcp_RpcProtocolVersions_init_zero {false, grpc_gcp_RpcProtocolVersions_Version_init_zero, false, grpc_gcp_RpcProtocolVersions_Version_init_zero} +#define grpc_gcp_RpcProtocolVersions_Version_init_zero {false, 0, false, 0} + +/* Field tags (for use in manual encoding/decoding) */ +#define grpc_gcp_RpcProtocolVersions_Version_major_tag 1 +#define grpc_gcp_RpcProtocolVersions_Version_minor_tag 2 +#define grpc_gcp_RpcProtocolVersions_max_rpc_version_tag 1 +#define grpc_gcp_RpcProtocolVersions_min_rpc_version_tag 2 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t grpc_gcp_RpcProtocolVersions_fields[3]; +extern const pb_field_t grpc_gcp_RpcProtocolVersions_Version_fields[3]; + +/* Maximum encoded size of messages (where known) */ +#define grpc_gcp_RpcProtocolVersions_size 28 +#define grpc_gcp_RpcProtocolVersions_Version_size 12 + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define TRANSPORT_SECURITY_COMMON_MESSAGES \ + + +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.cc b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.cc new file mode 100644 index 00000000000000..8a7edb53d4ffcd --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.cc @@ -0,0 +1,196 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/handshaker/transport_security_common_api.h" + +bool grpc_gcp_rpc_protocol_versions_set_max( + grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major, + uint32_t max_minor) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "versions is nullptr in " + "grpc_gcp_rpc_protocol_versions_set_max()."); + return false; + } + versions->has_max_rpc_version = true; + versions->max_rpc_version.has_major = true; + versions->max_rpc_version.has_minor = true; + versions->max_rpc_version.major = max_major; + versions->max_rpc_version.minor = max_minor; + return true; +} + +bool grpc_gcp_rpc_protocol_versions_set_min( + grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major, + uint32_t min_minor) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "versions is nullptr in " + "grpc_gcp_rpc_protocol_versions_set_min()."); + return false; + } + versions->has_min_rpc_version = true; + versions->min_rpc_version.has_major = true; + versions->min_rpc_version.has_minor = true; + versions->min_rpc_version.major = min_major; + versions->min_rpc_version.minor = min_minor; + return true; +} + +size_t grpc_gcp_rpc_protocol_versions_encode_length( + const grpc_gcp_rpc_protocol_versions* versions) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_rpc_protocol_versions_encode_length()."); + return 0; + } + pb_ostream_t size_stream; + memset(&size_stream, 0, sizeof(pb_ostream_t)); + if (!pb_encode(&size_stream, grpc_gcp_RpcProtocolVersions_fields, versions)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&size_stream)); + return 0; + } + return size_stream.bytes_written; +} + +bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes( + const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes, + size_t bytes_length) { + if (versions == nullptr || bytes == nullptr || bytes_length == 0) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes()."); + return false; + } + pb_ostream_t output_stream = pb_ostream_from_buffer(bytes, bytes_length); + if (!pb_encode(&output_stream, grpc_gcp_RpcProtocolVersions_fields, + versions)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&output_stream)); + return false; + } + return true; +} + +bool grpc_gcp_rpc_protocol_versions_encode( + const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice) { + if (versions == nullptr || slice == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to " + "grpc_gcp_rpc_protocol_versions_encode()."); + return false; + } + size_t encoded_length = + grpc_gcp_rpc_protocol_versions_encode_length(versions); + if (encoded_length == 0) return false; + *slice = grpc_slice_malloc(encoded_length); + return grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes( + versions, GRPC_SLICE_START_PTR(*slice), encoded_length); +} + +bool grpc_gcp_rpc_protocol_versions_decode( + grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions) { + if (versions == nullptr) { + gpr_log(GPR_ERROR, + "version is nullptr in " + "grpc_gcp_rpc_protocol_versions_decode()."); + return false; + } + pb_istream_t stream = pb_istream_from_buffer(GRPC_SLICE_START_PTR(slice), + GRPC_SLICE_LENGTH(slice)); + if (!pb_decode(&stream, grpc_gcp_RpcProtocolVersions_fields, versions)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return false; + } + return true; +} + +bool grpc_gcp_rpc_protocol_versions_copy( + const grpc_gcp_rpc_protocol_versions* src, + grpc_gcp_rpc_protocol_versions* dst) { + if ((src == nullptr && dst != nullptr) || + (src != nullptr && dst == nullptr)) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_rpc_protocol_versions_copy()."); + return false; + } + if (src == nullptr) { + return true; + } + grpc_gcp_rpc_protocol_versions_set_max(dst, src->max_rpc_version.major, + src->max_rpc_version.minor); + grpc_gcp_rpc_protocol_versions_set_min(dst, src->min_rpc_version.major, + src->min_rpc_version.minor); + return true; +} + +namespace grpc_core { +namespace internal { + +int grpc_gcp_rpc_protocol_version_compare( + const grpc_gcp_rpc_protocol_versions_version* v1, + const grpc_gcp_rpc_protocol_versions_version* v2) { + if ((v1->major > v2->major) || + (v1->major == v2->major && v1->minor > v2->minor)) { + return 1; + } + if ((v1->major < v2->major) || + (v1->major == v2->major && v1->minor < v2->minor)) { + return -1; + } + return 0; +} + +} // namespace internal +} // namespace grpc_core + +bool grpc_gcp_rpc_protocol_versions_check( + const grpc_gcp_rpc_protocol_versions* local_versions, + const grpc_gcp_rpc_protocol_versions* peer_versions, + grpc_gcp_rpc_protocol_versions_version* highest_common_version) { + if (local_versions == nullptr || peer_versions == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to " + "grpc_gcp_rpc_protocol_versions_check()."); + return false; + } + /* max_common_version is MIN(local.max, peer.max) */ + const grpc_gcp_rpc_protocol_versions_version* max_common_version = + grpc_core::internal::grpc_gcp_rpc_protocol_version_compare( + &local_versions->max_rpc_version, &peer_versions->max_rpc_version) > 0 + ? &peer_versions->max_rpc_version + : &local_versions->max_rpc_version; + /* min_common_version is MAX(local.min, peer.min) */ + const grpc_gcp_rpc_protocol_versions_version* min_common_version = + grpc_core::internal::grpc_gcp_rpc_protocol_version_compare( + &local_versions->min_rpc_version, &peer_versions->min_rpc_version) > 0 + ? &local_versions->min_rpc_version + : &peer_versions->min_rpc_version; + bool result = grpc_core::internal::grpc_gcp_rpc_protocol_version_compare( + max_common_version, min_common_version) >= 0 + ? true + : false; + if (result && highest_common_version != nullptr) { + memcpy(highest_common_version, max_common_version, + sizeof(grpc_gcp_rpc_protocol_versions_version)); + } + return result; +} diff --git a/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.h b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.h new file mode 100644 index 00000000000000..ec2a0b4b5e3784 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/handshaker/transport_security_common_api.h @@ -0,0 +1,163 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H +#define GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H + +#include + +#include "pb_decode.h" +#include "pb_encode.h" + +#include +#include +#include +#include + +#include "src/core/tsi/alts/handshaker/transport_security_common.pb.h" + +typedef grpc_gcp_RpcProtocolVersions grpc_gcp_rpc_protocol_versions; + +typedef grpc_gcp_RpcProtocolVersions_Version + grpc_gcp_rpc_protocol_versions_version; + +/** + * This method sets the value for max_rpc_versions field of rpc protocol + * versions. + * + * - versions: an rpc protocol version instance. + * - max_major: a major version of maximum supported RPC version. + * - max_minor: a minor version of maximum supported RPC version. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_set_max( + grpc_gcp_rpc_protocol_versions* versions, uint32_t max_major, + uint32_t max_minor); + +/** + * This method sets the value for min_rpc_versions field of rpc protocol + * versions. + * + * - versions: an rpc protocol version instance. + * - min_major: a major version of minimum supported RPC version. + * - min_minor: a minor version of minimum supported RPC version. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_set_min( + grpc_gcp_rpc_protocol_versions* versions, uint32_t min_major, + uint32_t min_minor); + +/** + * This method computes serialized byte length of rpc protocol versions. + * + * - versions: an rpc protocol versions instance. + * + * The method returns serialized byte length. It returns 0 on failure. + */ +size_t grpc_gcp_rpc_protocol_versions_encode_length( + const grpc_gcp_rpc_protocol_versions* versions); + +/** + * This method serializes rpc protocol versions and writes the result to + * the memory buffer provided by the caller. Caller is responsible for + * allocating sufficient memory to store the serialized data. + * + * - versions: an rpc protocol versions instance. + * - bytes: bytes buffer where the result will be written to. + * - bytes_length: length of the bytes buffer. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_encode_to_raw_bytes( + const grpc_gcp_rpc_protocol_versions* versions, uint8_t* bytes, + size_t bytes_length); + +/** + * This method serializes an rpc protocol version and returns serialized rpc + * versions in grpc slice. + * + * - versions: an rpc protocol versions instance. + * - slice: grpc slice where the serialized result will be written. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_encode( + const grpc_gcp_rpc_protocol_versions* versions, grpc_slice* slice); + +/** + * This method de-serializes input in grpc slice form and stores the result + * in rpc protocol versions. + * + * - slice: a data stream containing a serialized rpc protocol version. + * - versions: an rpc protocol version instance used to hold de-serialized + * result. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_decode( + grpc_slice slice, grpc_gcp_rpc_protocol_versions* versions); + +/** + * This method performs a deep copy operation on rpc protocol versions + * instance. + * + * - src: rpc protocol versions instance that needs to be copied. + * - dst: rpc protocol versions instance that stores the copied result. + * + * The method returns true on success and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_copy( + const grpc_gcp_rpc_protocol_versions* src, + grpc_gcp_rpc_protocol_versions* dst); + +/** + * This method performs a version check between local and peer rpc protocol + * versions. + * + * - local_versions: local rpc protocol versions instance. + * - peer_versions: peer rpc protocol versions instance. + * - highest_common_version: an output parameter that will store the highest + * common rpc protocol version both parties agreed on. + * + * The method returns true if the check passes which means both parties agreed + * on a common rpc protocol to use, and false otherwise. + */ +bool grpc_gcp_rpc_protocol_versions_check( + const grpc_gcp_rpc_protocol_versions* local_versions, + const grpc_gcp_rpc_protocol_versions* peer_versions, + grpc_gcp_rpc_protocol_versions_version* highest_common_version); + +namespace grpc_core { +namespace internal { + +/** + * Exposed for testing only. + * The method returns 0 if v1 = v2, + * returns 1 if v1 > v2, + * returns -1 if v1 < v2. + */ +int grpc_gcp_rpc_protocol_version_compare( + const grpc_gcp_rpc_protocol_versions_version* v1, + const grpc_gcp_rpc_protocol_versions_version* v2); + +} // namespace internal +} // namespace grpc_core + +#endif /* GRPC_CORE_TSI_ALTS_HANDSHAKER_TRANSPORT_SECURITY_COMMON_API_H */ diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc new file mode 100644 index 00000000000000..352561d4697f8b --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.cc @@ -0,0 +1,226 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h" + +#include +#include + +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +/* Main struct for alts_grpc_integrity_only_record_protocol. */ +typedef struct alts_grpc_integrity_only_record_protocol { + alts_grpc_record_protocol base; + bool enable_extra_copy; + grpc_slice_buffer data_sb; + unsigned char* tag_buf; +} alts_grpc_integrity_only_record_protocol; + +/* --- alts_grpc_record_protocol methods implementation. --- */ + +static tsi_result alts_grpc_integrity_only_extra_copy_protect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + /* Allocates memory for protected frame and copies data. */ + size_t data_length = unprotected_slices->length; + size_t protected_frame_size = + unprotected_slices->length + rp->header_length + rp->tag_length; + grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size); + uint8_t* data = GRPC_SLICE_START_PTR(protected_slice) + rp->header_length; + for (size_t i = 0; i < unprotected_slices->count; i++) { + memcpy(data, GRPC_SLICE_START_PTR(unprotected_slices->slices[i]), + GRPC_SLICE_LENGTH(unprotected_slices->slices[i])); + data += GRPC_SLICE_LENGTH(unprotected_slices->slices[i]); + } + /* Calls alts_iovec_record_protocol protect. */ + char* error_details = nullptr; + iovec_t header_iovec = {GRPC_SLICE_START_PTR(protected_slice), + rp->header_length}; + iovec_t tag_iovec = { + GRPC_SLICE_START_PTR(protected_slice) + rp->header_length + data_length, + rp->tag_length}; + rp->iovec_buf[0].iov_base = + GRPC_SLICE_START_PTR(protected_slice) + rp->header_length; + rp->iovec_buf[0].iov_len = data_length; + grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect( + rp->iovec_rp, rp->iovec_buf, 1, header_iovec, tag_iovec, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to protect, %s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + grpc_slice_buffer_add(protected_slices, protected_slice); + grpc_slice_buffer_reset_and_unref_internal(unprotected_slices); + return TSI_OK; +} + +static tsi_result alts_grpc_integrity_only_protect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + /* Input sanity check. */ + if (rp == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol protect."); + return TSI_INVALID_ARGUMENT; + } + alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol = + reinterpret_cast(rp); + if (integrity_only_record_protocol->enable_extra_copy) { + return alts_grpc_integrity_only_extra_copy_protect(rp, unprotected_slices, + protected_slices); + } + /* Allocates memory for header and tag slices. */ + grpc_slice header_slice = GRPC_SLICE_MALLOC(rp->header_length); + grpc_slice tag_slice = GRPC_SLICE_MALLOC(rp->tag_length); + /* Calls alts_iovec_record_protocol protect. */ + char* error_details = nullptr; + iovec_t header_iovec = {GRPC_SLICE_START_PTR(header_slice), + GRPC_SLICE_LENGTH(header_slice)}; + iovec_t tag_iovec = {GRPC_SLICE_START_PTR(tag_slice), + GRPC_SLICE_LENGTH(tag_slice)}; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, + unprotected_slices); + grpc_status_code status = alts_iovec_record_protocol_integrity_only_protect( + rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, header_iovec, + tag_iovec, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to protect, %s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + /* Appends result to protected_slices. */ + grpc_slice_buffer_add(protected_slices, header_slice); + grpc_slice_buffer_move_into(unprotected_slices, protected_slices); + grpc_slice_buffer_add(protected_slices, tag_slice); + return TSI_OK; +} + +static tsi_result alts_grpc_integrity_only_unprotect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + /* Input sanity check. */ + if (rp == nullptr || protected_slices == nullptr || + unprotected_slices == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol unprotect."); + return TSI_INVALID_ARGUMENT; + } + if (protected_slices->length < rp->header_length + rp->tag_length) { + gpr_log(GPR_ERROR, "Protected slices do not have sufficient data."); + return TSI_INVALID_ARGUMENT; + } + /* In this method, rp points to alts_grpc_record_protocol struct + * and integrity_only_record_protocol points to + * alts_grpc_integrity_only_record_protocol struct. */ + alts_grpc_integrity_only_record_protocol* integrity_only_record_protocol = + reinterpret_cast(rp); + /* Strips frame header from protected slices. */ + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_move_first(protected_slices, rp->header_length, + &rp->header_sb); + GPR_ASSERT(rp->header_sb.length == rp->header_length); + iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp); + /* Moves protected slices data to data_sb and leaves the remaining tag. */ + grpc_slice_buffer_reset_and_unref_internal( + &integrity_only_record_protocol->data_sb); + grpc_slice_buffer_move_first(protected_slices, + protected_slices->length - rp->tag_length, + &integrity_only_record_protocol->data_sb); + GPR_ASSERT(protected_slices->length == rp->tag_length); + iovec_t tag_iovec = {nullptr, rp->tag_length}; + if (protected_slices->count == 1) { + tag_iovec.iov_base = GRPC_SLICE_START_PTR(protected_slices->slices[0]); + } else { + /* Frame tag is in multiple slices, copies the tag bytes from slice + * buffer to a single flat buffer. */ + alts_grpc_record_protocol_copy_slice_buffer( + protected_slices, integrity_only_record_protocol->tag_buf); + tag_iovec.iov_base = integrity_only_record_protocol->tag_buf; + } + /* Calls alts_iovec_record_protocol unprotect. */ + char* error_details = nullptr; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec( + rp, &integrity_only_record_protocol->data_sb); + grpc_status_code status = alts_iovec_record_protocol_integrity_only_unprotect( + rp->iovec_rp, rp->iovec_buf, + integrity_only_record_protocol->data_sb.count, header_iovec, tag_iovec, + &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_reset_and_unref_internal(protected_slices); + grpc_slice_buffer_move_into(&integrity_only_record_protocol->data_sb, + unprotected_slices); + return TSI_OK; +} + +static void alts_grpc_integrity_only_destruct(alts_grpc_record_protocol* rp) { + if (rp == nullptr) { + return; + } + alts_grpc_integrity_only_record_protocol* integrity_only_rp = + reinterpret_cast(rp); + grpc_slice_buffer_destroy_internal(&integrity_only_rp->data_sb); + gpr_free(integrity_only_rp->tag_buf); +} + +static const alts_grpc_record_protocol_vtable + alts_grpc_integrity_only_record_protocol_vtable = { + alts_grpc_integrity_only_protect, alts_grpc_integrity_only_unprotect, + alts_grpc_integrity_only_destruct}; + +tsi_result alts_grpc_integrity_only_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, bool enable_extra_copy, alts_grpc_record_protocol** rp) { + if (crypter == nullptr || rp == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol create."); + return TSI_INVALID_ARGUMENT; + } + alts_grpc_integrity_only_record_protocol* impl = + static_cast( + gpr_zalloc(sizeof(alts_grpc_integrity_only_record_protocol))); + /* Calls alts_grpc_record_protocol init. */ + tsi_result result = alts_grpc_record_protocol_init( + &impl->base, crypter, overflow_size, is_client, + /*is_integrity_only=*/true, is_protect); + if (result != TSI_OK) { + gpr_free(impl); + return result; + } + impl->enable_extra_copy = enable_extra_copy; + /* Initializes slice buffer for data_sb. */ + grpc_slice_buffer_init(&impl->data_sb); + /* Allocates tag buffer. */ + impl->tag_buf = + static_cast(gpr_malloc(impl->base.tag_length)); + impl->base.vtable = &alts_grpc_integrity_only_record_protocol_vtable; + *rp = &impl->base; + return TSI_OK; +} diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h new file mode 100644 index 00000000000000..5456d34fad5b61 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h @@ -0,0 +1,54 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H + +#include + +#include + +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" + +/** + * This method creates an integrity-only alts_grpc_record_protocol instance, + * given a gsec_aead_crypter instance and a flag indicating if the created + * instance will be used at the client or server side. The ownership of + * gsec_aead_crypter instance is transferred to this new object. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. + * - overflow_size: overflow size of counter in bytes. + * - is_client: a flag indicating if the alts_grpc_record_protocol instance will + * be used at the client or server side. + * - is_protect: a flag indicating if the alts_grpc_record_protocol instance + * will be used for protect or unprotect. + *- enable_extra_copy: a flag indicating if the instance uses one-copy instead + * of zero-copy in the protect operation. + * - rp: an alts_grpc_record_protocol instance to be returned from + * the method. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_integrity_only_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, bool enable_extra_copy, alts_grpc_record_protocol** rp); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_INTEGRITY_ONLY_RECORD_PROTOCOL_H \ + */ diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc new file mode 100644 index 00000000000000..e7890903d57d52 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.cc @@ -0,0 +1,144 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h" + +#include +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +/* Privacy-integrity alts_grpc_record_protocol object uses the same struct + * defined in alts_grpc_record_protocol_common.h. */ + +/* --- alts_grpc_record_protocol methods implementation. --- */ + +static tsi_result alts_grpc_privacy_integrity_protect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + /* Input sanity check. */ + if (rp == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol protect."); + return TSI_INVALID_ARGUMENT; + } + /* Allocates memory for output frame. In privacy-integrity protect, the + * protected frame is stored in a newly allocated buffer. */ + size_t protected_frame_size = + unprotected_slices->length + rp->header_length + + alts_iovec_record_protocol_get_tag_length(rp->iovec_rp); + grpc_slice protected_slice = GRPC_SLICE_MALLOC(protected_frame_size); + iovec_t protected_iovec = {GRPC_SLICE_START_PTR(protected_slice), + GRPC_SLICE_LENGTH(protected_slice)}; + /* Calls alts_iovec_record_protocol protect. */ + char* error_details = nullptr; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, + unprotected_slices); + grpc_status_code status = + alts_iovec_record_protocol_privacy_integrity_protect( + rp->iovec_rp, rp->iovec_buf, unprotected_slices->count, + protected_iovec, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to protect, %s", error_details); + gpr_free(error_details); + grpc_slice_unref_internal(protected_slice); + return TSI_INTERNAL_ERROR; + } + grpc_slice_buffer_add(protected_slices, protected_slice); + grpc_slice_buffer_reset_and_unref_internal(unprotected_slices); + return TSI_OK; +} + +static tsi_result alts_grpc_privacy_integrity_unprotect( + alts_grpc_record_protocol* rp, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + /* Input sanity check. */ + if (rp == nullptr || protected_slices == nullptr || + unprotected_slices == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol unprotect."); + return TSI_INVALID_ARGUMENT; + } + /* Allocates memory for output frame. In privacy-integrity unprotect, the + * unprotected data are stored in a newly allocated buffer. */ + if (protected_slices->length < rp->header_length + rp->tag_length) { + gpr_log(GPR_ERROR, "Protected slices do not have sufficient data."); + return TSI_INVALID_ARGUMENT; + } + size_t unprotected_frame_size = + protected_slices->length - rp->header_length - rp->tag_length; + grpc_slice unprotected_slice = GRPC_SLICE_MALLOC(unprotected_frame_size); + iovec_t unprotected_iovec = {GRPC_SLICE_START_PTR(unprotected_slice), + GRPC_SLICE_LENGTH(unprotected_slice)}; + /* Strips frame header from protected slices. */ + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_move_first(protected_slices, rp->header_length, + &rp->header_sb); + iovec_t header_iovec = alts_grpc_record_protocol_get_header_iovec(rp); + /* Calls alts_iovec_record_protocol unprotect. */ + char* error_details = nullptr; + alts_grpc_record_protocol_convert_slice_buffer_to_iovec(rp, protected_slices); + grpc_status_code status = + alts_iovec_record_protocol_privacy_integrity_unprotect( + rp->iovec_rp, header_iovec, rp->iovec_buf, protected_slices->count, + unprotected_iovec, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to unprotect, %s", error_details); + gpr_free(error_details); + grpc_slice_unref_internal(unprotected_slice); + return TSI_INTERNAL_ERROR; + } + grpc_slice_buffer_reset_and_unref_internal(&rp->header_sb); + grpc_slice_buffer_reset_and_unref_internal(protected_slices); + grpc_slice_buffer_add(unprotected_slices, unprotected_slice); + return TSI_OK; +} + +static const alts_grpc_record_protocol_vtable + alts_grpc_privacy_integrity_record_protocol_vtable = { + alts_grpc_privacy_integrity_protect, + alts_grpc_privacy_integrity_unprotect, nullptr}; + +tsi_result alts_grpc_privacy_integrity_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, alts_grpc_record_protocol** rp) { + if (crypter == nullptr || rp == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol create."); + return TSI_INVALID_ARGUMENT; + } + auto* impl = static_cast( + gpr_zalloc(sizeof(alts_grpc_record_protocol))); + /* Calls alts_grpc_record_protocol init. */ + tsi_result result = + alts_grpc_record_protocol_init(impl, crypter, overflow_size, is_client, + /*is_integrity_only=*/false, is_protect); + if (result != TSI_OK) { + gpr_free(impl); + return result; + } + impl->vtable = &alts_grpc_privacy_integrity_record_protocol_vtable; + *rp = impl; + return TSI_OK; +} diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h new file mode 100644 index 00000000000000..1e34aef2d87678 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H + +#include + +#include + +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" + +/** + * This method creates a privacy-integrity alts_grpc_record_protocol instance, + * given a gsec_aead_crypter instance and a flag indicating if the created + * instance will be used at the client or server side. The ownership of + * gsec_aead_crypter instance is transferred to this new object. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. + * - is_client: a flag indicating if the alts_grpc_record_protocol instance will + * be used at the client or server side. + * - rp: an alts_grpc_record_protocol instance to be returned from + * the method. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_privacy_integrity_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_protect, alts_grpc_record_protocol** rp); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_PRIVACY_INTEGRITY_RECORD_PROTOCOL_H \ + */ diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h new file mode 100644 index 00000000000000..d1e433dac47cfa --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h @@ -0,0 +1,91 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H + +#include + +#include + +#include "src/core/tsi/transport_security_interface.h" + +/** + * This alts_grpc_record_protocol object protects and unprotects a single frame + * stored in grpc slice buffer with zero or minimized memory copy. + * Implementations of this object must be thread compatible. + */ +typedef struct alts_grpc_record_protocol alts_grpc_record_protocol; + +/** + * This methods performs protect operation on unprotected data and appends the + * protected frame to protected_slices. The caller needs to ensure the length + * of unprotected data plus the frame overhead is less than or equal to the + * maximum frame length. The input unprotected data slice buffer will be + * cleared, although the actual unprotected data bytes are not modified. + * + * - self: an alts_grpc_record_protocol instance. + * - unprotected_slices: the unprotected data to be protected. + * - protected_slices: slice buffer where the protected frame is appended. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_record_protocol_protect( + alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices); + +/** + * This methods performs unprotect operation on a full frame of protected data + * and appends unprotected data to unprotected_slices. It is the caller's + * responsibility to prepare a full frame of data before calling this method. + * The input protected frame slice buffer will be cleared, although the actual + * protected data bytes are not modified. + * + * - self: an alts_grpc_record_protocol instance. + * - protected_slices: a full frame of protected data in grpc slices. + * - unprotected_slices: slice buffer where unprotected data is appended. + * + * This method returns TSI_OK in case of success or a specific error code in + * case of failure. + */ +tsi_result alts_grpc_record_protocol_unprotect( + alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices); + +/** + * This method returns maximum allowed unprotected data size, given maximum + * protected frame size. + * + * - self: an alts_grpc_record_protocol instance. + * - max_protected_frame_size: maximum protected frame size. + * + * On success, the method returns the maximum allowed unprotected data size. + * Otherwise, it returns zero. + */ +size_t alts_grpc_record_protocol_max_unprotected_data_size( + const alts_grpc_record_protocol* self, size_t max_protected_frame_size); + +/** + * This method destroys an alts_grpc_record_protocol instance by de-allocating + * all of its occupied memory. + */ +void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_H \ + */ diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc new file mode 100644 index 00000000000000..1048b600f0b429 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.cc @@ -0,0 +1,174 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h" + +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" + +const size_t kInitialIovecBufferSize = 8; + +/* Makes sure iovec_buf in alts_grpc_record_protocol is large enough. */ +static void ensure_iovec_buf_size(alts_grpc_record_protocol* rp, + const grpc_slice_buffer* sb) { + GPR_ASSERT(rp != nullptr && sb != nullptr); + if (sb->count <= rp->iovec_buf_length) { + return; + } + /* At least double the iovec buffer size. */ + rp->iovec_buf_length = GPR_MAX(sb->count, 2 * rp->iovec_buf_length); + rp->iovec_buf = static_cast( + gpr_realloc(rp->iovec_buf, rp->iovec_buf_length * sizeof(iovec_t))); +} + +/* --- Implementation of methods defined in tsi_grpc_record_protocol_common.h. + * --- */ + +void alts_grpc_record_protocol_convert_slice_buffer_to_iovec( + alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb) { + GPR_ASSERT(rp != nullptr && sb != nullptr); + ensure_iovec_buf_size(rp, sb); + for (size_t i = 0; i < sb->count; i++) { + rp->iovec_buf[i].iov_base = GRPC_SLICE_START_PTR(sb->slices[i]); + rp->iovec_buf[i].iov_len = GRPC_SLICE_LENGTH(sb->slices[i]); + } +} + +void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src, + unsigned char* dst) { + GPR_ASSERT(src != nullptr && dst != nullptr); + for (size_t i = 0; i < src->count; i++) { + size_t slice_length = GRPC_SLICE_LENGTH(src->slices[i]); + memcpy(dst, GRPC_SLICE_START_PTR(src->slices[i]), slice_length); + dst += slice_length; + } +} + +iovec_t alts_grpc_record_protocol_get_header_iovec( + alts_grpc_record_protocol* rp) { + iovec_t header_iovec = {nullptr, 0}; + if (rp == nullptr) { + return header_iovec; + } + header_iovec.iov_len = rp->header_length; + if (rp->header_sb.count == 1) { + header_iovec.iov_base = GRPC_SLICE_START_PTR(rp->header_sb.slices[0]); + } else { + /* Frame header is in multiple slices, copies the header bytes from slice + * buffer to a single flat buffer. */ + alts_grpc_record_protocol_copy_slice_buffer(&rp->header_sb, rp->header_buf); + header_iovec.iov_base = rp->header_buf; + } + return header_iovec; +} + +tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp, + gsec_aead_crypter* crypter, + size_t overflow_size, bool is_client, + bool is_integrity_only, + bool is_protect) { + if (rp == nullptr || crypter == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to alts_grpc_record_protocol init."); + return TSI_INVALID_ARGUMENT; + } + /* Creates alts_iovec_record_protocol. */ + char* error_details = nullptr; + grpc_status_code status = alts_iovec_record_protocol_create( + crypter, overflow_size, is_client, is_integrity_only, is_protect, + &rp->iovec_rp, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to create alts_iovec_record_protocol, %s.", + error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + /* Allocates header slice buffer. */ + grpc_slice_buffer_init(&rp->header_sb); + /* Allocates header buffer. */ + rp->header_length = alts_iovec_record_protocol_get_header_length(); + rp->header_buf = static_cast(gpr_malloc(rp->header_length)); + rp->tag_length = alts_iovec_record_protocol_get_tag_length(rp->iovec_rp); + /* Allocates iovec buffer. */ + rp->iovec_buf_length = kInitialIovecBufferSize; + rp->iovec_buf = + static_cast(gpr_malloc(rp->iovec_buf_length * sizeof(iovec_t))); + return TSI_OK; +} + +/* --- Implementation of methods defined in tsi_grpc_record_protocol.h. --- */ +tsi_result alts_grpc_record_protocol_protect( + alts_grpc_record_protocol* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr || + self->vtable == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect == nullptr) { + return TSI_UNIMPLEMENTED; + } + return self->vtable->protect(self, unprotected_slices, protected_slices); +} + +tsi_result alts_grpc_record_protocol_unprotect( + alts_grpc_record_protocol* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + if (grpc_core::ExecCtx::Get() == nullptr || self == nullptr || + self->vtable == nullptr || protected_slices == nullptr || + unprotected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->unprotect == nullptr) { + return TSI_UNIMPLEMENTED; + } + return self->vtable->unprotect(self, protected_slices, unprotected_slices); +} + +void alts_grpc_record_protocol_destroy(alts_grpc_record_protocol* self) { + if (self == nullptr) { + return; + } + if (self->vtable->destruct != nullptr) { + self->vtable->destruct(self); + } + alts_iovec_record_protocol_destroy(self->iovec_rp); + grpc_slice_buffer_destroy_internal(&self->header_sb); + gpr_free(self->header_buf); + gpr_free(self->iovec_buf); + gpr_free(self); +} + +/* Integrity-only and privacy-integrity share the same implementation. No need + * to call vtable. */ +size_t alts_grpc_record_protocol_max_unprotected_data_size( + const alts_grpc_record_protocol* self, size_t max_protected_frame_size) { + if (self == nullptr) { + return 0; + } + return alts_iovec_record_protocol_max_unprotected_data_size( + self->iovec_rp, max_protected_frame_size); +} diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h new file mode 100644 index 00000000000000..43b8a4a2b82504 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol_common.h @@ -0,0 +1,100 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H + +/** + * this file contains alts_grpc_record_protocol internals and internal-only + * helper functions. The public functions of alts_grpc_record_protocol are + * defined in the alts_grpc_record_protocol.h. + */ + +#include + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +/* V-table for alts_grpc_record_protocol implementations. */ +typedef struct { + tsi_result (*protect)(alts_grpc_record_protocol* self, + grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices); + tsi_result (*unprotect)(alts_grpc_record_protocol* self, + grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices); + void (*destruct)(alts_grpc_record_protocol* self); +} alts_grpc_record_protocol_vtable; + +/* Main struct for alts_grpc_record_protocol implementation, shared by both + * integrity-only record protocol and privacy-integrity record protocol. + * Integrity-only record protocol has additional data elements. + * Privacy-integrity record protocol uses this struct directly. */ +struct alts_grpc_record_protocol { + const alts_grpc_record_protocol_vtable* vtable; + alts_iovec_record_protocol* iovec_rp; + grpc_slice_buffer header_sb; + unsigned char* header_buf; + size_t header_length; + size_t tag_length; + iovec_t* iovec_buf; + size_t iovec_buf_length; +}; + +/** + * Converts the slices of input sb into iovec_t's and puts the result into + * rp->iovec_buf. Note that the actual data are not copied, only + * pointers and lengths are copied. + */ +void alts_grpc_record_protocol_convert_slice_buffer_to_iovec( + alts_grpc_record_protocol* rp, const grpc_slice_buffer* sb); + +/** + * Copies bytes from slice buffer to destination buffer. Caller is responsible + * for allocating enough memory of destination buffer. This method is used for + * copying frame header and tag in case they are stored in multiple slices. + */ +void alts_grpc_record_protocol_copy_slice_buffer(const grpc_slice_buffer* src, + unsigned char* dst); + +/** + * This method returns an iovec object pointing to the frame header stored in + * rp->header_sb. If the frame header is stored in multiple slices, + * this method will copy the bytes in rp->header_sb to + * rp->header_buf, and return an iovec object pointing to + * rp->header_buf. + */ +iovec_t alts_grpc_record_protocol_get_header_iovec( + alts_grpc_record_protocol* rp); + +/** + * Initializes an alts_grpc_record_protocol object, given a gsec_aead_crypter + * instance, the overflow size of the counter in bytes, a flag indicating if the + * object is used for client or server side, a flag indicating if it is used for + * integrity-only or privacy-integrity mode, and a flag indicating if it is for + * protect or unprotect. The ownership of gsec_aead_crypter object is + * transferred to the alts_grpc_record_protocol object. + */ +tsi_result alts_grpc_record_protocol_init(alts_grpc_record_protocol* rp, + gsec_aead_crypter* crypter, + size_t overflow_size, bool is_client, + bool is_integrity_only, + bool is_protect); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_GRPC_RECORD_PROTOCOL_COMMON_H \ + */ diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc new file mode 100644 index 00000000000000..6a548e50dd40a1 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.cc @@ -0,0 +1,476 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" + +#include +#include + +#include +#include + +#include "src/core/tsi/alts/frame_protector/alts_counter.h" + +struct alts_iovec_record_protocol { + alts_counter* ctr; + gsec_aead_crypter* crypter; + size_t tag_length; + bool is_integrity_only; + bool is_protect; +}; + +/* Copies error message to destination. */ +static void maybe_copy_error_msg(const char* src, char** dst) { + if (dst != nullptr && src != nullptr) { + *dst = static_cast(gpr_malloc(strlen(src) + 1)); + memcpy(*dst, src, strlen(src) + 1); + } +} + +/* Appends error message to destination. */ +static void maybe_append_error_msg(const char* appendix, char** dst) { + if (dst != nullptr && appendix != nullptr) { + int dst_len = static_cast(strlen(*dst)); + *dst = static_cast(realloc(*dst, dst_len + strlen(appendix) + 1)); + assert(*dst != nullptr); + memcpy(*dst + dst_len, appendix, strlen(appendix) + 1); + } +} + +/* Use little endian to interpret a string of bytes as uint32_t. */ +static uint32_t load_32_le(const unsigned char* buffer) { + return (((uint32_t)buffer[3]) << 24) | (((uint32_t)buffer[2]) << 16) | + (((uint32_t)buffer[1]) << 8) | ((uint32_t)buffer[0]); +} + +/* Store uint32_t as a string of little endian bytes. */ +static void store_32_le(uint32_t value, unsigned char* buffer) { + buffer[3] = (unsigned char)(value >> 24) & 0xFF; + buffer[2] = (unsigned char)(value >> 16) & 0xFF; + buffer[1] = (unsigned char)(value >> 8) & 0xFF; + buffer[0] = (unsigned char)(value)&0xFF; +} + +/* Ensures header and tag iovec have sufficient length. */ +static grpc_status_code ensure_header_and_tag_length( + const alts_iovec_record_protocol* rp, iovec_t header, iovec_t tag, + char** error_details) { + if (rp == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (header.iov_base == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (header.iov_len != alts_iovec_record_protocol_get_header_length()) { + maybe_copy_error_msg("Header length is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (tag.iov_base == nullptr) { + maybe_copy_error_msg("Tag is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (tag.iov_len != rp->tag_length) { + maybe_copy_error_msg("Tag length is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + return GRPC_STATUS_OK; +} + +/* Increments crypter counter and checks overflow. */ +static grpc_status_code increment_counter(alts_counter* counter, + char** error_details) { + if (counter == nullptr) { + return GRPC_STATUS_FAILED_PRECONDITION; + } + bool is_overflow = false; + grpc_status_code status = + alts_counter_increment(counter, &is_overflow, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (is_overflow) { + maybe_copy_error_msg("Crypter counter is overflowed.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +/* Given an array of iovec, computes the total length of buffer. */ +static size_t get_total_length(const iovec_t* vec, size_t vec_length) { + size_t total_length = 0; + for (size_t i = 0; i < vec_length; ++i) { + total_length += vec[i].iov_len; + } + return total_length; +} + +/* Writes frame header given data and tag length. */ +static grpc_status_code write_frame_header(size_t data_length, + unsigned char* header, + char** error_details) { + if (header == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + size_t frame_length = kZeroCopyFrameMessageTypeFieldSize + data_length; + store_32_le(static_cast(frame_length), header); + store_32_le(kZeroCopyFrameMessageType, + header + kZeroCopyFrameLengthFieldSize); + return GRPC_STATUS_OK; +} + +/* Verifies frame header given protected data length. */ +static grpc_status_code verify_frame_header(size_t data_length, + unsigned char* header, + char** error_details) { + if (header == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + size_t frame_length = load_32_le(header); + if (frame_length != kZeroCopyFrameMessageTypeFieldSize + data_length) { + maybe_copy_error_msg("Bad frame length.", error_details); + return GRPC_STATUS_INTERNAL; + } + size_t message_type = load_32_le(header + kZeroCopyFrameLengthFieldSize); + if (message_type != kZeroCopyFrameMessageType) { + maybe_copy_error_msg("Unsupported message type.", error_details); + return GRPC_STATUS_INTERNAL; + } + return GRPC_STATUS_OK; +} + +/* --- alts_iovec_record_protocol methods implementation. --- */ + +size_t alts_iovec_record_protocol_get_header_length() { + return kZeroCopyFrameHeaderSize; +} + +size_t alts_iovec_record_protocol_get_tag_length( + const alts_iovec_record_protocol* rp) { + if (rp != nullptr) { + return rp->tag_length; + } + return 0; +} + +size_t alts_iovec_record_protocol_max_unprotected_data_size( + const alts_iovec_record_protocol* rp, size_t max_protected_frame_size) { + if (rp == nullptr) { + return 0; + } + size_t overhead_bytes_size = + kZeroCopyFrameMessageTypeFieldSize + rp->tag_length; + if (max_protected_frame_size <= overhead_bytes_size) return 0; + return max_protected_frame_size - overhead_bytes_size; +} + +grpc_status_code alts_iovec_record_protocol_integrity_only_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t header, iovec_t tag, + char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!rp->is_integrity_only) { + maybe_copy_error_msg( + "Integrity-only operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (!rp->is_protect) { + maybe_copy_error_msg("Protect operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + grpc_status_code status = + ensure_header_and_tag_length(rp, header, tag, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Unprotected data should not be zero length. */ + size_t data_length = + get_total_length(unprotected_vec, unprotected_vec_length); + /* Sets frame header. */ + status = write_frame_header(data_length + rp->tag_length, + static_cast(header.iov_base), + error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Computes frame tag by calling AEAD crypter. */ + size_t bytes_written = 0; + status = gsec_aead_crypter_encrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), unprotected_vec, unprotected_vec_length, + /* plaintext_vec = */ nullptr, /* plaintext_vec_length = */ 0, tag, + &bytes_written, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (bytes_written != rp->tag_length) { + maybe_copy_error_msg("Bytes written expects to be the same as tag length.", + error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect( + alts_iovec_record_protocol* rp, const iovec_t* protected_vec, + size_t protected_vec_length, iovec_t header, iovec_t tag, + char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (!rp->is_integrity_only) { + maybe_copy_error_msg( + "Integrity-only operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (rp->is_protect) { + maybe_copy_error_msg( + "Unprotect operations are not allowed for this object.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + grpc_status_code status = + ensure_header_and_tag_length(rp, header, tag, error_details); + if (status != GRPC_STATUS_OK) return status; + /* Protected data should not be zero length. */ + size_t data_length = get_total_length(protected_vec, protected_vec_length); + /* Verifies frame header. */ + status = verify_frame_header(data_length + rp->tag_length, + static_cast(header.iov_base), + error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Verifies frame tag by calling AEAD crypter. */ + iovec_t plaintext = {nullptr, 0}; + size_t bytes_written = 0; + status = gsec_aead_crypter_decrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), protected_vec, protected_vec_length, &tag, + 1, plaintext, &bytes_written, error_details); + if (status != GRPC_STATUS_OK || bytes_written != 0) { + maybe_append_error_msg(" Frame tag verification failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t protected_frame, + char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (rp->is_integrity_only) { + maybe_copy_error_msg( + "Privacy-integrity operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (!rp->is_protect) { + maybe_copy_error_msg("Protect operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + /* Unprotected data should not be zero length. */ + size_t data_length = + get_total_length(unprotected_vec, unprotected_vec_length); + /* Ensures protected frame iovec has sufficient size. */ + if (protected_frame.iov_base == nullptr) { + maybe_copy_error_msg("Protected frame is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (protected_frame.iov_len != + alts_iovec_record_protocol_get_header_length() + data_length + + rp->tag_length) { + maybe_copy_error_msg("Protected frame size is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Writer frame header. */ + grpc_status_code status = write_frame_header( + data_length + rp->tag_length, + static_cast(protected_frame.iov_base), error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Encrypt unprotected data by calling AEAD crypter. */ + unsigned char* ciphertext_buffer = + static_cast(protected_frame.iov_base) + + alts_iovec_record_protocol_get_header_length(); + iovec_t ciphertext = {ciphertext_buffer, data_length + rp->tag_length}; + size_t bytes_written = 0; + status = gsec_aead_crypter_encrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr, + /* aad_vec_length = */ 0, unprotected_vec, unprotected_vec_length, + ciphertext, &bytes_written, error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + if (bytes_written != data_length + rp->tag_length) { + maybe_copy_error_msg( + "Bytes written expects to be data length plus tag length.", + error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect( + alts_iovec_record_protocol* rp, iovec_t header, + const iovec_t* protected_vec, size_t protected_vec_length, + iovec_t unprotected_data, char** error_details) { + /* Input sanity checks. */ + if (rp == nullptr) { + maybe_copy_error_msg("Input iovec_record_protocol is nullptr.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (rp->is_integrity_only) { + maybe_copy_error_msg( + "Privacy-integrity operations are not allowed for this object.", + error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + if (rp->is_protect) { + maybe_copy_error_msg( + "Unprotect operations are not allowed for this object.", error_details); + return GRPC_STATUS_FAILED_PRECONDITION; + } + /* Protected data size should be no less than tag size. */ + size_t protected_data_length = + get_total_length(protected_vec, protected_vec_length); + if (protected_data_length < rp->tag_length) { + maybe_copy_error_msg( + "Protected data length should be more than the tag length.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Ensures header has sufficient size. */ + if (header.iov_base == nullptr) { + maybe_copy_error_msg("Header is nullptr.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + if (header.iov_len != alts_iovec_record_protocol_get_header_length()) { + maybe_copy_error_msg("Header length is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Ensures unprotected data iovec has sufficient size. */ + if (unprotected_data.iov_len != protected_data_length - rp->tag_length) { + maybe_copy_error_msg("Unprotected data size is incorrect.", error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + /* Verify frame header. */ + grpc_status_code status = verify_frame_header( + protected_data_length, static_cast(header.iov_base), + error_details); + if (status != GRPC_STATUS_OK) { + return status; + } + /* Decrypt protected data by calling AEAD crypter. */ + size_t bytes_written = 0; + status = gsec_aead_crypter_decrypt_iovec( + rp->crypter, alts_counter_get_counter(rp->ctr), + alts_counter_get_size(rp->ctr), /* aad_vec = */ nullptr, + /* aad_vec_length = */ 0, protected_vec, protected_vec_length, + unprotected_data, &bytes_written, error_details); + if (status != GRPC_STATUS_OK) { + maybe_append_error_msg(" Frame decryption failed.", error_details); + return GRPC_STATUS_INTERNAL; + } + if (bytes_written != protected_data_length - rp->tag_length) { + maybe_copy_error_msg( + "Bytes written expects to be protected data length minus tag length.", + error_details); + return GRPC_STATUS_INTERNAL; + } + /* Increments the crypter counter. */ + return increment_counter(rp->ctr, error_details); +} + +grpc_status_code alts_iovec_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp, + char** error_details) { + if (crypter == nullptr || rp == nullptr) { + maybe_copy_error_msg( + "Invalid nullptr arguments to alts_iovec_record_protocol create.", + error_details); + return GRPC_STATUS_INVALID_ARGUMENT; + } + alts_iovec_record_protocol* impl = static_cast( + gpr_zalloc(sizeof(alts_iovec_record_protocol))); + /* Gets counter length. */ + size_t counter_length = 0; + grpc_status_code status = + gsec_aead_crypter_nonce_length(crypter, &counter_length, error_details); + if (status != GRPC_STATUS_OK) { + goto cleanup; + } + /* Creates counters. */ + status = + alts_counter_create(is_protect ? !is_client : is_client, counter_length, + overflow_size, &impl->ctr, error_details); + if (status != GRPC_STATUS_OK) { + goto cleanup; + } + /* Gets tag length. */ + status = + gsec_aead_crypter_tag_length(crypter, &impl->tag_length, error_details); + if (status != GRPC_STATUS_OK) { + goto cleanup; + } + impl->crypter = crypter; + impl->is_integrity_only = is_integrity_only; + impl->is_protect = is_protect; + *rp = impl; + return GRPC_STATUS_OK; +cleanup: + alts_counter_destroy(impl->ctr); + gpr_free(impl); + return GRPC_STATUS_FAILED_PRECONDITION; +} + +void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp) { + if (rp != nullptr) { + alts_counter_destroy(rp->ctr); + gsec_aead_crypter_destroy(rp->crypter); + gpr_free(rp); + } +} diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h new file mode 100644 index 00000000000000..0b7d1bf5bf3476 --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h @@ -0,0 +1,199 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H + +#include + +#include + +#include "src/core/tsi/alts/crypt/gsec.h" + +constexpr size_t kZeroCopyFrameMessageType = 0x06; +constexpr size_t kZeroCopyFrameLengthFieldSize = 4; +constexpr size_t kZeroCopyFrameMessageTypeFieldSize = 4; +constexpr size_t kZeroCopyFrameHeaderSize = + kZeroCopyFrameLengthFieldSize + kZeroCopyFrameMessageTypeFieldSize; + +// Limit k on number of frames such that at most 2^(8 * k) frames can be sent. +constexpr size_t kAltsRecordProtocolRekeyFrameLimit = 8; +constexpr size_t kAltsRecordProtocolFrameLimit = 5; + +/* An implementation of alts record protocol. The API is thread-compatible. */ + +typedef struct iovec iovec_t; + +typedef struct alts_iovec_record_protocol alts_iovec_record_protocol; + +/** + * This method gets the length of record protocol frame header. + */ +size_t alts_iovec_record_protocol_get_header_length(); + +/** + * This method gets the length of record protocol frame tag. + * + * - rp: an alts_iovec_record_protocol instance. + * + * On success, the method returns the length of record protocol frame tag. + * Otherwise, it returns zero. + */ +size_t alts_iovec_record_protocol_get_tag_length( + const alts_iovec_record_protocol* rp); + +/** + * This method returns maximum allowed unprotected data size, given maximum + * protected frame size. + * + * - rp: an alts_iovec_record_protocol instance. + * - max_protected_frame_size: maximum protected frame size. + * + * On success, the method returns the maximum allowed unprotected data size. + * Otherwise, it returns zero. + */ +size_t alts_iovec_record_protocol_max_unprotected_data_size( + const alts_iovec_record_protocol* rp, size_t max_protected_frame_size); + +/** + * This method performs integrity-only protect operation on a + * alts_iovec_record_protocol instance, i.e., compute frame header and tag. The + * caller needs to allocate the memory for header and tag prior to calling this + * method. + * + * - rp: an alts_iovec_record_protocol instance. + * - unprotected_vec: an iovec array containing unprotected data. + * - unprotected_vec_length: the array length of unprotected_vec. + * - header: an iovec containing the output frame header. + * - tag: an iovec containing the output frame tag. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_integrity_only_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t header, iovec_t tag, + char** error_details); + +/** + * This method performs integrity-only unprotect operation on a + * alts_iovec_record_protocol instance, i.e., verify frame header and tag. + * + * - rp: an alts_iovec_record_protocol instance. + * - protected_vec: an iovec array containing protected data. + * - protected_vec_length: the array length of protected_vec. + * - header: an iovec containing the frame header. + * - tag: an iovec containing the frame tag. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_integrity_only_unprotect( + alts_iovec_record_protocol* rp, const iovec_t* protected_vec, + size_t protected_vec_length, iovec_t header, iovec_t tag, + char** error_details); + +/** + * This method performs privacy-integrity protect operation on a + * alts_iovec_record_protocol instance, i.e., compute a protected frame. The + * caller needs to allocate the memory for the protected frame prior to calling + * this method. + * + * - rp: an alts_iovec_record_protocol instance. + * - unprotected_vec: an iovec array containing unprotected data. + * - unprotected_vec_length: the array length of unprotected_vec. + * - protected_frame: an iovec containing the output protected frame. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_privacy_integrity_protect( + alts_iovec_record_protocol* rp, const iovec_t* unprotected_vec, + size_t unprotected_vec_length, iovec_t protected_frame, + char** error_details); + +/** + * This method performs privacy-integrity unprotect operation on a + * alts_iovec_record_protocol instance given a full protected frame, i.e., + * compute the unprotected data. The caller needs to allocated the memory for + * the unprotected data prior to calling this method. + * + * - rp: an alts_iovec_record_protocol instance. + * - header: an iovec containing the frame header. + * - protected_vec: an iovec array containing protected data including the tag. + * - protected_vec_length: the array length of protected_vec. + * - unprotected_data: an iovec containing the output unprotected data. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_privacy_integrity_unprotect( + alts_iovec_record_protocol* rp, iovec_t header, + const iovec_t* protected_vec, size_t protected_vec_length, + iovec_t unprotected_data, char** error_details); + +/** + * This method creates an alts_iovec_record_protocol instance, given a + * gsec_aead_crypter instance, a flag indicating if the created instance will be + * used at the client or server side, and a flag indicating if the created + * instance will be used for integrity-only mode or privacy-integrity mode. The + * ownership of gsec_aead_crypter instance is transferred to this new object. + * + * - crypter: a gsec_aead_crypter instance used to perform AEAD decryption. + * - overflow_size: overflow size of counter in bytes. + * - is_client: a flag indicating if the alts_iovec_record_protocol instance + * will be used at the client or server side. + * - is_integrity_only: a flag indicating if the alts_iovec_record_protocol + * instance will be used for integrity-only or privacy-integrity mode. + * - is_protect: a flag indicating if the alts_grpc_record_protocol instance + * will be used for protect or unprotect. + * - rp: an alts_iovec_record_protocol instance to be returned from + * the method. + * - error_details: a buffer containing an error message if the method does not + * function correctly. It is OK to pass nullptr into error_details. + * + * On success, the method returns GRPC_STATUS_OK. Otherwise, it returns an + * error status code along with its details specified in error_details (if + * error_details is not nullptr). + */ +grpc_status_code alts_iovec_record_protocol_create( + gsec_aead_crypter* crypter, size_t overflow_size, bool is_client, + bool is_integrity_only, bool is_protect, alts_iovec_record_protocol** rp, + char** error_details); + +/** + * This method destroys an alts_iovec_record_protocol instance by de-allocating + * all of its occupied memory. A gsec_aead_crypter instance passed in at + * gsec_alts_crypter instance creation time will be destroyed in this method. + */ +void alts_iovec_record_protocol_destroy(alts_iovec_record_protocol* rp); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_IOVEC_RECORD_PROTOCOL_H \ + */ diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc new file mode 100644 index 00000000000000..58aba9b747e0db --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.cc @@ -0,0 +1,297 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h" + +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/alts/crypt/gsec.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_integrity_only_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_privacy_integrity_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_grpc_record_protocol.h" +#include "src/core/tsi/alts/zero_copy_frame_protector/alts_iovec_record_protocol.h" +#include "src/core/tsi/transport_security_grpc.h" + +constexpr size_t kMinFrameLength = 1024; +constexpr size_t kDefaultFrameLength = 16 * 1024; +constexpr size_t kMaxFrameLength = 1024 * 1024; + +/** + * Main struct for alts_zero_copy_grpc_protector. + * We choose to have two alts_grpc_record_protocol objects and two sets of slice + * buffers: one for protect and the other for unprotect, so that protect and + * unprotect can be executed in parallel. Implementations of this object must be + * thread compatible. + */ +typedef struct alts_zero_copy_grpc_protector { + tsi_zero_copy_grpc_protector base; + alts_grpc_record_protocol* record_protocol; + alts_grpc_record_protocol* unrecord_protocol; + size_t max_protected_frame_size; + size_t max_unprotected_data_size; + grpc_slice_buffer unprotected_staging_sb; + grpc_slice_buffer protected_sb; + grpc_slice_buffer protected_staging_sb; + uint32_t parsed_frame_size; +} alts_zero_copy_grpc_protector; + +/** + * Given a slice buffer, parses the first 4 bytes little-endian unsigned frame + * size and returns the total frame size including the frame field. Caller + * needs to make sure the input slice buffer has at least 4 bytes. Returns true + * on success and false on failure. + */ +static bool read_frame_size(const grpc_slice_buffer* sb, + uint32_t* total_frame_size) { + if (sb == nullptr || sb->length < kZeroCopyFrameLengthFieldSize) { + return false; + } + uint8_t frame_size_buffer[kZeroCopyFrameLengthFieldSize]; + uint8_t* buf = frame_size_buffer; + /* Copies the first 4 bytes to a temporary buffer. */ + size_t remaining = kZeroCopyFrameLengthFieldSize; + for (size_t i = 0; i < sb->count; i++) { + size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]); + if (remaining <= slice_length) { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining); + remaining = 0; + break; + } else { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length); + buf += slice_length; + remaining -= slice_length; + } + } + GPR_ASSERT(remaining == 0); + /* Gets little-endian frame size. */ + uint32_t frame_size = (((uint32_t)frame_size_buffer[3]) << 24) | + (((uint32_t)frame_size_buffer[2]) << 16) | + (((uint32_t)frame_size_buffer[1]) << 8) | + ((uint32_t)frame_size_buffer[0]); + if (frame_size > kMaxFrameLength) { + gpr_log(GPR_ERROR, "Frame size is larger than maximum frame size"); + return false; + } + /* Returns frame size including frame length field. */ + *total_frame_size = + static_cast(frame_size + kZeroCopyFrameLengthFieldSize); + return true; +} + +/** + * Creates an alts_grpc_record_protocol object, given key, key size, and flags + * to indicate whether the record_protocol object uses the rekeying AEAD, + * whether the object is for client or server, whether the object is for + * integrity-only or privacy-integrity mode, and whether the object is is used + * for protect or unprotect. + */ +static tsi_result create_alts_grpc_record_protocol( + const uint8_t* key, size_t key_size, bool is_rekey, bool is_client, + bool is_integrity_only, bool is_protect, bool enable_extra_copy, + alts_grpc_record_protocol** record_protocol) { + if (key == nullptr || record_protocol == nullptr) { + return TSI_INVALID_ARGUMENT; + } + grpc_status_code status; + gsec_aead_crypter* crypter = nullptr; + char* error_details = nullptr; + status = gsec_aes_gcm_aead_crypter_create(key, key_size, kAesGcmNonceLength, + kAesGcmTagLength, is_rekey, + &crypter, &error_details); + if (status != GRPC_STATUS_OK) { + gpr_log(GPR_ERROR, "Failed to create AEAD crypter, %s", error_details); + gpr_free(error_details); + return TSI_INTERNAL_ERROR; + } + size_t overflow_limit = is_rekey ? kAltsRecordProtocolRekeyFrameLimit + : kAltsRecordProtocolFrameLimit; + /* Creates alts_grpc_record_protocol with AEAD crypter ownership transferred. + */ + tsi_result result = is_integrity_only + ? alts_grpc_integrity_only_record_protocol_create( + crypter, overflow_limit, is_client, is_protect, + enable_extra_copy, record_protocol) + : alts_grpc_privacy_integrity_record_protocol_create( + crypter, overflow_limit, is_client, is_protect, + record_protocol); + if (result != TSI_OK) { + gsec_aead_crypter_destroy(crypter); + return result; + } + return TSI_OK; +} + +/* --- tsi_zero_copy_grpc_protector methods implementation. --- */ + +static tsi_result alts_zero_copy_grpc_protector_protect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect."); + return TSI_INVALID_ARGUMENT; + } + alts_zero_copy_grpc_protector* protector = + reinterpret_cast(self); + /* Calls alts_grpc_record_protocol protect repeatly. */ + while (unprotected_slices->length > protector->max_unprotected_data_size) { + grpc_slice_buffer_move_first(unprotected_slices, + protector->max_unprotected_data_size, + &protector->unprotected_staging_sb); + tsi_result status = alts_grpc_record_protocol_protect( + protector->record_protocol, &protector->unprotected_staging_sb, + protected_slices); + if (status != TSI_OK) { + return status; + } + } + return alts_grpc_record_protocol_protect( + protector->record_protocol, unprotected_slices, protected_slices); +} + +static tsi_result alts_zero_copy_grpc_protector_unprotect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to zero-copy grpc unprotect."); + return TSI_INVALID_ARGUMENT; + } + alts_zero_copy_grpc_protector* protector = + reinterpret_cast(self); + grpc_slice_buffer_move_into(protected_slices, &protector->protected_sb); + /* Keep unprotecting each frame if possible. */ + while (protector->protected_sb.length >= kZeroCopyFrameLengthFieldSize) { + if (protector->parsed_frame_size == 0) { + /* We have not parsed frame size yet. Parses frame size. */ + if (!read_frame_size(&protector->protected_sb, + &protector->parsed_frame_size)) { + grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb); + return TSI_DATA_CORRUPTED; + } + } + if (protector->protected_sb.length < protector->parsed_frame_size) break; + /* At this point, protected_sb contains at least one frame of data. */ + tsi_result status; + if (protector->protected_sb.length == protector->parsed_frame_size) { + status = alts_grpc_record_protocol_unprotect(protector->unrecord_protocol, + &protector->protected_sb, + unprotected_slices); + } else { + grpc_slice_buffer_move_first(&protector->protected_sb, + protector->parsed_frame_size, + &protector->protected_staging_sb); + status = alts_grpc_record_protocol_unprotect( + protector->unrecord_protocol, &protector->protected_staging_sb, + unprotected_slices); + } + protector->parsed_frame_size = 0; + if (status != TSI_OK) { + grpc_slice_buffer_reset_and_unref_internal(&protector->protected_sb); + return status; + } + } + return TSI_OK; +} + +static void alts_zero_copy_grpc_protector_destroy( + tsi_zero_copy_grpc_protector* self) { + if (self == nullptr) { + return; + } + alts_zero_copy_grpc_protector* protector = + reinterpret_cast(self); + alts_grpc_record_protocol_destroy(protector->record_protocol); + alts_grpc_record_protocol_destroy(protector->unrecord_protocol); + grpc_slice_buffer_destroy_internal(&protector->unprotected_staging_sb); + grpc_slice_buffer_destroy_internal(&protector->protected_sb); + grpc_slice_buffer_destroy_internal(&protector->protected_staging_sb); + gpr_free(protector); +} + +static const tsi_zero_copy_grpc_protector_vtable + alts_zero_copy_grpc_protector_vtable = { + alts_zero_copy_grpc_protector_protect, + alts_zero_copy_grpc_protector_unprotect, + alts_zero_copy_grpc_protector_destroy}; + +tsi_result alts_zero_copy_grpc_protector_create( + const uint8_t* key, size_t key_size, bool is_rekey, bool is_client, + bool is_integrity_only, bool enable_extra_copy, + size_t* max_protected_frame_size, + tsi_zero_copy_grpc_protector** protector) { + if (grpc_core::ExecCtx::Get() == nullptr || key == nullptr || + protector == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to alts_zero_copy_grpc_protector create."); + return TSI_INVALID_ARGUMENT; + } + /* Creates alts_zero_copy_protector. */ + alts_zero_copy_grpc_protector* impl = + static_cast( + gpr_zalloc(sizeof(alts_zero_copy_grpc_protector))); + /* Creates alts_grpc_record_protocol objects. */ + tsi_result status = create_alts_grpc_record_protocol( + key, key_size, is_rekey, is_client, is_integrity_only, + /*is_protect=*/true, enable_extra_copy, &impl->record_protocol); + if (status == TSI_OK) { + status = create_alts_grpc_record_protocol( + key, key_size, is_rekey, is_client, is_integrity_only, + /*is_protect=*/false, enable_extra_copy, &impl->unrecord_protocol); + if (status == TSI_OK) { + /* Sets maximum frame size. */ + size_t max_protected_frame_size_to_set = kDefaultFrameLength; + if (max_protected_frame_size != nullptr) { + *max_protected_frame_size = + GPR_MIN(*max_protected_frame_size, kMaxFrameLength); + *max_protected_frame_size = + GPR_MAX(*max_protected_frame_size, kMinFrameLength); + max_protected_frame_size_to_set = *max_protected_frame_size; + } + impl->max_protected_frame_size = max_protected_frame_size_to_set; + impl->max_unprotected_data_size = + alts_grpc_record_protocol_max_unprotected_data_size( + impl->record_protocol, max_protected_frame_size_to_set); + GPR_ASSERT(impl->max_unprotected_data_size > 0); + /* Allocates internal slice buffers. */ + grpc_slice_buffer_init(&impl->unprotected_staging_sb); + grpc_slice_buffer_init(&impl->protected_sb); + grpc_slice_buffer_init(&impl->protected_staging_sb); + impl->parsed_frame_size = 0; + impl->base.vtable = &alts_zero_copy_grpc_protector_vtable; + *protector = &impl->base; + return TSI_OK; + } + } + + /* Cleanup if create failed. */ + alts_grpc_record_protocol_destroy(impl->record_protocol); + alts_grpc_record_protocol_destroy(impl->unrecord_protocol); + gpr_free(impl); + return TSI_INTERNAL_ERROR; +} diff --git a/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h new file mode 100644 index 00000000000000..515c27ea052baf --- /dev/null +++ b/third_party/grpc/src/core/tsi/alts/zero_copy_frame_protector/alts_zero_copy_grpc_protector.h @@ -0,0 +1,57 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H +#define GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H + +#include + +#include + +#include "src/core/tsi/transport_security_grpc.h" + +/** + * This method creates an ALTS zero-copy grpc protector. + * + * - key: a symmetric key used to seal/unseal frames. + * - key_size: the size of symmetric key. + * - is_rekey: use rekeying AEAD crypter. + * - is_client: a flag indicating if the protector will be used at client or + * server side. + * - is_integrity_only: a flag indicating if the protector instance will be + * used for integrity-only or privacy-integrity mode. + * - enable_extra_copy: a flag indicating if the protector instance does one + * extra memory copy during the protect operation for integrity_only mode. + * For the unprotect operation, it is still zero-copy. If application intends + * to modify the data buffer after the protect operation, we can turn on this + * mode to avoid integrity check failure. + * - max_protected_frame_size: an in/out parameter indicating max frame size + * to be used by the protector. If it is nullptr, the default frame size will + * be used. Otherwise, the provided frame size will be adjusted (if not + * falling into a valid frame range) and used. + * - protector: a pointer to the zero-copy protector returned from the method. + * + * This method returns TSI_OK on success or a specific error code otherwise. + */ +tsi_result alts_zero_copy_grpc_protector_create( + const uint8_t* key, size_t key_size, bool is_rekey, bool is_client, + bool is_integrity_only, bool enable_extra_copy, + size_t* max_protected_frame_size, tsi_zero_copy_grpc_protector** protector); + +#endif /* GRPC_CORE_TSI_ALTS_ZERO_COPY_FRAME_PROTECTOR_ALTS_ZERO_COPY_GRPC_PROTECTOR_H \ + */ diff --git a/third_party/grpc/src/core/tsi/fake_transport_security.c b/third_party/grpc/src/core/tsi/fake_transport_security.c deleted file mode 100644 index 72ac32a1716545..00000000000000 --- a/third_party/grpc/src/core/tsi/fake_transport_security.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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) { - tsi_fake_handshake_message 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 uint32_t load32_little_endian(const unsigned char *buf) { - return ((uint32_t)(buf[0]) | (uint32_t)(buf[1] << 8) | - (uint32_t)(buf[2] << 16) | (uint32_t)(buf[3] << 24)); -} - -static void store32_little_endian(uint32_t 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 = (size_t)(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 = (size_t)(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 = (size_t)(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((uint32_t)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((uint32_t)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((uint32_t)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) { - tsi_fake_handshake_message 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/third_party/grpc/src/core/tsi/fake_transport_security.cc b/third_party/grpc/src/core/tsi/fake_transport_security.cc new file mode 100644 index 00000000000000..4d4c4950451aaf --- /dev/null +++ b/third_party/grpc/src/core/tsi/fake_transport_security.cc @@ -0,0 +1,788 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/fake_transport_security.h" + +#include +#include + +#include +#include + +#include "src/core/lib/gpr/useful.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/transport_security_grpc.h" + +/* --- Constants. ---*/ +#define TSI_FAKE_FRAME_HEADER_SIZE 4 +#define TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE 64 +#define TSI_FAKE_DEFAULT_FRAME_SIZE 16384 +#define TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE 256 + +/* --- 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_frame; + tsi_fake_frame outgoing_frame; + unsigned char* outgoing_bytes_buffer; + size_t outgoing_bytes_buffer_size; + 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; + +typedef struct { + tsi_zero_copy_grpc_protector base; + grpc_slice_buffer header_sb; + grpc_slice_buffer protected_sb; + size_t max_frame_size; + size_t parsed_frame_size; +} tsi_fake_zero_copy_grpc_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) { + for (int 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 = static_cast(i); + return TSI_OK; + } + } + gpr_log(GPR_ERROR, "Invalid handshake message."); + return TSI_DATA_CORRUPTED; +} + +static uint32_t load32_little_endian(const unsigned char* buf) { + return (static_cast(buf[0]) | static_cast(buf[1] << 8) | + static_cast(buf[2] << 16) | + static_cast(buf[3] << 24)); +} + +static void store32_little_endian(uint32_t value, unsigned char* buf) { + buf[3] = static_cast((value >> 24) & 0xFF); + buf[2] = static_cast((value >> 16) & 0xFF); + buf[1] = static_cast((value >> 8) & 0xFF); + buf[0] = static_cast((value)&0xFF); +} + +static uint32_t read_frame_size(const grpc_slice_buffer* sb) { + GPR_ASSERT(sb != nullptr && sb->length >= TSI_FAKE_FRAME_HEADER_SIZE); + uint8_t frame_size_buffer[TSI_FAKE_FRAME_HEADER_SIZE]; + uint8_t* buf = frame_size_buffer; + /* Copies the first 4 bytes to a temporary buffer. */ + size_t remaining = TSI_FAKE_FRAME_HEADER_SIZE; + for (size_t i = 0; i < sb->count; i++) { + size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]); + if (remaining <= slice_length) { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining); + remaining = 0; + break; + } else { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length); + buf += slice_length; + remaining -= slice_length; + } + } + GPR_ASSERT(remaining == 0); + return load32_little_endian(frame_size_buffer); +} + +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; +} + +/* Checks if the frame's allocated size is at least frame->size, and reallocs + * more memory if necessary. */ +static void tsi_fake_frame_ensure_size(tsi_fake_frame* frame) { + if (frame->data == nullptr) { + frame->allocated_size = frame->size; + frame->data = + static_cast(gpr_malloc(frame->allocated_size)); + } else if (frame->size > frame->allocated_size) { + unsigned char* new_data = + static_cast(gpr_realloc(frame->data, frame->size)); + frame->data = new_data; + frame->allocated_size = frame->size; + } +} + +/* Decodes the serialized fake frame contained in incoming_bytes, and fills + * frame with the contents of the decoded frame. + * This method should not be called if frame->needs_framing is not 0. */ +static tsi_result tsi_fake_frame_decode(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 == nullptr) { + frame->allocated_size = TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE; + frame->data = + static_cast(gpr_malloc(frame->allocated_size)); + } + + 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 = static_cast(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); + tsi_fake_frame_ensure_size(frame); + } + + 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 = static_cast(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 = static_cast(bytes_cursor - incoming_bytes); + tsi_fake_frame_reset(frame, 1 /* needs_draining */); + return TSI_OK; +} + +/* Encodes a fake frame into its wire format and places the result in + * outgoing_bytes. outgoing_bytes_size indicates the size of the encoded frame. + * This method should not be called if frame->needs_framing is 0. */ +static tsi_result tsi_fake_frame_encode(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; +} + +/* Sets the payload of a fake frame to contain the given data blob, where + * data_size indicates the size of data. */ +static tsi_result tsi_fake_frame_set_data(unsigned char* data, size_t data_size, + tsi_fake_frame* frame) { + frame->offset = 0; + frame->size = data_size + TSI_FAKE_FRAME_HEADER_SIZE; + tsi_fake_frame_ensure_size(frame); + store32_little_endian(static_cast(frame->size), frame->data); + memcpy(frame->data + TSI_FAKE_FRAME_HEADER_SIZE, data, data_size); + tsi_fake_frame_reset(frame, 1 /* needs draining */); + return TSI_OK; +} + +/* Destroys the contents of a fake frame. */ +static void tsi_fake_frame_destruct(tsi_fake_frame* frame) { + if (frame->data != nullptr) gpr_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 = + reinterpret_cast(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 = + tsi_fake_frame_encode(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(static_cast(impl->max_frame_size), + frame_header); + written_in_frame_size = TSI_FAKE_FRAME_HEADER_SIZE; + result = tsi_fake_frame_decode(frame_header, &written_in_frame_size, frame); + if (result != TSI_INCOMPLETE_DATA) { + gpr_log(GPR_ERROR, "tsi_fake_frame_decode returned %s", + tsi_result_to_string(result)); + return result; + } + } + result = + tsi_fake_frame_decode(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 = tsi_fake_frame_encode(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 = + reinterpret_cast(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(static_cast(frame->size), + frame->data); /* Overwrite header. */ + } + result = tsi_fake_frame_encode(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 = + reinterpret_cast(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 = tsi_fake_frame_encode(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 = tsi_fake_frame_decode(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 = tsi_fake_frame_encode(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 = + reinterpret_cast(self); + tsi_fake_frame_destruct(&impl->protect_frame); + tsi_fake_frame_destruct(&impl->unprotect_frame); + gpr_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_zero_copy_grpc_protector methods implementation. ---*/ + +static tsi_result fake_zero_copy_grpc_protector_protect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + tsi_fake_zero_copy_grpc_protector* impl = + reinterpret_cast(self); + /* Protects each frame. */ + while (unprotected_slices->length > 0) { + size_t frame_length = + GPR_MIN(impl->max_frame_size, + unprotected_slices->length + TSI_FAKE_FRAME_HEADER_SIZE); + grpc_slice slice = GRPC_SLICE_MALLOC(TSI_FAKE_FRAME_HEADER_SIZE); + store32_little_endian(static_cast(frame_length), + GRPC_SLICE_START_PTR(slice)); + grpc_slice_buffer_add(protected_slices, slice); + size_t data_length = frame_length - TSI_FAKE_FRAME_HEADER_SIZE; + grpc_slice_buffer_move_first(unprotected_slices, data_length, + protected_slices); + } + return TSI_OK; +} + +static tsi_result fake_zero_copy_grpc_protector_unprotect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + tsi_fake_zero_copy_grpc_protector* impl = + reinterpret_cast(self); + grpc_slice_buffer_move_into(protected_slices, &impl->protected_sb); + /* Unprotect each frame, if we get a full frame. */ + while (impl->protected_sb.length >= TSI_FAKE_FRAME_HEADER_SIZE) { + if (impl->parsed_frame_size == 0) { + impl->parsed_frame_size = read_frame_size(&impl->protected_sb); + if (impl->parsed_frame_size <= 4) { + gpr_log(GPR_ERROR, "Invalid frame size."); + return TSI_DATA_CORRUPTED; + } + } + /* If we do not have a full frame, return with OK status. */ + if (impl->protected_sb.length < impl->parsed_frame_size) break; + /* Strips header bytes. */ + grpc_slice_buffer_move_first(&impl->protected_sb, + TSI_FAKE_FRAME_HEADER_SIZE, &impl->header_sb); + /* Moves data to unprotected slices. */ + grpc_slice_buffer_move_first( + &impl->protected_sb, + impl->parsed_frame_size - TSI_FAKE_FRAME_HEADER_SIZE, + unprotected_slices); + impl->parsed_frame_size = 0; + grpc_slice_buffer_reset_and_unref_internal(&impl->header_sb); + } + return TSI_OK; +} + +static void fake_zero_copy_grpc_protector_destroy( + tsi_zero_copy_grpc_protector* self) { + if (self == nullptr) return; + tsi_fake_zero_copy_grpc_protector* impl = + reinterpret_cast(self); + grpc_slice_buffer_destroy_internal(&impl->header_sb); + grpc_slice_buffer_destroy_internal(&impl->protected_sb); + gpr_free(impl); +} + +static const tsi_zero_copy_grpc_protector_vtable + zero_copy_grpc_protector_vtable = { + fake_zero_copy_grpc_protector_protect, + fake_zero_copy_grpc_protector_unprotect, + fake_zero_copy_grpc_protector_destroy, +}; + +/* --- tsi_handshaker_result methods implementation. ---*/ + +typedef struct { + tsi_handshaker_result base; + unsigned char* unused_bytes; + size_t unused_bytes_size; +} fake_handshaker_result; + +static tsi_result fake_handshaker_result_extract_peer( + const tsi_handshaker_result* self, tsi_peer* peer) { + /* Construct a tsi_peer with 1 property: certificate type. */ + 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_result_create_zero_copy_grpc_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_zero_copy_grpc_protector** protector) { + *protector = + tsi_create_fake_zero_copy_grpc_protector(max_output_protected_frame_size); + return TSI_OK; +} + +static tsi_result fake_handshaker_result_create_frame_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_frame_protector** protector) { + *protector = tsi_create_fake_frame_protector(max_output_protected_frame_size); + return TSI_OK; +} + +static tsi_result fake_handshaker_result_get_unused_bytes( + const tsi_handshaker_result* self, const unsigned char** bytes, + size_t* bytes_size) { + fake_handshaker_result* result = (fake_handshaker_result*)self; + *bytes_size = result->unused_bytes_size; + *bytes = result->unused_bytes; + return TSI_OK; +} + +static void fake_handshaker_result_destroy(tsi_handshaker_result* self) { + fake_handshaker_result* result = + reinterpret_cast(self); + gpr_free(result->unused_bytes); + gpr_free(self); +} + +static const tsi_handshaker_result_vtable handshaker_result_vtable = { + fake_handshaker_result_extract_peer, + fake_handshaker_result_create_zero_copy_grpc_protector, + fake_handshaker_result_create_frame_protector, + fake_handshaker_result_get_unused_bytes, + fake_handshaker_result_destroy, +}; + +static tsi_result fake_handshaker_result_create( + const unsigned char* unused_bytes, size_t unused_bytes_size, + tsi_handshaker_result** handshaker_result) { + if ((unused_bytes_size > 0 && unused_bytes == nullptr) || + handshaker_result == nullptr) { + return TSI_INVALID_ARGUMENT; + } + fake_handshaker_result* result = + static_cast(gpr_zalloc(sizeof(*result))); + result->base.vtable = &handshaker_result_vtable; + if (unused_bytes_size > 0) { + result->unused_bytes = + static_cast(gpr_malloc(unused_bytes_size)); + memcpy(result->unused_bytes, unused_bytes, unused_bytes_size); + } + result->unused_bytes_size = unused_bytes_size; + *handshaker_result = &result->base; + return TSI_OK; +} + +/* --- 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 = reinterpret_cast(self); + tsi_result result = TSI_OK; + if (impl->needs_incoming_message || impl->result == TSI_OK) { + *bytes_size = 0; + return TSI_OK; + } + if (!impl->outgoing_frame.needs_draining) { + tsi_fake_handshake_message next_message_to_send = + static_cast(impl->next_message_to_send + 2); + const char* msg_string = + tsi_fake_handshake_message_to_string(impl->next_message_to_send); + result = tsi_fake_frame_set_data((unsigned char*)msg_string, + strlen(msg_string), &impl->outgoing_frame); + 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.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 = tsi_fake_frame_encode(bytes, bytes_size, &impl->outgoing_frame); + 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.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 = reinterpret_cast(self); + tsi_fake_handshake_message expected_msg = + static_cast(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 = tsi_fake_frame_decode(bytes, bytes_size, &impl->incoming_frame); + if (result != TSI_OK) return result; + + /* We now have a complete frame. */ + result = tsi_fake_handshake_message_from_string( + reinterpret_cast(impl->incoming_frame.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.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_frame, 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.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 = reinterpret_cast(self); + return impl->result; +} + +static void fake_handshaker_destroy(tsi_handshaker* self) { + tsi_fake_handshaker* impl = reinterpret_cast(self); + tsi_fake_frame_destruct(&impl->incoming_frame); + tsi_fake_frame_destruct(&impl->outgoing_frame); + gpr_free(impl->outgoing_bytes_buffer); + gpr_free(self); +} + +static tsi_result fake_handshaker_next( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result, + tsi_handshaker_on_next_done_cb cb, void* user_data) { + /* Sanity check the arguments. */ + if ((received_bytes_size > 0 && received_bytes == nullptr) || + bytes_to_send == nullptr || bytes_to_send_size == nullptr || + handshaker_result == nullptr) { + return TSI_INVALID_ARGUMENT; + } + tsi_fake_handshaker* handshaker = + reinterpret_cast(self); + tsi_result result = TSI_OK; + + /* Decode and process a handshake frame from the peer. */ + size_t consumed_bytes_size = received_bytes_size; + if (received_bytes_size > 0) { + result = fake_handshaker_process_bytes_from_peer(self, received_bytes, + &consumed_bytes_size); + if (result != TSI_OK) return result; + } + + /* Create a handshake message to send to the peer and encode it as a fake + * frame. */ + size_t offset = 0; + do { + size_t sent_bytes_size = handshaker->outgoing_bytes_buffer_size - offset; + result = fake_handshaker_get_bytes_to_send_to_peer( + self, handshaker->outgoing_bytes_buffer + offset, &sent_bytes_size); + offset += sent_bytes_size; + if (result == TSI_INCOMPLETE_DATA) { + handshaker->outgoing_bytes_buffer_size *= 2; + handshaker->outgoing_bytes_buffer = static_cast( + gpr_realloc(handshaker->outgoing_bytes_buffer, + handshaker->outgoing_bytes_buffer_size)); + } + } while (result == TSI_INCOMPLETE_DATA); + if (result != TSI_OK) return result; + *bytes_to_send = handshaker->outgoing_bytes_buffer; + *bytes_to_send_size = offset; + + /* Check if the handshake was completed. */ + if (fake_handshaker_get_result(self) == TSI_HANDSHAKE_IN_PROGRESS) { + *handshaker_result = nullptr; + } else { + /* Calculate the unused bytes. */ + const unsigned char* unused_bytes = nullptr; + size_t unused_bytes_size = received_bytes_size - consumed_bytes_size; + if (unused_bytes_size > 0) { + unused_bytes = received_bytes + consumed_bytes_size; + } + + /* Create a handshaker_result containing the unused bytes. */ + result = fake_handshaker_result_create(unused_bytes, unused_bytes_size, + handshaker_result); + if (result == TSI_OK) { + /* Indicate that the handshake has completed and that a handshaker_result + * has been created. */ + self->handshaker_result_created = true; + } + } + return result; +} + +static const tsi_handshaker_vtable handshaker_vtable = { + nullptr, /* get_bytes_to_send_to_peer -- deprecated */ + nullptr, /* process_bytes_from_peer -- deprecated */ + nullptr, /* get_result -- deprecated */ + nullptr, /* extract_peer -- deprecated */ + nullptr, /* create_frame_protector -- deprecated */ + fake_handshaker_destroy, + fake_handshaker_next, + nullptr, /* shutdown */ +}; + +tsi_handshaker* tsi_create_fake_handshaker(int is_client) { + tsi_fake_handshaker* impl = + static_cast(gpr_zalloc(sizeof(*impl))); + impl->base.vtable = &handshaker_vtable; + impl->is_client = is_client; + impl->result = TSI_HANDSHAKE_IN_PROGRESS; + impl->outgoing_bytes_buffer_size = + TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE; + impl->outgoing_bytes_buffer = + static_cast(gpr_malloc(impl->outgoing_bytes_buffer_size)); + 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_frame_protector( + size_t* max_protected_frame_size) { + tsi_fake_frame_protector* impl = + static_cast(gpr_zalloc(sizeof(*impl))); + impl->max_frame_size = (max_protected_frame_size == nullptr) + ? TSI_FAKE_DEFAULT_FRAME_SIZE + : *max_protected_frame_size; + impl->base.vtable = &frame_protector_vtable; + return &impl->base; +} + +tsi_zero_copy_grpc_protector* tsi_create_fake_zero_copy_grpc_protector( + size_t* max_protected_frame_size) { + tsi_fake_zero_copy_grpc_protector* impl = + static_cast( + gpr_zalloc(sizeof(*impl))); + grpc_slice_buffer_init(&impl->header_sb); + grpc_slice_buffer_init(&impl->protected_sb); + impl->max_frame_size = (max_protected_frame_size == nullptr) + ? TSI_FAKE_DEFAULT_FRAME_SIZE + : *max_protected_frame_size; + impl->parsed_frame_size = 0; + impl->base.vtable = &zero_copy_grpc_protector_vtable; + return &impl->base; +} diff --git a/third_party/grpc/src/core/tsi/fake_transport_security.h b/third_party/grpc/src/core/tsi/fake_transport_security.h index fe295aa53607ca..37791827e17548 100644 --- a/third_party/grpc/src/core/tsi/fake_transport_security.h +++ b/third_party/grpc/src/core/tsi/fake_transport_security.h @@ -1,44 +1,27 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_TSI_FAKE_TRANSPORT_SECURITY_H -#define GRPC_INTERNAL_CORE_TSI_FAKE_TRANSPORT_SECURITY_H +#ifndef GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H +#define GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H -#include "src/core/tsi/transport_security_interface.h" +#include -#ifdef __cplusplus -extern "C" { -#endif +#include "src/core/tsi/transport_security_interface.h" /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for FAKE certs. */ #define TSI_FAKE_CERTIFICATE_TYPE "FAKE" @@ -48,14 +31,15 @@ extern "C" { 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); +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); +tsi_frame_protector* tsi_create_fake_frame_protector( + size_t* max_protected_frame_size); -#ifdef __cplusplus -} -#endif +/* Creates a zero-copy protector directly without going through the handshake + * phase. */ +tsi_zero_copy_grpc_protector* tsi_create_fake_zero_copy_grpc_protector( + size_t* max_protected_frame_size); -#endif /* GRPC_INTERNAL_CORE_TSI_FAKE_TRANSPORT_SECURITY_H */ +#endif /* GRPC_CORE_TSI_FAKE_TRANSPORT_SECURITY_H */ diff --git a/third_party/grpc/src/core/tsi/grpc_shadow_boringssl.h b/third_party/grpc/src/core/tsi/grpc_shadow_boringssl.h new file mode 100644 index 00000000000000..074be6d8d9c763 --- /dev/null +++ b/third_party/grpc/src/core/tsi/grpc_shadow_boringssl.h @@ -0,0 +1,3006 @@ + +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// This file is autogenerated from a template file. Please make +// modifications to +// `templates/src/objective-c/tsi/grpc_shadow_boringssl.h.template` +// instead. This file can be regenerated from the template by running +// `tools/buildgen/generate_projects.sh`. + +#ifndef GRPC_CORE_TSI_GRPC_SHADOW_BORINGSSL_H +#define GRPC_CORE_TSI_GRPC_SHADOW_BORINGSSL_H + +#ifdef GRPC_SHADOW_BORINGSSL_SYMBOLS + +#define BIO_f_ssl GRPC_SHADOW_BIO_f_ssl +#define BIO_set_ssl GRPC_SHADOW_BIO_set_ssl +#define SSL_CTX_add_client_custom_ext GRPC_SHADOW_SSL_CTX_add_client_custom_ext +#define SSL_CTX_add_server_custom_ext GRPC_SHADOW_SSL_CTX_add_server_custom_ext +#define DTLSv1_get_timeout GRPC_SHADOW_DTLSv1_get_timeout +#define DTLSv1_handle_timeout GRPC_SHADOW_DTLSv1_handle_timeout +#define DTLSv1_set_initial_timeout_duration GRPC_SHADOW_DTLSv1_set_initial_timeout_duration +#define SSL_CTX_set_srtp_profiles GRPC_SHADOW_SSL_CTX_set_srtp_profiles +#define SSL_CTX_set_tlsext_use_srtp GRPC_SHADOW_SSL_CTX_set_tlsext_use_srtp +#define SSL_get_selected_srtp_profile GRPC_SHADOW_SSL_get_selected_srtp_profile +#define SSL_get_srtp_profiles GRPC_SHADOW_SSL_get_srtp_profiles +#define SSL_set_srtp_profiles GRPC_SHADOW_SSL_set_srtp_profiles +#define SSL_set_tlsext_use_srtp GRPC_SHADOW_SSL_set_tlsext_use_srtp +#define DTLS_client_method GRPC_SHADOW_DTLS_client_method +#define DTLS_method GRPC_SHADOW_DTLS_method +#define DTLS_server_method GRPC_SHADOW_DTLS_server_method +#define DTLS_with_buffers_method GRPC_SHADOW_DTLS_with_buffers_method +#define DTLSv1_2_client_method GRPC_SHADOW_DTLSv1_2_client_method +#define DTLSv1_2_method GRPC_SHADOW_DTLSv1_2_method +#define DTLSv1_2_server_method GRPC_SHADOW_DTLSv1_2_server_method +#define DTLSv1_client_method GRPC_SHADOW_DTLSv1_client_method +#define DTLSv1_method GRPC_SHADOW_DTLSv1_method +#define DTLSv1_server_method GRPC_SHADOW_DTLSv1_server_method +#define SSL_SESSION_from_bytes GRPC_SHADOW_SSL_SESSION_from_bytes +#define SSL_SESSION_to_bytes GRPC_SHADOW_SSL_SESSION_to_bytes +#define SSL_SESSION_to_bytes_for_ticket GRPC_SHADOW_SSL_SESSION_to_bytes_for_ticket +#define i2d_SSL_SESSION GRPC_SHADOW_i2d_SSL_SESSION +#define SSL_CTX_set0_client_CAs GRPC_SHADOW_SSL_CTX_set0_client_CAs +#define SSL_CTX_set_cert_cb GRPC_SHADOW_SSL_CTX_set_cert_cb +#define SSL_CTX_set_chain_and_key GRPC_SHADOW_SSL_CTX_set_chain_and_key +#define SSL_CTX_set_ocsp_response GRPC_SHADOW_SSL_CTX_set_ocsp_response +#define SSL_CTX_set_signed_cert_timestamp_list GRPC_SHADOW_SSL_CTX_set_signed_cert_timestamp_list +#define SSL_CTX_use_certificate_ASN1 GRPC_SHADOW_SSL_CTX_use_certificate_ASN1 +#define SSL_get0_peer_certificates GRPC_SHADOW_SSL_get0_peer_certificates +#define SSL_get0_server_requested_CAs GRPC_SHADOW_SSL_get0_server_requested_CAs +#define SSL_set0_client_CAs GRPC_SHADOW_SSL_set0_client_CAs +#define SSL_set_cert_cb GRPC_SHADOW_SSL_set_cert_cb +#define SSL_set_chain_and_key GRPC_SHADOW_SSL_set_chain_and_key +#define SSL_set_ocsp_response GRPC_SHADOW_SSL_set_ocsp_response +#define SSL_set_signed_cert_timestamp_list GRPC_SHADOW_SSL_set_signed_cert_timestamp_list +#define SSL_use_certificate_ASN1 GRPC_SHADOW_SSL_use_certificate_ASN1 +#define SSL_CIPHER_description GRPC_SHADOW_SSL_CIPHER_description +#define SSL_CIPHER_get_auth_nid GRPC_SHADOW_SSL_CIPHER_get_auth_nid +#define SSL_CIPHER_get_bits GRPC_SHADOW_SSL_CIPHER_get_bits +#define SSL_CIPHER_get_cipher_nid GRPC_SHADOW_SSL_CIPHER_get_cipher_nid +#define SSL_CIPHER_get_digest_nid GRPC_SHADOW_SSL_CIPHER_get_digest_nid +#define SSL_CIPHER_get_id GRPC_SHADOW_SSL_CIPHER_get_id +#define SSL_CIPHER_get_kx_name GRPC_SHADOW_SSL_CIPHER_get_kx_name +#define SSL_CIPHER_get_kx_nid GRPC_SHADOW_SSL_CIPHER_get_kx_nid +#define SSL_CIPHER_get_max_version GRPC_SHADOW_SSL_CIPHER_get_max_version +#define SSL_CIPHER_get_min_version GRPC_SHADOW_SSL_CIPHER_get_min_version +#define SSL_CIPHER_get_name GRPC_SHADOW_SSL_CIPHER_get_name +#define SSL_CIPHER_get_prf_nid GRPC_SHADOW_SSL_CIPHER_get_prf_nid +#define SSL_CIPHER_get_rfc_name GRPC_SHADOW_SSL_CIPHER_get_rfc_name +#define SSL_CIPHER_get_version GRPC_SHADOW_SSL_CIPHER_get_version +#define SSL_CIPHER_is_aead GRPC_SHADOW_SSL_CIPHER_is_aead +#define SSL_CIPHER_is_block_cipher GRPC_SHADOW_SSL_CIPHER_is_block_cipher +#define SSL_CIPHER_standard_name GRPC_SHADOW_SSL_CIPHER_standard_name +#define SSL_COMP_add_compression_method GRPC_SHADOW_SSL_COMP_add_compression_method +#define SSL_COMP_free_compression_methods GRPC_SHADOW_SSL_COMP_free_compression_methods +#define SSL_COMP_get0_name GRPC_SHADOW_SSL_COMP_get0_name +#define SSL_COMP_get_compression_methods GRPC_SHADOW_SSL_COMP_get_compression_methods +#define SSL_COMP_get_id GRPC_SHADOW_SSL_COMP_get_id +#define SSL_COMP_get_name GRPC_SHADOW_SSL_COMP_get_name +#define SSL_get_cipher_by_value GRPC_SHADOW_SSL_get_cipher_by_value +#define SSL_CTX_get_default_passwd_cb GRPC_SHADOW_SSL_CTX_get_default_passwd_cb +#define SSL_CTX_get_default_passwd_cb_userdata GRPC_SHADOW_SSL_CTX_get_default_passwd_cb_userdata +#define SSL_CTX_set_default_passwd_cb GRPC_SHADOW_SSL_CTX_set_default_passwd_cb +#define SSL_CTX_set_default_passwd_cb_userdata GRPC_SHADOW_SSL_CTX_set_default_passwd_cb_userdata +#define SSL_CTX_use_PrivateKey_file GRPC_SHADOW_SSL_CTX_use_PrivateKey_file +#define SSL_CTX_use_RSAPrivateKey_file GRPC_SHADOW_SSL_CTX_use_RSAPrivateKey_file +#define SSL_CTX_use_certificate_chain_file GRPC_SHADOW_SSL_CTX_use_certificate_chain_file +#define SSL_CTX_use_certificate_file GRPC_SHADOW_SSL_CTX_use_certificate_file +#define SSL_add_file_cert_subjects_to_stack GRPC_SHADOW_SSL_add_file_cert_subjects_to_stack +#define SSL_load_client_CA_file GRPC_SHADOW_SSL_load_client_CA_file +#define SSL_use_PrivateKey_file GRPC_SHADOW_SSL_use_PrivateKey_file +#define SSL_use_RSAPrivateKey_file GRPC_SHADOW_SSL_use_RSAPrivateKey_file +#define SSL_use_certificate_file GRPC_SHADOW_SSL_use_certificate_file +#define SSL_get_curve_name GRPC_SHADOW_SSL_get_curve_name +#define ERR_load_SSL_strings GRPC_SHADOW_ERR_load_SSL_strings +#define OPENSSL_init_ssl GRPC_SHADOW_OPENSSL_init_ssl +#define SSL_CTX_check_private_key GRPC_SHADOW_SSL_CTX_check_private_key +#define SSL_CTX_cipher_in_group GRPC_SHADOW_SSL_CTX_cipher_in_group +#define SSL_CTX_clear_mode GRPC_SHADOW_SSL_CTX_clear_mode +#define SSL_CTX_clear_options GRPC_SHADOW_SSL_CTX_clear_options +#define SSL_CTX_enable_ocsp_stapling GRPC_SHADOW_SSL_CTX_enable_ocsp_stapling +#define SSL_CTX_enable_signed_cert_timestamps GRPC_SHADOW_SSL_CTX_enable_signed_cert_timestamps +#define SSL_CTX_enable_tls_channel_id GRPC_SHADOW_SSL_CTX_enable_tls_channel_id +#define SSL_CTX_free GRPC_SHADOW_SSL_CTX_free +#define SSL_CTX_get0_privatekey GRPC_SHADOW_SSL_CTX_get0_privatekey +#define SSL_CTX_get_ciphers GRPC_SHADOW_SSL_CTX_get_ciphers +#define SSL_CTX_get_ex_data GRPC_SHADOW_SSL_CTX_get_ex_data +#define SSL_CTX_get_ex_new_index GRPC_SHADOW_SSL_CTX_get_ex_new_index +#define SSL_CTX_get_keylog_callback GRPC_SHADOW_SSL_CTX_get_keylog_callback +#define SSL_CTX_get_max_cert_list GRPC_SHADOW_SSL_CTX_get_max_cert_list +#define SSL_CTX_get_mode GRPC_SHADOW_SSL_CTX_get_mode +#define SSL_CTX_get_options GRPC_SHADOW_SSL_CTX_get_options +#define SSL_CTX_get_quiet_shutdown GRPC_SHADOW_SSL_CTX_get_quiet_shutdown +#define SSL_CTX_get_read_ahead GRPC_SHADOW_SSL_CTX_get_read_ahead +#define SSL_CTX_get_session_cache_mode GRPC_SHADOW_SSL_CTX_get_session_cache_mode +#define SSL_CTX_get_tlsext_ticket_keys GRPC_SHADOW_SSL_CTX_get_tlsext_ticket_keys +#define SSL_CTX_need_tmp_RSA GRPC_SHADOW_SSL_CTX_need_tmp_RSA +#define SSL_CTX_new GRPC_SHADOW_SSL_CTX_new +#define SSL_CTX_sess_accept GRPC_SHADOW_SSL_CTX_sess_accept +#define SSL_CTX_sess_accept_good GRPC_SHADOW_SSL_CTX_sess_accept_good +#define SSL_CTX_sess_accept_renegotiate GRPC_SHADOW_SSL_CTX_sess_accept_renegotiate +#define SSL_CTX_sess_cache_full GRPC_SHADOW_SSL_CTX_sess_cache_full +#define SSL_CTX_sess_cb_hits GRPC_SHADOW_SSL_CTX_sess_cb_hits +#define SSL_CTX_sess_connect GRPC_SHADOW_SSL_CTX_sess_connect +#define SSL_CTX_sess_connect_good GRPC_SHADOW_SSL_CTX_sess_connect_good +#define SSL_CTX_sess_connect_renegotiate GRPC_SHADOW_SSL_CTX_sess_connect_renegotiate +#define SSL_CTX_sess_get_cache_size GRPC_SHADOW_SSL_CTX_sess_get_cache_size +#define SSL_CTX_sess_hits GRPC_SHADOW_SSL_CTX_sess_hits +#define SSL_CTX_sess_misses GRPC_SHADOW_SSL_CTX_sess_misses +#define SSL_CTX_sess_number GRPC_SHADOW_SSL_CTX_sess_number +#define SSL_CTX_sess_set_cache_size GRPC_SHADOW_SSL_CTX_sess_set_cache_size +#define SSL_CTX_sess_timeouts GRPC_SHADOW_SSL_CTX_sess_timeouts +#define SSL_CTX_set0_buffer_pool GRPC_SHADOW_SSL_CTX_set0_buffer_pool +#define SSL_CTX_set1_curves GRPC_SHADOW_SSL_CTX_set1_curves +#define SSL_CTX_set1_curves_list GRPC_SHADOW_SSL_CTX_set1_curves_list +#define SSL_CTX_set1_tls_channel_id GRPC_SHADOW_SSL_CTX_set1_tls_channel_id +#define SSL_CTX_set_allow_unknown_alpn_protos GRPC_SHADOW_SSL_CTX_set_allow_unknown_alpn_protos +#define SSL_CTX_set_alpn_protos GRPC_SHADOW_SSL_CTX_set_alpn_protos +#define SSL_CTX_set_alpn_select_cb GRPC_SHADOW_SSL_CTX_set_alpn_select_cb +#define SSL_CTX_set_cipher_list GRPC_SHADOW_SSL_CTX_set_cipher_list +#define SSL_CTX_set_current_time_cb GRPC_SHADOW_SSL_CTX_set_current_time_cb +#define SSL_CTX_set_custom_verify GRPC_SHADOW_SSL_CTX_set_custom_verify +#define SSL_CTX_set_dos_protection_cb GRPC_SHADOW_SSL_CTX_set_dos_protection_cb +#define SSL_CTX_set_early_data_enabled GRPC_SHADOW_SSL_CTX_set_early_data_enabled +#define SSL_CTX_set_ex_data GRPC_SHADOW_SSL_CTX_set_ex_data +#define SSL_CTX_set_false_start_allowed_without_alpn GRPC_SHADOW_SSL_CTX_set_false_start_allowed_without_alpn +#define SSL_CTX_set_grease_enabled GRPC_SHADOW_SSL_CTX_set_grease_enabled +#define SSL_CTX_set_keylog_callback GRPC_SHADOW_SSL_CTX_set_keylog_callback +#define SSL_CTX_set_max_cert_list GRPC_SHADOW_SSL_CTX_set_max_cert_list +#define SSL_CTX_set_max_send_fragment GRPC_SHADOW_SSL_CTX_set_max_send_fragment +#define SSL_CTX_set_mode GRPC_SHADOW_SSL_CTX_set_mode +#define SSL_CTX_set_msg_callback GRPC_SHADOW_SSL_CTX_set_msg_callback +#define SSL_CTX_set_msg_callback_arg GRPC_SHADOW_SSL_CTX_set_msg_callback_arg +#define SSL_CTX_set_next_proto_select_cb GRPC_SHADOW_SSL_CTX_set_next_proto_select_cb +#define SSL_CTX_set_next_protos_advertised_cb GRPC_SHADOW_SSL_CTX_set_next_protos_advertised_cb +#define SSL_CTX_set_options GRPC_SHADOW_SSL_CTX_set_options +#define SSL_CTX_set_psk_client_callback GRPC_SHADOW_SSL_CTX_set_psk_client_callback +#define SSL_CTX_set_psk_server_callback GRPC_SHADOW_SSL_CTX_set_psk_server_callback +#define SSL_CTX_set_quiet_shutdown GRPC_SHADOW_SSL_CTX_set_quiet_shutdown +#define SSL_CTX_set_read_ahead GRPC_SHADOW_SSL_CTX_set_read_ahead +#define SSL_CTX_set_retain_only_sha256_of_client_certs GRPC_SHADOW_SSL_CTX_set_retain_only_sha256_of_client_certs +#define SSL_CTX_set_select_certificate_cb GRPC_SHADOW_SSL_CTX_set_select_certificate_cb +#define SSL_CTX_set_session_cache_mode GRPC_SHADOW_SSL_CTX_set_session_cache_mode +#define SSL_CTX_set_session_id_context GRPC_SHADOW_SSL_CTX_set_session_id_context +#define SSL_CTX_set_strict_cipher_list GRPC_SHADOW_SSL_CTX_set_strict_cipher_list +#define SSL_CTX_set_ticket_aead_method GRPC_SHADOW_SSL_CTX_set_ticket_aead_method +#define SSL_CTX_set_tls13_variant GRPC_SHADOW_SSL_CTX_set_tls13_variant +#define SSL_CTX_set_tls_channel_id_enabled GRPC_SHADOW_SSL_CTX_set_tls_channel_id_enabled +#define SSL_CTX_set_tlsext_servername_arg GRPC_SHADOW_SSL_CTX_set_tlsext_servername_arg +#define SSL_CTX_set_tlsext_servername_callback GRPC_SHADOW_SSL_CTX_set_tlsext_servername_callback +#define SSL_CTX_set_tlsext_ticket_key_cb GRPC_SHADOW_SSL_CTX_set_tlsext_ticket_key_cb +#define SSL_CTX_set_tlsext_ticket_keys GRPC_SHADOW_SSL_CTX_set_tlsext_ticket_keys +#define SSL_CTX_set_tmp_dh GRPC_SHADOW_SSL_CTX_set_tmp_dh +#define SSL_CTX_set_tmp_dh_callback GRPC_SHADOW_SSL_CTX_set_tmp_dh_callback +#define SSL_CTX_set_tmp_ecdh GRPC_SHADOW_SSL_CTX_set_tmp_ecdh +#define SSL_CTX_set_tmp_rsa GRPC_SHADOW_SSL_CTX_set_tmp_rsa +#define SSL_CTX_set_tmp_rsa_callback GRPC_SHADOW_SSL_CTX_set_tmp_rsa_callback +#define SSL_CTX_up_ref GRPC_SHADOW_SSL_CTX_up_ref +#define SSL_CTX_use_psk_identity_hint GRPC_SHADOW_SSL_CTX_use_psk_identity_hint +#define SSL_accept GRPC_SHADOW_SSL_accept +#define SSL_cache_hit GRPC_SHADOW_SSL_cache_hit +#define SSL_certs_clear GRPC_SHADOW_SSL_certs_clear +#define SSL_check_private_key GRPC_SHADOW_SSL_check_private_key +#define SSL_clear GRPC_SHADOW_SSL_clear +#define SSL_clear_mode GRPC_SHADOW_SSL_clear_mode +#define SSL_clear_options GRPC_SHADOW_SSL_clear_options +#define SSL_connect GRPC_SHADOW_SSL_connect +#define SSL_cutthrough_complete GRPC_SHADOW_SSL_cutthrough_complete +#define SSL_do_handshake GRPC_SHADOW_SSL_do_handshake +#define SSL_dummy_pq_padding_used GRPC_SHADOW_SSL_dummy_pq_padding_used +#define SSL_early_data_accepted GRPC_SHADOW_SSL_early_data_accepted +#define SSL_enable_ocsp_stapling GRPC_SHADOW_SSL_enable_ocsp_stapling +#define SSL_enable_signed_cert_timestamps GRPC_SHADOW_SSL_enable_signed_cert_timestamps +#define SSL_enable_tls_channel_id GRPC_SHADOW_SSL_enable_tls_channel_id +#define SSL_free GRPC_SHADOW_SSL_free +#define SSL_get0_alpn_selected GRPC_SHADOW_SSL_get0_alpn_selected +#define SSL_get0_certificate_types GRPC_SHADOW_SSL_get0_certificate_types +#define SSL_get0_next_proto_negotiated GRPC_SHADOW_SSL_get0_next_proto_negotiated +#define SSL_get0_ocsp_response GRPC_SHADOW_SSL_get0_ocsp_response +#define SSL_get0_session_id_context GRPC_SHADOW_SSL_get0_session_id_context +#define SSL_get0_signed_cert_timestamp_list GRPC_SHADOW_SSL_get0_signed_cert_timestamp_list +#define SSL_get_SSL_CTX GRPC_SHADOW_SSL_get_SSL_CTX +#define SSL_get_cipher_list GRPC_SHADOW_SSL_get_cipher_list +#define SSL_get_ciphers GRPC_SHADOW_SSL_get_ciphers +#define SSL_get_client_random GRPC_SHADOW_SSL_get_client_random +#define SSL_get_current_cipher GRPC_SHADOW_SSL_get_current_cipher +#define SSL_get_current_compression GRPC_SHADOW_SSL_get_current_compression +#define SSL_get_current_expansion GRPC_SHADOW_SSL_get_current_expansion +#define SSL_get_curve_id GRPC_SHADOW_SSL_get_curve_id +#define SSL_get_default_timeout GRPC_SHADOW_SSL_get_default_timeout +#define SSL_get_error GRPC_SHADOW_SSL_get_error +#define SSL_get_ex_data GRPC_SHADOW_SSL_get_ex_data +#define SSL_get_ex_new_index GRPC_SHADOW_SSL_get_ex_new_index +#define SSL_get_extms_support GRPC_SHADOW_SSL_get_extms_support +#define SSL_get_fd GRPC_SHADOW_SSL_get_fd +#define SSL_get_finished GRPC_SHADOW_SSL_get_finished +#define SSL_get_info_callback GRPC_SHADOW_SSL_get_info_callback +#define SSL_get_ivs GRPC_SHADOW_SSL_get_ivs +#define SSL_get_max_cert_list GRPC_SHADOW_SSL_get_max_cert_list +#define SSL_get_mode GRPC_SHADOW_SSL_get_mode +#define SSL_get_negotiated_token_binding_param GRPC_SHADOW_SSL_get_negotiated_token_binding_param +#define SSL_get_options GRPC_SHADOW_SSL_get_options +#define SSL_get_peer_finished GRPC_SHADOW_SSL_get_peer_finished +#define SSL_get_peer_quic_transport_params GRPC_SHADOW_SSL_get_peer_quic_transport_params +#define SSL_get_peer_signature_algorithm GRPC_SHADOW_SSL_get_peer_signature_algorithm +#define SSL_get_pending_cipher GRPC_SHADOW_SSL_get_pending_cipher +#define SSL_get_privatekey GRPC_SHADOW_SSL_get_privatekey +#define SSL_get_psk_identity GRPC_SHADOW_SSL_get_psk_identity +#define SSL_get_psk_identity_hint GRPC_SHADOW_SSL_get_psk_identity_hint +#define SSL_get_quiet_shutdown GRPC_SHADOW_SSL_get_quiet_shutdown +#define SSL_get_rbio GRPC_SHADOW_SSL_get_rbio +#define SSL_get_read_ahead GRPC_SHADOW_SSL_get_read_ahead +#define SSL_get_read_sequence GRPC_SHADOW_SSL_get_read_sequence +#define SSL_get_rfd GRPC_SHADOW_SSL_get_rfd +#define SSL_get_secure_renegotiation_support GRPC_SHADOW_SSL_get_secure_renegotiation_support +#define SSL_get_server_random GRPC_SHADOW_SSL_get_server_random +#define SSL_get_server_tmp_key GRPC_SHADOW_SSL_get_server_tmp_key +#define SSL_get_servername GRPC_SHADOW_SSL_get_servername +#define SSL_get_servername_type GRPC_SHADOW_SSL_get_servername_type +#define SSL_get_shared_ciphers GRPC_SHADOW_SSL_get_shared_ciphers +#define SSL_get_shutdown GRPC_SHADOW_SSL_get_shutdown +#define SSL_get_structure_sizes GRPC_SHADOW_SSL_get_structure_sizes +#define SSL_get_ticket_age_skew GRPC_SHADOW_SSL_get_ticket_age_skew +#define SSL_get_tls_channel_id GRPC_SHADOW_SSL_get_tls_channel_id +#define SSL_get_tls_unique GRPC_SHADOW_SSL_get_tls_unique +#define SSL_get_verify_mode GRPC_SHADOW_SSL_get_verify_mode +#define SSL_get_wbio GRPC_SHADOW_SSL_get_wbio +#define SSL_get_wfd GRPC_SHADOW_SSL_get_wfd +#define SSL_get_write_sequence GRPC_SHADOW_SSL_get_write_sequence +#define SSL_in_early_data GRPC_SHADOW_SSL_in_early_data +#define SSL_in_false_start GRPC_SHADOW_SSL_in_false_start +#define SSL_in_init GRPC_SHADOW_SSL_in_init +#define SSL_is_draft_downgrade GRPC_SHADOW_SSL_is_draft_downgrade +#define SSL_is_dtls GRPC_SHADOW_SSL_is_dtls +#define SSL_is_init_finished GRPC_SHADOW_SSL_is_init_finished +#define SSL_is_server GRPC_SHADOW_SSL_is_server +#define SSL_is_token_binding_negotiated GRPC_SHADOW_SSL_is_token_binding_negotiated +#define SSL_library_init GRPC_SHADOW_SSL_library_init +#define SSL_load_error_strings GRPC_SHADOW_SSL_load_error_strings +#define SSL_need_tmp_RSA GRPC_SHADOW_SSL_need_tmp_RSA +#define SSL_new GRPC_SHADOW_SSL_new +#define SSL_num_renegotiations GRPC_SHADOW_SSL_num_renegotiations +#define SSL_peek GRPC_SHADOW_SSL_peek +#define SSL_pending GRPC_SHADOW_SSL_pending +#define SSL_read GRPC_SHADOW_SSL_read +#define SSL_renegotiate GRPC_SHADOW_SSL_renegotiate +#define SSL_renegotiate_pending GRPC_SHADOW_SSL_renegotiate_pending +#define SSL_reset_early_data_reject GRPC_SHADOW_SSL_reset_early_data_reject +#define SSL_select_next_proto GRPC_SHADOW_SSL_select_next_proto +#define SSL_send_fatal_alert GRPC_SHADOW_SSL_send_fatal_alert +#define SSL_session_reused GRPC_SHADOW_SSL_session_reused +#define SSL_set0_rbio GRPC_SHADOW_SSL_set0_rbio +#define SSL_set0_wbio GRPC_SHADOW_SSL_set0_wbio +#define SSL_set1_curves GRPC_SHADOW_SSL_set1_curves +#define SSL_set1_curves_list GRPC_SHADOW_SSL_set1_curves_list +#define SSL_set1_tls_channel_id GRPC_SHADOW_SSL_set1_tls_channel_id +#define SSL_set_SSL_CTX GRPC_SHADOW_SSL_set_SSL_CTX +#define SSL_set_accept_state GRPC_SHADOW_SSL_set_accept_state +#define SSL_set_alpn_protos GRPC_SHADOW_SSL_set_alpn_protos +#define SSL_set_bio GRPC_SHADOW_SSL_set_bio +#define SSL_set_cipher_list GRPC_SHADOW_SSL_set_cipher_list +#define SSL_set_connect_state GRPC_SHADOW_SSL_set_connect_state +#define SSL_set_custom_verify GRPC_SHADOW_SSL_set_custom_verify +#define SSL_set_dummy_pq_padding_size GRPC_SHADOW_SSL_set_dummy_pq_padding_size +#define SSL_set_early_data_enabled GRPC_SHADOW_SSL_set_early_data_enabled +#define SSL_set_ex_data GRPC_SHADOW_SSL_set_ex_data +#define SSL_set_fd GRPC_SHADOW_SSL_set_fd +#define SSL_set_info_callback GRPC_SHADOW_SSL_set_info_callback +#define SSL_set_max_cert_list GRPC_SHADOW_SSL_set_max_cert_list +#define SSL_set_max_send_fragment GRPC_SHADOW_SSL_set_max_send_fragment +#define SSL_set_mode GRPC_SHADOW_SSL_set_mode +#define SSL_set_msg_callback GRPC_SHADOW_SSL_set_msg_callback +#define SSL_set_msg_callback_arg GRPC_SHADOW_SSL_set_msg_callback_arg +#define SSL_set_mtu GRPC_SHADOW_SSL_set_mtu +#define SSL_set_options GRPC_SHADOW_SSL_set_options +#define SSL_set_psk_client_callback GRPC_SHADOW_SSL_set_psk_client_callback +#define SSL_set_psk_server_callback GRPC_SHADOW_SSL_set_psk_server_callback +#define SSL_set_quic_transport_params GRPC_SHADOW_SSL_set_quic_transport_params +#define SSL_set_quiet_shutdown GRPC_SHADOW_SSL_set_quiet_shutdown +#define SSL_set_read_ahead GRPC_SHADOW_SSL_set_read_ahead +#define SSL_set_renegotiate_mode GRPC_SHADOW_SSL_set_renegotiate_mode +#define SSL_set_retain_only_sha256_of_client_certs GRPC_SHADOW_SSL_set_retain_only_sha256_of_client_certs +#define SSL_set_rfd GRPC_SHADOW_SSL_set_rfd +#define SSL_set_session_id_context GRPC_SHADOW_SSL_set_session_id_context +#define SSL_set_shutdown GRPC_SHADOW_SSL_set_shutdown +#define SSL_set_state GRPC_SHADOW_SSL_set_state +#define SSL_set_strict_cipher_list GRPC_SHADOW_SSL_set_strict_cipher_list +#define SSL_set_tls13_variant GRPC_SHADOW_SSL_set_tls13_variant +#define SSL_set_tls_channel_id_enabled GRPC_SHADOW_SSL_set_tls_channel_id_enabled +#define SSL_set_tlsext_host_name GRPC_SHADOW_SSL_set_tlsext_host_name +#define SSL_set_tmp_dh GRPC_SHADOW_SSL_set_tmp_dh +#define SSL_set_tmp_dh_callback GRPC_SHADOW_SSL_set_tmp_dh_callback +#define SSL_set_tmp_ecdh GRPC_SHADOW_SSL_set_tmp_ecdh +#define SSL_set_tmp_rsa GRPC_SHADOW_SSL_set_tmp_rsa +#define SSL_set_tmp_rsa_callback GRPC_SHADOW_SSL_set_tmp_rsa_callback +#define SSL_set_token_binding_params GRPC_SHADOW_SSL_set_token_binding_params +#define SSL_set_wfd GRPC_SHADOW_SSL_set_wfd +#define SSL_shutdown GRPC_SHADOW_SSL_shutdown +#define SSL_state GRPC_SHADOW_SSL_state +#define SSL_total_renegotiations GRPC_SHADOW_SSL_total_renegotiations +#define SSL_use_psk_identity_hint GRPC_SHADOW_SSL_use_psk_identity_hint +#define SSL_want GRPC_SHADOW_SSL_want +#define SSL_write GRPC_SHADOW_SSL_write +#define SSL_CTX_set_private_key_method GRPC_SHADOW_SSL_CTX_set_private_key_method +#define SSL_CTX_set_signing_algorithm_prefs GRPC_SHADOW_SSL_CTX_set_signing_algorithm_prefs +#define SSL_CTX_set_verify_algorithm_prefs GRPC_SHADOW_SSL_CTX_set_verify_algorithm_prefs +#define SSL_CTX_use_PrivateKey GRPC_SHADOW_SSL_CTX_use_PrivateKey +#define SSL_CTX_use_PrivateKey_ASN1 GRPC_SHADOW_SSL_CTX_use_PrivateKey_ASN1 +#define SSL_CTX_use_RSAPrivateKey GRPC_SHADOW_SSL_CTX_use_RSAPrivateKey +#define SSL_CTX_use_RSAPrivateKey_ASN1 GRPC_SHADOW_SSL_CTX_use_RSAPrivateKey_ASN1 +#define SSL_get_signature_algorithm_digest GRPC_SHADOW_SSL_get_signature_algorithm_digest +#define SSL_get_signature_algorithm_key_type GRPC_SHADOW_SSL_get_signature_algorithm_key_type +#define SSL_get_signature_algorithm_name GRPC_SHADOW_SSL_get_signature_algorithm_name +#define SSL_is_signature_algorithm_rsa_pss GRPC_SHADOW_SSL_is_signature_algorithm_rsa_pss +#define SSL_set_private_key_method GRPC_SHADOW_SSL_set_private_key_method +#define SSL_set_signing_algorithm_prefs GRPC_SHADOW_SSL_set_signing_algorithm_prefs +#define SSL_use_PrivateKey GRPC_SHADOW_SSL_use_PrivateKey +#define SSL_use_PrivateKey_ASN1 GRPC_SHADOW_SSL_use_PrivateKey_ASN1 +#define SSL_use_RSAPrivateKey GRPC_SHADOW_SSL_use_RSAPrivateKey +#define SSL_use_RSAPrivateKey_ASN1 GRPC_SHADOW_SSL_use_RSAPrivateKey_ASN1 +#define SSL_CTX_add_session GRPC_SHADOW_SSL_CTX_add_session +#define SSL_CTX_flush_sessions GRPC_SHADOW_SSL_CTX_flush_sessions +#define SSL_CTX_get_channel_id_cb GRPC_SHADOW_SSL_CTX_get_channel_id_cb +#define SSL_CTX_get_info_callback GRPC_SHADOW_SSL_CTX_get_info_callback +#define SSL_CTX_get_timeout GRPC_SHADOW_SSL_CTX_get_timeout +#define SSL_CTX_remove_session GRPC_SHADOW_SSL_CTX_remove_session +#define SSL_CTX_sess_get_get_cb GRPC_SHADOW_SSL_CTX_sess_get_get_cb +#define SSL_CTX_sess_get_new_cb GRPC_SHADOW_SSL_CTX_sess_get_new_cb +#define SSL_CTX_sess_get_remove_cb GRPC_SHADOW_SSL_CTX_sess_get_remove_cb +#define SSL_CTX_sess_set_get_cb GRPC_SHADOW_SSL_CTX_sess_set_get_cb +#define SSL_CTX_sess_set_new_cb GRPC_SHADOW_SSL_CTX_sess_set_new_cb +#define SSL_CTX_sess_set_remove_cb GRPC_SHADOW_SSL_CTX_sess_set_remove_cb +#define SSL_CTX_set_channel_id_cb GRPC_SHADOW_SSL_CTX_set_channel_id_cb +#define SSL_CTX_set_info_callback GRPC_SHADOW_SSL_CTX_set_info_callback +#define SSL_CTX_set_session_psk_dhe_timeout GRPC_SHADOW_SSL_CTX_set_session_psk_dhe_timeout +#define SSL_CTX_set_timeout GRPC_SHADOW_SSL_CTX_set_timeout +#define SSL_SESSION_free GRPC_SHADOW_SSL_SESSION_free +#define SSL_SESSION_get0_peer GRPC_SHADOW_SSL_SESSION_get0_peer +#define SSL_SESSION_get0_ticket GRPC_SHADOW_SSL_SESSION_get0_ticket +#define SSL_SESSION_get_ex_data GRPC_SHADOW_SSL_SESSION_get_ex_data +#define SSL_SESSION_get_ex_new_index GRPC_SHADOW_SSL_SESSION_get_ex_new_index +#define SSL_SESSION_get_id GRPC_SHADOW_SSL_SESSION_get_id +#define SSL_SESSION_get_master_key GRPC_SHADOW_SSL_SESSION_get_master_key +#define SSL_SESSION_get_ticket_lifetime_hint GRPC_SHADOW_SSL_SESSION_get_ticket_lifetime_hint +#define SSL_SESSION_get_time GRPC_SHADOW_SSL_SESSION_get_time +#define SSL_SESSION_get_timeout GRPC_SHADOW_SSL_SESSION_get_timeout +#define SSL_SESSION_has_ticket GRPC_SHADOW_SSL_SESSION_has_ticket +#define SSL_SESSION_is_resumable GRPC_SHADOW_SSL_SESSION_is_resumable +#define SSL_SESSION_new GRPC_SHADOW_SSL_SESSION_new +#define SSL_SESSION_set1_id_context GRPC_SHADOW_SSL_SESSION_set1_id_context +#define SSL_SESSION_set_ex_data GRPC_SHADOW_SSL_SESSION_set_ex_data +#define SSL_SESSION_set_time GRPC_SHADOW_SSL_SESSION_set_time +#define SSL_SESSION_set_timeout GRPC_SHADOW_SSL_SESSION_set_timeout +#define SSL_SESSION_should_be_single_use GRPC_SHADOW_SSL_SESSION_should_be_single_use +#define SSL_SESSION_up_ref GRPC_SHADOW_SSL_SESSION_up_ref +#define SSL_get1_session GRPC_SHADOW_SSL_get1_session +#define SSL_get_session GRPC_SHADOW_SSL_get_session +#define SSL_magic_pending_session_ptr GRPC_SHADOW_SSL_magic_pending_session_ptr +#define SSL_set_session GRPC_SHADOW_SSL_set_session +#define SSL_alert_desc_string GRPC_SHADOW_SSL_alert_desc_string +#define SSL_alert_desc_string_long GRPC_SHADOW_SSL_alert_desc_string_long +#define SSL_alert_type_string GRPC_SHADOW_SSL_alert_type_string +#define SSL_alert_type_string_long GRPC_SHADOW_SSL_alert_type_string_long +#define SSL_state_string GRPC_SHADOW_SSL_state_string +#define SSL_state_string_long GRPC_SHADOW_SSL_state_string_long +#define SSL_CTX_set_max_proto_version GRPC_SHADOW_SSL_CTX_set_max_proto_version +#define SSL_CTX_set_min_proto_version GRPC_SHADOW_SSL_CTX_set_min_proto_version +#define SSL_SESSION_get_protocol_version GRPC_SHADOW_SSL_SESSION_get_protocol_version +#define SSL_SESSION_get_version GRPC_SHADOW_SSL_SESSION_get_version +#define SSL_SESSION_set_protocol_version GRPC_SHADOW_SSL_SESSION_set_protocol_version +#define SSL_get_version GRPC_SHADOW_SSL_get_version +#define SSL_set_max_proto_version GRPC_SHADOW_SSL_set_max_proto_version +#define SSL_set_min_proto_version GRPC_SHADOW_SSL_set_min_proto_version +#define SSL_version GRPC_SHADOW_SSL_version +#define PEM_read_SSL_SESSION GRPC_SHADOW_PEM_read_SSL_SESSION +#define PEM_read_bio_SSL_SESSION GRPC_SHADOW_PEM_read_bio_SSL_SESSION +#define PEM_write_SSL_SESSION GRPC_SHADOW_PEM_write_SSL_SESSION +#define PEM_write_bio_SSL_SESSION GRPC_SHADOW_PEM_write_bio_SSL_SESSION +#define SSL_CTX_add0_chain_cert GRPC_SHADOW_SSL_CTX_add0_chain_cert +#define SSL_CTX_add1_chain_cert GRPC_SHADOW_SSL_CTX_add1_chain_cert +#define SSL_CTX_add_client_CA GRPC_SHADOW_SSL_CTX_add_client_CA +#define SSL_CTX_add_extra_chain_cert GRPC_SHADOW_SSL_CTX_add_extra_chain_cert +#define SSL_CTX_clear_chain_certs GRPC_SHADOW_SSL_CTX_clear_chain_certs +#define SSL_CTX_clear_extra_chain_certs GRPC_SHADOW_SSL_CTX_clear_extra_chain_certs +#define SSL_CTX_get0_certificate GRPC_SHADOW_SSL_CTX_get0_certificate +#define SSL_CTX_get0_chain_certs GRPC_SHADOW_SSL_CTX_get0_chain_certs +#define SSL_CTX_get0_param GRPC_SHADOW_SSL_CTX_get0_param +#define SSL_CTX_get_cert_store GRPC_SHADOW_SSL_CTX_get_cert_store +#define SSL_CTX_get_client_CA_list GRPC_SHADOW_SSL_CTX_get_client_CA_list +#define SSL_CTX_get_extra_chain_certs GRPC_SHADOW_SSL_CTX_get_extra_chain_certs +#define SSL_CTX_get_verify_callback GRPC_SHADOW_SSL_CTX_get_verify_callback +#define SSL_CTX_get_verify_depth GRPC_SHADOW_SSL_CTX_get_verify_depth +#define SSL_CTX_get_verify_mode GRPC_SHADOW_SSL_CTX_get_verify_mode +#define SSL_CTX_load_verify_locations GRPC_SHADOW_SSL_CTX_load_verify_locations +#define SSL_CTX_set0_chain GRPC_SHADOW_SSL_CTX_set0_chain +#define SSL_CTX_set0_verify_cert_store GRPC_SHADOW_SSL_CTX_set0_verify_cert_store +#define SSL_CTX_set1_chain GRPC_SHADOW_SSL_CTX_set1_chain +#define SSL_CTX_set1_param GRPC_SHADOW_SSL_CTX_set1_param +#define SSL_CTX_set1_verify_cert_store GRPC_SHADOW_SSL_CTX_set1_verify_cert_store +#define SSL_CTX_set_cert_store GRPC_SHADOW_SSL_CTX_set_cert_store +#define SSL_CTX_set_cert_verify_callback GRPC_SHADOW_SSL_CTX_set_cert_verify_callback +#define SSL_CTX_set_client_CA_list GRPC_SHADOW_SSL_CTX_set_client_CA_list +#define SSL_CTX_set_client_cert_cb GRPC_SHADOW_SSL_CTX_set_client_cert_cb +#define SSL_CTX_set_default_verify_paths GRPC_SHADOW_SSL_CTX_set_default_verify_paths +#define SSL_CTX_set_purpose GRPC_SHADOW_SSL_CTX_set_purpose +#define SSL_CTX_set_trust GRPC_SHADOW_SSL_CTX_set_trust +#define SSL_CTX_set_verify GRPC_SHADOW_SSL_CTX_set_verify +#define SSL_CTX_set_verify_depth GRPC_SHADOW_SSL_CTX_set_verify_depth +#define SSL_CTX_use_certificate GRPC_SHADOW_SSL_CTX_use_certificate +#define SSL_add0_chain_cert GRPC_SHADOW_SSL_add0_chain_cert +#define SSL_add1_chain_cert GRPC_SHADOW_SSL_add1_chain_cert +#define SSL_add_client_CA GRPC_SHADOW_SSL_add_client_CA +#define SSL_alert_from_verify_result GRPC_SHADOW_SSL_alert_from_verify_result +#define SSL_clear_chain_certs GRPC_SHADOW_SSL_clear_chain_certs +#define SSL_dup_CA_list GRPC_SHADOW_SSL_dup_CA_list +#define SSL_get0_chain_certs GRPC_SHADOW_SSL_get0_chain_certs +#define SSL_get0_param GRPC_SHADOW_SSL_get0_param +#define SSL_get_certificate GRPC_SHADOW_SSL_get_certificate +#define SSL_get_client_CA_list GRPC_SHADOW_SSL_get_client_CA_list +#define SSL_get_ex_data_X509_STORE_CTX_idx GRPC_SHADOW_SSL_get_ex_data_X509_STORE_CTX_idx +#define SSL_get_peer_cert_chain GRPC_SHADOW_SSL_get_peer_cert_chain +#define SSL_get_peer_certificate GRPC_SHADOW_SSL_get_peer_certificate +#define SSL_get_peer_full_cert_chain GRPC_SHADOW_SSL_get_peer_full_cert_chain +#define SSL_get_verify_callback GRPC_SHADOW_SSL_get_verify_callback +#define SSL_get_verify_depth GRPC_SHADOW_SSL_get_verify_depth +#define SSL_get_verify_result GRPC_SHADOW_SSL_get_verify_result +#define SSL_set0_chain GRPC_SHADOW_SSL_set0_chain +#define SSL_set0_verify_cert_store GRPC_SHADOW_SSL_set0_verify_cert_store +#define SSL_set1_chain GRPC_SHADOW_SSL_set1_chain +#define SSL_set1_param GRPC_SHADOW_SSL_set1_param +#define SSL_set1_verify_cert_store GRPC_SHADOW_SSL_set1_verify_cert_store +#define SSL_set_client_CA_list GRPC_SHADOW_SSL_set_client_CA_list +#define SSL_set_purpose GRPC_SHADOW_SSL_set_purpose +#define SSL_set_trust GRPC_SHADOW_SSL_set_trust +#define SSL_set_verify GRPC_SHADOW_SSL_set_verify +#define SSL_set_verify_depth GRPC_SHADOW_SSL_set_verify_depth +#define SSL_set_verify_result GRPC_SHADOW_SSL_set_verify_result +#define SSL_use_certificate GRPC_SHADOW_SSL_use_certificate +#define d2i_SSL_SESSION GRPC_SHADOW_d2i_SSL_SESSION +#define d2i_SSL_SESSION_bio GRPC_SHADOW_d2i_SSL_SESSION_bio +#define i2d_SSL_SESSION_bio GRPC_SHADOW_i2d_SSL_SESSION_bio +#define SSL_export_early_keying_material GRPC_SHADOW_SSL_export_early_keying_material +#define SSL_export_keying_material GRPC_SHADOW_SSL_export_keying_material +#define SSL_generate_key_block GRPC_SHADOW_SSL_generate_key_block +#define SSL_get_key_block_len GRPC_SHADOW_SSL_get_key_block_len +#define SSL_CTX_set_ed25519_enabled GRPC_SHADOW_SSL_CTX_set_ed25519_enabled +#define SSL_early_callback_ctx_extension_get GRPC_SHADOW_SSL_early_callback_ctx_extension_get +#define SSL_extension_supported GRPC_SHADOW_SSL_extension_supported +#define SSLv23_client_method GRPC_SHADOW_SSLv23_client_method +#define SSLv23_method GRPC_SHADOW_SSLv23_method +#define SSLv23_server_method GRPC_SHADOW_SSLv23_server_method +#define TLS_client_method GRPC_SHADOW_TLS_client_method +#define TLS_method GRPC_SHADOW_TLS_method +#define TLS_server_method GRPC_SHADOW_TLS_server_method +#define TLS_with_buffers_method GRPC_SHADOW_TLS_with_buffers_method +#define TLSv1_1_client_method GRPC_SHADOW_TLSv1_1_client_method +#define TLSv1_1_method GRPC_SHADOW_TLSv1_1_method +#define TLSv1_1_server_method GRPC_SHADOW_TLSv1_1_server_method +#define TLSv1_2_client_method GRPC_SHADOW_TLSv1_2_client_method +#define TLSv1_2_method GRPC_SHADOW_TLSv1_2_method +#define TLSv1_2_server_method GRPC_SHADOW_TLSv1_2_server_method +#define TLSv1_client_method GRPC_SHADOW_TLSv1_client_method +#define TLSv1_method GRPC_SHADOW_TLSv1_method +#define TLSv1_server_method GRPC_SHADOW_TLSv1_server_method +#define SSL_max_seal_overhead GRPC_SHADOW_SSL_max_seal_overhead +#define OPENSSL_cpuid_setup GRPC_SHADOW_OPENSSL_cpuid_setup +#define CRYPTO_has_asm GRPC_SHADOW_CRYPTO_has_asm +#define CRYPTO_is_confidential_build GRPC_SHADOW_CRYPTO_is_confidential_build +#define CRYPTO_library_init GRPC_SHADOW_CRYPTO_library_init +#define CRYPTO_malloc_init GRPC_SHADOW_CRYPTO_malloc_init +#define ENGINE_load_builtin_engines GRPC_SHADOW_ENGINE_load_builtin_engines +#define ENGINE_register_all_complete GRPC_SHADOW_ENGINE_register_all_complete +#define OPENSSL_ia32cap_P GRPC_SHADOW_OPENSSL_ia32cap_P +#define OPENSSL_init_crypto GRPC_SHADOW_OPENSSL_init_crypto +#define OPENSSL_load_builtin_modules GRPC_SHADOW_OPENSSL_load_builtin_modules +#define OpenSSL_version GRPC_SHADOW_OpenSSL_version +#define OpenSSL_version_num GRPC_SHADOW_OpenSSL_version_num +#define SSLeay GRPC_SHADOW_SSLeay +#define SSLeay_version GRPC_SHADOW_SSLeay_version +#define CRYPTO_cleanup_all_ex_data GRPC_SHADOW_CRYPTO_cleanup_all_ex_data +#define CRYPTO_free_ex_data GRPC_SHADOW_CRYPTO_free_ex_data +#define CRYPTO_get_ex_data GRPC_SHADOW_CRYPTO_get_ex_data +#define CRYPTO_get_ex_new_index GRPC_SHADOW_CRYPTO_get_ex_new_index +#define CRYPTO_new_ex_data GRPC_SHADOW_CRYPTO_new_ex_data +#define CRYPTO_set_ex_data GRPC_SHADOW_CRYPTO_set_ex_data +#define BIO_snprintf GRPC_SHADOW_BIO_snprintf +#define BIO_vsnprintf GRPC_SHADOW_BIO_vsnprintf +#define CRYPTO_memcmp GRPC_SHADOW_CRYPTO_memcmp +#define OPENSSL_cleanse GRPC_SHADOW_OPENSSL_cleanse +#define OPENSSL_free GRPC_SHADOW_OPENSSL_free +#define OPENSSL_hash32 GRPC_SHADOW_OPENSSL_hash32 +#define OPENSSL_malloc GRPC_SHADOW_OPENSSL_malloc +#define OPENSSL_realloc GRPC_SHADOW_OPENSSL_realloc +#define OPENSSL_strcasecmp GRPC_SHADOW_OPENSSL_strcasecmp +#define OPENSSL_strdup GRPC_SHADOW_OPENSSL_strdup +#define OPENSSL_strncasecmp GRPC_SHADOW_OPENSSL_strncasecmp +#define OPENSSL_strnlen GRPC_SHADOW_OPENSSL_strnlen +#define OPENSSL_tolower GRPC_SHADOW_OPENSSL_tolower +#define CRYPTO_refcount_dec_and_test_zero GRPC_SHADOW_CRYPTO_refcount_dec_and_test_zero +#define CRYPTO_refcount_inc GRPC_SHADOW_CRYPTO_refcount_inc +#define CRYPTO_THREADID_current GRPC_SHADOW_CRYPTO_THREADID_current +#define CRYPTO_THREADID_set_callback GRPC_SHADOW_CRYPTO_THREADID_set_callback +#define CRYPTO_THREADID_set_numeric GRPC_SHADOW_CRYPTO_THREADID_set_numeric +#define CRYPTO_THREADID_set_pointer GRPC_SHADOW_CRYPTO_THREADID_set_pointer +#define CRYPTO_get_dynlock_create_callback GRPC_SHADOW_CRYPTO_get_dynlock_create_callback +#define CRYPTO_get_dynlock_destroy_callback GRPC_SHADOW_CRYPTO_get_dynlock_destroy_callback +#define CRYPTO_get_dynlock_lock_callback GRPC_SHADOW_CRYPTO_get_dynlock_lock_callback +#define CRYPTO_get_lock_name GRPC_SHADOW_CRYPTO_get_lock_name +#define CRYPTO_get_locking_callback GRPC_SHADOW_CRYPTO_get_locking_callback +#define CRYPTO_num_locks GRPC_SHADOW_CRYPTO_num_locks +#define CRYPTO_set_add_lock_callback GRPC_SHADOW_CRYPTO_set_add_lock_callback +#define CRYPTO_set_dynlock_create_callback GRPC_SHADOW_CRYPTO_set_dynlock_create_callback +#define CRYPTO_set_dynlock_destroy_callback GRPC_SHADOW_CRYPTO_set_dynlock_destroy_callback +#define CRYPTO_set_dynlock_lock_callback GRPC_SHADOW_CRYPTO_set_dynlock_lock_callback +#define CRYPTO_set_id_callback GRPC_SHADOW_CRYPTO_set_id_callback +#define CRYPTO_set_locking_callback GRPC_SHADOW_CRYPTO_set_locking_callback +#define CRYPTO_MUTEX_cleanup GRPC_SHADOW_CRYPTO_MUTEX_cleanup +#define CRYPTO_MUTEX_init GRPC_SHADOW_CRYPTO_MUTEX_init +#define CRYPTO_MUTEX_lock_read GRPC_SHADOW_CRYPTO_MUTEX_lock_read +#define CRYPTO_MUTEX_lock_write GRPC_SHADOW_CRYPTO_MUTEX_lock_write +#define CRYPTO_MUTEX_unlock_read GRPC_SHADOW_CRYPTO_MUTEX_unlock_read +#define CRYPTO_MUTEX_unlock_write GRPC_SHADOW_CRYPTO_MUTEX_unlock_write +#define CRYPTO_STATIC_MUTEX_lock_read GRPC_SHADOW_CRYPTO_STATIC_MUTEX_lock_read +#define CRYPTO_STATIC_MUTEX_lock_write GRPC_SHADOW_CRYPTO_STATIC_MUTEX_lock_write +#define CRYPTO_STATIC_MUTEX_unlock_read GRPC_SHADOW_CRYPTO_STATIC_MUTEX_unlock_read +#define CRYPTO_STATIC_MUTEX_unlock_write GRPC_SHADOW_CRYPTO_STATIC_MUTEX_unlock_write +#define CRYPTO_get_thread_local GRPC_SHADOW_CRYPTO_get_thread_local +#define CRYPTO_once GRPC_SHADOW_CRYPTO_once +#define CRYPTO_set_thread_local GRPC_SHADOW_CRYPTO_set_thread_local +#define sk_deep_copy GRPC_SHADOW_sk_deep_copy +#define sk_delete GRPC_SHADOW_sk_delete +#define sk_delete_ptr GRPC_SHADOW_sk_delete_ptr +#define sk_dup GRPC_SHADOW_sk_dup +#define sk_find GRPC_SHADOW_sk_find +#define sk_free GRPC_SHADOW_sk_free +#define sk_insert GRPC_SHADOW_sk_insert +#define sk_is_sorted GRPC_SHADOW_sk_is_sorted +#define sk_new GRPC_SHADOW_sk_new +#define sk_new_null GRPC_SHADOW_sk_new_null +#define sk_num GRPC_SHADOW_sk_num +#define sk_pop GRPC_SHADOW_sk_pop +#define sk_pop_free GRPC_SHADOW_sk_pop_free +#define sk_push GRPC_SHADOW_sk_push +#define sk_set GRPC_SHADOW_sk_set +#define sk_set_cmp_func GRPC_SHADOW_sk_set_cmp_func +#define sk_shift GRPC_SHADOW_sk_shift +#define sk_sort GRPC_SHADOW_sk_sort +#define sk_value GRPC_SHADOW_sk_value +#define sk_zero GRPC_SHADOW_sk_zero +#define lh_delete GRPC_SHADOW_lh_delete +#define lh_doall GRPC_SHADOW_lh_doall +#define lh_doall_arg GRPC_SHADOW_lh_doall_arg +#define lh_free GRPC_SHADOW_lh_free +#define lh_insert GRPC_SHADOW_lh_insert +#define lh_new GRPC_SHADOW_lh_new +#define lh_num_items GRPC_SHADOW_lh_num_items +#define lh_retrieve GRPC_SHADOW_lh_retrieve +#define lh_strhash GRPC_SHADOW_lh_strhash +#define ERR_SAVE_STATE_free GRPC_SHADOW_ERR_SAVE_STATE_free +#define ERR_add_error_data GRPC_SHADOW_ERR_add_error_data +#define ERR_add_error_dataf GRPC_SHADOW_ERR_add_error_dataf +#define ERR_clear_error GRPC_SHADOW_ERR_clear_error +#define ERR_clear_system_error GRPC_SHADOW_ERR_clear_system_error +#define ERR_error_string GRPC_SHADOW_ERR_error_string +#define ERR_error_string_n GRPC_SHADOW_ERR_error_string_n +#define ERR_free_strings GRPC_SHADOW_ERR_free_strings +#define ERR_func_error_string GRPC_SHADOW_ERR_func_error_string +#define ERR_get_error GRPC_SHADOW_ERR_get_error +#define ERR_get_error_line GRPC_SHADOW_ERR_get_error_line +#define ERR_get_error_line_data GRPC_SHADOW_ERR_get_error_line_data +#define ERR_get_next_error_library GRPC_SHADOW_ERR_get_next_error_library +#define ERR_lib_error_string GRPC_SHADOW_ERR_lib_error_string +#define ERR_load_BIO_strings GRPC_SHADOW_ERR_load_BIO_strings +#define ERR_load_ERR_strings GRPC_SHADOW_ERR_load_ERR_strings +#define ERR_load_crypto_strings GRPC_SHADOW_ERR_load_crypto_strings +#define ERR_peek_error GRPC_SHADOW_ERR_peek_error +#define ERR_peek_error_line GRPC_SHADOW_ERR_peek_error_line +#define ERR_peek_error_line_data GRPC_SHADOW_ERR_peek_error_line_data +#define ERR_peek_last_error GRPC_SHADOW_ERR_peek_last_error +#define ERR_peek_last_error_line GRPC_SHADOW_ERR_peek_last_error_line +#define ERR_peek_last_error_line_data GRPC_SHADOW_ERR_peek_last_error_line_data +#define ERR_pop_to_mark GRPC_SHADOW_ERR_pop_to_mark +#define ERR_print_errors_cb GRPC_SHADOW_ERR_print_errors_cb +#define ERR_print_errors_fp GRPC_SHADOW_ERR_print_errors_fp +#define ERR_put_error GRPC_SHADOW_ERR_put_error +#define ERR_reason_error_string GRPC_SHADOW_ERR_reason_error_string +#define ERR_remove_state GRPC_SHADOW_ERR_remove_state +#define ERR_remove_thread_state GRPC_SHADOW_ERR_remove_thread_state +#define ERR_restore_state GRPC_SHADOW_ERR_restore_state +#define ERR_save_state GRPC_SHADOW_ERR_save_state +#define ERR_set_mark GRPC_SHADOW_ERR_set_mark +#define kOpenSSLReasonStringData GRPC_SHADOW_kOpenSSLReasonStringData +#define kOpenSSLReasonValues GRPC_SHADOW_kOpenSSLReasonValues +#define kOpenSSLReasonValuesLen GRPC_SHADOW_kOpenSSLReasonValuesLen +#define EVP_DecodeBase64 GRPC_SHADOW_EVP_DecodeBase64 +#define EVP_DecodeBlock GRPC_SHADOW_EVP_DecodeBlock +#define EVP_DecodeFinal GRPC_SHADOW_EVP_DecodeFinal +#define EVP_DecodeInit GRPC_SHADOW_EVP_DecodeInit +#define EVP_DecodeUpdate GRPC_SHADOW_EVP_DecodeUpdate +#define EVP_DecodedLength GRPC_SHADOW_EVP_DecodedLength +#define EVP_EncodeBlock GRPC_SHADOW_EVP_EncodeBlock +#define EVP_EncodeFinal GRPC_SHADOW_EVP_EncodeFinal +#define EVP_EncodeInit GRPC_SHADOW_EVP_EncodeInit +#define EVP_EncodeUpdate GRPC_SHADOW_EVP_EncodeUpdate +#define EVP_EncodedLength GRPC_SHADOW_EVP_EncodedLength +#define CBB_finish_i2d GRPC_SHADOW_CBB_finish_i2d +#define CBS_asn1_ber_to_der GRPC_SHADOW_CBS_asn1_ber_to_der +#define CBS_get_asn1_implicit_string GRPC_SHADOW_CBS_get_asn1_implicit_string +#define CBS_asn1_bitstring_has_bit GRPC_SHADOW_CBS_asn1_bitstring_has_bit +#define CBS_asn1_oid_to_text GRPC_SHADOW_CBS_asn1_oid_to_text +#define CBS_contains_zero_byte GRPC_SHADOW_CBS_contains_zero_byte +#define CBS_copy_bytes GRPC_SHADOW_CBS_copy_bytes +#define CBS_data GRPC_SHADOW_CBS_data +#define CBS_get_any_asn1 GRPC_SHADOW_CBS_get_any_asn1 +#define CBS_get_any_asn1_element GRPC_SHADOW_CBS_get_any_asn1_element +#define CBS_get_any_ber_asn1_element GRPC_SHADOW_CBS_get_any_ber_asn1_element +#define CBS_get_asn1 GRPC_SHADOW_CBS_get_asn1 +#define CBS_get_asn1_bool GRPC_SHADOW_CBS_get_asn1_bool +#define CBS_get_asn1_element GRPC_SHADOW_CBS_get_asn1_element +#define CBS_get_asn1_uint64 GRPC_SHADOW_CBS_get_asn1_uint64 +#define CBS_get_bytes GRPC_SHADOW_CBS_get_bytes +#define CBS_get_last_u8 GRPC_SHADOW_CBS_get_last_u8 +#define CBS_get_optional_asn1 GRPC_SHADOW_CBS_get_optional_asn1 +#define CBS_get_optional_asn1_bool GRPC_SHADOW_CBS_get_optional_asn1_bool +#define CBS_get_optional_asn1_octet_string GRPC_SHADOW_CBS_get_optional_asn1_octet_string +#define CBS_get_optional_asn1_uint64 GRPC_SHADOW_CBS_get_optional_asn1_uint64 +#define CBS_get_u16 GRPC_SHADOW_CBS_get_u16 +#define CBS_get_u16_length_prefixed GRPC_SHADOW_CBS_get_u16_length_prefixed +#define CBS_get_u24 GRPC_SHADOW_CBS_get_u24 +#define CBS_get_u24_length_prefixed GRPC_SHADOW_CBS_get_u24_length_prefixed +#define CBS_get_u32 GRPC_SHADOW_CBS_get_u32 +#define CBS_get_u8 GRPC_SHADOW_CBS_get_u8 +#define CBS_get_u8_length_prefixed GRPC_SHADOW_CBS_get_u8_length_prefixed +#define CBS_init GRPC_SHADOW_CBS_init +#define CBS_is_valid_asn1_bitstring GRPC_SHADOW_CBS_is_valid_asn1_bitstring +#define CBS_len GRPC_SHADOW_CBS_len +#define CBS_mem_equal GRPC_SHADOW_CBS_mem_equal +#define CBS_peek_asn1_tag GRPC_SHADOW_CBS_peek_asn1_tag +#define CBS_skip GRPC_SHADOW_CBS_skip +#define CBS_stow GRPC_SHADOW_CBS_stow +#define CBS_strdup GRPC_SHADOW_CBS_strdup +#define CBB_add_asn1 GRPC_SHADOW_CBB_add_asn1 +#define CBB_add_asn1_bool GRPC_SHADOW_CBB_add_asn1_bool +#define CBB_add_asn1_octet_string GRPC_SHADOW_CBB_add_asn1_octet_string +#define CBB_add_asn1_oid_from_text GRPC_SHADOW_CBB_add_asn1_oid_from_text +#define CBB_add_asn1_uint64 GRPC_SHADOW_CBB_add_asn1_uint64 +#define CBB_add_bytes GRPC_SHADOW_CBB_add_bytes +#define CBB_add_space GRPC_SHADOW_CBB_add_space +#define CBB_add_u16 GRPC_SHADOW_CBB_add_u16 +#define CBB_add_u16_length_prefixed GRPC_SHADOW_CBB_add_u16_length_prefixed +#define CBB_add_u24 GRPC_SHADOW_CBB_add_u24 +#define CBB_add_u24_length_prefixed GRPC_SHADOW_CBB_add_u24_length_prefixed +#define CBB_add_u32 GRPC_SHADOW_CBB_add_u32 +#define CBB_add_u8 GRPC_SHADOW_CBB_add_u8 +#define CBB_add_u8_length_prefixed GRPC_SHADOW_CBB_add_u8_length_prefixed +#define CBB_cleanup GRPC_SHADOW_CBB_cleanup +#define CBB_data GRPC_SHADOW_CBB_data +#define CBB_did_write GRPC_SHADOW_CBB_did_write +#define CBB_discard_child GRPC_SHADOW_CBB_discard_child +#define CBB_finish GRPC_SHADOW_CBB_finish +#define CBB_flush GRPC_SHADOW_CBB_flush +#define CBB_flush_asn1_set_of GRPC_SHADOW_CBB_flush_asn1_set_of +#define CBB_init GRPC_SHADOW_CBB_init +#define CBB_init_fixed GRPC_SHADOW_CBB_init_fixed +#define CBB_len GRPC_SHADOW_CBB_len +#define CBB_reserve GRPC_SHADOW_CBB_reserve +#define CBB_zero GRPC_SHADOW_CBB_zero +#define CRYPTO_BUFFER_POOL_free GRPC_SHADOW_CRYPTO_BUFFER_POOL_free +#define CRYPTO_BUFFER_POOL_new GRPC_SHADOW_CRYPTO_BUFFER_POOL_new +#define CRYPTO_BUFFER_data GRPC_SHADOW_CRYPTO_BUFFER_data +#define CRYPTO_BUFFER_free GRPC_SHADOW_CRYPTO_BUFFER_free +#define CRYPTO_BUFFER_init_CBS GRPC_SHADOW_CRYPTO_BUFFER_init_CBS +#define CRYPTO_BUFFER_len GRPC_SHADOW_CRYPTO_BUFFER_len +#define CRYPTO_BUFFER_new GRPC_SHADOW_CRYPTO_BUFFER_new +#define CRYPTO_BUFFER_new_from_CBS GRPC_SHADOW_CRYPTO_BUFFER_new_from_CBS +#define CRYPTO_BUFFER_up_ref GRPC_SHADOW_CRYPTO_BUFFER_up_ref +#define AES_cbc_encrypt GRPC_SHADOW_AES_cbc_encrypt +#define AES_cfb128_encrypt GRPC_SHADOW_AES_cfb128_encrypt +#define AES_ctr128_encrypt GRPC_SHADOW_AES_ctr128_encrypt +#define AES_decrypt GRPC_SHADOW_AES_decrypt +#define AES_ecb_encrypt GRPC_SHADOW_AES_ecb_encrypt +#define AES_encrypt GRPC_SHADOW_AES_encrypt +#define AES_ofb128_encrypt GRPC_SHADOW_AES_ofb128_encrypt +#define AES_set_decrypt_key GRPC_SHADOW_AES_set_decrypt_key +#define AES_set_encrypt_key GRPC_SHADOW_AES_set_encrypt_key +#define AES_unwrap_key GRPC_SHADOW_AES_unwrap_key +#define AES_wrap_key GRPC_SHADOW_AES_wrap_key +#define BN_BLINDING_convert GRPC_SHADOW_BN_BLINDING_convert +#define BN_BLINDING_free GRPC_SHADOW_BN_BLINDING_free +#define BN_BLINDING_invert GRPC_SHADOW_BN_BLINDING_invert +#define BN_BLINDING_new GRPC_SHADOW_BN_BLINDING_new +#define BN_CTX_end GRPC_SHADOW_BN_CTX_end +#define BN_CTX_free GRPC_SHADOW_BN_CTX_free +#define BN_CTX_get GRPC_SHADOW_BN_CTX_get +#define BN_CTX_new GRPC_SHADOW_BN_CTX_new +#define BN_CTX_start GRPC_SHADOW_BN_CTX_start +#define BN_GENCB_call GRPC_SHADOW_BN_GENCB_call +#define BN_GENCB_set GRPC_SHADOW_BN_GENCB_set +#define BN_MONT_CTX_copy GRPC_SHADOW_BN_MONT_CTX_copy +#define BN_MONT_CTX_free GRPC_SHADOW_BN_MONT_CTX_free +#define BN_MONT_CTX_new GRPC_SHADOW_BN_MONT_CTX_new +#define BN_MONT_CTX_new_for_modulus GRPC_SHADOW_BN_MONT_CTX_new_for_modulus +#define BN_MONT_CTX_set GRPC_SHADOW_BN_MONT_CTX_set +#define BN_MONT_CTX_set_locked GRPC_SHADOW_BN_MONT_CTX_set_locked +#define BN_abs_is_word GRPC_SHADOW_BN_abs_is_word +#define BN_add GRPC_SHADOW_BN_add +#define BN_add_word GRPC_SHADOW_BN_add_word +#define BN_bin2bn GRPC_SHADOW_BN_bin2bn +#define BN_bn2bin GRPC_SHADOW_BN_bn2bin +#define BN_bn2bin_padded GRPC_SHADOW_BN_bn2bin_padded +#define BN_bn2le_padded GRPC_SHADOW_BN_bn2le_padded +#define BN_clear GRPC_SHADOW_BN_clear +#define BN_clear_bit GRPC_SHADOW_BN_clear_bit +#define BN_clear_free GRPC_SHADOW_BN_clear_free +#define BN_cmp GRPC_SHADOW_BN_cmp +#define BN_cmp_word GRPC_SHADOW_BN_cmp_word +#define BN_copy GRPC_SHADOW_BN_copy +#define BN_count_low_zero_bits GRPC_SHADOW_BN_count_low_zero_bits +#define BN_div GRPC_SHADOW_BN_div +#define BN_div_word GRPC_SHADOW_BN_div_word +#define BN_dup GRPC_SHADOW_BN_dup +#define BN_enhanced_miller_rabin_primality_test GRPC_SHADOW_BN_enhanced_miller_rabin_primality_test +#define BN_equal_consttime GRPC_SHADOW_BN_equal_consttime +#define BN_exp GRPC_SHADOW_BN_exp +#define BN_free GRPC_SHADOW_BN_free +#define BN_from_montgomery GRPC_SHADOW_BN_from_montgomery +#define BN_gcd GRPC_SHADOW_BN_gcd +#define BN_generate_prime_ex GRPC_SHADOW_BN_generate_prime_ex +#define BN_get_u64 GRPC_SHADOW_BN_get_u64 +#define BN_get_word GRPC_SHADOW_BN_get_word +#define BN_init GRPC_SHADOW_BN_init +#define BN_is_bit_set GRPC_SHADOW_BN_is_bit_set +#define BN_is_negative GRPC_SHADOW_BN_is_negative +#define BN_is_odd GRPC_SHADOW_BN_is_odd +#define BN_is_one GRPC_SHADOW_BN_is_one +#define BN_is_pow2 GRPC_SHADOW_BN_is_pow2 +#define BN_is_prime_ex GRPC_SHADOW_BN_is_prime_ex +#define BN_is_prime_fasttest_ex GRPC_SHADOW_BN_is_prime_fasttest_ex +#define BN_is_word GRPC_SHADOW_BN_is_word +#define BN_is_zero GRPC_SHADOW_BN_is_zero +#define BN_le2bn GRPC_SHADOW_BN_le2bn +#define BN_lshift GRPC_SHADOW_BN_lshift +#define BN_lshift1 GRPC_SHADOW_BN_lshift1 +#define BN_mask_bits GRPC_SHADOW_BN_mask_bits +#define BN_mod_add GRPC_SHADOW_BN_mod_add +#define BN_mod_add_quick GRPC_SHADOW_BN_mod_add_quick +#define BN_mod_exp GRPC_SHADOW_BN_mod_exp +#define BN_mod_exp2_mont GRPC_SHADOW_BN_mod_exp2_mont +#define BN_mod_exp_mont GRPC_SHADOW_BN_mod_exp_mont +#define BN_mod_exp_mont_consttime GRPC_SHADOW_BN_mod_exp_mont_consttime +#define BN_mod_exp_mont_word GRPC_SHADOW_BN_mod_exp_mont_word +#define BN_mod_inverse GRPC_SHADOW_BN_mod_inverse +#define BN_mod_inverse_blinded GRPC_SHADOW_BN_mod_inverse_blinded +#define BN_mod_inverse_odd GRPC_SHADOW_BN_mod_inverse_odd +#define BN_mod_lshift GRPC_SHADOW_BN_mod_lshift +#define BN_mod_lshift1 GRPC_SHADOW_BN_mod_lshift1 +#define BN_mod_lshift1_quick GRPC_SHADOW_BN_mod_lshift1_quick +#define BN_mod_lshift_quick GRPC_SHADOW_BN_mod_lshift_quick +#define BN_mod_mul GRPC_SHADOW_BN_mod_mul +#define BN_mod_mul_montgomery GRPC_SHADOW_BN_mod_mul_montgomery +#define BN_mod_pow2 GRPC_SHADOW_BN_mod_pow2 +#define BN_mod_sqr GRPC_SHADOW_BN_mod_sqr +#define BN_mod_sqrt GRPC_SHADOW_BN_mod_sqrt +#define BN_mod_sub GRPC_SHADOW_BN_mod_sub +#define BN_mod_sub_quick GRPC_SHADOW_BN_mod_sub_quick +#define BN_mod_word GRPC_SHADOW_BN_mod_word +#define BN_mul GRPC_SHADOW_BN_mul +#define BN_mul_word GRPC_SHADOW_BN_mul_word +#define BN_new GRPC_SHADOW_BN_new +#define BN_nnmod GRPC_SHADOW_BN_nnmod +#define BN_nnmod_pow2 GRPC_SHADOW_BN_nnmod_pow2 +#define BN_num_bits GRPC_SHADOW_BN_num_bits +#define BN_num_bits_word GRPC_SHADOW_BN_num_bits_word +#define BN_num_bytes GRPC_SHADOW_BN_num_bytes +#define BN_one GRPC_SHADOW_BN_one +#define BN_primality_test GRPC_SHADOW_BN_primality_test +#define BN_pseudo_rand GRPC_SHADOW_BN_pseudo_rand +#define BN_pseudo_rand_range GRPC_SHADOW_BN_pseudo_rand_range +#define BN_rand GRPC_SHADOW_BN_rand +#define BN_rand_range GRPC_SHADOW_BN_rand_range +#define BN_rand_range_ex GRPC_SHADOW_BN_rand_range_ex +#define BN_rshift GRPC_SHADOW_BN_rshift +#define BN_rshift1 GRPC_SHADOW_BN_rshift1 +#define BN_set_bit GRPC_SHADOW_BN_set_bit +#define BN_set_negative GRPC_SHADOW_BN_set_negative +#define BN_set_u64 GRPC_SHADOW_BN_set_u64 +#define BN_set_word GRPC_SHADOW_BN_set_word +#define BN_sqr GRPC_SHADOW_BN_sqr +#define BN_sqrt GRPC_SHADOW_BN_sqrt +#define BN_sub GRPC_SHADOW_BN_sub +#define BN_sub_word GRPC_SHADOW_BN_sub_word +#define BN_to_montgomery GRPC_SHADOW_BN_to_montgomery +#define BN_uadd GRPC_SHADOW_BN_uadd +#define BN_ucmp GRPC_SHADOW_BN_ucmp +#define BN_usub GRPC_SHADOW_BN_usub +#define BN_value_one GRPC_SHADOW_BN_value_one +#define BN_zero GRPC_SHADOW_BN_zero +#define BORINGSSL_self_test GRPC_SHADOW_BORINGSSL_self_test +#define CRYPTO_POLYVAL_finish GRPC_SHADOW_CRYPTO_POLYVAL_finish +#define CRYPTO_POLYVAL_init GRPC_SHADOW_CRYPTO_POLYVAL_init +#define CRYPTO_POLYVAL_update_blocks GRPC_SHADOW_CRYPTO_POLYVAL_update_blocks +#define CRYPTO_cbc128_decrypt GRPC_SHADOW_CRYPTO_cbc128_decrypt +#define CRYPTO_cbc128_encrypt GRPC_SHADOW_CRYPTO_cbc128_encrypt +#define CRYPTO_ccm128_decrypt GRPC_SHADOW_CRYPTO_ccm128_decrypt +#define CRYPTO_ccm128_encrypt GRPC_SHADOW_CRYPTO_ccm128_encrypt +#define CRYPTO_ccm128_init GRPC_SHADOW_CRYPTO_ccm128_init +#define CRYPTO_ccm128_max_input GRPC_SHADOW_CRYPTO_ccm128_max_input +#define CRYPTO_cfb128_1_encrypt GRPC_SHADOW_CRYPTO_cfb128_1_encrypt +#define CRYPTO_cfb128_8_encrypt GRPC_SHADOW_CRYPTO_cfb128_8_encrypt +#define CRYPTO_cfb128_encrypt GRPC_SHADOW_CRYPTO_cfb128_encrypt +#define CRYPTO_ctr128_encrypt GRPC_SHADOW_CRYPTO_ctr128_encrypt +#define CRYPTO_ctr128_encrypt_ctr32 GRPC_SHADOW_CRYPTO_ctr128_encrypt_ctr32 +#define CRYPTO_gcm128_aad GRPC_SHADOW_CRYPTO_gcm128_aad +#define CRYPTO_gcm128_decrypt GRPC_SHADOW_CRYPTO_gcm128_decrypt +#define CRYPTO_gcm128_decrypt_ctr32 GRPC_SHADOW_CRYPTO_gcm128_decrypt_ctr32 +#define CRYPTO_gcm128_encrypt GRPC_SHADOW_CRYPTO_gcm128_encrypt +#define CRYPTO_gcm128_encrypt_ctr32 GRPC_SHADOW_CRYPTO_gcm128_encrypt_ctr32 +#define CRYPTO_gcm128_finish GRPC_SHADOW_CRYPTO_gcm128_finish +#define CRYPTO_gcm128_init GRPC_SHADOW_CRYPTO_gcm128_init +#define CRYPTO_gcm128_setiv GRPC_SHADOW_CRYPTO_gcm128_setiv +#define CRYPTO_gcm128_tag GRPC_SHADOW_CRYPTO_gcm128_tag +#define CRYPTO_ghash_init GRPC_SHADOW_CRYPTO_ghash_init +#define CRYPTO_ofb128_encrypt GRPC_SHADOW_CRYPTO_ofb128_encrypt +#define CRYPTO_sysrand GRPC_SHADOW_CRYPTO_sysrand +#define CRYPTO_tls1_prf GRPC_SHADOW_CRYPTO_tls1_prf +#define CTR_DRBG_clear GRPC_SHADOW_CTR_DRBG_clear +#define CTR_DRBG_generate GRPC_SHADOW_CTR_DRBG_generate +#define CTR_DRBG_init GRPC_SHADOW_CTR_DRBG_init +#define CTR_DRBG_reseed GRPC_SHADOW_CTR_DRBG_reseed +#define DES_decrypt3 GRPC_SHADOW_DES_decrypt3 +#define DES_ecb3_encrypt GRPC_SHADOW_DES_ecb3_encrypt +#define DES_ecb_encrypt GRPC_SHADOW_DES_ecb_encrypt +#define DES_ede2_cbc_encrypt GRPC_SHADOW_DES_ede2_cbc_encrypt +#define DES_ede3_cbc_encrypt GRPC_SHADOW_DES_ede3_cbc_encrypt +#define DES_encrypt3 GRPC_SHADOW_DES_encrypt3 +#define DES_ncbc_encrypt GRPC_SHADOW_DES_ncbc_encrypt +#define DES_set_key GRPC_SHADOW_DES_set_key +#define DES_set_key_unchecked GRPC_SHADOW_DES_set_key_unchecked +#define DES_set_odd_parity GRPC_SHADOW_DES_set_odd_parity +#define ECDSA_SIG_free GRPC_SHADOW_ECDSA_SIG_free +#define ECDSA_SIG_get0 GRPC_SHADOW_ECDSA_SIG_get0 +#define ECDSA_SIG_new GRPC_SHADOW_ECDSA_SIG_new +#define ECDSA_SIG_set0 GRPC_SHADOW_ECDSA_SIG_set0 +#define ECDSA_do_sign GRPC_SHADOW_ECDSA_do_sign +#define ECDSA_do_verify GRPC_SHADOW_ECDSA_do_verify +#define EC_GFp_mont_method GRPC_SHADOW_EC_GFp_mont_method +#define EC_GFp_nistp224_method GRPC_SHADOW_EC_GFp_nistp224_method +#define EC_GFp_nistp256_method GRPC_SHADOW_EC_GFp_nistp256_method +#define EC_GFp_nistz256_method GRPC_SHADOW_EC_GFp_nistz256_method +#define EC_GROUP_cmp GRPC_SHADOW_EC_GROUP_cmp +#define EC_GROUP_dup GRPC_SHADOW_EC_GROUP_dup +#define EC_GROUP_free GRPC_SHADOW_EC_GROUP_free +#define EC_GROUP_get0_generator GRPC_SHADOW_EC_GROUP_get0_generator +#define EC_GROUP_get0_order GRPC_SHADOW_EC_GROUP_get0_order +#define EC_GROUP_get_cofactor GRPC_SHADOW_EC_GROUP_get_cofactor +#define EC_GROUP_get_curve_GFp GRPC_SHADOW_EC_GROUP_get_curve_GFp +#define EC_GROUP_get_curve_name GRPC_SHADOW_EC_GROUP_get_curve_name +#define EC_GROUP_get_degree GRPC_SHADOW_EC_GROUP_get_degree +#define EC_GROUP_get_order GRPC_SHADOW_EC_GROUP_get_order +#define EC_GROUP_method_of GRPC_SHADOW_EC_GROUP_method_of +#define EC_GROUP_new_by_curve_name GRPC_SHADOW_EC_GROUP_new_by_curve_name +#define EC_GROUP_new_curve_GFp GRPC_SHADOW_EC_GROUP_new_curve_GFp +#define EC_GROUP_set_asn1_flag GRPC_SHADOW_EC_GROUP_set_asn1_flag +#define EC_GROUP_set_generator GRPC_SHADOW_EC_GROUP_set_generator +#define EC_GROUP_set_point_conversion_form GRPC_SHADOW_EC_GROUP_set_point_conversion_form +#define EC_KEY_check_fips GRPC_SHADOW_EC_KEY_check_fips +#define EC_KEY_check_key GRPC_SHADOW_EC_KEY_check_key +#define EC_KEY_dup GRPC_SHADOW_EC_KEY_dup +#define EC_KEY_free GRPC_SHADOW_EC_KEY_free +#define EC_KEY_generate_key GRPC_SHADOW_EC_KEY_generate_key +#define EC_KEY_generate_key_fips GRPC_SHADOW_EC_KEY_generate_key_fips +#define EC_KEY_get0_group GRPC_SHADOW_EC_KEY_get0_group +#define EC_KEY_get0_private_key GRPC_SHADOW_EC_KEY_get0_private_key +#define EC_KEY_get0_public_key GRPC_SHADOW_EC_KEY_get0_public_key +#define EC_KEY_get_conv_form GRPC_SHADOW_EC_KEY_get_conv_form +#define EC_KEY_get_enc_flags GRPC_SHADOW_EC_KEY_get_enc_flags +#define EC_KEY_get_ex_data GRPC_SHADOW_EC_KEY_get_ex_data +#define EC_KEY_get_ex_new_index GRPC_SHADOW_EC_KEY_get_ex_new_index +#define EC_KEY_is_opaque GRPC_SHADOW_EC_KEY_is_opaque +#define EC_KEY_new GRPC_SHADOW_EC_KEY_new +#define EC_KEY_new_by_curve_name GRPC_SHADOW_EC_KEY_new_by_curve_name +#define EC_KEY_new_method GRPC_SHADOW_EC_KEY_new_method +#define EC_KEY_set_asn1_flag GRPC_SHADOW_EC_KEY_set_asn1_flag +#define EC_KEY_set_conv_form GRPC_SHADOW_EC_KEY_set_conv_form +#define EC_KEY_set_enc_flags GRPC_SHADOW_EC_KEY_set_enc_flags +#define EC_KEY_set_ex_data GRPC_SHADOW_EC_KEY_set_ex_data +#define EC_KEY_set_group GRPC_SHADOW_EC_KEY_set_group +#define EC_KEY_set_private_key GRPC_SHADOW_EC_KEY_set_private_key +#define EC_KEY_set_public_key GRPC_SHADOW_EC_KEY_set_public_key +#define EC_KEY_set_public_key_affine_coordinates GRPC_SHADOW_EC_KEY_set_public_key_affine_coordinates +#define EC_KEY_up_ref GRPC_SHADOW_EC_KEY_up_ref +#define EC_METHOD_get_field_type GRPC_SHADOW_EC_METHOD_get_field_type +#define EC_POINT_add GRPC_SHADOW_EC_POINT_add +#define EC_POINT_clear_free GRPC_SHADOW_EC_POINT_clear_free +#define EC_POINT_cmp GRPC_SHADOW_EC_POINT_cmp +#define EC_POINT_copy GRPC_SHADOW_EC_POINT_copy +#define EC_POINT_dbl GRPC_SHADOW_EC_POINT_dbl +#define EC_POINT_dup GRPC_SHADOW_EC_POINT_dup +#define EC_POINT_free GRPC_SHADOW_EC_POINT_free +#define EC_POINT_get_affine_coordinates_GFp GRPC_SHADOW_EC_POINT_get_affine_coordinates_GFp +#define EC_POINT_invert GRPC_SHADOW_EC_POINT_invert +#define EC_POINT_is_at_infinity GRPC_SHADOW_EC_POINT_is_at_infinity +#define EC_POINT_is_on_curve GRPC_SHADOW_EC_POINT_is_on_curve +#define EC_POINT_make_affine GRPC_SHADOW_EC_POINT_make_affine +#define EC_POINT_mul GRPC_SHADOW_EC_POINT_mul +#define EC_POINT_new GRPC_SHADOW_EC_POINT_new +#define EC_POINT_oct2point GRPC_SHADOW_EC_POINT_oct2point +#define EC_POINT_point2oct GRPC_SHADOW_EC_POINT_point2oct +#define EC_POINT_set_affine_coordinates_GFp GRPC_SHADOW_EC_POINT_set_affine_coordinates_GFp +#define EC_POINT_set_compressed_coordinates_GFp GRPC_SHADOW_EC_POINT_set_compressed_coordinates_GFp +#define EC_POINT_set_to_infinity GRPC_SHADOW_EC_POINT_set_to_infinity +#define EC_POINTs_make_affine GRPC_SHADOW_EC_POINTs_make_affine +#define EC_get_builtin_curves GRPC_SHADOW_EC_get_builtin_curves +#define EVP_AEAD_CTX_aead GRPC_SHADOW_EVP_AEAD_CTX_aead +#define EVP_AEAD_CTX_cleanup GRPC_SHADOW_EVP_AEAD_CTX_cleanup +#define EVP_AEAD_CTX_free GRPC_SHADOW_EVP_AEAD_CTX_free +#define EVP_AEAD_CTX_get_iv GRPC_SHADOW_EVP_AEAD_CTX_get_iv +#define EVP_AEAD_CTX_init GRPC_SHADOW_EVP_AEAD_CTX_init +#define EVP_AEAD_CTX_init_with_direction GRPC_SHADOW_EVP_AEAD_CTX_init_with_direction +#define EVP_AEAD_CTX_new GRPC_SHADOW_EVP_AEAD_CTX_new +#define EVP_AEAD_CTX_open GRPC_SHADOW_EVP_AEAD_CTX_open +#define EVP_AEAD_CTX_open_gather GRPC_SHADOW_EVP_AEAD_CTX_open_gather +#define EVP_AEAD_CTX_seal GRPC_SHADOW_EVP_AEAD_CTX_seal +#define EVP_AEAD_CTX_seal_scatter GRPC_SHADOW_EVP_AEAD_CTX_seal_scatter +#define EVP_AEAD_CTX_tag_len GRPC_SHADOW_EVP_AEAD_CTX_tag_len +#define EVP_AEAD_CTX_zero GRPC_SHADOW_EVP_AEAD_CTX_zero +#define EVP_AEAD_key_length GRPC_SHADOW_EVP_AEAD_key_length +#define EVP_AEAD_max_overhead GRPC_SHADOW_EVP_AEAD_max_overhead +#define EVP_AEAD_max_tag_len GRPC_SHADOW_EVP_AEAD_max_tag_len +#define EVP_AEAD_nonce_length GRPC_SHADOW_EVP_AEAD_nonce_length +#define EVP_CIPHER_CTX_block_size GRPC_SHADOW_EVP_CIPHER_CTX_block_size +#define EVP_CIPHER_CTX_cipher GRPC_SHADOW_EVP_CIPHER_CTX_cipher +#define EVP_CIPHER_CTX_cleanup GRPC_SHADOW_EVP_CIPHER_CTX_cleanup +#define EVP_CIPHER_CTX_copy GRPC_SHADOW_EVP_CIPHER_CTX_copy +#define EVP_CIPHER_CTX_ctrl GRPC_SHADOW_EVP_CIPHER_CTX_ctrl +#define EVP_CIPHER_CTX_flags GRPC_SHADOW_EVP_CIPHER_CTX_flags +#define EVP_CIPHER_CTX_free GRPC_SHADOW_EVP_CIPHER_CTX_free +#define EVP_CIPHER_CTX_get_app_data GRPC_SHADOW_EVP_CIPHER_CTX_get_app_data +#define EVP_CIPHER_CTX_init GRPC_SHADOW_EVP_CIPHER_CTX_init +#define EVP_CIPHER_CTX_iv_length GRPC_SHADOW_EVP_CIPHER_CTX_iv_length +#define EVP_CIPHER_CTX_key_length GRPC_SHADOW_EVP_CIPHER_CTX_key_length +#define EVP_CIPHER_CTX_mode GRPC_SHADOW_EVP_CIPHER_CTX_mode +#define EVP_CIPHER_CTX_new GRPC_SHADOW_EVP_CIPHER_CTX_new +#define EVP_CIPHER_CTX_nid GRPC_SHADOW_EVP_CIPHER_CTX_nid +#define EVP_CIPHER_CTX_reset GRPC_SHADOW_EVP_CIPHER_CTX_reset +#define EVP_CIPHER_CTX_set_app_data GRPC_SHADOW_EVP_CIPHER_CTX_set_app_data +#define EVP_CIPHER_CTX_set_flags GRPC_SHADOW_EVP_CIPHER_CTX_set_flags +#define EVP_CIPHER_CTX_set_key_length GRPC_SHADOW_EVP_CIPHER_CTX_set_key_length +#define EVP_CIPHER_CTX_set_padding GRPC_SHADOW_EVP_CIPHER_CTX_set_padding +#define EVP_CIPHER_block_size GRPC_SHADOW_EVP_CIPHER_block_size +#define EVP_CIPHER_flags GRPC_SHADOW_EVP_CIPHER_flags +#define EVP_CIPHER_iv_length GRPC_SHADOW_EVP_CIPHER_iv_length +#define EVP_CIPHER_key_length GRPC_SHADOW_EVP_CIPHER_key_length +#define EVP_CIPHER_mode GRPC_SHADOW_EVP_CIPHER_mode +#define EVP_CIPHER_nid GRPC_SHADOW_EVP_CIPHER_nid +#define EVP_Cipher GRPC_SHADOW_EVP_Cipher +#define EVP_CipherFinal_ex GRPC_SHADOW_EVP_CipherFinal_ex +#define EVP_CipherInit GRPC_SHADOW_EVP_CipherInit +#define EVP_CipherInit_ex GRPC_SHADOW_EVP_CipherInit_ex +#define EVP_CipherUpdate GRPC_SHADOW_EVP_CipherUpdate +#define EVP_DecryptFinal_ex GRPC_SHADOW_EVP_DecryptFinal_ex +#define EVP_DecryptInit GRPC_SHADOW_EVP_DecryptInit +#define EVP_DecryptInit_ex GRPC_SHADOW_EVP_DecryptInit_ex +#define EVP_DecryptUpdate GRPC_SHADOW_EVP_DecryptUpdate +#define EVP_Digest GRPC_SHADOW_EVP_Digest +#define EVP_DigestFinal GRPC_SHADOW_EVP_DigestFinal +#define EVP_DigestFinal_ex GRPC_SHADOW_EVP_DigestFinal_ex +#define EVP_DigestInit GRPC_SHADOW_EVP_DigestInit +#define EVP_DigestInit_ex GRPC_SHADOW_EVP_DigestInit_ex +#define EVP_DigestUpdate GRPC_SHADOW_EVP_DigestUpdate +#define EVP_EncryptFinal_ex GRPC_SHADOW_EVP_EncryptFinal_ex +#define EVP_EncryptInit GRPC_SHADOW_EVP_EncryptInit +#define EVP_EncryptInit_ex GRPC_SHADOW_EVP_EncryptInit_ex +#define EVP_EncryptUpdate GRPC_SHADOW_EVP_EncryptUpdate +#define EVP_MD_CTX_block_size GRPC_SHADOW_EVP_MD_CTX_block_size +#define EVP_MD_CTX_cleanup GRPC_SHADOW_EVP_MD_CTX_cleanup +#define EVP_MD_CTX_copy GRPC_SHADOW_EVP_MD_CTX_copy +#define EVP_MD_CTX_copy_ex GRPC_SHADOW_EVP_MD_CTX_copy_ex +#define EVP_MD_CTX_create GRPC_SHADOW_EVP_MD_CTX_create +#define EVP_MD_CTX_destroy GRPC_SHADOW_EVP_MD_CTX_destroy +#define EVP_MD_CTX_free GRPC_SHADOW_EVP_MD_CTX_free +#define EVP_MD_CTX_init GRPC_SHADOW_EVP_MD_CTX_init +#define EVP_MD_CTX_md GRPC_SHADOW_EVP_MD_CTX_md +#define EVP_MD_CTX_new GRPC_SHADOW_EVP_MD_CTX_new +#define EVP_MD_CTX_reset GRPC_SHADOW_EVP_MD_CTX_reset +#define EVP_MD_CTX_size GRPC_SHADOW_EVP_MD_CTX_size +#define EVP_MD_CTX_type GRPC_SHADOW_EVP_MD_CTX_type +#define EVP_MD_block_size GRPC_SHADOW_EVP_MD_block_size +#define EVP_MD_flags GRPC_SHADOW_EVP_MD_flags +#define EVP_MD_size GRPC_SHADOW_EVP_MD_size +#define EVP_MD_type GRPC_SHADOW_EVP_MD_type +#define EVP_add_cipher_alias GRPC_SHADOW_EVP_add_cipher_alias +#define EVP_add_digest GRPC_SHADOW_EVP_add_digest +#define EVP_aead_aes_128_gcm GRPC_SHADOW_EVP_aead_aes_128_gcm +#define EVP_aead_aes_128_gcm_tls12 GRPC_SHADOW_EVP_aead_aes_128_gcm_tls12 +#define EVP_aead_aes_256_gcm GRPC_SHADOW_EVP_aead_aes_256_gcm +#define EVP_aead_aes_256_gcm_tls12 GRPC_SHADOW_EVP_aead_aes_256_gcm_tls12 +#define EVP_aes_128_cbc GRPC_SHADOW_EVP_aes_128_cbc +#define EVP_aes_128_ctr GRPC_SHADOW_EVP_aes_128_ctr +#define EVP_aes_128_ecb GRPC_SHADOW_EVP_aes_128_ecb +#define EVP_aes_128_gcm GRPC_SHADOW_EVP_aes_128_gcm +#define EVP_aes_128_ofb GRPC_SHADOW_EVP_aes_128_ofb +#define EVP_aes_192_cbc GRPC_SHADOW_EVP_aes_192_cbc +#define EVP_aes_192_ctr GRPC_SHADOW_EVP_aes_192_ctr +#define EVP_aes_192_ecb GRPC_SHADOW_EVP_aes_192_ecb +#define EVP_aes_192_gcm GRPC_SHADOW_EVP_aes_192_gcm +#define EVP_aes_256_cbc GRPC_SHADOW_EVP_aes_256_cbc +#define EVP_aes_256_ctr GRPC_SHADOW_EVP_aes_256_ctr +#define EVP_aes_256_ecb GRPC_SHADOW_EVP_aes_256_ecb +#define EVP_aes_256_gcm GRPC_SHADOW_EVP_aes_256_gcm +#define EVP_aes_256_ofb GRPC_SHADOW_EVP_aes_256_ofb +#define EVP_des_cbc GRPC_SHADOW_EVP_des_cbc +#define EVP_des_ecb GRPC_SHADOW_EVP_des_ecb +#define EVP_des_ede GRPC_SHADOW_EVP_des_ede +#define EVP_des_ede3 GRPC_SHADOW_EVP_des_ede3 +#define EVP_des_ede3_cbc GRPC_SHADOW_EVP_des_ede3_cbc +#define EVP_des_ede_cbc GRPC_SHADOW_EVP_des_ede_cbc +#define EVP_has_aes_hardware GRPC_SHADOW_EVP_has_aes_hardware +#define EVP_md4 GRPC_SHADOW_EVP_md4 +#define EVP_md5 GRPC_SHADOW_EVP_md5 +#define EVP_md5_sha1 GRPC_SHADOW_EVP_md5_sha1 +#define EVP_sha1 GRPC_SHADOW_EVP_sha1 +#define EVP_sha224 GRPC_SHADOW_EVP_sha224 +#define EVP_sha256 GRPC_SHADOW_EVP_sha256 +#define EVP_sha384 GRPC_SHADOW_EVP_sha384 +#define EVP_sha512 GRPC_SHADOW_EVP_sha512 +#define HMAC GRPC_SHADOW_HMAC +#define HMAC_CTX_cleanup GRPC_SHADOW_HMAC_CTX_cleanup +#define HMAC_CTX_copy GRPC_SHADOW_HMAC_CTX_copy +#define HMAC_CTX_copy_ex GRPC_SHADOW_HMAC_CTX_copy_ex +#define HMAC_CTX_free GRPC_SHADOW_HMAC_CTX_free +#define HMAC_CTX_init GRPC_SHADOW_HMAC_CTX_init +#define HMAC_CTX_new GRPC_SHADOW_HMAC_CTX_new +#define HMAC_CTX_reset GRPC_SHADOW_HMAC_CTX_reset +#define HMAC_Final GRPC_SHADOW_HMAC_Final +#define HMAC_Init GRPC_SHADOW_HMAC_Init +#define HMAC_Init_ex GRPC_SHADOW_HMAC_Init_ex +#define HMAC_Update GRPC_SHADOW_HMAC_Update +#define HMAC_size GRPC_SHADOW_HMAC_size +#define MD4 GRPC_SHADOW_MD4 +#define MD4_Final GRPC_SHADOW_MD4_Final +#define MD4_Init GRPC_SHADOW_MD4_Init +#define MD4_Transform GRPC_SHADOW_MD4_Transform +#define MD4_Update GRPC_SHADOW_MD4_Update +#define MD5 GRPC_SHADOW_MD5 +#define MD5_Final GRPC_SHADOW_MD5_Final +#define MD5_Init GRPC_SHADOW_MD5_Init +#define MD5_Transform GRPC_SHADOW_MD5_Transform +#define MD5_Update GRPC_SHADOW_MD5_Update +#define OPENSSL_built_in_curves GRPC_SHADOW_OPENSSL_built_in_curves +#define RAND_bytes GRPC_SHADOW_RAND_bytes +#define RAND_bytes_with_additional_data GRPC_SHADOW_RAND_bytes_with_additional_data +#define RAND_pseudo_bytes GRPC_SHADOW_RAND_pseudo_bytes +#define RAND_set_urandom_fd GRPC_SHADOW_RAND_set_urandom_fd +#define RSAZ_1024_mod_exp_avx2 GRPC_SHADOW_RSAZ_1024_mod_exp_avx2 +#define RSA_add_pkcs1_prefix GRPC_SHADOW_RSA_add_pkcs1_prefix +#define RSA_bits GRPC_SHADOW_RSA_bits +#define RSA_blinding_on GRPC_SHADOW_RSA_blinding_on +#define RSA_check_fips GRPC_SHADOW_RSA_check_fips +#define RSA_check_key GRPC_SHADOW_RSA_check_key +#define RSA_decrypt GRPC_SHADOW_RSA_decrypt +#define RSA_default_method GRPC_SHADOW_RSA_default_method +#define RSA_encrypt GRPC_SHADOW_RSA_encrypt +#define RSA_flags GRPC_SHADOW_RSA_flags +#define RSA_free GRPC_SHADOW_RSA_free +#define RSA_generate_key_ex GRPC_SHADOW_RSA_generate_key_ex +#define RSA_generate_key_fips GRPC_SHADOW_RSA_generate_key_fips +#define RSA_get0_crt_params GRPC_SHADOW_RSA_get0_crt_params +#define RSA_get0_factors GRPC_SHADOW_RSA_get0_factors +#define RSA_get0_key GRPC_SHADOW_RSA_get0_key +#define RSA_get_ex_data GRPC_SHADOW_RSA_get_ex_data +#define RSA_get_ex_new_index GRPC_SHADOW_RSA_get_ex_new_index +#define RSA_is_opaque GRPC_SHADOW_RSA_is_opaque +#define RSA_new GRPC_SHADOW_RSA_new +#define RSA_new_method GRPC_SHADOW_RSA_new_method +#define RSA_padding_add_PKCS1_OAEP_mgf1 GRPC_SHADOW_RSA_padding_add_PKCS1_OAEP_mgf1 +#define RSA_padding_add_PKCS1_PSS_mgf1 GRPC_SHADOW_RSA_padding_add_PKCS1_PSS_mgf1 +#define RSA_padding_add_PKCS1_type_1 GRPC_SHADOW_RSA_padding_add_PKCS1_type_1 +#define RSA_padding_add_PKCS1_type_2 GRPC_SHADOW_RSA_padding_add_PKCS1_type_2 +#define RSA_padding_add_none GRPC_SHADOW_RSA_padding_add_none +#define RSA_padding_check_PKCS1_OAEP_mgf1 GRPC_SHADOW_RSA_padding_check_PKCS1_OAEP_mgf1 +#define RSA_padding_check_PKCS1_type_1 GRPC_SHADOW_RSA_padding_check_PKCS1_type_1 +#define RSA_padding_check_PKCS1_type_2 GRPC_SHADOW_RSA_padding_check_PKCS1_type_2 +#define RSA_private_decrypt GRPC_SHADOW_RSA_private_decrypt +#define RSA_private_encrypt GRPC_SHADOW_RSA_private_encrypt +#define RSA_private_transform GRPC_SHADOW_RSA_private_transform +#define RSA_public_decrypt GRPC_SHADOW_RSA_public_decrypt +#define RSA_public_encrypt GRPC_SHADOW_RSA_public_encrypt +#define RSA_set0_crt_params GRPC_SHADOW_RSA_set0_crt_params +#define RSA_set0_factors GRPC_SHADOW_RSA_set0_factors +#define RSA_set0_key GRPC_SHADOW_RSA_set0_key +#define RSA_set_ex_data GRPC_SHADOW_RSA_set_ex_data +#define RSA_sign GRPC_SHADOW_RSA_sign +#define RSA_sign_pss_mgf1 GRPC_SHADOW_RSA_sign_pss_mgf1 +#define RSA_sign_raw GRPC_SHADOW_RSA_sign_raw +#define RSA_size GRPC_SHADOW_RSA_size +#define RSA_up_ref GRPC_SHADOW_RSA_up_ref +#define RSA_verify GRPC_SHADOW_RSA_verify +#define RSA_verify_PKCS1_PSS_mgf1 GRPC_SHADOW_RSA_verify_PKCS1_PSS_mgf1 +#define RSA_verify_pss_mgf1 GRPC_SHADOW_RSA_verify_pss_mgf1 +#define RSA_verify_raw GRPC_SHADOW_RSA_verify_raw +#define SHA1 GRPC_SHADOW_SHA1 +#define SHA1_Final GRPC_SHADOW_SHA1_Final +#define SHA1_Init GRPC_SHADOW_SHA1_Init +#define SHA1_Transform GRPC_SHADOW_SHA1_Transform +#define SHA1_Update GRPC_SHADOW_SHA1_Update +#define SHA224 GRPC_SHADOW_SHA224 +#define SHA224_Final GRPC_SHADOW_SHA224_Final +#define SHA224_Init GRPC_SHADOW_SHA224_Init +#define SHA224_Update GRPC_SHADOW_SHA224_Update +#define SHA256 GRPC_SHADOW_SHA256 +#define SHA256_Final GRPC_SHADOW_SHA256_Final +#define SHA256_Init GRPC_SHADOW_SHA256_Init +#define SHA256_Transform GRPC_SHADOW_SHA256_Transform +#define SHA256_Update GRPC_SHADOW_SHA256_Update +#define SHA384 GRPC_SHADOW_SHA384 +#define SHA384_Final GRPC_SHADOW_SHA384_Final +#define SHA384_Init GRPC_SHADOW_SHA384_Init +#define SHA384_Update GRPC_SHADOW_SHA384_Update +#define SHA512 GRPC_SHADOW_SHA512 +#define SHA512_Final GRPC_SHADOW_SHA512_Final +#define SHA512_Init GRPC_SHADOW_SHA512_Init +#define SHA512_Transform GRPC_SHADOW_SHA512_Transform +#define SHA512_Update GRPC_SHADOW_SHA512_Update +#define aes_ctr_set_key GRPC_SHADOW_aes_ctr_set_key +#define bn_abs_sub_consttime GRPC_SHADOW_bn_abs_sub_consttime +#define bn_add_words GRPC_SHADOW_bn_add_words +#define bn_copy_words GRPC_SHADOW_bn_copy_words +#define bn_div_consttime GRPC_SHADOW_bn_div_consttime +#define bn_expand GRPC_SHADOW_bn_expand +#define bn_fits_in_words GRPC_SHADOW_bn_fits_in_words +#define bn_from_montgomery_small GRPC_SHADOW_bn_from_montgomery_small +#define bn_in_range_words GRPC_SHADOW_bn_in_range_words +#define bn_is_bit_set_words GRPC_SHADOW_bn_is_bit_set_words +#define bn_is_relatively_prime GRPC_SHADOW_bn_is_relatively_prime +#define bn_jacobi GRPC_SHADOW_bn_jacobi +#define bn_lcm_consttime GRPC_SHADOW_bn_lcm_consttime +#define bn_less_than_montgomery_R GRPC_SHADOW_bn_less_than_montgomery_R +#define bn_less_than_words GRPC_SHADOW_bn_less_than_words +#define bn_minimal_width GRPC_SHADOW_bn_minimal_width +#define bn_mod_add_consttime GRPC_SHADOW_bn_mod_add_consttime +#define bn_mod_exp_base_2_consttime GRPC_SHADOW_bn_mod_exp_base_2_consttime +#define bn_mod_exp_mont_small GRPC_SHADOW_bn_mod_exp_mont_small +#define bn_mod_inverse_consttime GRPC_SHADOW_bn_mod_inverse_consttime +#define bn_mod_inverse_prime GRPC_SHADOW_bn_mod_inverse_prime +#define bn_mod_inverse_prime_mont_small GRPC_SHADOW_bn_mod_inverse_prime_mont_small +#define bn_mod_inverse_secret_prime GRPC_SHADOW_bn_mod_inverse_secret_prime +#define bn_mod_lshift1_consttime GRPC_SHADOW_bn_mod_lshift1_consttime +#define bn_mod_lshift_consttime GRPC_SHADOW_bn_mod_lshift_consttime +#define bn_mod_mul_montgomery_small GRPC_SHADOW_bn_mod_mul_montgomery_small +#define bn_mod_sub_consttime GRPC_SHADOW_bn_mod_sub_consttime +#define bn_mod_u16_consttime GRPC_SHADOW_bn_mod_u16_consttime +#define bn_mont_n0 GRPC_SHADOW_bn_mont_n0 +#define bn_mul_add_words GRPC_SHADOW_bn_mul_add_words +#define bn_mul_comba4 GRPC_SHADOW_bn_mul_comba4 +#define bn_mul_comba8 GRPC_SHADOW_bn_mul_comba8 +#define bn_mul_consttime GRPC_SHADOW_bn_mul_consttime +#define bn_mul_small GRPC_SHADOW_bn_mul_small +#define bn_mul_words GRPC_SHADOW_bn_mul_words +#define bn_odd_number_is_obviously_composite GRPC_SHADOW_bn_odd_number_is_obviously_composite +#define bn_one_to_montgomery GRPC_SHADOW_bn_one_to_montgomery +#define bn_one_to_montgomery_small GRPC_SHADOW_bn_one_to_montgomery_small +#define bn_rand_range_words GRPC_SHADOW_bn_rand_range_words +#define bn_rand_secret_range GRPC_SHADOW_bn_rand_secret_range +#define bn_resize_words GRPC_SHADOW_bn_resize_words +#define bn_rshift1_words GRPC_SHADOW_bn_rshift1_words +#define bn_rshift_secret_shift GRPC_SHADOW_bn_rshift_secret_shift +#define bn_select_words GRPC_SHADOW_bn_select_words +#define bn_set_minimal_width GRPC_SHADOW_bn_set_minimal_width +#define bn_set_words GRPC_SHADOW_bn_set_words +#define bn_sqr_comba4 GRPC_SHADOW_bn_sqr_comba4 +#define bn_sqr_comba8 GRPC_SHADOW_bn_sqr_comba8 +#define bn_sqr_consttime GRPC_SHADOW_bn_sqr_consttime +#define bn_sqr_small GRPC_SHADOW_bn_sqr_small +#define bn_sqr_words GRPC_SHADOW_bn_sqr_words +#define bn_sub_words GRPC_SHADOW_bn_sub_words +#define bn_to_montgomery_small GRPC_SHADOW_bn_to_montgomery_small +#define bn_uadd_consttime GRPC_SHADOW_bn_uadd_consttime +#define bn_usub_consttime GRPC_SHADOW_bn_usub_consttime +#define bn_wexpand GRPC_SHADOW_bn_wexpand +#define crypto_gcm_clmul_enabled GRPC_SHADOW_crypto_gcm_clmul_enabled +#define ec_GFp_mont_field_decode GRPC_SHADOW_ec_GFp_mont_field_decode +#define ec_GFp_mont_field_encode GRPC_SHADOW_ec_GFp_mont_field_encode +#define ec_GFp_mont_field_mul GRPC_SHADOW_ec_GFp_mont_field_mul +#define ec_GFp_mont_field_sqr GRPC_SHADOW_ec_GFp_mont_field_sqr +#define ec_GFp_mont_group_finish GRPC_SHADOW_ec_GFp_mont_group_finish +#define ec_GFp_mont_group_init GRPC_SHADOW_ec_GFp_mont_group_init +#define ec_GFp_mont_group_set_curve GRPC_SHADOW_ec_GFp_mont_group_set_curve +#define ec_GFp_nistp_recode_scalar_bits GRPC_SHADOW_ec_GFp_nistp_recode_scalar_bits +#define ec_GFp_simple_add GRPC_SHADOW_ec_GFp_simple_add +#define ec_GFp_simple_cmp GRPC_SHADOW_ec_GFp_simple_cmp +#define ec_GFp_simple_dbl GRPC_SHADOW_ec_GFp_simple_dbl +#define ec_GFp_simple_field_mul GRPC_SHADOW_ec_GFp_simple_field_mul +#define ec_GFp_simple_field_sqr GRPC_SHADOW_ec_GFp_simple_field_sqr +#define ec_GFp_simple_group_finish GRPC_SHADOW_ec_GFp_simple_group_finish +#define ec_GFp_simple_group_get_curve GRPC_SHADOW_ec_GFp_simple_group_get_curve +#define ec_GFp_simple_group_get_degree GRPC_SHADOW_ec_GFp_simple_group_get_degree +#define ec_GFp_simple_group_init GRPC_SHADOW_ec_GFp_simple_group_init +#define ec_GFp_simple_group_set_curve GRPC_SHADOW_ec_GFp_simple_group_set_curve +#define ec_GFp_simple_invert GRPC_SHADOW_ec_GFp_simple_invert +#define ec_GFp_simple_is_at_infinity GRPC_SHADOW_ec_GFp_simple_is_at_infinity +#define ec_GFp_simple_is_on_curve GRPC_SHADOW_ec_GFp_simple_is_on_curve +#define ec_GFp_simple_make_affine GRPC_SHADOW_ec_GFp_simple_make_affine +#define ec_GFp_simple_point_copy GRPC_SHADOW_ec_GFp_simple_point_copy +#define ec_GFp_simple_point_finish GRPC_SHADOW_ec_GFp_simple_point_finish +#define ec_GFp_simple_point_init GRPC_SHADOW_ec_GFp_simple_point_init +#define ec_GFp_simple_point_set_affine_coordinates GRPC_SHADOW_ec_GFp_simple_point_set_affine_coordinates +#define ec_GFp_simple_point_set_to_infinity GRPC_SHADOW_ec_GFp_simple_point_set_to_infinity +#define ec_GFp_simple_points_make_affine GRPC_SHADOW_ec_GFp_simple_points_make_affine +#define ec_bignum_to_scalar GRPC_SHADOW_ec_bignum_to_scalar +#define ec_bignum_to_scalar_unchecked GRPC_SHADOW_ec_bignum_to_scalar_unchecked +#define ec_compute_wNAF GRPC_SHADOW_ec_compute_wNAF +#define ec_group_new GRPC_SHADOW_ec_group_new +#define ec_point_mul_scalar GRPC_SHADOW_ec_point_mul_scalar +#define ec_point_mul_scalar_public GRPC_SHADOW_ec_point_mul_scalar_public +#define ec_random_nonzero_scalar GRPC_SHADOW_ec_random_nonzero_scalar +#define ec_wNAF_mul GRPC_SHADOW_ec_wNAF_mul +#define kBoringSSLRSASqrtTwo GRPC_SHADOW_kBoringSSLRSASqrtTwo +#define kBoringSSLRSASqrtTwoLen GRPC_SHADOW_kBoringSSLRSASqrtTwoLen +#define md4_block_data_order GRPC_SHADOW_md4_block_data_order +#define rsa_default_decrypt GRPC_SHADOW_rsa_default_decrypt +#define rsa_default_private_transform GRPC_SHADOW_rsa_default_private_transform +#define rsa_default_sign_raw GRPC_SHADOW_rsa_default_sign_raw +#define rsa_default_size GRPC_SHADOW_rsa_default_size +#define FIPS_mode GRPC_SHADOW_FIPS_mode +#define aesni_gcm_decrypt GRPC_SHADOW_aesni_gcm_decrypt +#define aesni_gcm_encrypt GRPC_SHADOW_aesni_gcm_encrypt +#define aesni_cbc_encrypt GRPC_SHADOW_aesni_cbc_encrypt +#define aesni_ccm64_decrypt_blocks GRPC_SHADOW_aesni_ccm64_decrypt_blocks +#define aesni_ccm64_encrypt_blocks GRPC_SHADOW_aesni_ccm64_encrypt_blocks +#define aesni_ctr32_encrypt_blocks GRPC_SHADOW_aesni_ctr32_encrypt_blocks +#define aesni_decrypt GRPC_SHADOW_aesni_decrypt +#define aesni_ecb_encrypt GRPC_SHADOW_aesni_ecb_encrypt +#define aesni_encrypt GRPC_SHADOW_aesni_encrypt +#define aesni_ocb_decrypt GRPC_SHADOW_aesni_ocb_decrypt +#define aesni_ocb_encrypt GRPC_SHADOW_aesni_ocb_encrypt +#define aesni_set_decrypt_key GRPC_SHADOW_aesni_set_decrypt_key +#define aesni_set_encrypt_key GRPC_SHADOW_aesni_set_encrypt_key +#define aesni_xts_decrypt GRPC_SHADOW_aesni_xts_decrypt +#define aesni_xts_encrypt GRPC_SHADOW_aesni_xts_encrypt +#define asm_AES_cbc_encrypt GRPC_SHADOW_asm_AES_cbc_encrypt +#define asm_AES_decrypt GRPC_SHADOW_asm_AES_decrypt +#define asm_AES_encrypt GRPC_SHADOW_asm_AES_encrypt +#define asm_AES_set_decrypt_key GRPC_SHADOW_asm_AES_set_decrypt_key +#define asm_AES_set_encrypt_key GRPC_SHADOW_asm_AES_set_encrypt_key +#define bsaes_cbc_encrypt GRPC_SHADOW_bsaes_cbc_encrypt +#define bsaes_ctr32_encrypt_blocks GRPC_SHADOW_bsaes_ctr32_encrypt_blocks +#define bsaes_xts_decrypt GRPC_SHADOW_bsaes_xts_decrypt +#define bsaes_xts_encrypt GRPC_SHADOW_bsaes_xts_encrypt +#define gcm_ghash_4bit GRPC_SHADOW_gcm_ghash_4bit +#define gcm_ghash_avx GRPC_SHADOW_gcm_ghash_avx +#define gcm_ghash_clmul GRPC_SHADOW_gcm_ghash_clmul +#define gcm_gmult_4bit GRPC_SHADOW_gcm_gmult_4bit +#define gcm_gmult_avx GRPC_SHADOW_gcm_gmult_avx +#define gcm_gmult_clmul GRPC_SHADOW_gcm_gmult_clmul +#define gcm_init_avx GRPC_SHADOW_gcm_init_avx +#define gcm_init_clmul GRPC_SHADOW_gcm_init_clmul +#define md5_block_asm_data_order GRPC_SHADOW_md5_block_asm_data_order +#define ecp_nistz256_avx2_select_w7 GRPC_SHADOW_ecp_nistz256_avx2_select_w7 +#define ecp_nistz256_mul_mont GRPC_SHADOW_ecp_nistz256_mul_mont +#define ecp_nistz256_neg GRPC_SHADOW_ecp_nistz256_neg +#define ecp_nistz256_point_add GRPC_SHADOW_ecp_nistz256_point_add +#define ecp_nistz256_point_add_affine GRPC_SHADOW_ecp_nistz256_point_add_affine +#define ecp_nistz256_point_double GRPC_SHADOW_ecp_nistz256_point_double +#define ecp_nistz256_select_w5 GRPC_SHADOW_ecp_nistz256_select_w5 +#define ecp_nistz256_select_w7 GRPC_SHADOW_ecp_nistz256_select_w7 +#define ecp_nistz256_sqr_mont GRPC_SHADOW_ecp_nistz256_sqr_mont +#define CRYPTO_rdrand GRPC_SHADOW_CRYPTO_rdrand +#define CRYPTO_rdrand_multiple8_buf GRPC_SHADOW_CRYPTO_rdrand_multiple8_buf +#define rsaz_1024_gather5_avx2 GRPC_SHADOW_rsaz_1024_gather5_avx2 +#define rsaz_1024_mul_avx2 GRPC_SHADOW_rsaz_1024_mul_avx2 +#define rsaz_1024_norm2red_avx2 GRPC_SHADOW_rsaz_1024_norm2red_avx2 +#define rsaz_1024_red2norm_avx2 GRPC_SHADOW_rsaz_1024_red2norm_avx2 +#define rsaz_1024_scatter5_avx2 GRPC_SHADOW_rsaz_1024_scatter5_avx2 +#define rsaz_1024_sqr_avx2 GRPC_SHADOW_rsaz_1024_sqr_avx2 +#define rsaz_avx2_eligible GRPC_SHADOW_rsaz_avx2_eligible +#define sha1_block_data_order GRPC_SHADOW_sha1_block_data_order +#define sha256_block_data_order GRPC_SHADOW_sha256_block_data_order +#define sha512_block_data_order GRPC_SHADOW_sha512_block_data_order +#define vpaes_cbc_encrypt GRPC_SHADOW_vpaes_cbc_encrypt +#define vpaes_decrypt GRPC_SHADOW_vpaes_decrypt +#define vpaes_encrypt GRPC_SHADOW_vpaes_encrypt +#define vpaes_set_decrypt_key GRPC_SHADOW_vpaes_set_decrypt_key +#define vpaes_set_encrypt_key GRPC_SHADOW_vpaes_set_encrypt_key +#define bn_from_montgomery GRPC_SHADOW_bn_from_montgomery +#define bn_gather5 GRPC_SHADOW_bn_gather5 +#define bn_mul_mont_gather5 GRPC_SHADOW_bn_mul_mont_gather5 +#define bn_power5 GRPC_SHADOW_bn_power5 +#define bn_scatter5 GRPC_SHADOW_bn_scatter5 +#define bn_sqr8x_internal GRPC_SHADOW_bn_sqr8x_internal +#define bn_mul_mont GRPC_SHADOW_bn_mul_mont +#define EVP_get_digestbyname GRPC_SHADOW_EVP_get_digestbyname +#define EVP_get_digestbynid GRPC_SHADOW_EVP_get_digestbynid +#define EVP_get_digestbyobj GRPC_SHADOW_EVP_get_digestbyobj +#define EVP_marshal_digest_algorithm GRPC_SHADOW_EVP_marshal_digest_algorithm +#define EVP_parse_digest_algorithm GRPC_SHADOW_EVP_parse_digest_algorithm +#define EVP_get_cipherbyname GRPC_SHADOW_EVP_get_cipherbyname +#define EVP_get_cipherbynid GRPC_SHADOW_EVP_get_cipherbynid +#define EVP_BytesToKey GRPC_SHADOW_EVP_BytesToKey +#define EVP_enc_null GRPC_SHADOW_EVP_enc_null +#define EVP_rc2_40_cbc GRPC_SHADOW_EVP_rc2_40_cbc +#define EVP_rc2_cbc GRPC_SHADOW_EVP_rc2_cbc +#define EVP_rc4 GRPC_SHADOW_EVP_rc4 +#define EVP_aead_aes_128_gcm_siv GRPC_SHADOW_EVP_aead_aes_128_gcm_siv +#define EVP_aead_aes_256_gcm_siv GRPC_SHADOW_EVP_aead_aes_256_gcm_siv +#define EVP_aead_aes_128_ctr_hmac_sha256 GRPC_SHADOW_EVP_aead_aes_128_ctr_hmac_sha256 +#define EVP_aead_aes_256_ctr_hmac_sha256 GRPC_SHADOW_EVP_aead_aes_256_ctr_hmac_sha256 +#define EVP_aead_aes_128_ccm_bluetooth GRPC_SHADOW_EVP_aead_aes_128_ccm_bluetooth +#define EVP_aead_aes_128_ccm_bluetooth_8 GRPC_SHADOW_EVP_aead_aes_128_ccm_bluetooth_8 +#define EVP_aead_chacha20_poly1305 GRPC_SHADOW_EVP_aead_chacha20_poly1305 +#define EVP_tls_cbc_copy_mac GRPC_SHADOW_EVP_tls_cbc_copy_mac +#define EVP_tls_cbc_digest_record GRPC_SHADOW_EVP_tls_cbc_digest_record +#define EVP_tls_cbc_record_digest_supported GRPC_SHADOW_EVP_tls_cbc_record_digest_supported +#define EVP_tls_cbc_remove_padding GRPC_SHADOW_EVP_tls_cbc_remove_padding +#define EVP_aead_aes_128_cbc_sha1_tls GRPC_SHADOW_EVP_aead_aes_128_cbc_sha1_tls +#define EVP_aead_aes_128_cbc_sha1_tls_implicit_iv GRPC_SHADOW_EVP_aead_aes_128_cbc_sha1_tls_implicit_iv +#define EVP_aead_aes_128_cbc_sha256_tls GRPC_SHADOW_EVP_aead_aes_128_cbc_sha256_tls +#define EVP_aead_aes_256_cbc_sha1_tls GRPC_SHADOW_EVP_aead_aes_256_cbc_sha1_tls +#define EVP_aead_aes_256_cbc_sha1_tls_implicit_iv GRPC_SHADOW_EVP_aead_aes_256_cbc_sha1_tls_implicit_iv +#define EVP_aead_aes_256_cbc_sha256_tls GRPC_SHADOW_EVP_aead_aes_256_cbc_sha256_tls +#define EVP_aead_aes_256_cbc_sha384_tls GRPC_SHADOW_EVP_aead_aes_256_cbc_sha384_tls +#define EVP_aead_des_ede3_cbc_sha1_tls GRPC_SHADOW_EVP_aead_des_ede3_cbc_sha1_tls +#define EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv GRPC_SHADOW_EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv +#define EVP_aead_null_sha1_tls GRPC_SHADOW_EVP_aead_null_sha1_tls +#define EVP_aead_aes_128_cbc_sha1_ssl3 GRPC_SHADOW_EVP_aead_aes_128_cbc_sha1_ssl3 +#define EVP_aead_aes_256_cbc_sha1_ssl3 GRPC_SHADOW_EVP_aead_aes_256_cbc_sha1_ssl3 +#define EVP_aead_des_ede3_cbc_sha1_ssl3 GRPC_SHADOW_EVP_aead_des_ede3_cbc_sha1_ssl3 +#define EVP_aead_null_sha1_ssl3 GRPC_SHADOW_EVP_aead_null_sha1_ssl3 +#define aes128gcmsiv_aes_ks GRPC_SHADOW_aes128gcmsiv_aes_ks +#define aes128gcmsiv_aes_ks_enc_x1 GRPC_SHADOW_aes128gcmsiv_aes_ks_enc_x1 +#define aes128gcmsiv_dec GRPC_SHADOW_aes128gcmsiv_dec +#define aes128gcmsiv_ecb_enc_block GRPC_SHADOW_aes128gcmsiv_ecb_enc_block +#define aes128gcmsiv_enc_msg_x4 GRPC_SHADOW_aes128gcmsiv_enc_msg_x4 +#define aes128gcmsiv_enc_msg_x8 GRPC_SHADOW_aes128gcmsiv_enc_msg_x8 +#define aes128gcmsiv_kdf GRPC_SHADOW_aes128gcmsiv_kdf +#define aes256gcmsiv_aes_ks GRPC_SHADOW_aes256gcmsiv_aes_ks +#define aes256gcmsiv_aes_ks_enc_x1 GRPC_SHADOW_aes256gcmsiv_aes_ks_enc_x1 +#define aes256gcmsiv_dec GRPC_SHADOW_aes256gcmsiv_dec +#define aes256gcmsiv_ecb_enc_block GRPC_SHADOW_aes256gcmsiv_ecb_enc_block +#define aes256gcmsiv_enc_msg_x4 GRPC_SHADOW_aes256gcmsiv_enc_msg_x4 +#define aes256gcmsiv_enc_msg_x8 GRPC_SHADOW_aes256gcmsiv_enc_msg_x8 +#define aes256gcmsiv_kdf GRPC_SHADOW_aes256gcmsiv_kdf +#define aesgcmsiv_htable6_init GRPC_SHADOW_aesgcmsiv_htable6_init +#define aesgcmsiv_htable_init GRPC_SHADOW_aesgcmsiv_htable_init +#define aesgcmsiv_htable_polyval GRPC_SHADOW_aesgcmsiv_htable_polyval +#define aesgcmsiv_polyval_horner GRPC_SHADOW_aesgcmsiv_polyval_horner +#define chacha20_poly1305_open GRPC_SHADOW_chacha20_poly1305_open +#define chacha20_poly1305_seal GRPC_SHADOW_chacha20_poly1305_seal +#define RC4 GRPC_SHADOW_RC4 +#define RC4_set_key GRPC_SHADOW_RC4_set_key +#define CONF_VALUE_new GRPC_SHADOW_CONF_VALUE_new +#define CONF_modules_free GRPC_SHADOW_CONF_modules_free +#define CONF_modules_load_file GRPC_SHADOW_CONF_modules_load_file +#define CONF_parse_list GRPC_SHADOW_CONF_parse_list +#define NCONF_free GRPC_SHADOW_NCONF_free +#define NCONF_get_section GRPC_SHADOW_NCONF_get_section +#define NCONF_get_string GRPC_SHADOW_NCONF_get_string +#define NCONF_load GRPC_SHADOW_NCONF_load +#define NCONF_load_bio GRPC_SHADOW_NCONF_load_bio +#define NCONF_new GRPC_SHADOW_NCONF_new +#define OPENSSL_config GRPC_SHADOW_OPENSSL_config +#define OPENSSL_no_config GRPC_SHADOW_OPENSSL_no_config +#define CRYPTO_chacha_20 GRPC_SHADOW_CRYPTO_chacha_20 +#define ChaCha20_ctr32 GRPC_SHADOW_ChaCha20_ctr32 +#define CRYPTO_poly1305_finish GRPC_SHADOW_CRYPTO_poly1305_finish +#define CRYPTO_poly1305_init GRPC_SHADOW_CRYPTO_poly1305_init +#define CRYPTO_poly1305_update GRPC_SHADOW_CRYPTO_poly1305_update +#define SPAKE2_CTX_free GRPC_SHADOW_SPAKE2_CTX_free +#define SPAKE2_CTX_new GRPC_SHADOW_SPAKE2_CTX_new +#define SPAKE2_generate_msg GRPC_SHADOW_SPAKE2_generate_msg +#define SPAKE2_process_msg GRPC_SHADOW_SPAKE2_process_msg +#define ED25519_keypair GRPC_SHADOW_ED25519_keypair +#define ED25519_keypair_from_seed GRPC_SHADOW_ED25519_keypair_from_seed +#define ED25519_sign GRPC_SHADOW_ED25519_sign +#define ED25519_verify GRPC_SHADOW_ED25519_verify +#define X25519 GRPC_SHADOW_X25519 +#define X25519_keypair GRPC_SHADOW_X25519_keypair +#define X25519_public_from_private GRPC_SHADOW_X25519_public_from_private +#define x25519_ge_add GRPC_SHADOW_x25519_ge_add +#define x25519_ge_frombytes_vartime GRPC_SHADOW_x25519_ge_frombytes_vartime +#define x25519_ge_p1p1_to_p2 GRPC_SHADOW_x25519_ge_p1p1_to_p2 +#define x25519_ge_p1p1_to_p3 GRPC_SHADOW_x25519_ge_p1p1_to_p3 +#define x25519_ge_p3_to_cached GRPC_SHADOW_x25519_ge_p3_to_cached +#define x25519_ge_scalarmult GRPC_SHADOW_x25519_ge_scalarmult +#define x25519_ge_scalarmult_base GRPC_SHADOW_x25519_ge_scalarmult_base +#define x25519_ge_scalarmult_small_precomp GRPC_SHADOW_x25519_ge_scalarmult_small_precomp +#define x25519_ge_sub GRPC_SHADOW_x25519_ge_sub +#define x25519_ge_tobytes GRPC_SHADOW_x25519_ge_tobytes +#define x25519_sc_reduce GRPC_SHADOW_x25519_sc_reduce +#define BUF_MEM_append GRPC_SHADOW_BUF_MEM_append +#define BUF_MEM_free GRPC_SHADOW_BUF_MEM_free +#define BUF_MEM_grow GRPC_SHADOW_BUF_MEM_grow +#define BUF_MEM_grow_clean GRPC_SHADOW_BUF_MEM_grow_clean +#define BUF_MEM_new GRPC_SHADOW_BUF_MEM_new +#define BUF_MEM_reserve GRPC_SHADOW_BUF_MEM_reserve +#define BUF_memdup GRPC_SHADOW_BUF_memdup +#define BUF_strdup GRPC_SHADOW_BUF_strdup +#define BUF_strlcat GRPC_SHADOW_BUF_strlcat +#define BUF_strlcpy GRPC_SHADOW_BUF_strlcpy +#define BUF_strndup GRPC_SHADOW_BUF_strndup +#define BUF_strnlen GRPC_SHADOW_BUF_strnlen +#define BN_marshal_asn1 GRPC_SHADOW_BN_marshal_asn1 +#define BN_parse_asn1_unsigned GRPC_SHADOW_BN_parse_asn1_unsigned +#define BN_asc2bn GRPC_SHADOW_BN_asc2bn +#define BN_bn2cbb_padded GRPC_SHADOW_BN_bn2cbb_padded +#define BN_bn2dec GRPC_SHADOW_BN_bn2dec +#define BN_bn2hex GRPC_SHADOW_BN_bn2hex +#define BN_bn2mpi GRPC_SHADOW_BN_bn2mpi +#define BN_dec2bn GRPC_SHADOW_BN_dec2bn +#define BN_hex2bn GRPC_SHADOW_BN_hex2bn +#define BN_mpi2bn GRPC_SHADOW_BN_mpi2bn +#define BN_print GRPC_SHADOW_BN_print +#define BN_print_fp GRPC_SHADOW_BN_print_fp +#define BIO_callback_ctrl GRPC_SHADOW_BIO_callback_ctrl +#define BIO_clear_flags GRPC_SHADOW_BIO_clear_flags +#define BIO_clear_retry_flags GRPC_SHADOW_BIO_clear_retry_flags +#define BIO_copy_next_retry GRPC_SHADOW_BIO_copy_next_retry +#define BIO_ctrl GRPC_SHADOW_BIO_ctrl +#define BIO_ctrl_pending GRPC_SHADOW_BIO_ctrl_pending +#define BIO_eof GRPC_SHADOW_BIO_eof +#define BIO_find_type GRPC_SHADOW_BIO_find_type +#define BIO_flush GRPC_SHADOW_BIO_flush +#define BIO_free GRPC_SHADOW_BIO_free +#define BIO_free_all GRPC_SHADOW_BIO_free_all +#define BIO_get_data GRPC_SHADOW_BIO_get_data +#define BIO_get_init GRPC_SHADOW_BIO_get_init +#define BIO_get_new_index GRPC_SHADOW_BIO_get_new_index +#define BIO_get_retry_flags GRPC_SHADOW_BIO_get_retry_flags +#define BIO_get_retry_reason GRPC_SHADOW_BIO_get_retry_reason +#define BIO_get_shutdown GRPC_SHADOW_BIO_get_shutdown +#define BIO_gets GRPC_SHADOW_BIO_gets +#define BIO_indent GRPC_SHADOW_BIO_indent +#define BIO_int_ctrl GRPC_SHADOW_BIO_int_ctrl +#define BIO_meth_free GRPC_SHADOW_BIO_meth_free +#define BIO_meth_new GRPC_SHADOW_BIO_meth_new +#define BIO_meth_set_create GRPC_SHADOW_BIO_meth_set_create +#define BIO_meth_set_ctrl GRPC_SHADOW_BIO_meth_set_ctrl +#define BIO_meth_set_destroy GRPC_SHADOW_BIO_meth_set_destroy +#define BIO_meth_set_gets GRPC_SHADOW_BIO_meth_set_gets +#define BIO_meth_set_puts GRPC_SHADOW_BIO_meth_set_puts +#define BIO_meth_set_read GRPC_SHADOW_BIO_meth_set_read +#define BIO_meth_set_write GRPC_SHADOW_BIO_meth_set_write +#define BIO_method_type GRPC_SHADOW_BIO_method_type +#define BIO_new GRPC_SHADOW_BIO_new +#define BIO_next GRPC_SHADOW_BIO_next +#define BIO_number_read GRPC_SHADOW_BIO_number_read +#define BIO_number_written GRPC_SHADOW_BIO_number_written +#define BIO_pending GRPC_SHADOW_BIO_pending +#define BIO_pop GRPC_SHADOW_BIO_pop +#define BIO_ptr_ctrl GRPC_SHADOW_BIO_ptr_ctrl +#define BIO_push GRPC_SHADOW_BIO_push +#define BIO_puts GRPC_SHADOW_BIO_puts +#define BIO_read GRPC_SHADOW_BIO_read +#define BIO_read_asn1 GRPC_SHADOW_BIO_read_asn1 +#define BIO_reset GRPC_SHADOW_BIO_reset +#define BIO_set_close GRPC_SHADOW_BIO_set_close +#define BIO_set_data GRPC_SHADOW_BIO_set_data +#define BIO_set_flags GRPC_SHADOW_BIO_set_flags +#define BIO_set_init GRPC_SHADOW_BIO_set_init +#define BIO_set_retry_read GRPC_SHADOW_BIO_set_retry_read +#define BIO_set_retry_special GRPC_SHADOW_BIO_set_retry_special +#define BIO_set_retry_write GRPC_SHADOW_BIO_set_retry_write +#define BIO_set_shutdown GRPC_SHADOW_BIO_set_shutdown +#define BIO_set_write_buffer_size GRPC_SHADOW_BIO_set_write_buffer_size +#define BIO_should_io_special GRPC_SHADOW_BIO_should_io_special +#define BIO_should_read GRPC_SHADOW_BIO_should_read +#define BIO_should_retry GRPC_SHADOW_BIO_should_retry +#define BIO_should_write GRPC_SHADOW_BIO_should_write +#define BIO_test_flags GRPC_SHADOW_BIO_test_flags +#define BIO_up_ref GRPC_SHADOW_BIO_up_ref +#define BIO_vfree GRPC_SHADOW_BIO_vfree +#define BIO_wpending GRPC_SHADOW_BIO_wpending +#define BIO_write GRPC_SHADOW_BIO_write +#define ERR_print_errors GRPC_SHADOW_ERR_print_errors +#define BIO_get_mem_data GRPC_SHADOW_BIO_get_mem_data +#define BIO_get_mem_ptr GRPC_SHADOW_BIO_get_mem_ptr +#define BIO_mem_contents GRPC_SHADOW_BIO_mem_contents +#define BIO_new_mem_buf GRPC_SHADOW_BIO_new_mem_buf +#define BIO_s_mem GRPC_SHADOW_BIO_s_mem +#define BIO_set_mem_buf GRPC_SHADOW_BIO_set_mem_buf +#define BIO_set_mem_eof_return GRPC_SHADOW_BIO_set_mem_eof_return +#define BIO_do_connect GRPC_SHADOW_BIO_do_connect +#define BIO_new_connect GRPC_SHADOW_BIO_new_connect +#define BIO_s_connect GRPC_SHADOW_BIO_s_connect +#define BIO_set_conn_hostname GRPC_SHADOW_BIO_set_conn_hostname +#define BIO_set_conn_int_port GRPC_SHADOW_BIO_set_conn_int_port +#define BIO_set_conn_port GRPC_SHADOW_BIO_set_conn_port +#define BIO_set_nbio GRPC_SHADOW_BIO_set_nbio +#define BIO_get_fd GRPC_SHADOW_BIO_get_fd +#define BIO_new_fd GRPC_SHADOW_BIO_new_fd +#define BIO_s_fd GRPC_SHADOW_BIO_s_fd +#define BIO_set_fd GRPC_SHADOW_BIO_set_fd +#define bio_fd_should_retry GRPC_SHADOW_bio_fd_should_retry +#define BIO_append_filename GRPC_SHADOW_BIO_append_filename +#define BIO_get_fp GRPC_SHADOW_BIO_get_fp +#define BIO_new_file GRPC_SHADOW_BIO_new_file +#define BIO_new_fp GRPC_SHADOW_BIO_new_fp +#define BIO_read_filename GRPC_SHADOW_BIO_read_filename +#define BIO_rw_filename GRPC_SHADOW_BIO_rw_filename +#define BIO_s_file GRPC_SHADOW_BIO_s_file +#define BIO_set_fp GRPC_SHADOW_BIO_set_fp +#define BIO_write_filename GRPC_SHADOW_BIO_write_filename +#define BIO_hexdump GRPC_SHADOW_BIO_hexdump +#define BIO_ctrl_get_read_request GRPC_SHADOW_BIO_ctrl_get_read_request +#define BIO_ctrl_get_write_guarantee GRPC_SHADOW_BIO_ctrl_get_write_guarantee +#define BIO_new_bio_pair GRPC_SHADOW_BIO_new_bio_pair +#define BIO_shutdown_wr GRPC_SHADOW_BIO_shutdown_wr +#define BIO_printf GRPC_SHADOW_BIO_printf +#define BIO_new_socket GRPC_SHADOW_BIO_new_socket +#define BIO_s_socket GRPC_SHADOW_BIO_s_socket +#define bio_clear_socket_error GRPC_SHADOW_bio_clear_socket_error +#define bio_ip_and_port_to_socket_and_addr GRPC_SHADOW_bio_ip_and_port_to_socket_and_addr +#define bio_sock_error GRPC_SHADOW_bio_sock_error +#define bio_socket_nbio GRPC_SHADOW_bio_socket_nbio +#define RAND_enable_fork_unsafe_buffering GRPC_SHADOW_RAND_enable_fork_unsafe_buffering +#define rand_fork_unsafe_buffering_enabled GRPC_SHADOW_rand_fork_unsafe_buffering_enabled +#define RAND_SSLeay GRPC_SHADOW_RAND_SSLeay +#define RAND_add GRPC_SHADOW_RAND_add +#define RAND_cleanup GRPC_SHADOW_RAND_cleanup +#define RAND_egd GRPC_SHADOW_RAND_egd +#define RAND_file_name GRPC_SHADOW_RAND_file_name +#define RAND_get_rand_method GRPC_SHADOW_RAND_get_rand_method +#define RAND_load_file GRPC_SHADOW_RAND_load_file +#define RAND_poll GRPC_SHADOW_RAND_poll +#define RAND_seed GRPC_SHADOW_RAND_seed +#define RAND_set_rand_method GRPC_SHADOW_RAND_set_rand_method +#define RAND_status GRPC_SHADOW_RAND_status +#define OBJ_cbs2nid GRPC_SHADOW_OBJ_cbs2nid +#define OBJ_cmp GRPC_SHADOW_OBJ_cmp +#define OBJ_create GRPC_SHADOW_OBJ_create +#define OBJ_dup GRPC_SHADOW_OBJ_dup +#define OBJ_get0_data GRPC_SHADOW_OBJ_get0_data +#define OBJ_length GRPC_SHADOW_OBJ_length +#define OBJ_ln2nid GRPC_SHADOW_OBJ_ln2nid +#define OBJ_nid2cbb GRPC_SHADOW_OBJ_nid2cbb +#define OBJ_nid2ln GRPC_SHADOW_OBJ_nid2ln +#define OBJ_nid2obj GRPC_SHADOW_OBJ_nid2obj +#define OBJ_nid2sn GRPC_SHADOW_OBJ_nid2sn +#define OBJ_obj2nid GRPC_SHADOW_OBJ_obj2nid +#define OBJ_obj2txt GRPC_SHADOW_OBJ_obj2txt +#define OBJ_sn2nid GRPC_SHADOW_OBJ_sn2nid +#define OBJ_txt2nid GRPC_SHADOW_OBJ_txt2nid +#define OBJ_txt2obj GRPC_SHADOW_OBJ_txt2obj +#define OBJ_find_sigid_algs GRPC_SHADOW_OBJ_find_sigid_algs +#define OBJ_find_sigid_by_algs GRPC_SHADOW_OBJ_find_sigid_by_algs +#define ASN1_BIT_STRING_check GRPC_SHADOW_ASN1_BIT_STRING_check +#define ASN1_BIT_STRING_get_bit GRPC_SHADOW_ASN1_BIT_STRING_get_bit +#define ASN1_BIT_STRING_set GRPC_SHADOW_ASN1_BIT_STRING_set +#define ASN1_BIT_STRING_set_bit GRPC_SHADOW_ASN1_BIT_STRING_set_bit +#define c2i_ASN1_BIT_STRING GRPC_SHADOW_c2i_ASN1_BIT_STRING +#define i2c_ASN1_BIT_STRING GRPC_SHADOW_i2c_ASN1_BIT_STRING +#define d2i_ASN1_BOOLEAN GRPC_SHADOW_d2i_ASN1_BOOLEAN +#define i2d_ASN1_BOOLEAN GRPC_SHADOW_i2d_ASN1_BOOLEAN +#define ASN1_d2i_bio GRPC_SHADOW_ASN1_d2i_bio +#define ASN1_d2i_fp GRPC_SHADOW_ASN1_d2i_fp +#define ASN1_item_d2i_bio GRPC_SHADOW_ASN1_item_d2i_bio +#define ASN1_item_d2i_fp GRPC_SHADOW_ASN1_item_d2i_fp +#define ASN1_dup GRPC_SHADOW_ASN1_dup +#define ASN1_item_dup GRPC_SHADOW_ASN1_item_dup +#define ASN1_ENUMERATED_get GRPC_SHADOW_ASN1_ENUMERATED_get +#define ASN1_ENUMERATED_set GRPC_SHADOW_ASN1_ENUMERATED_set +#define ASN1_ENUMERATED_to_BN GRPC_SHADOW_ASN1_ENUMERATED_to_BN +#define BN_to_ASN1_ENUMERATED GRPC_SHADOW_BN_to_ASN1_ENUMERATED +#define ASN1_GENERALIZEDTIME_adj GRPC_SHADOW_ASN1_GENERALIZEDTIME_adj +#define ASN1_GENERALIZEDTIME_check GRPC_SHADOW_ASN1_GENERALIZEDTIME_check +#define ASN1_GENERALIZEDTIME_set GRPC_SHADOW_ASN1_GENERALIZEDTIME_set +#define ASN1_GENERALIZEDTIME_set_string GRPC_SHADOW_ASN1_GENERALIZEDTIME_set_string +#define asn1_generalizedtime_to_tm GRPC_SHADOW_asn1_generalizedtime_to_tm +#define ASN1_i2d_bio GRPC_SHADOW_ASN1_i2d_bio +#define ASN1_i2d_fp GRPC_SHADOW_ASN1_i2d_fp +#define ASN1_item_i2d_bio GRPC_SHADOW_ASN1_item_i2d_bio +#define ASN1_item_i2d_fp GRPC_SHADOW_ASN1_item_i2d_fp +#define ASN1_INTEGER_cmp GRPC_SHADOW_ASN1_INTEGER_cmp +#define ASN1_INTEGER_dup GRPC_SHADOW_ASN1_INTEGER_dup +#define ASN1_INTEGER_get GRPC_SHADOW_ASN1_INTEGER_get +#define ASN1_INTEGER_set GRPC_SHADOW_ASN1_INTEGER_set +#define ASN1_INTEGER_set_uint64 GRPC_SHADOW_ASN1_INTEGER_set_uint64 +#define ASN1_INTEGER_to_BN GRPC_SHADOW_ASN1_INTEGER_to_BN +#define BN_to_ASN1_INTEGER GRPC_SHADOW_BN_to_ASN1_INTEGER +#define c2i_ASN1_INTEGER GRPC_SHADOW_c2i_ASN1_INTEGER +#define d2i_ASN1_UINTEGER GRPC_SHADOW_d2i_ASN1_UINTEGER +#define i2c_ASN1_INTEGER GRPC_SHADOW_i2c_ASN1_INTEGER +#define ASN1_mbstring_copy GRPC_SHADOW_ASN1_mbstring_copy +#define ASN1_mbstring_ncopy GRPC_SHADOW_ASN1_mbstring_ncopy +#define ASN1_OBJECT_create GRPC_SHADOW_ASN1_OBJECT_create +#define ASN1_OBJECT_free GRPC_SHADOW_ASN1_OBJECT_free +#define ASN1_OBJECT_new GRPC_SHADOW_ASN1_OBJECT_new +#define c2i_ASN1_OBJECT GRPC_SHADOW_c2i_ASN1_OBJECT +#define d2i_ASN1_OBJECT GRPC_SHADOW_d2i_ASN1_OBJECT +#define i2a_ASN1_OBJECT GRPC_SHADOW_i2a_ASN1_OBJECT +#define i2d_ASN1_OBJECT GRPC_SHADOW_i2d_ASN1_OBJECT +#define i2t_ASN1_OBJECT GRPC_SHADOW_i2t_ASN1_OBJECT +#define ASN1_OCTET_STRING_cmp GRPC_SHADOW_ASN1_OCTET_STRING_cmp +#define ASN1_OCTET_STRING_dup GRPC_SHADOW_ASN1_OCTET_STRING_dup +#define ASN1_OCTET_STRING_set GRPC_SHADOW_ASN1_OCTET_STRING_set +#define ASN1_PRINTABLE_type GRPC_SHADOW_ASN1_PRINTABLE_type +#define ASN1_STRING_TABLE_add GRPC_SHADOW_ASN1_STRING_TABLE_add +#define ASN1_STRING_TABLE_cleanup GRPC_SHADOW_ASN1_STRING_TABLE_cleanup +#define ASN1_STRING_TABLE_get GRPC_SHADOW_ASN1_STRING_TABLE_get +#define ASN1_STRING_get_default_mask GRPC_SHADOW_ASN1_STRING_get_default_mask +#define ASN1_STRING_set_by_NID GRPC_SHADOW_ASN1_STRING_set_by_NID +#define ASN1_STRING_set_default_mask GRPC_SHADOW_ASN1_STRING_set_default_mask +#define ASN1_STRING_set_default_mask_asc GRPC_SHADOW_ASN1_STRING_set_default_mask_asc +#define ASN1_TIME_adj GRPC_SHADOW_ASN1_TIME_adj +#define ASN1_TIME_check GRPC_SHADOW_ASN1_TIME_check +#define ASN1_TIME_diff GRPC_SHADOW_ASN1_TIME_diff +#define ASN1_TIME_free GRPC_SHADOW_ASN1_TIME_free +#define ASN1_TIME_it GRPC_SHADOW_ASN1_TIME_it +#define ASN1_TIME_new GRPC_SHADOW_ASN1_TIME_new +#define ASN1_TIME_set GRPC_SHADOW_ASN1_TIME_set +#define ASN1_TIME_set_string GRPC_SHADOW_ASN1_TIME_set_string +#define ASN1_TIME_to_generalizedtime GRPC_SHADOW_ASN1_TIME_to_generalizedtime +#define d2i_ASN1_TIME GRPC_SHADOW_d2i_ASN1_TIME +#define i2d_ASN1_TIME GRPC_SHADOW_i2d_ASN1_TIME +#define ASN1_TYPE_cmp GRPC_SHADOW_ASN1_TYPE_cmp +#define ASN1_TYPE_get GRPC_SHADOW_ASN1_TYPE_get +#define ASN1_TYPE_set GRPC_SHADOW_ASN1_TYPE_set +#define ASN1_TYPE_set1 GRPC_SHADOW_ASN1_TYPE_set1 +#define ASN1_UTCTIME_adj GRPC_SHADOW_ASN1_UTCTIME_adj +#define ASN1_UTCTIME_check GRPC_SHADOW_ASN1_UTCTIME_check +#define ASN1_UTCTIME_cmp_time_t GRPC_SHADOW_ASN1_UTCTIME_cmp_time_t +#define ASN1_UTCTIME_set GRPC_SHADOW_ASN1_UTCTIME_set +#define ASN1_UTCTIME_set_string GRPC_SHADOW_ASN1_UTCTIME_set_string +#define asn1_utctime_to_tm GRPC_SHADOW_asn1_utctime_to_tm +#define UTF8_getc GRPC_SHADOW_UTF8_getc +#define UTF8_putc GRPC_SHADOW_UTF8_putc +#define ASN1_STRING_cmp GRPC_SHADOW_ASN1_STRING_cmp +#define ASN1_STRING_copy GRPC_SHADOW_ASN1_STRING_copy +#define ASN1_STRING_data GRPC_SHADOW_ASN1_STRING_data +#define ASN1_STRING_dup GRPC_SHADOW_ASN1_STRING_dup +#define ASN1_STRING_free GRPC_SHADOW_ASN1_STRING_free +#define ASN1_STRING_get0_data GRPC_SHADOW_ASN1_STRING_get0_data +#define ASN1_STRING_length GRPC_SHADOW_ASN1_STRING_length +#define ASN1_STRING_length_set GRPC_SHADOW_ASN1_STRING_length_set +#define ASN1_STRING_new GRPC_SHADOW_ASN1_STRING_new +#define ASN1_STRING_set GRPC_SHADOW_ASN1_STRING_set +#define ASN1_STRING_set0 GRPC_SHADOW_ASN1_STRING_set0 +#define ASN1_STRING_type GRPC_SHADOW_ASN1_STRING_type +#define ASN1_STRING_type_new GRPC_SHADOW_ASN1_STRING_type_new +#define ASN1_get_object GRPC_SHADOW_ASN1_get_object +#define ASN1_object_size GRPC_SHADOW_ASN1_object_size +#define ASN1_put_eoc GRPC_SHADOW_ASN1_put_eoc +#define ASN1_put_object GRPC_SHADOW_ASN1_put_object +#define ASN1_tag2str GRPC_SHADOW_ASN1_tag2str +#define ASN1_item_pack GRPC_SHADOW_ASN1_item_pack +#define ASN1_item_unpack GRPC_SHADOW_ASN1_item_unpack +#define i2a_ASN1_ENUMERATED GRPC_SHADOW_i2a_ASN1_ENUMERATED +#define i2a_ASN1_INTEGER GRPC_SHADOW_i2a_ASN1_INTEGER +#define i2a_ASN1_STRING GRPC_SHADOW_i2a_ASN1_STRING +#define ASN1_item_d2i GRPC_SHADOW_ASN1_item_d2i +#define ASN1_item_ex_d2i GRPC_SHADOW_ASN1_item_ex_d2i +#define ASN1_tag2bit GRPC_SHADOW_ASN1_tag2bit +#define asn1_ex_c2i GRPC_SHADOW_asn1_ex_c2i +#define ASN1_item_ex_i2d GRPC_SHADOW_ASN1_item_ex_i2d +#define ASN1_item_i2d GRPC_SHADOW_ASN1_item_i2d +#define ASN1_item_ndef_i2d GRPC_SHADOW_ASN1_item_ndef_i2d +#define asn1_ex_i2c GRPC_SHADOW_asn1_ex_i2c +#define ASN1_item_ex_free GRPC_SHADOW_ASN1_item_ex_free +#define ASN1_item_free GRPC_SHADOW_ASN1_item_free +#define ASN1_primitive_free GRPC_SHADOW_ASN1_primitive_free +#define ASN1_template_free GRPC_SHADOW_ASN1_template_free +#define asn1_item_combine_free GRPC_SHADOW_asn1_item_combine_free +#define ASN1_item_ex_new GRPC_SHADOW_ASN1_item_ex_new +#define ASN1_item_new GRPC_SHADOW_ASN1_item_new +#define ASN1_primitive_new GRPC_SHADOW_ASN1_primitive_new +#define ASN1_template_new GRPC_SHADOW_ASN1_template_new +#define ASN1_ANY_it GRPC_SHADOW_ASN1_ANY_it +#define ASN1_BIT_STRING_free GRPC_SHADOW_ASN1_BIT_STRING_free +#define ASN1_BIT_STRING_it GRPC_SHADOW_ASN1_BIT_STRING_it +#define ASN1_BIT_STRING_new GRPC_SHADOW_ASN1_BIT_STRING_new +#define ASN1_BMPSTRING_free GRPC_SHADOW_ASN1_BMPSTRING_free +#define ASN1_BMPSTRING_it GRPC_SHADOW_ASN1_BMPSTRING_it +#define ASN1_BMPSTRING_new GRPC_SHADOW_ASN1_BMPSTRING_new +#define ASN1_BOOLEAN_it GRPC_SHADOW_ASN1_BOOLEAN_it +#define ASN1_ENUMERATED_free GRPC_SHADOW_ASN1_ENUMERATED_free +#define ASN1_ENUMERATED_it GRPC_SHADOW_ASN1_ENUMERATED_it +#define ASN1_ENUMERATED_new GRPC_SHADOW_ASN1_ENUMERATED_new +#define ASN1_FBOOLEAN_it GRPC_SHADOW_ASN1_FBOOLEAN_it +#define ASN1_GENERALIZEDTIME_free GRPC_SHADOW_ASN1_GENERALIZEDTIME_free +#define ASN1_GENERALIZEDTIME_it GRPC_SHADOW_ASN1_GENERALIZEDTIME_it +#define ASN1_GENERALIZEDTIME_new GRPC_SHADOW_ASN1_GENERALIZEDTIME_new +#define ASN1_GENERALSTRING_free GRPC_SHADOW_ASN1_GENERALSTRING_free +#define ASN1_GENERALSTRING_it GRPC_SHADOW_ASN1_GENERALSTRING_it +#define ASN1_GENERALSTRING_new GRPC_SHADOW_ASN1_GENERALSTRING_new +#define ASN1_IA5STRING_free GRPC_SHADOW_ASN1_IA5STRING_free +#define ASN1_IA5STRING_it GRPC_SHADOW_ASN1_IA5STRING_it +#define ASN1_IA5STRING_new GRPC_SHADOW_ASN1_IA5STRING_new +#define ASN1_INTEGER_free GRPC_SHADOW_ASN1_INTEGER_free +#define ASN1_INTEGER_it GRPC_SHADOW_ASN1_INTEGER_it +#define ASN1_INTEGER_new GRPC_SHADOW_ASN1_INTEGER_new +#define ASN1_NULL_free GRPC_SHADOW_ASN1_NULL_free +#define ASN1_NULL_it GRPC_SHADOW_ASN1_NULL_it +#define ASN1_NULL_new GRPC_SHADOW_ASN1_NULL_new +#define ASN1_OBJECT_it GRPC_SHADOW_ASN1_OBJECT_it +#define ASN1_OCTET_STRING_NDEF_it GRPC_SHADOW_ASN1_OCTET_STRING_NDEF_it +#define ASN1_OCTET_STRING_free GRPC_SHADOW_ASN1_OCTET_STRING_free +#define ASN1_OCTET_STRING_it GRPC_SHADOW_ASN1_OCTET_STRING_it +#define ASN1_OCTET_STRING_new GRPC_SHADOW_ASN1_OCTET_STRING_new +#define ASN1_PRINTABLESTRING_free GRPC_SHADOW_ASN1_PRINTABLESTRING_free +#define ASN1_PRINTABLESTRING_it GRPC_SHADOW_ASN1_PRINTABLESTRING_it +#define ASN1_PRINTABLESTRING_new GRPC_SHADOW_ASN1_PRINTABLESTRING_new +#define ASN1_PRINTABLE_free GRPC_SHADOW_ASN1_PRINTABLE_free +#define ASN1_PRINTABLE_it GRPC_SHADOW_ASN1_PRINTABLE_it +#define ASN1_PRINTABLE_new GRPC_SHADOW_ASN1_PRINTABLE_new +#define ASN1_SEQUENCE_ANY_it GRPC_SHADOW_ASN1_SEQUENCE_ANY_it +#define ASN1_SEQUENCE_it GRPC_SHADOW_ASN1_SEQUENCE_it +#define ASN1_SET_ANY_it GRPC_SHADOW_ASN1_SET_ANY_it +#define ASN1_T61STRING_free GRPC_SHADOW_ASN1_T61STRING_free +#define ASN1_T61STRING_it GRPC_SHADOW_ASN1_T61STRING_it +#define ASN1_T61STRING_new GRPC_SHADOW_ASN1_T61STRING_new +#define ASN1_TBOOLEAN_it GRPC_SHADOW_ASN1_TBOOLEAN_it +#define ASN1_TYPE_free GRPC_SHADOW_ASN1_TYPE_free +#define ASN1_TYPE_new GRPC_SHADOW_ASN1_TYPE_new +#define ASN1_UNIVERSALSTRING_free GRPC_SHADOW_ASN1_UNIVERSALSTRING_free +#define ASN1_UNIVERSALSTRING_it GRPC_SHADOW_ASN1_UNIVERSALSTRING_it +#define ASN1_UNIVERSALSTRING_new GRPC_SHADOW_ASN1_UNIVERSALSTRING_new +#define ASN1_UTCTIME_free GRPC_SHADOW_ASN1_UTCTIME_free +#define ASN1_UTCTIME_it GRPC_SHADOW_ASN1_UTCTIME_it +#define ASN1_UTCTIME_new GRPC_SHADOW_ASN1_UTCTIME_new +#define ASN1_UTF8STRING_free GRPC_SHADOW_ASN1_UTF8STRING_free +#define ASN1_UTF8STRING_it GRPC_SHADOW_ASN1_UTF8STRING_it +#define ASN1_UTF8STRING_new GRPC_SHADOW_ASN1_UTF8STRING_new +#define ASN1_VISIBLESTRING_free GRPC_SHADOW_ASN1_VISIBLESTRING_free +#define ASN1_VISIBLESTRING_it GRPC_SHADOW_ASN1_VISIBLESTRING_it +#define ASN1_VISIBLESTRING_new GRPC_SHADOW_ASN1_VISIBLESTRING_new +#define DIRECTORYSTRING_free GRPC_SHADOW_DIRECTORYSTRING_free +#define DIRECTORYSTRING_it GRPC_SHADOW_DIRECTORYSTRING_it +#define DIRECTORYSTRING_new GRPC_SHADOW_DIRECTORYSTRING_new +#define DISPLAYTEXT_free GRPC_SHADOW_DISPLAYTEXT_free +#define DISPLAYTEXT_it GRPC_SHADOW_DISPLAYTEXT_it +#define DISPLAYTEXT_new GRPC_SHADOW_DISPLAYTEXT_new +#define d2i_ASN1_BIT_STRING GRPC_SHADOW_d2i_ASN1_BIT_STRING +#define d2i_ASN1_BMPSTRING GRPC_SHADOW_d2i_ASN1_BMPSTRING +#define d2i_ASN1_ENUMERATED GRPC_SHADOW_d2i_ASN1_ENUMERATED +#define d2i_ASN1_GENERALIZEDTIME GRPC_SHADOW_d2i_ASN1_GENERALIZEDTIME +#define d2i_ASN1_GENERALSTRING GRPC_SHADOW_d2i_ASN1_GENERALSTRING +#define d2i_ASN1_IA5STRING GRPC_SHADOW_d2i_ASN1_IA5STRING +#define d2i_ASN1_INTEGER GRPC_SHADOW_d2i_ASN1_INTEGER +#define d2i_ASN1_NULL GRPC_SHADOW_d2i_ASN1_NULL +#define d2i_ASN1_OCTET_STRING GRPC_SHADOW_d2i_ASN1_OCTET_STRING +#define d2i_ASN1_PRINTABLE GRPC_SHADOW_d2i_ASN1_PRINTABLE +#define d2i_ASN1_PRINTABLESTRING GRPC_SHADOW_d2i_ASN1_PRINTABLESTRING +#define d2i_ASN1_SEQUENCE_ANY GRPC_SHADOW_d2i_ASN1_SEQUENCE_ANY +#define d2i_ASN1_SET_ANY GRPC_SHADOW_d2i_ASN1_SET_ANY +#define d2i_ASN1_T61STRING GRPC_SHADOW_d2i_ASN1_T61STRING +#define d2i_ASN1_TYPE GRPC_SHADOW_d2i_ASN1_TYPE +#define d2i_ASN1_UNIVERSALSTRING GRPC_SHADOW_d2i_ASN1_UNIVERSALSTRING +#define d2i_ASN1_UTCTIME GRPC_SHADOW_d2i_ASN1_UTCTIME +#define d2i_ASN1_UTF8STRING GRPC_SHADOW_d2i_ASN1_UTF8STRING +#define d2i_ASN1_VISIBLESTRING GRPC_SHADOW_d2i_ASN1_VISIBLESTRING +#define d2i_DIRECTORYSTRING GRPC_SHADOW_d2i_DIRECTORYSTRING +#define d2i_DISPLAYTEXT GRPC_SHADOW_d2i_DISPLAYTEXT +#define i2d_ASN1_BIT_STRING GRPC_SHADOW_i2d_ASN1_BIT_STRING +#define i2d_ASN1_BMPSTRING GRPC_SHADOW_i2d_ASN1_BMPSTRING +#define i2d_ASN1_ENUMERATED GRPC_SHADOW_i2d_ASN1_ENUMERATED +#define i2d_ASN1_GENERALIZEDTIME GRPC_SHADOW_i2d_ASN1_GENERALIZEDTIME +#define i2d_ASN1_GENERALSTRING GRPC_SHADOW_i2d_ASN1_GENERALSTRING +#define i2d_ASN1_IA5STRING GRPC_SHADOW_i2d_ASN1_IA5STRING +#define i2d_ASN1_INTEGER GRPC_SHADOW_i2d_ASN1_INTEGER +#define i2d_ASN1_NULL GRPC_SHADOW_i2d_ASN1_NULL +#define i2d_ASN1_OCTET_STRING GRPC_SHADOW_i2d_ASN1_OCTET_STRING +#define i2d_ASN1_PRINTABLE GRPC_SHADOW_i2d_ASN1_PRINTABLE +#define i2d_ASN1_PRINTABLESTRING GRPC_SHADOW_i2d_ASN1_PRINTABLESTRING +#define i2d_ASN1_SEQUENCE_ANY GRPC_SHADOW_i2d_ASN1_SEQUENCE_ANY +#define i2d_ASN1_SET_ANY GRPC_SHADOW_i2d_ASN1_SET_ANY +#define i2d_ASN1_T61STRING GRPC_SHADOW_i2d_ASN1_T61STRING +#define i2d_ASN1_TYPE GRPC_SHADOW_i2d_ASN1_TYPE +#define i2d_ASN1_UNIVERSALSTRING GRPC_SHADOW_i2d_ASN1_UNIVERSALSTRING +#define i2d_ASN1_UTCTIME GRPC_SHADOW_i2d_ASN1_UTCTIME +#define i2d_ASN1_UTF8STRING GRPC_SHADOW_i2d_ASN1_UTF8STRING +#define i2d_ASN1_VISIBLESTRING GRPC_SHADOW_i2d_ASN1_VISIBLESTRING +#define i2d_DIRECTORYSTRING GRPC_SHADOW_i2d_DIRECTORYSTRING +#define i2d_DISPLAYTEXT GRPC_SHADOW_i2d_DISPLAYTEXT +#define asn1_do_adb GRPC_SHADOW_asn1_do_adb +#define asn1_enc_free GRPC_SHADOW_asn1_enc_free +#define asn1_enc_init GRPC_SHADOW_asn1_enc_init +#define asn1_enc_restore GRPC_SHADOW_asn1_enc_restore +#define asn1_enc_save GRPC_SHADOW_asn1_enc_save +#define asn1_get_choice_selector GRPC_SHADOW_asn1_get_choice_selector +#define asn1_get_field_ptr GRPC_SHADOW_asn1_get_field_ptr +#define asn1_refcount_dec_and_test_zero GRPC_SHADOW_asn1_refcount_dec_and_test_zero +#define asn1_refcount_set_one GRPC_SHADOW_asn1_refcount_set_one +#define asn1_set_choice_selector GRPC_SHADOW_asn1_set_choice_selector +#define OPENSSL_gmtime GRPC_SHADOW_OPENSSL_gmtime +#define OPENSSL_gmtime_adj GRPC_SHADOW_OPENSSL_gmtime_adj +#define OPENSSL_gmtime_diff GRPC_SHADOW_OPENSSL_gmtime_diff +#define ENGINE_free GRPC_SHADOW_ENGINE_free +#define ENGINE_get_ECDSA_method GRPC_SHADOW_ENGINE_get_ECDSA_method +#define ENGINE_get_RSA_method GRPC_SHADOW_ENGINE_get_RSA_method +#define ENGINE_new GRPC_SHADOW_ENGINE_new +#define ENGINE_set_ECDSA_method GRPC_SHADOW_ENGINE_set_ECDSA_method +#define ENGINE_set_RSA_method GRPC_SHADOW_ENGINE_set_RSA_method +#define METHOD_ref GRPC_SHADOW_METHOD_ref +#define METHOD_unref GRPC_SHADOW_METHOD_unref +#define DH_compute_key GRPC_SHADOW_DH_compute_key +#define DH_free GRPC_SHADOW_DH_free +#define DH_generate_key GRPC_SHADOW_DH_generate_key +#define DH_generate_parameters_ex GRPC_SHADOW_DH_generate_parameters_ex +#define DH_get0_key GRPC_SHADOW_DH_get0_key +#define DH_get0_pqg GRPC_SHADOW_DH_get0_pqg +#define DH_get_ex_data GRPC_SHADOW_DH_get_ex_data +#define DH_get_ex_new_index GRPC_SHADOW_DH_get_ex_new_index +#define DH_new GRPC_SHADOW_DH_new +#define DH_num_bits GRPC_SHADOW_DH_num_bits +#define DH_set0_key GRPC_SHADOW_DH_set0_key +#define DH_set0_pqg GRPC_SHADOW_DH_set0_pqg +#define DH_set_ex_data GRPC_SHADOW_DH_set_ex_data +#define DH_size GRPC_SHADOW_DH_size +#define DH_up_ref GRPC_SHADOW_DH_up_ref +#define DHparams_dup GRPC_SHADOW_DHparams_dup +#define BN_get_rfc3526_prime_1536 GRPC_SHADOW_BN_get_rfc3526_prime_1536 +#define DH_check GRPC_SHADOW_DH_check +#define DH_check_pub_key GRPC_SHADOW_DH_check_pub_key +#define DH_marshal_parameters GRPC_SHADOW_DH_marshal_parameters +#define DH_parse_parameters GRPC_SHADOW_DH_parse_parameters +#define d2i_DHparams GRPC_SHADOW_d2i_DHparams +#define i2d_DHparams GRPC_SHADOW_i2d_DHparams +#define DSA_SIG_free GRPC_SHADOW_DSA_SIG_free +#define DSA_SIG_new GRPC_SHADOW_DSA_SIG_new +#define DSA_check_signature GRPC_SHADOW_DSA_check_signature +#define DSA_do_check_signature GRPC_SHADOW_DSA_do_check_signature +#define DSA_do_sign GRPC_SHADOW_DSA_do_sign +#define DSA_do_verify GRPC_SHADOW_DSA_do_verify +#define DSA_dup_DH GRPC_SHADOW_DSA_dup_DH +#define DSA_free GRPC_SHADOW_DSA_free +#define DSA_generate_key GRPC_SHADOW_DSA_generate_key +#define DSA_generate_parameters_ex GRPC_SHADOW_DSA_generate_parameters_ex +#define DSA_get0_key GRPC_SHADOW_DSA_get0_key +#define DSA_get0_pqg GRPC_SHADOW_DSA_get0_pqg +#define DSA_get_ex_data GRPC_SHADOW_DSA_get_ex_data +#define DSA_get_ex_new_index GRPC_SHADOW_DSA_get_ex_new_index +#define DSA_new GRPC_SHADOW_DSA_new +#define DSA_set0_key GRPC_SHADOW_DSA_set0_key +#define DSA_set0_pqg GRPC_SHADOW_DSA_set0_pqg +#define DSA_set_ex_data GRPC_SHADOW_DSA_set_ex_data +#define DSA_sign GRPC_SHADOW_DSA_sign +#define DSA_size GRPC_SHADOW_DSA_size +#define DSA_up_ref GRPC_SHADOW_DSA_up_ref +#define DSA_verify GRPC_SHADOW_DSA_verify +#define DSAparams_dup GRPC_SHADOW_DSAparams_dup +#define DSA_SIG_marshal GRPC_SHADOW_DSA_SIG_marshal +#define DSA_SIG_parse GRPC_SHADOW_DSA_SIG_parse +#define DSA_marshal_parameters GRPC_SHADOW_DSA_marshal_parameters +#define DSA_marshal_private_key GRPC_SHADOW_DSA_marshal_private_key +#define DSA_marshal_public_key GRPC_SHADOW_DSA_marshal_public_key +#define DSA_parse_parameters GRPC_SHADOW_DSA_parse_parameters +#define DSA_parse_private_key GRPC_SHADOW_DSA_parse_private_key +#define DSA_parse_public_key GRPC_SHADOW_DSA_parse_public_key +#define d2i_DSAPrivateKey GRPC_SHADOW_d2i_DSAPrivateKey +#define d2i_DSAPublicKey GRPC_SHADOW_d2i_DSAPublicKey +#define d2i_DSA_SIG GRPC_SHADOW_d2i_DSA_SIG +#define d2i_DSAparams GRPC_SHADOW_d2i_DSAparams +#define i2d_DSAPrivateKey GRPC_SHADOW_i2d_DSAPrivateKey +#define i2d_DSAPublicKey GRPC_SHADOW_i2d_DSAPublicKey +#define i2d_DSA_SIG GRPC_SHADOW_i2d_DSA_SIG +#define i2d_DSAparams GRPC_SHADOW_i2d_DSAparams +#define RSAPrivateKey_dup GRPC_SHADOW_RSAPrivateKey_dup +#define RSAPublicKey_dup GRPC_SHADOW_RSAPublicKey_dup +#define RSA_marshal_private_key GRPC_SHADOW_RSA_marshal_private_key +#define RSA_marshal_public_key GRPC_SHADOW_RSA_marshal_public_key +#define RSA_parse_private_key GRPC_SHADOW_RSA_parse_private_key +#define RSA_parse_public_key GRPC_SHADOW_RSA_parse_public_key +#define RSA_private_key_from_bytes GRPC_SHADOW_RSA_private_key_from_bytes +#define RSA_private_key_to_bytes GRPC_SHADOW_RSA_private_key_to_bytes +#define RSA_public_key_from_bytes GRPC_SHADOW_RSA_public_key_from_bytes +#define RSA_public_key_to_bytes GRPC_SHADOW_RSA_public_key_to_bytes +#define d2i_RSAPrivateKey GRPC_SHADOW_d2i_RSAPrivateKey +#define d2i_RSAPublicKey GRPC_SHADOW_d2i_RSAPublicKey +#define i2d_RSAPrivateKey GRPC_SHADOW_i2d_RSAPrivateKey +#define i2d_RSAPublicKey GRPC_SHADOW_i2d_RSAPublicKey +#define EC_KEY_marshal_curve_name GRPC_SHADOW_EC_KEY_marshal_curve_name +#define EC_KEY_marshal_private_key GRPC_SHADOW_EC_KEY_marshal_private_key +#define EC_KEY_parse_curve_name GRPC_SHADOW_EC_KEY_parse_curve_name +#define EC_KEY_parse_parameters GRPC_SHADOW_EC_KEY_parse_parameters +#define EC_KEY_parse_private_key GRPC_SHADOW_EC_KEY_parse_private_key +#define EC_POINT_point2cbb GRPC_SHADOW_EC_POINT_point2cbb +#define d2i_ECParameters GRPC_SHADOW_d2i_ECParameters +#define d2i_ECPrivateKey GRPC_SHADOW_d2i_ECPrivateKey +#define i2d_ECParameters GRPC_SHADOW_i2d_ECParameters +#define i2d_ECPrivateKey GRPC_SHADOW_i2d_ECPrivateKey +#define i2o_ECPublicKey GRPC_SHADOW_i2o_ECPublicKey +#define o2i_ECPublicKey GRPC_SHADOW_o2i_ECPublicKey +#define ECDH_compute_key GRPC_SHADOW_ECDH_compute_key +#define ECDSA_SIG_from_bytes GRPC_SHADOW_ECDSA_SIG_from_bytes +#define ECDSA_SIG_marshal GRPC_SHADOW_ECDSA_SIG_marshal +#define ECDSA_SIG_max_len GRPC_SHADOW_ECDSA_SIG_max_len +#define ECDSA_SIG_parse GRPC_SHADOW_ECDSA_SIG_parse +#define ECDSA_SIG_to_bytes GRPC_SHADOW_ECDSA_SIG_to_bytes +#define ECDSA_sign GRPC_SHADOW_ECDSA_sign +#define ECDSA_size GRPC_SHADOW_ECDSA_size +#define ECDSA_verify GRPC_SHADOW_ECDSA_verify +#define d2i_ECDSA_SIG GRPC_SHADOW_d2i_ECDSA_SIG +#define i2d_ECDSA_SIG GRPC_SHADOW_i2d_ECDSA_SIG +#define AES_CMAC GRPC_SHADOW_AES_CMAC +#define CMAC_CTX_free GRPC_SHADOW_CMAC_CTX_free +#define CMAC_CTX_new GRPC_SHADOW_CMAC_CTX_new +#define CMAC_Final GRPC_SHADOW_CMAC_Final +#define CMAC_Init GRPC_SHADOW_CMAC_Init +#define CMAC_Reset GRPC_SHADOW_CMAC_Reset +#define CMAC_Update GRPC_SHADOW_CMAC_Update +#define EVP_DigestSign GRPC_SHADOW_EVP_DigestSign +#define EVP_DigestSignFinal GRPC_SHADOW_EVP_DigestSignFinal +#define EVP_DigestSignInit GRPC_SHADOW_EVP_DigestSignInit +#define EVP_DigestSignUpdate GRPC_SHADOW_EVP_DigestSignUpdate +#define EVP_DigestVerify GRPC_SHADOW_EVP_DigestVerify +#define EVP_DigestVerifyFinal GRPC_SHADOW_EVP_DigestVerifyFinal +#define EVP_DigestVerifyInit GRPC_SHADOW_EVP_DigestVerifyInit +#define EVP_DigestVerifyUpdate GRPC_SHADOW_EVP_DigestVerifyUpdate +#define EVP_PKEY_CTX_get_signature_md GRPC_SHADOW_EVP_PKEY_CTX_get_signature_md +#define EVP_PKEY_CTX_set_signature_md GRPC_SHADOW_EVP_PKEY_CTX_set_signature_md +#define EVP_PKEY_assign GRPC_SHADOW_EVP_PKEY_assign +#define EVP_PKEY_assign_DSA GRPC_SHADOW_EVP_PKEY_assign_DSA +#define EVP_PKEY_assign_EC_KEY GRPC_SHADOW_EVP_PKEY_assign_EC_KEY +#define EVP_PKEY_assign_RSA GRPC_SHADOW_EVP_PKEY_assign_RSA +#define EVP_PKEY_bits GRPC_SHADOW_EVP_PKEY_bits +#define EVP_PKEY_cmp GRPC_SHADOW_EVP_PKEY_cmp +#define EVP_PKEY_cmp_parameters GRPC_SHADOW_EVP_PKEY_cmp_parameters +#define EVP_PKEY_copy_parameters GRPC_SHADOW_EVP_PKEY_copy_parameters +#define EVP_PKEY_free GRPC_SHADOW_EVP_PKEY_free +#define EVP_PKEY_get0_DH GRPC_SHADOW_EVP_PKEY_get0_DH +#define EVP_PKEY_get0_DSA GRPC_SHADOW_EVP_PKEY_get0_DSA +#define EVP_PKEY_get0_EC_KEY GRPC_SHADOW_EVP_PKEY_get0_EC_KEY +#define EVP_PKEY_get0_RSA GRPC_SHADOW_EVP_PKEY_get0_RSA +#define EVP_PKEY_get1_DSA GRPC_SHADOW_EVP_PKEY_get1_DSA +#define EVP_PKEY_get1_EC_KEY GRPC_SHADOW_EVP_PKEY_get1_EC_KEY +#define EVP_PKEY_get1_RSA GRPC_SHADOW_EVP_PKEY_get1_RSA +#define EVP_PKEY_id GRPC_SHADOW_EVP_PKEY_id +#define EVP_PKEY_is_opaque GRPC_SHADOW_EVP_PKEY_is_opaque +#define EVP_PKEY_missing_parameters GRPC_SHADOW_EVP_PKEY_missing_parameters +#define EVP_PKEY_new GRPC_SHADOW_EVP_PKEY_new +#define EVP_PKEY_set1_DSA GRPC_SHADOW_EVP_PKEY_set1_DSA +#define EVP_PKEY_set1_EC_KEY GRPC_SHADOW_EVP_PKEY_set1_EC_KEY +#define EVP_PKEY_set1_RSA GRPC_SHADOW_EVP_PKEY_set1_RSA +#define EVP_PKEY_set_type GRPC_SHADOW_EVP_PKEY_set_type +#define EVP_PKEY_size GRPC_SHADOW_EVP_PKEY_size +#define EVP_PKEY_type GRPC_SHADOW_EVP_PKEY_type +#define EVP_PKEY_up_ref GRPC_SHADOW_EVP_PKEY_up_ref +#define EVP_cleanup GRPC_SHADOW_EVP_cleanup +#define OPENSSL_add_all_algorithms_conf GRPC_SHADOW_OPENSSL_add_all_algorithms_conf +#define OpenSSL_add_all_algorithms GRPC_SHADOW_OpenSSL_add_all_algorithms +#define OpenSSL_add_all_ciphers GRPC_SHADOW_OpenSSL_add_all_ciphers +#define OpenSSL_add_all_digests GRPC_SHADOW_OpenSSL_add_all_digests +#define EVP_marshal_private_key GRPC_SHADOW_EVP_marshal_private_key +#define EVP_marshal_public_key GRPC_SHADOW_EVP_marshal_public_key +#define EVP_parse_private_key GRPC_SHADOW_EVP_parse_private_key +#define EVP_parse_public_key GRPC_SHADOW_EVP_parse_public_key +#define d2i_AutoPrivateKey GRPC_SHADOW_d2i_AutoPrivateKey +#define d2i_PrivateKey GRPC_SHADOW_d2i_PrivateKey +#define i2d_PublicKey GRPC_SHADOW_i2d_PublicKey +#define EVP_PKEY_CTX_ctrl GRPC_SHADOW_EVP_PKEY_CTX_ctrl +#define EVP_PKEY_CTX_dup GRPC_SHADOW_EVP_PKEY_CTX_dup +#define EVP_PKEY_CTX_free GRPC_SHADOW_EVP_PKEY_CTX_free +#define EVP_PKEY_CTX_get0_pkey GRPC_SHADOW_EVP_PKEY_CTX_get0_pkey +#define EVP_PKEY_CTX_new GRPC_SHADOW_EVP_PKEY_CTX_new +#define EVP_PKEY_CTX_new_id GRPC_SHADOW_EVP_PKEY_CTX_new_id +#define EVP_PKEY_decrypt GRPC_SHADOW_EVP_PKEY_decrypt +#define EVP_PKEY_decrypt_init GRPC_SHADOW_EVP_PKEY_decrypt_init +#define EVP_PKEY_derive GRPC_SHADOW_EVP_PKEY_derive +#define EVP_PKEY_derive_init GRPC_SHADOW_EVP_PKEY_derive_init +#define EVP_PKEY_derive_set_peer GRPC_SHADOW_EVP_PKEY_derive_set_peer +#define EVP_PKEY_encrypt GRPC_SHADOW_EVP_PKEY_encrypt +#define EVP_PKEY_encrypt_init GRPC_SHADOW_EVP_PKEY_encrypt_init +#define EVP_PKEY_keygen GRPC_SHADOW_EVP_PKEY_keygen +#define EVP_PKEY_keygen_init GRPC_SHADOW_EVP_PKEY_keygen_init +#define EVP_PKEY_sign GRPC_SHADOW_EVP_PKEY_sign +#define EVP_PKEY_sign_init GRPC_SHADOW_EVP_PKEY_sign_init +#define EVP_PKEY_verify GRPC_SHADOW_EVP_PKEY_verify +#define EVP_PKEY_verify_init GRPC_SHADOW_EVP_PKEY_verify_init +#define EVP_PKEY_verify_recover GRPC_SHADOW_EVP_PKEY_verify_recover +#define EVP_PKEY_verify_recover_init GRPC_SHADOW_EVP_PKEY_verify_recover_init +#define dsa_asn1_meth GRPC_SHADOW_dsa_asn1_meth +#define ec_pkey_meth GRPC_SHADOW_ec_pkey_meth +#define ec_asn1_meth GRPC_SHADOW_ec_asn1_meth +#define ed25519_pkey_meth GRPC_SHADOW_ed25519_pkey_meth +#define EVP_PKEY_new_ed25519_private GRPC_SHADOW_EVP_PKEY_new_ed25519_private +#define EVP_PKEY_new_ed25519_public GRPC_SHADOW_EVP_PKEY_new_ed25519_public +#define ed25519_asn1_meth GRPC_SHADOW_ed25519_asn1_meth +#define EVP_PKEY_CTX_get0_rsa_oaep_label GRPC_SHADOW_EVP_PKEY_CTX_get0_rsa_oaep_label +#define EVP_PKEY_CTX_get_rsa_mgf1_md GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_mgf1_md +#define EVP_PKEY_CTX_get_rsa_oaep_md GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_oaep_md +#define EVP_PKEY_CTX_get_rsa_padding GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_padding +#define EVP_PKEY_CTX_get_rsa_pss_saltlen GRPC_SHADOW_EVP_PKEY_CTX_get_rsa_pss_saltlen +#define EVP_PKEY_CTX_set0_rsa_oaep_label GRPC_SHADOW_EVP_PKEY_CTX_set0_rsa_oaep_label +#define EVP_PKEY_CTX_set_rsa_keygen_bits GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_keygen_bits +#define EVP_PKEY_CTX_set_rsa_keygen_pubexp GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_keygen_pubexp +#define EVP_PKEY_CTX_set_rsa_mgf1_md GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_mgf1_md +#define EVP_PKEY_CTX_set_rsa_oaep_md GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_oaep_md +#define EVP_PKEY_CTX_set_rsa_padding GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_padding +#define EVP_PKEY_CTX_set_rsa_pss_saltlen GRPC_SHADOW_EVP_PKEY_CTX_set_rsa_pss_saltlen +#define rsa_pkey_meth GRPC_SHADOW_rsa_pkey_meth +#define rsa_asn1_meth GRPC_SHADOW_rsa_asn1_meth +#define PKCS5_PBKDF2_HMAC GRPC_SHADOW_PKCS5_PBKDF2_HMAC +#define PKCS5_PBKDF2_HMAC_SHA1 GRPC_SHADOW_PKCS5_PBKDF2_HMAC_SHA1 +#define EVP_PKEY_print_params GRPC_SHADOW_EVP_PKEY_print_params +#define EVP_PKEY_print_private GRPC_SHADOW_EVP_PKEY_print_private +#define EVP_PKEY_print_public GRPC_SHADOW_EVP_PKEY_print_public +#define EVP_PBE_scrypt GRPC_SHADOW_EVP_PBE_scrypt +#define EVP_SignFinal GRPC_SHADOW_EVP_SignFinal +#define EVP_SignInit GRPC_SHADOW_EVP_SignInit +#define EVP_SignInit_ex GRPC_SHADOW_EVP_SignInit_ex +#define EVP_SignUpdate GRPC_SHADOW_EVP_SignUpdate +#define EVP_VerifyFinal GRPC_SHADOW_EVP_VerifyFinal +#define EVP_VerifyInit GRPC_SHADOW_EVP_VerifyInit +#define EVP_VerifyInit_ex GRPC_SHADOW_EVP_VerifyInit_ex +#define EVP_VerifyUpdate GRPC_SHADOW_EVP_VerifyUpdate +#define HKDF GRPC_SHADOW_HKDF +#define HKDF_expand GRPC_SHADOW_HKDF_expand +#define HKDF_extract GRPC_SHADOW_HKDF_extract +#define PEM_read_DSAPrivateKey GRPC_SHADOW_PEM_read_DSAPrivateKey +#define PEM_read_DSA_PUBKEY GRPC_SHADOW_PEM_read_DSA_PUBKEY +#define PEM_read_DSAparams GRPC_SHADOW_PEM_read_DSAparams +#define PEM_read_ECPrivateKey GRPC_SHADOW_PEM_read_ECPrivateKey +#define PEM_read_EC_PUBKEY GRPC_SHADOW_PEM_read_EC_PUBKEY +#define PEM_read_PUBKEY GRPC_SHADOW_PEM_read_PUBKEY +#define PEM_read_RSAPrivateKey GRPC_SHADOW_PEM_read_RSAPrivateKey +#define PEM_read_RSAPublicKey GRPC_SHADOW_PEM_read_RSAPublicKey +#define PEM_read_RSA_PUBKEY GRPC_SHADOW_PEM_read_RSA_PUBKEY +#define PEM_read_X509_CRL GRPC_SHADOW_PEM_read_X509_CRL +#define PEM_read_X509_REQ GRPC_SHADOW_PEM_read_X509_REQ +#define PEM_read_bio_DSAPrivateKey GRPC_SHADOW_PEM_read_bio_DSAPrivateKey +#define PEM_read_bio_DSA_PUBKEY GRPC_SHADOW_PEM_read_bio_DSA_PUBKEY +#define PEM_read_bio_DSAparams GRPC_SHADOW_PEM_read_bio_DSAparams +#define PEM_read_bio_ECPrivateKey GRPC_SHADOW_PEM_read_bio_ECPrivateKey +#define PEM_read_bio_EC_PUBKEY GRPC_SHADOW_PEM_read_bio_EC_PUBKEY +#define PEM_read_bio_PUBKEY GRPC_SHADOW_PEM_read_bio_PUBKEY +#define PEM_read_bio_RSAPrivateKey GRPC_SHADOW_PEM_read_bio_RSAPrivateKey +#define PEM_read_bio_RSAPublicKey GRPC_SHADOW_PEM_read_bio_RSAPublicKey +#define PEM_read_bio_RSA_PUBKEY GRPC_SHADOW_PEM_read_bio_RSA_PUBKEY +#define PEM_read_bio_X509_CRL GRPC_SHADOW_PEM_read_bio_X509_CRL +#define PEM_read_bio_X509_REQ GRPC_SHADOW_PEM_read_bio_X509_REQ +#define PEM_write_DHparams GRPC_SHADOW_PEM_write_DHparams +#define PEM_write_DSAPrivateKey GRPC_SHADOW_PEM_write_DSAPrivateKey +#define PEM_write_DSA_PUBKEY GRPC_SHADOW_PEM_write_DSA_PUBKEY +#define PEM_write_DSAparams GRPC_SHADOW_PEM_write_DSAparams +#define PEM_write_ECPrivateKey GRPC_SHADOW_PEM_write_ECPrivateKey +#define PEM_write_EC_PUBKEY GRPC_SHADOW_PEM_write_EC_PUBKEY +#define PEM_write_PUBKEY GRPC_SHADOW_PEM_write_PUBKEY +#define PEM_write_RSAPrivateKey GRPC_SHADOW_PEM_write_RSAPrivateKey +#define PEM_write_RSAPublicKey GRPC_SHADOW_PEM_write_RSAPublicKey +#define PEM_write_RSA_PUBKEY GRPC_SHADOW_PEM_write_RSA_PUBKEY +#define PEM_write_X509_CRL GRPC_SHADOW_PEM_write_X509_CRL +#define PEM_write_X509_REQ GRPC_SHADOW_PEM_write_X509_REQ +#define PEM_write_X509_REQ_NEW GRPC_SHADOW_PEM_write_X509_REQ_NEW +#define PEM_write_bio_DHparams GRPC_SHADOW_PEM_write_bio_DHparams +#define PEM_write_bio_DSAPrivateKey GRPC_SHADOW_PEM_write_bio_DSAPrivateKey +#define PEM_write_bio_DSA_PUBKEY GRPC_SHADOW_PEM_write_bio_DSA_PUBKEY +#define PEM_write_bio_DSAparams GRPC_SHADOW_PEM_write_bio_DSAparams +#define PEM_write_bio_ECPrivateKey GRPC_SHADOW_PEM_write_bio_ECPrivateKey +#define PEM_write_bio_EC_PUBKEY GRPC_SHADOW_PEM_write_bio_EC_PUBKEY +#define PEM_write_bio_PUBKEY GRPC_SHADOW_PEM_write_bio_PUBKEY +#define PEM_write_bio_RSAPrivateKey GRPC_SHADOW_PEM_write_bio_RSAPrivateKey +#define PEM_write_bio_RSAPublicKey GRPC_SHADOW_PEM_write_bio_RSAPublicKey +#define PEM_write_bio_RSA_PUBKEY GRPC_SHADOW_PEM_write_bio_RSA_PUBKEY +#define PEM_write_bio_X509_CRL GRPC_SHADOW_PEM_write_bio_X509_CRL +#define PEM_write_bio_X509_REQ GRPC_SHADOW_PEM_write_bio_X509_REQ +#define PEM_write_bio_X509_REQ_NEW GRPC_SHADOW_PEM_write_bio_X509_REQ_NEW +#define PEM_X509_INFO_read GRPC_SHADOW_PEM_X509_INFO_read +#define PEM_X509_INFO_read_bio GRPC_SHADOW_PEM_X509_INFO_read_bio +#define PEM_X509_INFO_write_bio GRPC_SHADOW_PEM_X509_INFO_write_bio +#define PEM_ASN1_read GRPC_SHADOW_PEM_ASN1_read +#define PEM_ASN1_write GRPC_SHADOW_PEM_ASN1_write +#define PEM_ASN1_write_bio GRPC_SHADOW_PEM_ASN1_write_bio +#define PEM_bytes_read_bio GRPC_SHADOW_PEM_bytes_read_bio +#define PEM_def_callback GRPC_SHADOW_PEM_def_callback +#define PEM_dek_info GRPC_SHADOW_PEM_dek_info +#define PEM_do_header GRPC_SHADOW_PEM_do_header +#define PEM_get_EVP_CIPHER_INFO GRPC_SHADOW_PEM_get_EVP_CIPHER_INFO +#define PEM_proc_type GRPC_SHADOW_PEM_proc_type +#define PEM_read GRPC_SHADOW_PEM_read +#define PEM_read_bio GRPC_SHADOW_PEM_read_bio +#define PEM_write GRPC_SHADOW_PEM_write +#define PEM_write_bio GRPC_SHADOW_PEM_write_bio +#define PEM_ASN1_read_bio GRPC_SHADOW_PEM_ASN1_read_bio +#define PEM_read_PKCS8 GRPC_SHADOW_PEM_read_PKCS8 +#define PEM_read_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_read_PKCS8_PRIV_KEY_INFO +#define PEM_read_bio_PKCS8 GRPC_SHADOW_PEM_read_bio_PKCS8 +#define PEM_read_bio_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_read_bio_PKCS8_PRIV_KEY_INFO +#define PEM_write_PKCS8 GRPC_SHADOW_PEM_write_PKCS8 +#define PEM_write_PKCS8PrivateKey GRPC_SHADOW_PEM_write_PKCS8PrivateKey +#define PEM_write_PKCS8PrivateKey_nid GRPC_SHADOW_PEM_write_PKCS8PrivateKey_nid +#define PEM_write_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_write_PKCS8_PRIV_KEY_INFO +#define PEM_write_bio_PKCS8 GRPC_SHADOW_PEM_write_bio_PKCS8 +#define PEM_write_bio_PKCS8PrivateKey GRPC_SHADOW_PEM_write_bio_PKCS8PrivateKey +#define PEM_write_bio_PKCS8PrivateKey_nid GRPC_SHADOW_PEM_write_bio_PKCS8PrivateKey_nid +#define PEM_write_bio_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_PEM_write_bio_PKCS8_PRIV_KEY_INFO +#define d2i_PKCS8PrivateKey_bio GRPC_SHADOW_d2i_PKCS8PrivateKey_bio +#define d2i_PKCS8PrivateKey_fp GRPC_SHADOW_d2i_PKCS8PrivateKey_fp +#define i2d_PKCS8PrivateKey_bio GRPC_SHADOW_i2d_PKCS8PrivateKey_bio +#define i2d_PKCS8PrivateKey_fp GRPC_SHADOW_i2d_PKCS8PrivateKey_fp +#define i2d_PKCS8PrivateKey_nid_bio GRPC_SHADOW_i2d_PKCS8PrivateKey_nid_bio +#define i2d_PKCS8PrivateKey_nid_fp GRPC_SHADOW_i2d_PKCS8PrivateKey_nid_fp +#define PEM_read_DHparams GRPC_SHADOW_PEM_read_DHparams +#define PEM_read_PrivateKey GRPC_SHADOW_PEM_read_PrivateKey +#define PEM_read_bio_DHparams GRPC_SHADOW_PEM_read_bio_DHparams +#define PEM_read_bio_PrivateKey GRPC_SHADOW_PEM_read_bio_PrivateKey +#define PEM_write_PrivateKey GRPC_SHADOW_PEM_write_PrivateKey +#define PEM_write_bio_PrivateKey GRPC_SHADOW_PEM_write_bio_PrivateKey +#define PEM_read_X509 GRPC_SHADOW_PEM_read_X509 +#define PEM_read_bio_X509 GRPC_SHADOW_PEM_read_bio_X509 +#define PEM_write_X509 GRPC_SHADOW_PEM_write_X509 +#define PEM_write_bio_X509 GRPC_SHADOW_PEM_write_bio_X509 +#define PEM_read_X509_AUX GRPC_SHADOW_PEM_read_X509_AUX +#define PEM_read_bio_X509_AUX GRPC_SHADOW_PEM_read_bio_X509_AUX +#define PEM_write_X509_AUX GRPC_SHADOW_PEM_write_X509_AUX +#define PEM_write_bio_X509_AUX GRPC_SHADOW_PEM_write_bio_X509_AUX +#define ASN1_digest GRPC_SHADOW_ASN1_digest +#define ASN1_item_digest GRPC_SHADOW_ASN1_item_digest +#define ASN1_item_sign GRPC_SHADOW_ASN1_item_sign +#define ASN1_item_sign_ctx GRPC_SHADOW_ASN1_item_sign_ctx +#define ASN1_STRING_print_ex GRPC_SHADOW_ASN1_STRING_print_ex +#define ASN1_STRING_print_ex_fp GRPC_SHADOW_ASN1_STRING_print_ex_fp +#define ASN1_STRING_to_UTF8 GRPC_SHADOW_ASN1_STRING_to_UTF8 +#define X509_NAME_print_ex GRPC_SHADOW_X509_NAME_print_ex +#define X509_NAME_print_ex_fp GRPC_SHADOW_X509_NAME_print_ex_fp +#define ASN1_item_verify GRPC_SHADOW_ASN1_item_verify +#define x509_digest_sign_algorithm GRPC_SHADOW_x509_digest_sign_algorithm +#define x509_digest_verify_init GRPC_SHADOW_x509_digest_verify_init +#define ASN1_generate_nconf GRPC_SHADOW_ASN1_generate_nconf +#define ASN1_generate_v3 GRPC_SHADOW_ASN1_generate_v3 +#define X509_LOOKUP_hash_dir GRPC_SHADOW_X509_LOOKUP_hash_dir +#define X509_LOOKUP_file GRPC_SHADOW_X509_LOOKUP_file +#define X509_load_cert_crl_file GRPC_SHADOW_X509_load_cert_crl_file +#define X509_load_cert_file GRPC_SHADOW_X509_load_cert_file +#define X509_load_crl_file GRPC_SHADOW_X509_load_crl_file +#define i2d_PrivateKey GRPC_SHADOW_i2d_PrivateKey +#define RSA_PSS_PARAMS_free GRPC_SHADOW_RSA_PSS_PARAMS_free +#define RSA_PSS_PARAMS_it GRPC_SHADOW_RSA_PSS_PARAMS_it +#define RSA_PSS_PARAMS_new GRPC_SHADOW_RSA_PSS_PARAMS_new +#define d2i_RSA_PSS_PARAMS GRPC_SHADOW_d2i_RSA_PSS_PARAMS +#define i2d_RSA_PSS_PARAMS GRPC_SHADOW_i2d_RSA_PSS_PARAMS +#define x509_print_rsa_pss_params GRPC_SHADOW_x509_print_rsa_pss_params +#define x509_rsa_ctx_to_pss GRPC_SHADOW_x509_rsa_ctx_to_pss +#define x509_rsa_pss_to_ctx GRPC_SHADOW_x509_rsa_pss_to_ctx +#define X509_CRL_print GRPC_SHADOW_X509_CRL_print +#define X509_CRL_print_fp GRPC_SHADOW_X509_CRL_print_fp +#define X509_REQ_print GRPC_SHADOW_X509_REQ_print +#define X509_REQ_print_ex GRPC_SHADOW_X509_REQ_print_ex +#define X509_REQ_print_fp GRPC_SHADOW_X509_REQ_print_fp +#define ASN1_GENERALIZEDTIME_print GRPC_SHADOW_ASN1_GENERALIZEDTIME_print +#define ASN1_STRING_print GRPC_SHADOW_ASN1_STRING_print +#define ASN1_TIME_print GRPC_SHADOW_ASN1_TIME_print +#define ASN1_UTCTIME_print GRPC_SHADOW_ASN1_UTCTIME_print +#define X509_NAME_print GRPC_SHADOW_X509_NAME_print +#define X509_ocspid_print GRPC_SHADOW_X509_ocspid_print +#define X509_print GRPC_SHADOW_X509_print +#define X509_print_ex GRPC_SHADOW_X509_print_ex +#define X509_print_ex_fp GRPC_SHADOW_X509_print_ex_fp +#define X509_print_fp GRPC_SHADOW_X509_print_fp +#define X509_signature_print GRPC_SHADOW_X509_signature_print +#define X509_CERT_AUX_print GRPC_SHADOW_X509_CERT_AUX_print +#define PKCS8_pkey_get0 GRPC_SHADOW_PKCS8_pkey_get0 +#define PKCS8_pkey_set0 GRPC_SHADOW_PKCS8_pkey_set0 +#define X509_signature_dump GRPC_SHADOW_X509_signature_dump +#define X509_ATTRIBUTE_count GRPC_SHADOW_X509_ATTRIBUTE_count +#define X509_ATTRIBUTE_create_by_NID GRPC_SHADOW_X509_ATTRIBUTE_create_by_NID +#define X509_ATTRIBUTE_create_by_OBJ GRPC_SHADOW_X509_ATTRIBUTE_create_by_OBJ +#define X509_ATTRIBUTE_create_by_txt GRPC_SHADOW_X509_ATTRIBUTE_create_by_txt +#define X509_ATTRIBUTE_get0_data GRPC_SHADOW_X509_ATTRIBUTE_get0_data +#define X509_ATTRIBUTE_get0_object GRPC_SHADOW_X509_ATTRIBUTE_get0_object +#define X509_ATTRIBUTE_get0_type GRPC_SHADOW_X509_ATTRIBUTE_get0_type +#define X509_ATTRIBUTE_set1_data GRPC_SHADOW_X509_ATTRIBUTE_set1_data +#define X509_ATTRIBUTE_set1_object GRPC_SHADOW_X509_ATTRIBUTE_set1_object +#define X509at_add1_attr GRPC_SHADOW_X509at_add1_attr +#define X509at_add1_attr_by_NID GRPC_SHADOW_X509at_add1_attr_by_NID +#define X509at_add1_attr_by_OBJ GRPC_SHADOW_X509at_add1_attr_by_OBJ +#define X509at_add1_attr_by_txt GRPC_SHADOW_X509at_add1_attr_by_txt +#define X509at_delete_attr GRPC_SHADOW_X509at_delete_attr +#define X509at_get0_data_by_OBJ GRPC_SHADOW_X509at_get0_data_by_OBJ +#define X509at_get_attr GRPC_SHADOW_X509at_get_attr +#define X509at_get_attr_by_NID GRPC_SHADOW_X509at_get_attr_by_NID +#define X509at_get_attr_by_OBJ GRPC_SHADOW_X509at_get_attr_by_OBJ +#define X509at_get_attr_count GRPC_SHADOW_X509at_get_attr_count +#define X509_CRL_check_suiteb GRPC_SHADOW_X509_CRL_check_suiteb +#define X509_CRL_cmp GRPC_SHADOW_X509_CRL_cmp +#define X509_CRL_match GRPC_SHADOW_X509_CRL_match +#define X509_NAME_cmp GRPC_SHADOW_X509_NAME_cmp +#define X509_NAME_hash GRPC_SHADOW_X509_NAME_hash +#define X509_NAME_hash_old GRPC_SHADOW_X509_NAME_hash_old +#define X509_chain_check_suiteb GRPC_SHADOW_X509_chain_check_suiteb +#define X509_chain_up_ref GRPC_SHADOW_X509_chain_up_ref +#define X509_check_private_key GRPC_SHADOW_X509_check_private_key +#define X509_cmp GRPC_SHADOW_X509_cmp +#define X509_find_by_issuer_and_serial GRPC_SHADOW_X509_find_by_issuer_and_serial +#define X509_find_by_subject GRPC_SHADOW_X509_find_by_subject +#define X509_get0_pubkey_bitstr GRPC_SHADOW_X509_get0_pubkey_bitstr +#define X509_get_issuer_name GRPC_SHADOW_X509_get_issuer_name +#define X509_get_pubkey GRPC_SHADOW_X509_get_pubkey +#define X509_get_serialNumber GRPC_SHADOW_X509_get_serialNumber +#define X509_get_subject_name GRPC_SHADOW_X509_get_subject_name +#define X509_issuer_and_serial_cmp GRPC_SHADOW_X509_issuer_and_serial_cmp +#define X509_issuer_and_serial_hash GRPC_SHADOW_X509_issuer_and_serial_hash +#define X509_issuer_name_cmp GRPC_SHADOW_X509_issuer_name_cmp +#define X509_issuer_name_hash GRPC_SHADOW_X509_issuer_name_hash +#define X509_issuer_name_hash_old GRPC_SHADOW_X509_issuer_name_hash_old +#define X509_subject_name_cmp GRPC_SHADOW_X509_subject_name_cmp +#define X509_subject_name_hash GRPC_SHADOW_X509_subject_name_hash +#define X509_subject_name_hash_old GRPC_SHADOW_X509_subject_name_hash_old +#define X509_STORE_load_locations GRPC_SHADOW_X509_STORE_load_locations +#define X509_STORE_set_default_paths GRPC_SHADOW_X509_STORE_set_default_paths +#define X509_get_default_cert_area GRPC_SHADOW_X509_get_default_cert_area +#define X509_get_default_cert_dir GRPC_SHADOW_X509_get_default_cert_dir +#define X509_get_default_cert_dir_env GRPC_SHADOW_X509_get_default_cert_dir_env +#define X509_get_default_cert_file GRPC_SHADOW_X509_get_default_cert_file +#define X509_get_default_cert_file_env GRPC_SHADOW_X509_get_default_cert_file_env +#define X509_get_default_private_dir GRPC_SHADOW_X509_get_default_private_dir +#define X509_CRL_add1_ext_i2d GRPC_SHADOW_X509_CRL_add1_ext_i2d +#define X509_CRL_add_ext GRPC_SHADOW_X509_CRL_add_ext +#define X509_CRL_delete_ext GRPC_SHADOW_X509_CRL_delete_ext +#define X509_CRL_get_ext GRPC_SHADOW_X509_CRL_get_ext +#define X509_CRL_get_ext_by_NID GRPC_SHADOW_X509_CRL_get_ext_by_NID +#define X509_CRL_get_ext_by_OBJ GRPC_SHADOW_X509_CRL_get_ext_by_OBJ +#define X509_CRL_get_ext_by_critical GRPC_SHADOW_X509_CRL_get_ext_by_critical +#define X509_CRL_get_ext_count GRPC_SHADOW_X509_CRL_get_ext_count +#define X509_CRL_get_ext_d2i GRPC_SHADOW_X509_CRL_get_ext_d2i +#define X509_REVOKED_add1_ext_i2d GRPC_SHADOW_X509_REVOKED_add1_ext_i2d +#define X509_REVOKED_add_ext GRPC_SHADOW_X509_REVOKED_add_ext +#define X509_REVOKED_delete_ext GRPC_SHADOW_X509_REVOKED_delete_ext +#define X509_REVOKED_get_ext GRPC_SHADOW_X509_REVOKED_get_ext +#define X509_REVOKED_get_ext_by_NID GRPC_SHADOW_X509_REVOKED_get_ext_by_NID +#define X509_REVOKED_get_ext_by_OBJ GRPC_SHADOW_X509_REVOKED_get_ext_by_OBJ +#define X509_REVOKED_get_ext_by_critical GRPC_SHADOW_X509_REVOKED_get_ext_by_critical +#define X509_REVOKED_get_ext_count GRPC_SHADOW_X509_REVOKED_get_ext_count +#define X509_REVOKED_get_ext_d2i GRPC_SHADOW_X509_REVOKED_get_ext_d2i +#define X509_add1_ext_i2d GRPC_SHADOW_X509_add1_ext_i2d +#define X509_add_ext GRPC_SHADOW_X509_add_ext +#define X509_delete_ext GRPC_SHADOW_X509_delete_ext +#define X509_get_ext GRPC_SHADOW_X509_get_ext +#define X509_get_ext_by_NID GRPC_SHADOW_X509_get_ext_by_NID +#define X509_get_ext_by_OBJ GRPC_SHADOW_X509_get_ext_by_OBJ +#define X509_get_ext_by_critical GRPC_SHADOW_X509_get_ext_by_critical +#define X509_get_ext_count GRPC_SHADOW_X509_get_ext_count +#define X509_get_ext_d2i GRPC_SHADOW_X509_get_ext_d2i +#define X509_LOOKUP_by_alias GRPC_SHADOW_X509_LOOKUP_by_alias +#define X509_LOOKUP_by_fingerprint GRPC_SHADOW_X509_LOOKUP_by_fingerprint +#define X509_LOOKUP_by_issuer_serial GRPC_SHADOW_X509_LOOKUP_by_issuer_serial +#define X509_LOOKUP_by_subject GRPC_SHADOW_X509_LOOKUP_by_subject +#define X509_LOOKUP_ctrl GRPC_SHADOW_X509_LOOKUP_ctrl +#define X509_LOOKUP_free GRPC_SHADOW_X509_LOOKUP_free +#define X509_LOOKUP_init GRPC_SHADOW_X509_LOOKUP_init +#define X509_LOOKUP_new GRPC_SHADOW_X509_LOOKUP_new +#define X509_LOOKUP_shutdown GRPC_SHADOW_X509_LOOKUP_shutdown +#define X509_OBJECT_free_contents GRPC_SHADOW_X509_OBJECT_free_contents +#define X509_OBJECT_get0_X509 GRPC_SHADOW_X509_OBJECT_get0_X509 +#define X509_OBJECT_get_type GRPC_SHADOW_X509_OBJECT_get_type +#define X509_OBJECT_idx_by_subject GRPC_SHADOW_X509_OBJECT_idx_by_subject +#define X509_OBJECT_retrieve_by_subject GRPC_SHADOW_X509_OBJECT_retrieve_by_subject +#define X509_OBJECT_retrieve_match GRPC_SHADOW_X509_OBJECT_retrieve_match +#define X509_OBJECT_up_ref_count GRPC_SHADOW_X509_OBJECT_up_ref_count +#define X509_STORE_CTX_get0_store GRPC_SHADOW_X509_STORE_CTX_get0_store +#define X509_STORE_CTX_get1_issuer GRPC_SHADOW_X509_STORE_CTX_get1_issuer +#define X509_STORE_add_cert GRPC_SHADOW_X509_STORE_add_cert +#define X509_STORE_add_crl GRPC_SHADOW_X509_STORE_add_crl +#define X509_STORE_add_lookup GRPC_SHADOW_X509_STORE_add_lookup +#define X509_STORE_free GRPC_SHADOW_X509_STORE_free +#define X509_STORE_get0_objects GRPC_SHADOW_X509_STORE_get0_objects +#define X509_STORE_get0_param GRPC_SHADOW_X509_STORE_get0_param +#define X509_STORE_get1_certs GRPC_SHADOW_X509_STORE_get1_certs +#define X509_STORE_get1_crls GRPC_SHADOW_X509_STORE_get1_crls +#define X509_STORE_get_by_subject GRPC_SHADOW_X509_STORE_get_by_subject +#define X509_STORE_new GRPC_SHADOW_X509_STORE_new +#define X509_STORE_set0_additional_untrusted GRPC_SHADOW_X509_STORE_set0_additional_untrusted +#define X509_STORE_set1_param GRPC_SHADOW_X509_STORE_set1_param +#define X509_STORE_set_depth GRPC_SHADOW_X509_STORE_set_depth +#define X509_STORE_set_flags GRPC_SHADOW_X509_STORE_set_flags +#define X509_STORE_set_lookup_crls_cb GRPC_SHADOW_X509_STORE_set_lookup_crls_cb +#define X509_STORE_set_purpose GRPC_SHADOW_X509_STORE_set_purpose +#define X509_STORE_set_trust GRPC_SHADOW_X509_STORE_set_trust +#define X509_STORE_set_verify_cb GRPC_SHADOW_X509_STORE_set_verify_cb +#define X509_STORE_up_ref GRPC_SHADOW_X509_STORE_up_ref +#define X509_NAME_oneline GRPC_SHADOW_X509_NAME_oneline +#define X509_REQ_to_X509 GRPC_SHADOW_X509_REQ_to_X509 +#define X509_REQ_add1_attr GRPC_SHADOW_X509_REQ_add1_attr +#define X509_REQ_add1_attr_by_NID GRPC_SHADOW_X509_REQ_add1_attr_by_NID +#define X509_REQ_add1_attr_by_OBJ GRPC_SHADOW_X509_REQ_add1_attr_by_OBJ +#define X509_REQ_add1_attr_by_txt GRPC_SHADOW_X509_REQ_add1_attr_by_txt +#define X509_REQ_add_extensions GRPC_SHADOW_X509_REQ_add_extensions +#define X509_REQ_add_extensions_nid GRPC_SHADOW_X509_REQ_add_extensions_nid +#define X509_REQ_check_private_key GRPC_SHADOW_X509_REQ_check_private_key +#define X509_REQ_delete_attr GRPC_SHADOW_X509_REQ_delete_attr +#define X509_REQ_extension_nid GRPC_SHADOW_X509_REQ_extension_nid +#define X509_REQ_get_attr GRPC_SHADOW_X509_REQ_get_attr +#define X509_REQ_get_attr_by_NID GRPC_SHADOW_X509_REQ_get_attr_by_NID +#define X509_REQ_get_attr_by_OBJ GRPC_SHADOW_X509_REQ_get_attr_by_OBJ +#define X509_REQ_get_attr_count GRPC_SHADOW_X509_REQ_get_attr_count +#define X509_REQ_get_extension_nids GRPC_SHADOW_X509_REQ_get_extension_nids +#define X509_REQ_get_extensions GRPC_SHADOW_X509_REQ_get_extensions +#define X509_REQ_get_pubkey GRPC_SHADOW_X509_REQ_get_pubkey +#define X509_REQ_set_extension_nids GRPC_SHADOW_X509_REQ_set_extension_nids +#define X509_to_X509_REQ GRPC_SHADOW_X509_to_X509_REQ +#define X509_get0_extensions GRPC_SHADOW_X509_get0_extensions +#define X509_get0_notAfter GRPC_SHADOW_X509_get0_notAfter +#define X509_get0_notBefore GRPC_SHADOW_X509_get0_notBefore +#define X509_set_issuer_name GRPC_SHADOW_X509_set_issuer_name +#define X509_set_notAfter GRPC_SHADOW_X509_set_notAfter +#define X509_set_notBefore GRPC_SHADOW_X509_set_notBefore +#define X509_set_pubkey GRPC_SHADOW_X509_set_pubkey +#define X509_set_serialNumber GRPC_SHADOW_X509_set_serialNumber +#define X509_set_subject_name GRPC_SHADOW_X509_set_subject_name +#define X509_set_version GRPC_SHADOW_X509_set_version +#define X509_TRUST_add GRPC_SHADOW_X509_TRUST_add +#define X509_TRUST_cleanup GRPC_SHADOW_X509_TRUST_cleanup +#define X509_TRUST_get0 GRPC_SHADOW_X509_TRUST_get0 +#define X509_TRUST_get0_name GRPC_SHADOW_X509_TRUST_get0_name +#define X509_TRUST_get_by_id GRPC_SHADOW_X509_TRUST_get_by_id +#define X509_TRUST_get_count GRPC_SHADOW_X509_TRUST_get_count +#define X509_TRUST_get_flags GRPC_SHADOW_X509_TRUST_get_flags +#define X509_TRUST_get_trust GRPC_SHADOW_X509_TRUST_get_trust +#define X509_TRUST_set GRPC_SHADOW_X509_TRUST_set +#define X509_TRUST_set_default GRPC_SHADOW_X509_TRUST_set_default +#define X509_check_trust GRPC_SHADOW_X509_check_trust +#define X509_verify_cert_error_string GRPC_SHADOW_X509_verify_cert_error_string +#define X509_EXTENSION_create_by_NID GRPC_SHADOW_X509_EXTENSION_create_by_NID +#define X509_EXTENSION_create_by_OBJ GRPC_SHADOW_X509_EXTENSION_create_by_OBJ +#define X509_EXTENSION_get_critical GRPC_SHADOW_X509_EXTENSION_get_critical +#define X509_EXTENSION_get_data GRPC_SHADOW_X509_EXTENSION_get_data +#define X509_EXTENSION_get_object GRPC_SHADOW_X509_EXTENSION_get_object +#define X509_EXTENSION_set_critical GRPC_SHADOW_X509_EXTENSION_set_critical +#define X509_EXTENSION_set_data GRPC_SHADOW_X509_EXTENSION_set_data +#define X509_EXTENSION_set_object GRPC_SHADOW_X509_EXTENSION_set_object +#define X509v3_add_ext GRPC_SHADOW_X509v3_add_ext +#define X509v3_delete_ext GRPC_SHADOW_X509v3_delete_ext +#define X509v3_get_ext GRPC_SHADOW_X509v3_get_ext +#define X509v3_get_ext_by_NID GRPC_SHADOW_X509v3_get_ext_by_NID +#define X509v3_get_ext_by_OBJ GRPC_SHADOW_X509v3_get_ext_by_OBJ +#define X509v3_get_ext_by_critical GRPC_SHADOW_X509v3_get_ext_by_critical +#define X509v3_get_ext_count GRPC_SHADOW_X509v3_get_ext_count +#define X509_CRL_diff GRPC_SHADOW_X509_CRL_diff +#define X509_STORE_CTX_cleanup GRPC_SHADOW_X509_STORE_CTX_cleanup +#define X509_STORE_CTX_free GRPC_SHADOW_X509_STORE_CTX_free +#define X509_STORE_CTX_get0_current_crl GRPC_SHADOW_X509_STORE_CTX_get0_current_crl +#define X509_STORE_CTX_get0_current_issuer GRPC_SHADOW_X509_STORE_CTX_get0_current_issuer +#define X509_STORE_CTX_get0_param GRPC_SHADOW_X509_STORE_CTX_get0_param +#define X509_STORE_CTX_get0_parent_ctx GRPC_SHADOW_X509_STORE_CTX_get0_parent_ctx +#define X509_STORE_CTX_get0_policy_tree GRPC_SHADOW_X509_STORE_CTX_get0_policy_tree +#define X509_STORE_CTX_get0_untrusted GRPC_SHADOW_X509_STORE_CTX_get0_untrusted +#define X509_STORE_CTX_get1_chain GRPC_SHADOW_X509_STORE_CTX_get1_chain +#define X509_STORE_CTX_get_chain GRPC_SHADOW_X509_STORE_CTX_get_chain +#define X509_STORE_CTX_get_current_cert GRPC_SHADOW_X509_STORE_CTX_get_current_cert +#define X509_STORE_CTX_get_error GRPC_SHADOW_X509_STORE_CTX_get_error +#define X509_STORE_CTX_get_error_depth GRPC_SHADOW_X509_STORE_CTX_get_error_depth +#define X509_STORE_CTX_get_ex_data GRPC_SHADOW_X509_STORE_CTX_get_ex_data +#define X509_STORE_CTX_get_ex_new_index GRPC_SHADOW_X509_STORE_CTX_get_ex_new_index +#define X509_STORE_CTX_get_explicit_policy GRPC_SHADOW_X509_STORE_CTX_get_explicit_policy +#define X509_STORE_CTX_init GRPC_SHADOW_X509_STORE_CTX_init +#define X509_STORE_CTX_new GRPC_SHADOW_X509_STORE_CTX_new +#define X509_STORE_CTX_purpose_inherit GRPC_SHADOW_X509_STORE_CTX_purpose_inherit +#define X509_STORE_CTX_set0_crls GRPC_SHADOW_X509_STORE_CTX_set0_crls +#define X509_STORE_CTX_set0_param GRPC_SHADOW_X509_STORE_CTX_set0_param +#define X509_STORE_CTX_set_cert GRPC_SHADOW_X509_STORE_CTX_set_cert +#define X509_STORE_CTX_set_chain GRPC_SHADOW_X509_STORE_CTX_set_chain +#define X509_STORE_CTX_set_default GRPC_SHADOW_X509_STORE_CTX_set_default +#define X509_STORE_CTX_set_depth GRPC_SHADOW_X509_STORE_CTX_set_depth +#define X509_STORE_CTX_set_error GRPC_SHADOW_X509_STORE_CTX_set_error +#define X509_STORE_CTX_set_ex_data GRPC_SHADOW_X509_STORE_CTX_set_ex_data +#define X509_STORE_CTX_set_flags GRPC_SHADOW_X509_STORE_CTX_set_flags +#define X509_STORE_CTX_set_purpose GRPC_SHADOW_X509_STORE_CTX_set_purpose +#define X509_STORE_CTX_set_time GRPC_SHADOW_X509_STORE_CTX_set_time +#define X509_STORE_CTX_set_trust GRPC_SHADOW_X509_STORE_CTX_set_trust +#define X509_STORE_CTX_set_verify_cb GRPC_SHADOW_X509_STORE_CTX_set_verify_cb +#define X509_STORE_CTX_trusted_stack GRPC_SHADOW_X509_STORE_CTX_trusted_stack +#define X509_STORE_CTX_zero GRPC_SHADOW_X509_STORE_CTX_zero +#define X509_cmp_current_time GRPC_SHADOW_X509_cmp_current_time +#define X509_cmp_time GRPC_SHADOW_X509_cmp_time +#define X509_gmtime_adj GRPC_SHADOW_X509_gmtime_adj +#define X509_time_adj GRPC_SHADOW_X509_time_adj +#define X509_time_adj_ex GRPC_SHADOW_X509_time_adj_ex +#define X509_verify_cert GRPC_SHADOW_X509_verify_cert +#define X509_VERIFY_PARAM_add0_policy GRPC_SHADOW_X509_VERIFY_PARAM_add0_policy +#define X509_VERIFY_PARAM_add0_table GRPC_SHADOW_X509_VERIFY_PARAM_add0_table +#define X509_VERIFY_PARAM_add1_host GRPC_SHADOW_X509_VERIFY_PARAM_add1_host +#define X509_VERIFY_PARAM_clear_flags GRPC_SHADOW_X509_VERIFY_PARAM_clear_flags +#define X509_VERIFY_PARAM_free GRPC_SHADOW_X509_VERIFY_PARAM_free +#define X509_VERIFY_PARAM_get0 GRPC_SHADOW_X509_VERIFY_PARAM_get0 +#define X509_VERIFY_PARAM_get0_name GRPC_SHADOW_X509_VERIFY_PARAM_get0_name +#define X509_VERIFY_PARAM_get0_peername GRPC_SHADOW_X509_VERIFY_PARAM_get0_peername +#define X509_VERIFY_PARAM_get_count GRPC_SHADOW_X509_VERIFY_PARAM_get_count +#define X509_VERIFY_PARAM_get_depth GRPC_SHADOW_X509_VERIFY_PARAM_get_depth +#define X509_VERIFY_PARAM_get_flags GRPC_SHADOW_X509_VERIFY_PARAM_get_flags +#define X509_VERIFY_PARAM_inherit GRPC_SHADOW_X509_VERIFY_PARAM_inherit +#define X509_VERIFY_PARAM_lookup GRPC_SHADOW_X509_VERIFY_PARAM_lookup +#define X509_VERIFY_PARAM_new GRPC_SHADOW_X509_VERIFY_PARAM_new +#define X509_VERIFY_PARAM_set1 GRPC_SHADOW_X509_VERIFY_PARAM_set1 +#define X509_VERIFY_PARAM_set1_email GRPC_SHADOW_X509_VERIFY_PARAM_set1_email +#define X509_VERIFY_PARAM_set1_host GRPC_SHADOW_X509_VERIFY_PARAM_set1_host +#define X509_VERIFY_PARAM_set1_ip GRPC_SHADOW_X509_VERIFY_PARAM_set1_ip +#define X509_VERIFY_PARAM_set1_ip_asc GRPC_SHADOW_X509_VERIFY_PARAM_set1_ip_asc +#define X509_VERIFY_PARAM_set1_name GRPC_SHADOW_X509_VERIFY_PARAM_set1_name +#define X509_VERIFY_PARAM_set1_policies GRPC_SHADOW_X509_VERIFY_PARAM_set1_policies +#define X509_VERIFY_PARAM_set_depth GRPC_SHADOW_X509_VERIFY_PARAM_set_depth +#define X509_VERIFY_PARAM_set_flags GRPC_SHADOW_X509_VERIFY_PARAM_set_flags +#define X509_VERIFY_PARAM_set_hostflags GRPC_SHADOW_X509_VERIFY_PARAM_set_hostflags +#define X509_VERIFY_PARAM_set_purpose GRPC_SHADOW_X509_VERIFY_PARAM_set_purpose +#define X509_VERIFY_PARAM_set_time GRPC_SHADOW_X509_VERIFY_PARAM_set_time +#define X509_VERIFY_PARAM_set_trust GRPC_SHADOW_X509_VERIFY_PARAM_set_trust +#define X509_VERIFY_PARAM_table_cleanup GRPC_SHADOW_X509_VERIFY_PARAM_table_cleanup +#define X509_CRL_set_issuer_name GRPC_SHADOW_X509_CRL_set_issuer_name +#define X509_CRL_set_lastUpdate GRPC_SHADOW_X509_CRL_set_lastUpdate +#define X509_CRL_set_nextUpdate GRPC_SHADOW_X509_CRL_set_nextUpdate +#define X509_CRL_set_version GRPC_SHADOW_X509_CRL_set_version +#define X509_CRL_sort GRPC_SHADOW_X509_CRL_sort +#define X509_CRL_up_ref GRPC_SHADOW_X509_CRL_up_ref +#define X509_REVOKED_set_revocationDate GRPC_SHADOW_X509_REVOKED_set_revocationDate +#define X509_REVOKED_set_serialNumber GRPC_SHADOW_X509_REVOKED_set_serialNumber +#define X509_NAME_ENTRY_create_by_NID GRPC_SHADOW_X509_NAME_ENTRY_create_by_NID +#define X509_NAME_ENTRY_create_by_OBJ GRPC_SHADOW_X509_NAME_ENTRY_create_by_OBJ +#define X509_NAME_ENTRY_create_by_txt GRPC_SHADOW_X509_NAME_ENTRY_create_by_txt +#define X509_NAME_ENTRY_get_data GRPC_SHADOW_X509_NAME_ENTRY_get_data +#define X509_NAME_ENTRY_get_object GRPC_SHADOW_X509_NAME_ENTRY_get_object +#define X509_NAME_ENTRY_set_data GRPC_SHADOW_X509_NAME_ENTRY_set_data +#define X509_NAME_ENTRY_set_object GRPC_SHADOW_X509_NAME_ENTRY_set_object +#define X509_NAME_add_entry GRPC_SHADOW_X509_NAME_add_entry +#define X509_NAME_add_entry_by_NID GRPC_SHADOW_X509_NAME_add_entry_by_NID +#define X509_NAME_add_entry_by_OBJ GRPC_SHADOW_X509_NAME_add_entry_by_OBJ +#define X509_NAME_add_entry_by_txt GRPC_SHADOW_X509_NAME_add_entry_by_txt +#define X509_NAME_delete_entry GRPC_SHADOW_X509_NAME_delete_entry +#define X509_NAME_entry_count GRPC_SHADOW_X509_NAME_entry_count +#define X509_NAME_get_entry GRPC_SHADOW_X509_NAME_get_entry +#define X509_NAME_get_index_by_NID GRPC_SHADOW_X509_NAME_get_index_by_NID +#define X509_NAME_get_index_by_OBJ GRPC_SHADOW_X509_NAME_get_index_by_OBJ +#define X509_NAME_get_text_by_NID GRPC_SHADOW_X509_NAME_get_text_by_NID +#define X509_NAME_get_text_by_OBJ GRPC_SHADOW_X509_NAME_get_text_by_OBJ +#define X509_REQ_set_pubkey GRPC_SHADOW_X509_REQ_set_pubkey +#define X509_REQ_set_subject_name GRPC_SHADOW_X509_REQ_set_subject_name +#define X509_REQ_set_version GRPC_SHADOW_X509_REQ_set_version +#define NETSCAPE_SPKI_b64_decode GRPC_SHADOW_NETSCAPE_SPKI_b64_decode +#define NETSCAPE_SPKI_b64_encode GRPC_SHADOW_NETSCAPE_SPKI_b64_encode +#define NETSCAPE_SPKI_get_pubkey GRPC_SHADOW_NETSCAPE_SPKI_get_pubkey +#define NETSCAPE_SPKI_set_pubkey GRPC_SHADOW_NETSCAPE_SPKI_set_pubkey +#define X509_ALGORS_it GRPC_SHADOW_X509_ALGORS_it +#define X509_ALGOR_cmp GRPC_SHADOW_X509_ALGOR_cmp +#define X509_ALGOR_dup GRPC_SHADOW_X509_ALGOR_dup +#define X509_ALGOR_free GRPC_SHADOW_X509_ALGOR_free +#define X509_ALGOR_get0 GRPC_SHADOW_X509_ALGOR_get0 +#define X509_ALGOR_it GRPC_SHADOW_X509_ALGOR_it +#define X509_ALGOR_new GRPC_SHADOW_X509_ALGOR_new +#define X509_ALGOR_set0 GRPC_SHADOW_X509_ALGOR_set0 +#define X509_ALGOR_set_md GRPC_SHADOW_X509_ALGOR_set_md +#define d2i_X509_ALGOR GRPC_SHADOW_d2i_X509_ALGOR +#define d2i_X509_ALGORS GRPC_SHADOW_d2i_X509_ALGORS +#define i2d_X509_ALGOR GRPC_SHADOW_i2d_X509_ALGOR +#define i2d_X509_ALGORS GRPC_SHADOW_i2d_X509_ALGORS +#define NETSCAPE_SPKI_sign GRPC_SHADOW_NETSCAPE_SPKI_sign +#define NETSCAPE_SPKI_verify GRPC_SHADOW_NETSCAPE_SPKI_verify +#define X509_CRL_digest GRPC_SHADOW_X509_CRL_digest +#define X509_CRL_sign GRPC_SHADOW_X509_CRL_sign +#define X509_CRL_sign_ctx GRPC_SHADOW_X509_CRL_sign_ctx +#define X509_NAME_digest GRPC_SHADOW_X509_NAME_digest +#define X509_REQ_digest GRPC_SHADOW_X509_REQ_digest +#define X509_REQ_sign GRPC_SHADOW_X509_REQ_sign +#define X509_REQ_sign_ctx GRPC_SHADOW_X509_REQ_sign_ctx +#define X509_REQ_verify GRPC_SHADOW_X509_REQ_verify +#define X509_digest GRPC_SHADOW_X509_digest +#define X509_pubkey_digest GRPC_SHADOW_X509_pubkey_digest +#define X509_sign GRPC_SHADOW_X509_sign +#define X509_sign_ctx GRPC_SHADOW_X509_sign_ctx +#define X509_verify GRPC_SHADOW_X509_verify +#define d2i_DSAPrivateKey_bio GRPC_SHADOW_d2i_DSAPrivateKey_bio +#define d2i_DSAPrivateKey_fp GRPC_SHADOW_d2i_DSAPrivateKey_fp +#define d2i_DSA_PUBKEY_bio GRPC_SHADOW_d2i_DSA_PUBKEY_bio +#define d2i_DSA_PUBKEY_fp GRPC_SHADOW_d2i_DSA_PUBKEY_fp +#define d2i_ECPrivateKey_bio GRPC_SHADOW_d2i_ECPrivateKey_bio +#define d2i_ECPrivateKey_fp GRPC_SHADOW_d2i_ECPrivateKey_fp +#define d2i_EC_PUBKEY_bio GRPC_SHADOW_d2i_EC_PUBKEY_bio +#define d2i_EC_PUBKEY_fp GRPC_SHADOW_d2i_EC_PUBKEY_fp +#define d2i_PKCS8_PRIV_KEY_INFO_bio GRPC_SHADOW_d2i_PKCS8_PRIV_KEY_INFO_bio +#define d2i_PKCS8_PRIV_KEY_INFO_fp GRPC_SHADOW_d2i_PKCS8_PRIV_KEY_INFO_fp +#define d2i_PKCS8_bio GRPC_SHADOW_d2i_PKCS8_bio +#define d2i_PKCS8_fp GRPC_SHADOW_d2i_PKCS8_fp +#define d2i_PUBKEY_bio GRPC_SHADOW_d2i_PUBKEY_bio +#define d2i_PUBKEY_fp GRPC_SHADOW_d2i_PUBKEY_fp +#define d2i_PrivateKey_bio GRPC_SHADOW_d2i_PrivateKey_bio +#define d2i_PrivateKey_fp GRPC_SHADOW_d2i_PrivateKey_fp +#define d2i_RSAPrivateKey_bio GRPC_SHADOW_d2i_RSAPrivateKey_bio +#define d2i_RSAPrivateKey_fp GRPC_SHADOW_d2i_RSAPrivateKey_fp +#define d2i_RSAPublicKey_bio GRPC_SHADOW_d2i_RSAPublicKey_bio +#define d2i_RSAPublicKey_fp GRPC_SHADOW_d2i_RSAPublicKey_fp +#define d2i_RSA_PUBKEY_bio GRPC_SHADOW_d2i_RSA_PUBKEY_bio +#define d2i_RSA_PUBKEY_fp GRPC_SHADOW_d2i_RSA_PUBKEY_fp +#define d2i_X509_CRL_bio GRPC_SHADOW_d2i_X509_CRL_bio +#define d2i_X509_CRL_fp GRPC_SHADOW_d2i_X509_CRL_fp +#define d2i_X509_REQ_bio GRPC_SHADOW_d2i_X509_REQ_bio +#define d2i_X509_REQ_fp GRPC_SHADOW_d2i_X509_REQ_fp +#define d2i_X509_bio GRPC_SHADOW_d2i_X509_bio +#define d2i_X509_fp GRPC_SHADOW_d2i_X509_fp +#define i2d_DSAPrivateKey_bio GRPC_SHADOW_i2d_DSAPrivateKey_bio +#define i2d_DSAPrivateKey_fp GRPC_SHADOW_i2d_DSAPrivateKey_fp +#define i2d_DSA_PUBKEY_bio GRPC_SHADOW_i2d_DSA_PUBKEY_bio +#define i2d_DSA_PUBKEY_fp GRPC_SHADOW_i2d_DSA_PUBKEY_fp +#define i2d_ECPrivateKey_bio GRPC_SHADOW_i2d_ECPrivateKey_bio +#define i2d_ECPrivateKey_fp GRPC_SHADOW_i2d_ECPrivateKey_fp +#define i2d_EC_PUBKEY_bio GRPC_SHADOW_i2d_EC_PUBKEY_bio +#define i2d_EC_PUBKEY_fp GRPC_SHADOW_i2d_EC_PUBKEY_fp +#define i2d_PKCS8PrivateKeyInfo_bio GRPC_SHADOW_i2d_PKCS8PrivateKeyInfo_bio +#define i2d_PKCS8PrivateKeyInfo_fp GRPC_SHADOW_i2d_PKCS8PrivateKeyInfo_fp +#define i2d_PKCS8_PRIV_KEY_INFO_bio GRPC_SHADOW_i2d_PKCS8_PRIV_KEY_INFO_bio +#define i2d_PKCS8_PRIV_KEY_INFO_fp GRPC_SHADOW_i2d_PKCS8_PRIV_KEY_INFO_fp +#define i2d_PKCS8_bio GRPC_SHADOW_i2d_PKCS8_bio +#define i2d_PKCS8_fp GRPC_SHADOW_i2d_PKCS8_fp +#define i2d_PUBKEY_bio GRPC_SHADOW_i2d_PUBKEY_bio +#define i2d_PUBKEY_fp GRPC_SHADOW_i2d_PUBKEY_fp +#define i2d_PrivateKey_bio GRPC_SHADOW_i2d_PrivateKey_bio +#define i2d_PrivateKey_fp GRPC_SHADOW_i2d_PrivateKey_fp +#define i2d_RSAPrivateKey_bio GRPC_SHADOW_i2d_RSAPrivateKey_bio +#define i2d_RSAPrivateKey_fp GRPC_SHADOW_i2d_RSAPrivateKey_fp +#define i2d_RSAPublicKey_bio GRPC_SHADOW_i2d_RSAPublicKey_bio +#define i2d_RSAPublicKey_fp GRPC_SHADOW_i2d_RSAPublicKey_fp +#define i2d_RSA_PUBKEY_bio GRPC_SHADOW_i2d_RSA_PUBKEY_bio +#define i2d_RSA_PUBKEY_fp GRPC_SHADOW_i2d_RSA_PUBKEY_fp +#define i2d_X509_CRL_bio GRPC_SHADOW_i2d_X509_CRL_bio +#define i2d_X509_CRL_fp GRPC_SHADOW_i2d_X509_CRL_fp +#define i2d_X509_REQ_bio GRPC_SHADOW_i2d_X509_REQ_bio +#define i2d_X509_REQ_fp GRPC_SHADOW_i2d_X509_REQ_fp +#define i2d_X509_bio GRPC_SHADOW_i2d_X509_bio +#define i2d_X509_fp GRPC_SHADOW_i2d_X509_fp +#define X509_ATTRIBUTE_SET_it GRPC_SHADOW_X509_ATTRIBUTE_SET_it +#define X509_ATTRIBUTE_create GRPC_SHADOW_X509_ATTRIBUTE_create +#define X509_ATTRIBUTE_dup GRPC_SHADOW_X509_ATTRIBUTE_dup +#define X509_ATTRIBUTE_free GRPC_SHADOW_X509_ATTRIBUTE_free +#define X509_ATTRIBUTE_it GRPC_SHADOW_X509_ATTRIBUTE_it +#define X509_ATTRIBUTE_new GRPC_SHADOW_X509_ATTRIBUTE_new +#define d2i_X509_ATTRIBUTE GRPC_SHADOW_d2i_X509_ATTRIBUTE +#define i2d_X509_ATTRIBUTE GRPC_SHADOW_i2d_X509_ATTRIBUTE +#define X509_CRL_INFO_free GRPC_SHADOW_X509_CRL_INFO_free +#define X509_CRL_INFO_it GRPC_SHADOW_X509_CRL_INFO_it +#define X509_CRL_INFO_new GRPC_SHADOW_X509_CRL_INFO_new +#define X509_CRL_METHOD_free GRPC_SHADOW_X509_CRL_METHOD_free +#define X509_CRL_METHOD_new GRPC_SHADOW_X509_CRL_METHOD_new +#define X509_CRL_add0_revoked GRPC_SHADOW_X509_CRL_add0_revoked +#define X509_CRL_dup GRPC_SHADOW_X509_CRL_dup +#define X509_CRL_free GRPC_SHADOW_X509_CRL_free +#define X509_CRL_get0_by_cert GRPC_SHADOW_X509_CRL_get0_by_cert +#define X509_CRL_get0_by_serial GRPC_SHADOW_X509_CRL_get0_by_serial +#define X509_CRL_get_meth_data GRPC_SHADOW_X509_CRL_get_meth_data +#define X509_CRL_it GRPC_SHADOW_X509_CRL_it +#define X509_CRL_new GRPC_SHADOW_X509_CRL_new +#define X509_CRL_set_default_method GRPC_SHADOW_X509_CRL_set_default_method +#define X509_CRL_set_meth_data GRPC_SHADOW_X509_CRL_set_meth_data +#define X509_CRL_verify GRPC_SHADOW_X509_CRL_verify +#define X509_REVOKED_dup GRPC_SHADOW_X509_REVOKED_dup +#define X509_REVOKED_free GRPC_SHADOW_X509_REVOKED_free +#define X509_REVOKED_it GRPC_SHADOW_X509_REVOKED_it +#define X509_REVOKED_new GRPC_SHADOW_X509_REVOKED_new +#define d2i_X509_CRL GRPC_SHADOW_d2i_X509_CRL +#define d2i_X509_CRL_INFO GRPC_SHADOW_d2i_X509_CRL_INFO +#define d2i_X509_REVOKED GRPC_SHADOW_d2i_X509_REVOKED +#define i2d_X509_CRL GRPC_SHADOW_i2d_X509_CRL +#define i2d_X509_CRL_INFO GRPC_SHADOW_i2d_X509_CRL_INFO +#define i2d_X509_REVOKED GRPC_SHADOW_i2d_X509_REVOKED +#define X509_EXTENSIONS_it GRPC_SHADOW_X509_EXTENSIONS_it +#define X509_EXTENSION_dup GRPC_SHADOW_X509_EXTENSION_dup +#define X509_EXTENSION_free GRPC_SHADOW_X509_EXTENSION_free +#define X509_EXTENSION_it GRPC_SHADOW_X509_EXTENSION_it +#define X509_EXTENSION_new GRPC_SHADOW_X509_EXTENSION_new +#define d2i_X509_EXTENSION GRPC_SHADOW_d2i_X509_EXTENSION +#define d2i_X509_EXTENSIONS GRPC_SHADOW_d2i_X509_EXTENSIONS +#define i2d_X509_EXTENSION GRPC_SHADOW_i2d_X509_EXTENSION +#define i2d_X509_EXTENSIONS GRPC_SHADOW_i2d_X509_EXTENSIONS +#define X509_INFO_free GRPC_SHADOW_X509_INFO_free +#define X509_INFO_new GRPC_SHADOW_X509_INFO_new +#define X509_NAME_ENTRIES_it GRPC_SHADOW_X509_NAME_ENTRIES_it +#define X509_NAME_ENTRY_dup GRPC_SHADOW_X509_NAME_ENTRY_dup +#define X509_NAME_ENTRY_free GRPC_SHADOW_X509_NAME_ENTRY_free +#define X509_NAME_ENTRY_it GRPC_SHADOW_X509_NAME_ENTRY_it +#define X509_NAME_ENTRY_new GRPC_SHADOW_X509_NAME_ENTRY_new +#define X509_NAME_ENTRY_set GRPC_SHADOW_X509_NAME_ENTRY_set +#define X509_NAME_INTERNAL_it GRPC_SHADOW_X509_NAME_INTERNAL_it +#define X509_NAME_dup GRPC_SHADOW_X509_NAME_dup +#define X509_NAME_free GRPC_SHADOW_X509_NAME_free +#define X509_NAME_get0_der GRPC_SHADOW_X509_NAME_get0_der +#define X509_NAME_it GRPC_SHADOW_X509_NAME_it +#define X509_NAME_new GRPC_SHADOW_X509_NAME_new +#define X509_NAME_set GRPC_SHADOW_X509_NAME_set +#define d2i_X509_NAME GRPC_SHADOW_d2i_X509_NAME +#define d2i_X509_NAME_ENTRY GRPC_SHADOW_d2i_X509_NAME_ENTRY +#define i2d_X509_NAME GRPC_SHADOW_i2d_X509_NAME +#define i2d_X509_NAME_ENTRY GRPC_SHADOW_i2d_X509_NAME_ENTRY +#define X509_PKEY_free GRPC_SHADOW_X509_PKEY_free +#define X509_PKEY_new GRPC_SHADOW_X509_PKEY_new +#define X509_PUBKEY_free GRPC_SHADOW_X509_PUBKEY_free +#define X509_PUBKEY_get GRPC_SHADOW_X509_PUBKEY_get +#define X509_PUBKEY_get0_param GRPC_SHADOW_X509_PUBKEY_get0_param +#define X509_PUBKEY_it GRPC_SHADOW_X509_PUBKEY_it +#define X509_PUBKEY_new GRPC_SHADOW_X509_PUBKEY_new +#define X509_PUBKEY_set GRPC_SHADOW_X509_PUBKEY_set +#define X509_PUBKEY_set0_param GRPC_SHADOW_X509_PUBKEY_set0_param +#define d2i_DSA_PUBKEY GRPC_SHADOW_d2i_DSA_PUBKEY +#define d2i_EC_PUBKEY GRPC_SHADOW_d2i_EC_PUBKEY +#define d2i_PUBKEY GRPC_SHADOW_d2i_PUBKEY +#define d2i_RSA_PUBKEY GRPC_SHADOW_d2i_RSA_PUBKEY +#define d2i_X509_PUBKEY GRPC_SHADOW_d2i_X509_PUBKEY +#define i2d_DSA_PUBKEY GRPC_SHADOW_i2d_DSA_PUBKEY +#define i2d_EC_PUBKEY GRPC_SHADOW_i2d_EC_PUBKEY +#define i2d_PUBKEY GRPC_SHADOW_i2d_PUBKEY +#define i2d_RSA_PUBKEY GRPC_SHADOW_i2d_RSA_PUBKEY +#define i2d_X509_PUBKEY GRPC_SHADOW_i2d_X509_PUBKEY +#define X509_REQ_INFO_free GRPC_SHADOW_X509_REQ_INFO_free +#define X509_REQ_INFO_it GRPC_SHADOW_X509_REQ_INFO_it +#define X509_REQ_INFO_new GRPC_SHADOW_X509_REQ_INFO_new +#define X509_REQ_dup GRPC_SHADOW_X509_REQ_dup +#define X509_REQ_free GRPC_SHADOW_X509_REQ_free +#define X509_REQ_it GRPC_SHADOW_X509_REQ_it +#define X509_REQ_new GRPC_SHADOW_X509_REQ_new +#define d2i_X509_REQ GRPC_SHADOW_d2i_X509_REQ +#define d2i_X509_REQ_INFO GRPC_SHADOW_d2i_X509_REQ_INFO +#define i2d_X509_REQ GRPC_SHADOW_i2d_X509_REQ +#define i2d_X509_REQ_INFO GRPC_SHADOW_i2d_X509_REQ_INFO +#define X509_SIG_free GRPC_SHADOW_X509_SIG_free +#define X509_SIG_it GRPC_SHADOW_X509_SIG_it +#define X509_SIG_new GRPC_SHADOW_X509_SIG_new +#define d2i_X509_SIG GRPC_SHADOW_d2i_X509_SIG +#define i2d_X509_SIG GRPC_SHADOW_i2d_X509_SIG +#define NETSCAPE_SPKAC_free GRPC_SHADOW_NETSCAPE_SPKAC_free +#define NETSCAPE_SPKAC_it GRPC_SHADOW_NETSCAPE_SPKAC_it +#define NETSCAPE_SPKAC_new GRPC_SHADOW_NETSCAPE_SPKAC_new +#define NETSCAPE_SPKI_free GRPC_SHADOW_NETSCAPE_SPKI_free +#define NETSCAPE_SPKI_it GRPC_SHADOW_NETSCAPE_SPKI_it +#define NETSCAPE_SPKI_new GRPC_SHADOW_NETSCAPE_SPKI_new +#define d2i_NETSCAPE_SPKAC GRPC_SHADOW_d2i_NETSCAPE_SPKAC +#define d2i_NETSCAPE_SPKI GRPC_SHADOW_d2i_NETSCAPE_SPKI +#define i2d_NETSCAPE_SPKAC GRPC_SHADOW_i2d_NETSCAPE_SPKAC +#define i2d_NETSCAPE_SPKI GRPC_SHADOW_i2d_NETSCAPE_SPKI +#define X509_VAL_free GRPC_SHADOW_X509_VAL_free +#define X509_VAL_it GRPC_SHADOW_X509_VAL_it +#define X509_VAL_new GRPC_SHADOW_X509_VAL_new +#define d2i_X509_VAL GRPC_SHADOW_d2i_X509_VAL +#define i2d_X509_VAL GRPC_SHADOW_i2d_X509_VAL +#define X509_CINF_free GRPC_SHADOW_X509_CINF_free +#define X509_CINF_it GRPC_SHADOW_X509_CINF_it +#define X509_CINF_new GRPC_SHADOW_X509_CINF_new +#define X509_dup GRPC_SHADOW_X509_dup +#define X509_free GRPC_SHADOW_X509_free +#define X509_get0_signature GRPC_SHADOW_X509_get0_signature +#define X509_get_ex_data GRPC_SHADOW_X509_get_ex_data +#define X509_get_ex_new_index GRPC_SHADOW_X509_get_ex_new_index +#define X509_get_signature_nid GRPC_SHADOW_X509_get_signature_nid +#define X509_it GRPC_SHADOW_X509_it +#define X509_new GRPC_SHADOW_X509_new +#define X509_parse_from_buffer GRPC_SHADOW_X509_parse_from_buffer +#define X509_set_ex_data GRPC_SHADOW_X509_set_ex_data +#define X509_up_ref GRPC_SHADOW_X509_up_ref +#define d2i_X509 GRPC_SHADOW_d2i_X509 +#define d2i_X509_AUX GRPC_SHADOW_d2i_X509_AUX +#define d2i_X509_CINF GRPC_SHADOW_d2i_X509_CINF +#define i2d_X509 GRPC_SHADOW_i2d_X509 +#define i2d_X509_AUX GRPC_SHADOW_i2d_X509_AUX +#define i2d_X509_CINF GRPC_SHADOW_i2d_X509_CINF +#define X509_CERT_AUX_free GRPC_SHADOW_X509_CERT_AUX_free +#define X509_CERT_AUX_it GRPC_SHADOW_X509_CERT_AUX_it +#define X509_CERT_AUX_new GRPC_SHADOW_X509_CERT_AUX_new +#define X509_add1_reject_object GRPC_SHADOW_X509_add1_reject_object +#define X509_add1_trust_object GRPC_SHADOW_X509_add1_trust_object +#define X509_alias_get0 GRPC_SHADOW_X509_alias_get0 +#define X509_alias_set1 GRPC_SHADOW_X509_alias_set1 +#define X509_keyid_get0 GRPC_SHADOW_X509_keyid_get0 +#define X509_keyid_set1 GRPC_SHADOW_X509_keyid_set1 +#define X509_reject_clear GRPC_SHADOW_X509_reject_clear +#define X509_trust_clear GRPC_SHADOW_X509_trust_clear +#define d2i_X509_CERT_AUX GRPC_SHADOW_d2i_X509_CERT_AUX +#define i2d_X509_CERT_AUX GRPC_SHADOW_i2d_X509_CERT_AUX +#define policy_cache_find_data GRPC_SHADOW_policy_cache_find_data +#define policy_cache_free GRPC_SHADOW_policy_cache_free +#define policy_cache_set GRPC_SHADOW_policy_cache_set +#define policy_data_free GRPC_SHADOW_policy_data_free +#define policy_data_new GRPC_SHADOW_policy_data_new +#define X509_policy_level_get0_node GRPC_SHADOW_X509_policy_level_get0_node +#define X509_policy_level_node_count GRPC_SHADOW_X509_policy_level_node_count +#define X509_policy_node_get0_parent GRPC_SHADOW_X509_policy_node_get0_parent +#define X509_policy_node_get0_policy GRPC_SHADOW_X509_policy_node_get0_policy +#define X509_policy_node_get0_qualifiers GRPC_SHADOW_X509_policy_node_get0_qualifiers +#define X509_policy_tree_get0_level GRPC_SHADOW_X509_policy_tree_get0_level +#define X509_policy_tree_get0_policies GRPC_SHADOW_X509_policy_tree_get0_policies +#define X509_policy_tree_get0_user_policies GRPC_SHADOW_X509_policy_tree_get0_user_policies +#define X509_policy_tree_level_count GRPC_SHADOW_X509_policy_tree_level_count +#define policy_cache_set_mapping GRPC_SHADOW_policy_cache_set_mapping +#define level_add_node GRPC_SHADOW_level_add_node +#define level_find_node GRPC_SHADOW_level_find_node +#define policy_node_cmp_new GRPC_SHADOW_policy_node_cmp_new +#define policy_node_free GRPC_SHADOW_policy_node_free +#define policy_node_match GRPC_SHADOW_policy_node_match +#define tree_find_sk GRPC_SHADOW_tree_find_sk +#define X509_policy_check GRPC_SHADOW_X509_policy_check +#define X509_policy_tree_free GRPC_SHADOW_X509_policy_tree_free +#define v3_akey_id GRPC_SHADOW_v3_akey_id +#define AUTHORITY_KEYID_free GRPC_SHADOW_AUTHORITY_KEYID_free +#define AUTHORITY_KEYID_it GRPC_SHADOW_AUTHORITY_KEYID_it +#define AUTHORITY_KEYID_new GRPC_SHADOW_AUTHORITY_KEYID_new +#define d2i_AUTHORITY_KEYID GRPC_SHADOW_d2i_AUTHORITY_KEYID +#define i2d_AUTHORITY_KEYID GRPC_SHADOW_i2d_AUTHORITY_KEYID +#define GENERAL_NAME_print GRPC_SHADOW_GENERAL_NAME_print +#define a2i_GENERAL_NAME GRPC_SHADOW_a2i_GENERAL_NAME +#define i2v_GENERAL_NAME GRPC_SHADOW_i2v_GENERAL_NAME +#define i2v_GENERAL_NAMES GRPC_SHADOW_i2v_GENERAL_NAMES +#define v2i_GENERAL_NAME GRPC_SHADOW_v2i_GENERAL_NAME +#define v2i_GENERAL_NAMES GRPC_SHADOW_v2i_GENERAL_NAMES +#define v2i_GENERAL_NAME_ex GRPC_SHADOW_v2i_GENERAL_NAME_ex +#define v3_alt GRPC_SHADOW_v3_alt +#define BASIC_CONSTRAINTS_free GRPC_SHADOW_BASIC_CONSTRAINTS_free +#define BASIC_CONSTRAINTS_it GRPC_SHADOW_BASIC_CONSTRAINTS_it +#define BASIC_CONSTRAINTS_new GRPC_SHADOW_BASIC_CONSTRAINTS_new +#define d2i_BASIC_CONSTRAINTS GRPC_SHADOW_d2i_BASIC_CONSTRAINTS +#define i2d_BASIC_CONSTRAINTS GRPC_SHADOW_i2d_BASIC_CONSTRAINTS +#define v3_bcons GRPC_SHADOW_v3_bcons +#define i2v_ASN1_BIT_STRING GRPC_SHADOW_i2v_ASN1_BIT_STRING +#define v2i_ASN1_BIT_STRING GRPC_SHADOW_v2i_ASN1_BIT_STRING +#define v3_key_usage GRPC_SHADOW_v3_key_usage +#define v3_nscert GRPC_SHADOW_v3_nscert +#define X509V3_EXT_CRL_add_nconf GRPC_SHADOW_X509V3_EXT_CRL_add_nconf +#define X509V3_EXT_REQ_add_nconf GRPC_SHADOW_X509V3_EXT_REQ_add_nconf +#define X509V3_EXT_add_nconf GRPC_SHADOW_X509V3_EXT_add_nconf +#define X509V3_EXT_add_nconf_sk GRPC_SHADOW_X509V3_EXT_add_nconf_sk +#define X509V3_EXT_i2d GRPC_SHADOW_X509V3_EXT_i2d +#define X509V3_EXT_nconf GRPC_SHADOW_X509V3_EXT_nconf +#define X509V3_EXT_nconf_nid GRPC_SHADOW_X509V3_EXT_nconf_nid +#define X509V3_get_section GRPC_SHADOW_X509V3_get_section +#define X509V3_get_string GRPC_SHADOW_X509V3_get_string +#define X509V3_section_free GRPC_SHADOW_X509V3_section_free +#define X509V3_set_ctx GRPC_SHADOW_X509V3_set_ctx +#define X509V3_set_nconf GRPC_SHADOW_X509V3_set_nconf +#define X509V3_string_free GRPC_SHADOW_X509V3_string_free +#define CERTIFICATEPOLICIES_free GRPC_SHADOW_CERTIFICATEPOLICIES_free +#define CERTIFICATEPOLICIES_it GRPC_SHADOW_CERTIFICATEPOLICIES_it +#define CERTIFICATEPOLICIES_new GRPC_SHADOW_CERTIFICATEPOLICIES_new +#define NOTICEREF_free GRPC_SHADOW_NOTICEREF_free +#define NOTICEREF_it GRPC_SHADOW_NOTICEREF_it +#define NOTICEREF_new GRPC_SHADOW_NOTICEREF_new +#define POLICYINFO_free GRPC_SHADOW_POLICYINFO_free +#define POLICYINFO_it GRPC_SHADOW_POLICYINFO_it +#define POLICYINFO_new GRPC_SHADOW_POLICYINFO_new +#define POLICYQUALINFO_free GRPC_SHADOW_POLICYQUALINFO_free +#define POLICYQUALINFO_it GRPC_SHADOW_POLICYQUALINFO_it +#define POLICYQUALINFO_new GRPC_SHADOW_POLICYQUALINFO_new +#define USERNOTICE_free GRPC_SHADOW_USERNOTICE_free +#define USERNOTICE_it GRPC_SHADOW_USERNOTICE_it +#define USERNOTICE_new GRPC_SHADOW_USERNOTICE_new +#define X509_POLICY_NODE_print GRPC_SHADOW_X509_POLICY_NODE_print +#define d2i_CERTIFICATEPOLICIES GRPC_SHADOW_d2i_CERTIFICATEPOLICIES +#define d2i_NOTICEREF GRPC_SHADOW_d2i_NOTICEREF +#define d2i_POLICYINFO GRPC_SHADOW_d2i_POLICYINFO +#define d2i_POLICYQUALINFO GRPC_SHADOW_d2i_POLICYQUALINFO +#define d2i_USERNOTICE GRPC_SHADOW_d2i_USERNOTICE +#define i2d_CERTIFICATEPOLICIES GRPC_SHADOW_i2d_CERTIFICATEPOLICIES +#define i2d_NOTICEREF GRPC_SHADOW_i2d_NOTICEREF +#define i2d_POLICYINFO GRPC_SHADOW_i2d_POLICYINFO +#define i2d_POLICYQUALINFO GRPC_SHADOW_i2d_POLICYQUALINFO +#define i2d_USERNOTICE GRPC_SHADOW_i2d_USERNOTICE +#define v3_cpols GRPC_SHADOW_v3_cpols +#define CRL_DIST_POINTS_free GRPC_SHADOW_CRL_DIST_POINTS_free +#define CRL_DIST_POINTS_it GRPC_SHADOW_CRL_DIST_POINTS_it +#define CRL_DIST_POINTS_new GRPC_SHADOW_CRL_DIST_POINTS_new +#define DIST_POINT_NAME_free GRPC_SHADOW_DIST_POINT_NAME_free +#define DIST_POINT_NAME_it GRPC_SHADOW_DIST_POINT_NAME_it +#define DIST_POINT_NAME_new GRPC_SHADOW_DIST_POINT_NAME_new +#define DIST_POINT_free GRPC_SHADOW_DIST_POINT_free +#define DIST_POINT_it GRPC_SHADOW_DIST_POINT_it +#define DIST_POINT_new GRPC_SHADOW_DIST_POINT_new +#define DIST_POINT_set_dpname GRPC_SHADOW_DIST_POINT_set_dpname +#define ISSUING_DIST_POINT_free GRPC_SHADOW_ISSUING_DIST_POINT_free +#define ISSUING_DIST_POINT_it GRPC_SHADOW_ISSUING_DIST_POINT_it +#define ISSUING_DIST_POINT_new GRPC_SHADOW_ISSUING_DIST_POINT_new +#define d2i_CRL_DIST_POINTS GRPC_SHADOW_d2i_CRL_DIST_POINTS +#define d2i_DIST_POINT GRPC_SHADOW_d2i_DIST_POINT +#define d2i_DIST_POINT_NAME GRPC_SHADOW_d2i_DIST_POINT_NAME +#define d2i_ISSUING_DIST_POINT GRPC_SHADOW_d2i_ISSUING_DIST_POINT +#define i2d_CRL_DIST_POINTS GRPC_SHADOW_i2d_CRL_DIST_POINTS +#define i2d_DIST_POINT GRPC_SHADOW_i2d_DIST_POINT +#define i2d_DIST_POINT_NAME GRPC_SHADOW_i2d_DIST_POINT_NAME +#define i2d_ISSUING_DIST_POINT GRPC_SHADOW_i2d_ISSUING_DIST_POINT +#define v3_crld GRPC_SHADOW_v3_crld +#define v3_freshest_crl GRPC_SHADOW_v3_freshest_crl +#define v3_idp GRPC_SHADOW_v3_idp +#define i2s_ASN1_ENUMERATED_TABLE GRPC_SHADOW_i2s_ASN1_ENUMERATED_TABLE +#define v3_crl_reason GRPC_SHADOW_v3_crl_reason +#define EXTENDED_KEY_USAGE_free GRPC_SHADOW_EXTENDED_KEY_USAGE_free +#define EXTENDED_KEY_USAGE_it GRPC_SHADOW_EXTENDED_KEY_USAGE_it +#define EXTENDED_KEY_USAGE_new GRPC_SHADOW_EXTENDED_KEY_USAGE_new +#define d2i_EXTENDED_KEY_USAGE GRPC_SHADOW_d2i_EXTENDED_KEY_USAGE +#define i2d_EXTENDED_KEY_USAGE GRPC_SHADOW_i2d_EXTENDED_KEY_USAGE +#define v3_ext_ku GRPC_SHADOW_v3_ext_ku +#define v3_ocsp_accresp GRPC_SHADOW_v3_ocsp_accresp +#define EDIPARTYNAME_free GRPC_SHADOW_EDIPARTYNAME_free +#define EDIPARTYNAME_it GRPC_SHADOW_EDIPARTYNAME_it +#define EDIPARTYNAME_new GRPC_SHADOW_EDIPARTYNAME_new +#define GENERAL_NAMES_free GRPC_SHADOW_GENERAL_NAMES_free +#define GENERAL_NAMES_it GRPC_SHADOW_GENERAL_NAMES_it +#define GENERAL_NAMES_new GRPC_SHADOW_GENERAL_NAMES_new +#define GENERAL_NAME_cmp GRPC_SHADOW_GENERAL_NAME_cmp +#define GENERAL_NAME_dup GRPC_SHADOW_GENERAL_NAME_dup +#define GENERAL_NAME_free GRPC_SHADOW_GENERAL_NAME_free +#define GENERAL_NAME_get0_otherName GRPC_SHADOW_GENERAL_NAME_get0_otherName +#define GENERAL_NAME_get0_value GRPC_SHADOW_GENERAL_NAME_get0_value +#define GENERAL_NAME_it GRPC_SHADOW_GENERAL_NAME_it +#define GENERAL_NAME_new GRPC_SHADOW_GENERAL_NAME_new +#define GENERAL_NAME_set0_othername GRPC_SHADOW_GENERAL_NAME_set0_othername +#define GENERAL_NAME_set0_value GRPC_SHADOW_GENERAL_NAME_set0_value +#define OTHERNAME_cmp GRPC_SHADOW_OTHERNAME_cmp +#define OTHERNAME_free GRPC_SHADOW_OTHERNAME_free +#define OTHERNAME_it GRPC_SHADOW_OTHERNAME_it +#define OTHERNAME_new GRPC_SHADOW_OTHERNAME_new +#define d2i_EDIPARTYNAME GRPC_SHADOW_d2i_EDIPARTYNAME +#define d2i_GENERAL_NAME GRPC_SHADOW_d2i_GENERAL_NAME +#define d2i_GENERAL_NAMES GRPC_SHADOW_d2i_GENERAL_NAMES +#define d2i_OTHERNAME GRPC_SHADOW_d2i_OTHERNAME +#define i2d_EDIPARTYNAME GRPC_SHADOW_i2d_EDIPARTYNAME +#define i2d_GENERAL_NAME GRPC_SHADOW_i2d_GENERAL_NAME +#define i2d_GENERAL_NAMES GRPC_SHADOW_i2d_GENERAL_NAMES +#define i2d_OTHERNAME GRPC_SHADOW_i2d_OTHERNAME +#define v3_ns_ia5_list GRPC_SHADOW_v3_ns_ia5_list +#define ACCESS_DESCRIPTION_free GRPC_SHADOW_ACCESS_DESCRIPTION_free +#define ACCESS_DESCRIPTION_it GRPC_SHADOW_ACCESS_DESCRIPTION_it +#define ACCESS_DESCRIPTION_new GRPC_SHADOW_ACCESS_DESCRIPTION_new +#define AUTHORITY_INFO_ACCESS_free GRPC_SHADOW_AUTHORITY_INFO_ACCESS_free +#define AUTHORITY_INFO_ACCESS_it GRPC_SHADOW_AUTHORITY_INFO_ACCESS_it +#define AUTHORITY_INFO_ACCESS_new GRPC_SHADOW_AUTHORITY_INFO_ACCESS_new +#define d2i_ACCESS_DESCRIPTION GRPC_SHADOW_d2i_ACCESS_DESCRIPTION +#define d2i_AUTHORITY_INFO_ACCESS GRPC_SHADOW_d2i_AUTHORITY_INFO_ACCESS +#define i2a_ACCESS_DESCRIPTION GRPC_SHADOW_i2a_ACCESS_DESCRIPTION +#define i2d_ACCESS_DESCRIPTION GRPC_SHADOW_i2d_ACCESS_DESCRIPTION +#define i2d_AUTHORITY_INFO_ACCESS GRPC_SHADOW_i2d_AUTHORITY_INFO_ACCESS +#define v3_info GRPC_SHADOW_v3_info +#define v3_sinfo GRPC_SHADOW_v3_sinfo +#define v3_crl_num GRPC_SHADOW_v3_crl_num +#define v3_delta_crl GRPC_SHADOW_v3_delta_crl +#define v3_inhibit_anyp GRPC_SHADOW_v3_inhibit_anyp +#define X509V3_EXT_add GRPC_SHADOW_X509V3_EXT_add +#define X509V3_EXT_add_alias GRPC_SHADOW_X509V3_EXT_add_alias +#define X509V3_EXT_add_list GRPC_SHADOW_X509V3_EXT_add_list +#define X509V3_EXT_cleanup GRPC_SHADOW_X509V3_EXT_cleanup +#define X509V3_EXT_d2i GRPC_SHADOW_X509V3_EXT_d2i +#define X509V3_EXT_free GRPC_SHADOW_X509V3_EXT_free +#define X509V3_EXT_get GRPC_SHADOW_X509V3_EXT_get +#define X509V3_EXT_get_nid GRPC_SHADOW_X509V3_EXT_get_nid +#define X509V3_add1_i2d GRPC_SHADOW_X509V3_add1_i2d +#define X509V3_add_standard_extensions GRPC_SHADOW_X509V3_add_standard_extensions +#define X509V3_get_d2i GRPC_SHADOW_X509V3_get_d2i +#define GENERAL_SUBTREE_free GRPC_SHADOW_GENERAL_SUBTREE_free +#define GENERAL_SUBTREE_it GRPC_SHADOW_GENERAL_SUBTREE_it +#define GENERAL_SUBTREE_new GRPC_SHADOW_GENERAL_SUBTREE_new +#define NAME_CONSTRAINTS_check GRPC_SHADOW_NAME_CONSTRAINTS_check +#define NAME_CONSTRAINTS_free GRPC_SHADOW_NAME_CONSTRAINTS_free +#define NAME_CONSTRAINTS_it GRPC_SHADOW_NAME_CONSTRAINTS_it +#define NAME_CONSTRAINTS_new GRPC_SHADOW_NAME_CONSTRAINTS_new +#define v3_name_constraints GRPC_SHADOW_v3_name_constraints +#define v3_pci GRPC_SHADOW_v3_pci +#define PROXY_CERT_INFO_EXTENSION_free GRPC_SHADOW_PROXY_CERT_INFO_EXTENSION_free +#define PROXY_CERT_INFO_EXTENSION_it GRPC_SHADOW_PROXY_CERT_INFO_EXTENSION_it +#define PROXY_CERT_INFO_EXTENSION_new GRPC_SHADOW_PROXY_CERT_INFO_EXTENSION_new +#define PROXY_POLICY_free GRPC_SHADOW_PROXY_POLICY_free +#define PROXY_POLICY_it GRPC_SHADOW_PROXY_POLICY_it +#define PROXY_POLICY_new GRPC_SHADOW_PROXY_POLICY_new +#define d2i_PROXY_CERT_INFO_EXTENSION GRPC_SHADOW_d2i_PROXY_CERT_INFO_EXTENSION +#define d2i_PROXY_POLICY GRPC_SHADOW_d2i_PROXY_POLICY +#define i2d_PROXY_CERT_INFO_EXTENSION GRPC_SHADOW_i2d_PROXY_CERT_INFO_EXTENSION +#define i2d_PROXY_POLICY GRPC_SHADOW_i2d_PROXY_POLICY +#define POLICY_CONSTRAINTS_free GRPC_SHADOW_POLICY_CONSTRAINTS_free +#define POLICY_CONSTRAINTS_it GRPC_SHADOW_POLICY_CONSTRAINTS_it +#define POLICY_CONSTRAINTS_new GRPC_SHADOW_POLICY_CONSTRAINTS_new +#define v3_policy_constraints GRPC_SHADOW_v3_policy_constraints +#define PKEY_USAGE_PERIOD_free GRPC_SHADOW_PKEY_USAGE_PERIOD_free +#define PKEY_USAGE_PERIOD_it GRPC_SHADOW_PKEY_USAGE_PERIOD_it +#define PKEY_USAGE_PERIOD_new GRPC_SHADOW_PKEY_USAGE_PERIOD_new +#define d2i_PKEY_USAGE_PERIOD GRPC_SHADOW_d2i_PKEY_USAGE_PERIOD +#define i2d_PKEY_USAGE_PERIOD GRPC_SHADOW_i2d_PKEY_USAGE_PERIOD +#define v3_pkey_usage_period GRPC_SHADOW_v3_pkey_usage_period +#define POLICY_MAPPINGS_it GRPC_SHADOW_POLICY_MAPPINGS_it +#define POLICY_MAPPING_free GRPC_SHADOW_POLICY_MAPPING_free +#define POLICY_MAPPING_it GRPC_SHADOW_POLICY_MAPPING_it +#define POLICY_MAPPING_new GRPC_SHADOW_POLICY_MAPPING_new +#define v3_policy_mappings GRPC_SHADOW_v3_policy_mappings +#define X509V3_EXT_print GRPC_SHADOW_X509V3_EXT_print +#define X509V3_EXT_print_fp GRPC_SHADOW_X509V3_EXT_print_fp +#define X509V3_EXT_val_prn GRPC_SHADOW_X509V3_EXT_val_prn +#define X509V3_extensions_print GRPC_SHADOW_X509V3_extensions_print +#define X509_PURPOSE_add GRPC_SHADOW_X509_PURPOSE_add +#define X509_PURPOSE_cleanup GRPC_SHADOW_X509_PURPOSE_cleanup +#define X509_PURPOSE_get0 GRPC_SHADOW_X509_PURPOSE_get0 +#define X509_PURPOSE_get0_name GRPC_SHADOW_X509_PURPOSE_get0_name +#define X509_PURPOSE_get0_sname GRPC_SHADOW_X509_PURPOSE_get0_sname +#define X509_PURPOSE_get_by_id GRPC_SHADOW_X509_PURPOSE_get_by_id +#define X509_PURPOSE_get_by_sname GRPC_SHADOW_X509_PURPOSE_get_by_sname +#define X509_PURPOSE_get_count GRPC_SHADOW_X509_PURPOSE_get_count +#define X509_PURPOSE_get_id GRPC_SHADOW_X509_PURPOSE_get_id +#define X509_PURPOSE_get_trust GRPC_SHADOW_X509_PURPOSE_get_trust +#define X509_PURPOSE_set GRPC_SHADOW_X509_PURPOSE_set +#define X509_check_akid GRPC_SHADOW_X509_check_akid +#define X509_check_ca GRPC_SHADOW_X509_check_ca +#define X509_check_issued GRPC_SHADOW_X509_check_issued +#define X509_check_purpose GRPC_SHADOW_X509_check_purpose +#define X509_supported_extension GRPC_SHADOW_X509_supported_extension +#define i2s_ASN1_OCTET_STRING GRPC_SHADOW_i2s_ASN1_OCTET_STRING +#define s2i_ASN1_OCTET_STRING GRPC_SHADOW_s2i_ASN1_OCTET_STRING +#define v3_skey_id GRPC_SHADOW_v3_skey_id +#define SXNETID_free GRPC_SHADOW_SXNETID_free +#define SXNETID_it GRPC_SHADOW_SXNETID_it +#define SXNETID_new GRPC_SHADOW_SXNETID_new +#define SXNET_add_id_INTEGER GRPC_SHADOW_SXNET_add_id_INTEGER +#define SXNET_add_id_asc GRPC_SHADOW_SXNET_add_id_asc +#define SXNET_add_id_ulong GRPC_SHADOW_SXNET_add_id_ulong +#define SXNET_free GRPC_SHADOW_SXNET_free +#define SXNET_get_id_INTEGER GRPC_SHADOW_SXNET_get_id_INTEGER +#define SXNET_get_id_asc GRPC_SHADOW_SXNET_get_id_asc +#define SXNET_get_id_ulong GRPC_SHADOW_SXNET_get_id_ulong +#define SXNET_it GRPC_SHADOW_SXNET_it +#define SXNET_new GRPC_SHADOW_SXNET_new +#define d2i_SXNET GRPC_SHADOW_d2i_SXNET +#define d2i_SXNETID GRPC_SHADOW_d2i_SXNETID +#define i2d_SXNET GRPC_SHADOW_i2d_SXNET +#define i2d_SXNETID GRPC_SHADOW_i2d_SXNETID +#define v3_sxnet GRPC_SHADOW_v3_sxnet +#define X509V3_NAME_from_section GRPC_SHADOW_X509V3_NAME_from_section +#define X509V3_add_value GRPC_SHADOW_X509V3_add_value +#define X509V3_add_value_bool GRPC_SHADOW_X509V3_add_value_bool +#define X509V3_add_value_bool_nf GRPC_SHADOW_X509V3_add_value_bool_nf +#define X509V3_add_value_int GRPC_SHADOW_X509V3_add_value_int +#define X509V3_add_value_uchar GRPC_SHADOW_X509V3_add_value_uchar +#define X509V3_conf_free GRPC_SHADOW_X509V3_conf_free +#define X509V3_get_value_bool GRPC_SHADOW_X509V3_get_value_bool +#define X509V3_get_value_int GRPC_SHADOW_X509V3_get_value_int +#define X509V3_parse_list GRPC_SHADOW_X509V3_parse_list +#define X509_REQ_get1_email GRPC_SHADOW_X509_REQ_get1_email +#define X509_check_email GRPC_SHADOW_X509_check_email +#define X509_check_host GRPC_SHADOW_X509_check_host +#define X509_check_ip GRPC_SHADOW_X509_check_ip +#define X509_check_ip_asc GRPC_SHADOW_X509_check_ip_asc +#define X509_email_free GRPC_SHADOW_X509_email_free +#define X509_get1_email GRPC_SHADOW_X509_get1_email +#define X509_get1_ocsp GRPC_SHADOW_X509_get1_ocsp +#define a2i_IPADDRESS GRPC_SHADOW_a2i_IPADDRESS +#define a2i_IPADDRESS_NC GRPC_SHADOW_a2i_IPADDRESS_NC +#define a2i_ipadd GRPC_SHADOW_a2i_ipadd +#define hex_to_string GRPC_SHADOW_hex_to_string +#define i2s_ASN1_ENUMERATED GRPC_SHADOW_i2s_ASN1_ENUMERATED +#define i2s_ASN1_INTEGER GRPC_SHADOW_i2s_ASN1_INTEGER +#define name_cmp GRPC_SHADOW_name_cmp +#define s2i_ASN1_INTEGER GRPC_SHADOW_s2i_ASN1_INTEGER +#define string_to_hex GRPC_SHADOW_string_to_hex +#define PKCS7_get_raw_certificates GRPC_SHADOW_PKCS7_get_raw_certificates +#define pkcs7_bundle GRPC_SHADOW_pkcs7_bundle +#define pkcs7_parse_header GRPC_SHADOW_pkcs7_parse_header +#define PKCS7_bundle_CRLs GRPC_SHADOW_PKCS7_bundle_CRLs +#define PKCS7_bundle_certificates GRPC_SHADOW_PKCS7_bundle_certificates +#define PKCS7_get_CRLs GRPC_SHADOW_PKCS7_get_CRLs +#define PKCS7_get_PEM_CRLs GRPC_SHADOW_PKCS7_get_PEM_CRLs +#define PKCS7_get_PEM_certificates GRPC_SHADOW_PKCS7_get_PEM_certificates +#define PKCS7_get_certificates GRPC_SHADOW_PKCS7_get_certificates +#define PKCS8_marshal_encrypted_private_key GRPC_SHADOW_PKCS8_marshal_encrypted_private_key +#define PKCS8_parse_encrypted_private_key GRPC_SHADOW_PKCS8_parse_encrypted_private_key +#define pkcs12_key_gen GRPC_SHADOW_pkcs12_key_gen +#define pkcs8_pbe_decrypt GRPC_SHADOW_pkcs8_pbe_decrypt +#define EVP_PKCS82PKEY GRPC_SHADOW_EVP_PKCS82PKEY +#define EVP_PKEY2PKCS8 GRPC_SHADOW_EVP_PKEY2PKCS8 +#define PKCS12_PBE_add GRPC_SHADOW_PKCS12_PBE_add +#define PKCS12_free GRPC_SHADOW_PKCS12_free +#define PKCS12_get_key_and_certs GRPC_SHADOW_PKCS12_get_key_and_certs +#define PKCS12_parse GRPC_SHADOW_PKCS12_parse +#define PKCS12_verify_mac GRPC_SHADOW_PKCS12_verify_mac +#define PKCS8_PRIV_KEY_INFO_free GRPC_SHADOW_PKCS8_PRIV_KEY_INFO_free +#define PKCS8_PRIV_KEY_INFO_it GRPC_SHADOW_PKCS8_PRIV_KEY_INFO_it +#define PKCS8_PRIV_KEY_INFO_new GRPC_SHADOW_PKCS8_PRIV_KEY_INFO_new +#define PKCS8_decrypt GRPC_SHADOW_PKCS8_decrypt +#define PKCS8_encrypt GRPC_SHADOW_PKCS8_encrypt +#define d2i_PKCS12 GRPC_SHADOW_d2i_PKCS12 +#define d2i_PKCS12_bio GRPC_SHADOW_d2i_PKCS12_bio +#define d2i_PKCS12_fp GRPC_SHADOW_d2i_PKCS12_fp +#define d2i_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_d2i_PKCS8_PRIV_KEY_INFO +#define i2d_PKCS8_PRIV_KEY_INFO GRPC_SHADOW_i2d_PKCS8_PRIV_KEY_INFO +#define PKCS5_pbe2_decrypt_init GRPC_SHADOW_PKCS5_pbe2_decrypt_init +#define PKCS5_pbe2_encrypt_init GRPC_SHADOW_PKCS5_pbe2_encrypt_init + +#endif /* GRPC_SHADOW_BORINGSSL_SYMBOLS */ + +#endif /* GRPC_CORE_TSI_GRPC_SHADOW_BORINGSSL_H */ diff --git a/third_party/grpc/src/core/tsi/local_transport_security.cc b/third_party/grpc/src/core/tsi/local_transport_security.cc new file mode 100644 index 00000000000000..99fd56dbd2b2f7 --- /dev/null +++ b/third_party/grpc/src/core/tsi/local_transport_security.cc @@ -0,0 +1,209 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/local_transport_security.h" + +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/tsi/transport_security_grpc.h" + +/* Main struct for local TSI zero-copy frame protector. */ +typedef struct local_zero_copy_grpc_protector { + tsi_zero_copy_grpc_protector base; +} local_zero_copy_grpc_protector; + +/* Main struct for local TSI handshaker result. */ +typedef struct local_tsi_handshaker_result { + tsi_handshaker_result base; + bool is_client; +} local_tsi_handshaker_result; + +/* Main struct for local TSI handshaker. */ +typedef struct local_tsi_handshaker { + tsi_handshaker base; + bool is_client; +} local_tsi_handshaker; + +/* --- tsi_zero_copy_grpc_protector methods implementation. --- */ + +static tsi_result local_zero_copy_grpc_protector_protect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, "Invalid nullptr arguments to zero-copy grpc protect."); + return TSI_INVALID_ARGUMENT; + } + grpc_slice_buffer_move_into(unprotected_slices, protected_slices); + return TSI_OK; +} + +static tsi_result local_zero_copy_grpc_protector_unprotect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + if (self == nullptr || unprotected_slices == nullptr || + protected_slices == nullptr) { + gpr_log(GPR_ERROR, + "Invalid nullptr arguments to zero-copy grpc unprotect."); + return TSI_INVALID_ARGUMENT; + } + grpc_slice_buffer_move_into(protected_slices, unprotected_slices); + return TSI_OK; +} + +static void local_zero_copy_grpc_protector_destroy( + tsi_zero_copy_grpc_protector* self) { + gpr_free(self); +} + +static const tsi_zero_copy_grpc_protector_vtable + local_zero_copy_grpc_protector_vtable = { + local_zero_copy_grpc_protector_protect, + local_zero_copy_grpc_protector_unprotect, + local_zero_copy_grpc_protector_destroy}; + +tsi_result local_zero_copy_grpc_protector_create( + tsi_zero_copy_grpc_protector** protector) { + if (grpc_core::ExecCtx::Get() == nullptr || protector == nullptr) { + gpr_log( + GPR_ERROR, + "Invalid nullptr arguments to local_zero_copy_grpc_protector create."); + return TSI_INVALID_ARGUMENT; + } + local_zero_copy_grpc_protector* impl = + static_cast(gpr_zalloc(sizeof(*impl))); + impl->base.vtable = &local_zero_copy_grpc_protector_vtable; + *protector = &impl->base; + return TSI_OK; +} + +/* --- tsi_handshaker_result methods implementation. --- */ + +static tsi_result handshaker_result_extract_peer( + const tsi_handshaker_result* self, tsi_peer* peer) { + return TSI_OK; +} + +static tsi_result handshaker_result_create_zero_copy_grpc_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_zero_copy_grpc_protector** protector) { + if (self == nullptr || protector == nullptr) { + gpr_log(GPR_ERROR, + "Invalid arguments to create_zero_copy_grpc_protector()"); + return TSI_INVALID_ARGUMENT; + } + tsi_result ok = local_zero_copy_grpc_protector_create(protector); + if (ok != TSI_OK) { + gpr_log(GPR_ERROR, "Failed to create zero-copy grpc protector"); + } + return ok; +} + +static void handshaker_result_destroy(tsi_handshaker_result* self) { + if (self == nullptr) { + return; + } + local_tsi_handshaker_result* result = + reinterpret_cast( + const_cast(self)); + gpr_free(result); +} + +static const tsi_handshaker_result_vtable result_vtable = { + handshaker_result_extract_peer, + handshaker_result_create_zero_copy_grpc_protector, + nullptr, /* handshaker_result_create_frame_protector */ + nullptr, /* handshaker_result_get_unused_bytes */ + handshaker_result_destroy}; + +static tsi_result create_handshaker_result(bool is_client, + tsi_handshaker_result** self) { + if (self == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to create_handshaker_result()"); + return TSI_INVALID_ARGUMENT; + } + local_tsi_handshaker_result* result = + static_cast(gpr_zalloc(sizeof(*result))); + result->is_client = is_client; + result->base.vtable = &result_vtable; + *self = &result->base; + return TSI_OK; +} + +/* --- tsi_handshaker methods implementation. --- */ + +static tsi_result handshaker_next( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** result, + tsi_handshaker_on_next_done_cb cb, void* user_data) { + if (self == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to handshaker_next()"); + return TSI_INVALID_ARGUMENT; + } + /* Note that there is no interaction between TSI peers, and all operations are + * local. + */ + local_tsi_handshaker* handshaker = + reinterpret_cast(self); + *bytes_to_send_size = 0; + create_handshaker_result(handshaker->is_client, result); + return TSI_OK; +} + +static void handshaker_destroy(tsi_handshaker* self) { + if (self == nullptr) { + return; + } + local_tsi_handshaker* handshaker = + reinterpret_cast(self); + gpr_free(handshaker); +} + +static const tsi_handshaker_vtable handshaker_vtable = { + nullptr, /* get_bytes_to_send_to_peer -- deprecated */ + nullptr, /* process_bytes_from_peer -- deprecated */ + nullptr, /* get_result -- deprecated */ + nullptr, /* extract_peer -- deprecated */ + nullptr, /* create_frame_protector -- deprecated */ + handshaker_destroy, + handshaker_next, + nullptr, /* shutdown */ +}; + +tsi_result local_tsi_handshaker_create(bool is_client, tsi_handshaker** self) { + if (self == nullptr) { + gpr_log(GPR_ERROR, "Invalid arguments to local_tsi_handshaker_create()"); + return TSI_INVALID_ARGUMENT; + } + local_tsi_handshaker* handshaker = + static_cast(gpr_zalloc(sizeof(*handshaker))); + handshaker->is_client = is_client; + handshaker->base.vtable = &handshaker_vtable; + *self = &handshaker->base; + return TSI_OK; +} diff --git a/third_party/grpc/src/core/tsi/local_transport_security.h b/third_party/grpc/src/core/tsi/local_transport_security.h new file mode 100644 index 00000000000000..17213ecf35a193 --- /dev/null +++ b/third_party/grpc/src/core/tsi/local_transport_security.h @@ -0,0 +1,51 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_LOCAL_TRANSPORT_SECURITY_H +#define GRPC_CORE_TSI_LOCAL_TRANSPORT_SECURITY_H + +#include + +#include + +#include "src/core/tsi/transport_security.h" +#include "src/core/tsi/transport_security_interface.h" + +#define TSI_LOCAL_NUM_OF_PEER_PROPERTIES 1 +#define TSI_LOCAL_PROCESS_ID_PEER_PROPERTY "process_id" + +/** + * Main struct for local TSI handshaker. All APIs in the header are + * thread-comptabile. + */ +typedef struct local_tsi_handshaker local_tsi_handshaker; + +/** + * This method creates a local TSI handshaker instance. + * + * - is_client: boolean value indicating if the handshaker is used at the client + * (is_client = true) or server (is_client = false) side. The parameter is + * added for future extension. + * - self: address of local TSI handshaker instance to be returned from the + * method. + * + * It returns TSI_OK on success and an error status code on failure. + */ +tsi_result local_tsi_handshaker_create(bool is_client, tsi_handshaker** self); + +#endif /* GRPC_CORE_TSI_LOCAL_TRANSPORT_SECURITY_H */ diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session.h b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session.h new file mode 100644 index 00000000000000..e847267cb30344 --- /dev/null +++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session.h @@ -0,0 +1,75 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H +#define GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H + +#include + +#include "src/core/tsi/grpc_shadow_boringssl.h" + +#include + +extern "C" { +#include +} + +#include "src/core/lib/gprpp/ref_counted.h" + +// The main purpose of code here is to provide means to cache SSL sessions +// in a way that they can be shared between connections. +// +// SSL_SESSION stands for single instance of session and is not generally safe +// to share between SSL contexts with different lifetimes. It happens because +// not all SSL implementations guarantee immutability of SSL_SESSION object. +// See SSL_SESSION documentation in BoringSSL and OpenSSL for more details. + +namespace tsi { + +struct SslSessionDeleter { + void operator()(SSL_SESSION* session) { SSL_SESSION_free(session); } +}; + +typedef std::unique_ptr SslSessionPtr; + +/// SslCachedSession is an immutable thread-safe storage for single session +/// representation. It provides means to share SSL session data (e.g. TLS +/// ticket) between encrypted connections regardless of SSL context lifetime. +class SslCachedSession { + public: + // Not copyable nor movable. + SslCachedSession(const SslCachedSession&) = delete; + SslCachedSession& operator=(const SslCachedSession&) = delete; + + /// Create single cached instance of \a session. + static grpc_core::UniquePtr Create(SslSessionPtr session); + + virtual ~SslCachedSession() = default; + + /// Returns a copy of previously cached session. + virtual SslSessionPtr CopySession() const GRPC_ABSTRACT; + + GRPC_ABSTRACT_BASE_CLASS + + protected: + SslCachedSession() = default; +}; + +} // namespace tsi + +#endif /* GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_H */ diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc new file mode 100644 index 00000000000000..0da5a96164e422 --- /dev/null +++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_boringssl.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/ssl/session_cache/ssl_session.h" + +#ifdef OPENSSL_IS_BORINGSSL + +// BoringSSL allows SSL_SESSION to outlive SSL and SSL_CTX objects which are +// re-created by gRPC on every certificate rotation or subchannel creation. +// BoringSSL guarantees that SSL_SESSION is immutable so it's safe to share +// the same original session object between different threads and connections. + +namespace tsi { +namespace { + +class BoringSslCachedSession : public SslCachedSession { + public: + BoringSslCachedSession(SslSessionPtr session) + : session_(std::move(session)) {} + + SslSessionPtr CopySession() const override { + // SslSessionPtr will dereference on destruction. + SSL_SESSION_up_ref(session_.get()); + return SslSessionPtr(session_.get()); + } + + private: + SslSessionPtr session_; +}; + +} // namespace + +grpc_core::UniquePtr SslCachedSession::Create( + SslSessionPtr session) { + return grpc_core::UniquePtr( + grpc_core::New(std::move(session))); +} + +} // namespace tsi + +#endif /* OPENSSL_IS_BORINGSSL */ diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.cc b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.cc new file mode 100644 index 00000000000000..f9184bcc34f573 --- /dev/null +++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.cc @@ -0,0 +1,212 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/gprpp/mutex_lock.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/ssl/session_cache/ssl_session.h" +#include "src/core/tsi/ssl/session_cache/ssl_session_cache.h" + +#include +#include + +namespace tsi { + +static void cache_key_avl_destroy(void* key, void* unused) {} + +static void* cache_key_avl_copy(void* key, void* unused) { return key; } + +static long cache_key_avl_compare(void* key1, void* key2, void* unused) { + return grpc_slice_cmp(*static_cast(key1), + *static_cast(key2)); +} + +static void cache_value_avl_destroy(void* value, void* unused) {} + +static void* cache_value_avl_copy(void* value, void* unused) { return value; } + +// AVL only stores pointers, ownership belonges to the linked list. +static const grpc_avl_vtable cache_avl_vtable = { + cache_key_avl_destroy, cache_key_avl_copy, cache_key_avl_compare, + cache_value_avl_destroy, cache_value_avl_copy, +}; + +/// Node for single cached session. +class SslSessionLRUCache::Node { + public: + Node(const grpc_slice& key, SslSessionPtr session) : key_(key) { + SetSession(std::move(session)); + } + + ~Node() { grpc_slice_unref_internal(key_); } + + // Not copyable nor movable. + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + + void* AvlKey() { return &key_; } + + /// Returns a copy of the node's cache session. + SslSessionPtr CopySession() const { return session_->CopySession(); } + + /// Set the \a session (which is moved) for the node. + void SetSession(SslSessionPtr session) { + session_ = SslCachedSession::Create(std::move(session)); + } + + private: + friend class SslSessionLRUCache; + + grpc_slice key_; + grpc_core::UniquePtr session_; + + Node* next_ = nullptr; + Node* prev_ = nullptr; +}; + +SslSessionLRUCache::SslSessionLRUCache(size_t capacity) : capacity_(capacity) { + GPR_ASSERT(capacity > 0); + gpr_mu_init(&lock_); + entry_by_key_ = grpc_avl_create(&cache_avl_vtable); +} + +SslSessionLRUCache::~SslSessionLRUCache() { + Node* node = use_order_list_head_; + while (node) { + Node* next = node->next_; + grpc_core::Delete(node); + node = next; + } + grpc_avl_unref(entry_by_key_, nullptr); + gpr_mu_destroy(&lock_); +} + +size_t SslSessionLRUCache::Size() { + grpc_core::MutexLock lock(&lock_); + return use_order_list_size_; +} + +SslSessionLRUCache::Node* SslSessionLRUCache::FindLocked( + const grpc_slice& key) { + void* value = + grpc_avl_get(entry_by_key_, const_cast(&key), nullptr); + if (value == nullptr) { + return nullptr; + } + Node* node = static_cast(value); + // Move to the beginning. + Remove(node); + PushFront(node); + AssertInvariants(); + return node; +} + +void SslSessionLRUCache::Put(const char* key, SslSessionPtr session) { + grpc_core::MutexLock lock(&lock_); + Node* node = FindLocked(grpc_slice_from_static_string(key)); + if (node != nullptr) { + node->SetSession(std::move(session)); + return; + } + grpc_slice key_slice = grpc_slice_from_copied_string(key); + node = grpc_core::New(key_slice, std::move(session)); + PushFront(node); + entry_by_key_ = grpc_avl_add(entry_by_key_, node->AvlKey(), node, nullptr); + AssertInvariants(); + if (use_order_list_size_ > capacity_) { + GPR_ASSERT(use_order_list_tail_); + node = use_order_list_tail_; + Remove(node); + // Order matters, key is destroyed after deleting node. + entry_by_key_ = grpc_avl_remove(entry_by_key_, node->AvlKey(), nullptr); + grpc_core::Delete(node); + AssertInvariants(); + } +} + +SslSessionPtr SslSessionLRUCache::Get(const char* key) { + grpc_core::MutexLock lock(&lock_); + // Key is only used for lookups. + grpc_slice key_slice = grpc_slice_from_static_string(key); + Node* node = FindLocked(key_slice); + if (node == nullptr) { + return nullptr; + } + return node->CopySession(); +} + +void SslSessionLRUCache::Remove(SslSessionLRUCache::Node* node) { + if (node->prev_ == nullptr) { + use_order_list_head_ = node->next_; + } else { + node->prev_->next_ = node->next_; + } + if (node->next_ == nullptr) { + use_order_list_tail_ = node->prev_; + } else { + node->next_->prev_ = node->prev_; + } + GPR_ASSERT(use_order_list_size_ >= 1); + use_order_list_size_--; +} + +void SslSessionLRUCache::PushFront(SslSessionLRUCache::Node* node) { + if (use_order_list_head_ == nullptr) { + use_order_list_head_ = node; + use_order_list_tail_ = node; + node->next_ = nullptr; + node->prev_ = nullptr; + } else { + node->next_ = use_order_list_head_; + node->next_->prev_ = node; + use_order_list_head_ = node; + node->prev_ = nullptr; + } + use_order_list_size_++; +} + +#ifndef NDEBUG +static size_t calculate_tree_size(grpc_avl_node* node) { + if (node == nullptr) { + return 0; + } + return 1 + calculate_tree_size(node->left) + calculate_tree_size(node->right); +} + +void SslSessionLRUCache::AssertInvariants() { + size_t size = 0; + Node* prev = nullptr; + Node* current = use_order_list_head_; + while (current != nullptr) { + size++; + GPR_ASSERT(current->prev_ == prev); + void* node = grpc_avl_get(entry_by_key_, current->AvlKey(), nullptr); + GPR_ASSERT(node == current); + prev = current; + current = current->next_; + } + GPR_ASSERT(prev == use_order_list_tail_); + GPR_ASSERT(size == use_order_list_size_); + GPR_ASSERT(calculate_tree_size(entry_by_key_.root) == use_order_list_size_); +} +#else +void SslSessionLRUCache::AssertInvariants() {} +#endif + +} // namespace tsi diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.h b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.h new file mode 100644 index 00000000000000..37fa2d124c986e --- /dev/null +++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_cache.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H +#define GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H + +#include + +#include "src/core/tsi/grpc_shadow_boringssl.h" + +#include +#include + +extern "C" { +#include +} + +#include "src/core/lib/avl/avl.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/ref_counted.h" +#include "src/core/tsi/ssl/session_cache/ssl_session.h" + +/// Cache for SSL sessions for sessions resumption. +/// +/// Older sessions may be evicted from the cache using LRU policy if capacity +/// limit is hit. All sessions are associated with some key, usually server +/// name. Note that servers are required to share session ticket encryption keys +/// in order for cache to be effective. +/// +/// This class is thread safe. + +namespace tsi { + +class SslSessionLRUCache : public grpc_core::RefCounted { + public: + /// Create new LRU cache with the given capacity. + static grpc_core::RefCountedPtr Create(size_t capacity) { + return grpc_core::MakeRefCounted(capacity); + } + + // Not copyable nor movable. + SslSessionLRUCache(const SslSessionLRUCache&) = delete; + SslSessionLRUCache& operator=(const SslSessionLRUCache&) = delete; + + /// Returns current number of sessions in the cache. + size_t Size(); + /// Add \a session in the cache using \a key. This operation may discard older + /// sessions. + void Put(const char* key, SslSessionPtr session); + /// Returns the session from the cache associated with \a key or null if not + /// found. + SslSessionPtr Get(const char* key); + + private: + // So New() can call our private ctor. + template + friend T* grpc_core::New(Args&&... args); + + // So Delete() can call our private dtor. + template + friend void grpc_core::Delete(T*); + + class Node; + + explicit SslSessionLRUCache(size_t capacity); + ~SslSessionLRUCache(); + + Node* FindLocked(const grpc_slice& key); + void Remove(Node* node); + void PushFront(Node* node); + void AssertInvariants(); + + gpr_mu lock_; + size_t capacity_; + + Node* use_order_list_head_ = nullptr; + Node* use_order_list_tail_ = nullptr; + size_t use_order_list_size_ = 0; + grpc_avl entry_by_key_; +}; + +} // namespace tsi + +#endif /* GRPC_CORE_TSI_SSL_SESSION_CACHE_SSL_SESSION_CACHE_H */ diff --git a/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc new file mode 100644 index 00000000000000..61c036c7eb5681 --- /dev/null +++ b/third_party/grpc/src/core/tsi/ssl/session_cache/ssl_session_openssl.cc @@ -0,0 +1,76 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/ssl/session_cache/ssl_session.h" + +#include + +#ifndef OPENSSL_IS_BORINGSSL + +// OpenSSL invalidates SSL_SESSION on SSL destruction making it pointless +// to cache sessions. The workaround is to serialize (relatively expensive) +// session into binary blob and re-create it from blob on every handshake. +// Note that it's safe to keep serialized session outside of SSL lifetime +// as openssl performs all necessary validation while attempting to use a +// session and creates a new one if something is wrong (e.g. server changed +// set of allowed codecs). + +namespace tsi { +namespace { + +class OpenSslCachedSession : public SslCachedSession { + public: + OpenSslCachedSession(SslSessionPtr session) { + int size = i2d_SSL_SESSION(session.get(), nullptr); + GPR_ASSERT(size > 0); + grpc_slice slice = grpc_slice_malloc(size_t(size)); + unsigned char* start = GRPC_SLICE_START_PTR(slice); + int second_size = i2d_SSL_SESSION(session.get(), &start); + GPR_ASSERT(size == second_size); + serialized_session_ = slice; + } + + virtual ~OpenSslCachedSession() { grpc_slice_unref(serialized_session_); } + + SslSessionPtr CopySession() const override { + const unsigned char* data = GRPC_SLICE_START_PTR(serialized_session_); + size_t length = GRPC_SLICE_LENGTH(serialized_session_); + SSL_SESSION* session = d2i_SSL_SESSION(nullptr, &data, length); + if (session == nullptr) { + return SslSessionPtr(); + } + return SslSessionPtr(session); + } + + private: + grpc_slice serialized_session_; +}; + +} // namespace + +grpc_core::UniquePtr SslCachedSession::Create( + SslSessionPtr session) { + return grpc_core::UniquePtr( + grpc_core::New(std::move(session))); +} + +} // namespace tsi + +#endif /* OPENSSL_IS_BORINGSSL */ diff --git a/third_party/grpc/src/core/tsi/ssl_transport_security.c b/third_party/grpc/src/core/tsi/ssl_transport_security.c deleted file mode 100644 index 6adcaac9edefd2..00000000000000 --- a/third_party/grpc/src/core/tsi/ssl_transport_security.c +++ /dev/null @@ -1,1469 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 -#include /* For OPENSSL_free */ -#include -#include -#include -#include - -#include "src/core/tsi/ssl_types.h" -#include "src/core/tsi/transport_security.h" - -/* --- 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; - int num_locks; - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - num_locks = CRYPTO_num_locks(); - GPR_ASSERT(num_locks > 0); - openssl_mutexes = malloc((size_t)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 = (size_t)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, - size_t subject_alt_name_count) { - size_t 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, TSI_SIZE_AS_SIZE(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, (size_t)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) - ? (int)sk_GENERAL_NAME_num(subject_alt_names) - : 0; - size_t property_count; - tsi_result result; - GPR_ASSERT(subject_alt_name_count >= 0); - property_count = (include_certificate_type ? (size_t)1 : 0) + - 1 /* common name */ + (size_t)subject_alt_name_count; - 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, (size_t)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((uint32_t)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; - GPR_ASSERT(*unprotected_bytes_size <= INT_MAX); - read_from_ssl = - SSL_read(ssl, unprotected_bytes, (int)*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 = (size_t)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; - GPR_ASSERT(unprotected_bytes_size <= INT_MAX); - ssl_write_result = - SSL_write(ssl, unprotected_bytes, (int)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; - GPR_ASSERT(pem_cert_chain_size <= INT_MAX); - pem = BIO_new_mem_buf((void *)pem_cert_chain, (int)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; - GPR_ASSERT(pem_key_size <= INT_MAX); - pem = BIO_new_mem_buf((void *)pem_key, (int)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; - X509_STORE *root_store; - GPR_ASSERT(pem_roots_size <= INT_MAX); - pem = BIO_new_mem_buf((void *)pem_roots, (int)pem_roots_size); - 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; - GPR_ASSERT(pem_cert_size <= INT_MAX); - pem = BIO_new_mem_buf((void *)pem_cert, (int)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 += (size_t)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) || - ((uintptr_t)(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. */ - int pending_in_ssl = (int)BIO_pending(impl->from_ssl); - if (pending_in_ssl > 0) { - *unprotected_bytes_size = 0; - GPR_ASSERT(*protected_output_frames_size <= INT_MAX); - read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - (int)*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 = (size_t)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; - - GPR_ASSERT(*protected_output_frames_size <= INT_MAX); - read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - (int)*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 = (size_t)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; - int pending; - - 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; - } - - pending = (int)BIO_pending(impl->from_ssl); - GPR_ASSERT(pending >= 0); - *still_pending_size = (size_t)pending; - if (*still_pending_size == 0) return TSI_OK; - - GPR_ASSERT(*protected_output_frames_size <= INT_MAX); - read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - (int)*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 = (size_t)read_from_ssl; - pending = (int)BIO_pending(impl->from_ssl); - GPR_ASSERT(pending >= 0); - *still_pending_size = (size_t)pending; - 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. */ - GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX); - written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes, - (int)*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 = (size_t)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; - } - GPR_ASSERT(*bytes_size <= INT_MAX); - bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, (int)*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; - } - GPR_ASSERT(*bytes_size <= INT_MAX); - bytes_written_into_ssl_size = - BIO_write(impl->into_ssl, bytes, (int)*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 = (size_t)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, - size_t client_list_len, - const unsigned char *server_list, - size_t 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) && - (uintptr_t)(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; - GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX); - *outlen = (unsigned int)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 - GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX); - if (SSL_CTX_set_alpn_protos( - ssl_context, impl->alpn_protocol_list, - (unsigned int)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/third_party/grpc/src/core/tsi/ssl_transport_security.cc b/third_party/grpc/src/core/tsi/ssl_transport_security.cc new file mode 100644 index 00000000000000..fb6ea192106981 --- /dev/null +++ b/third_party/grpc/src/core/tsi/ssl_transport_security.cc @@ -0,0 +1,1971 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/grpc_shadow_boringssl.h" + +#include "src/core/tsi/ssl_transport_security.h" + +#include +#include + +/* TODO(jboeuf): refactor inet_ntop into a portability header. */ +/* Note: for whomever reads this and tries to refactor this, this + can't be in grpc, it has to be in gpr. */ +#ifdef GPR_WINDOWS +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include + +extern "C" { +#include +#include /* For OPENSSL_free */ +#include +#include +#include +#include +} + +#include "src/core/lib/gpr/useful.h" +#include "src/core/tsi/ssl/session_cache/ssl_session_cache.h" +#include "src/core/tsi/ssl_types.h" +#include "src/core/tsi/transport_security.h" + +/* --- Constants. ---*/ + +#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384 +#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024 +#define TSI_SSL_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE 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_root_certs_store { + X509_STORE* store; +}; + +struct tsi_ssl_handshaker_factory { + const tsi_ssl_handshaker_factory_vtable* vtable; + gpr_refcount refcount; +}; + +struct tsi_ssl_client_handshaker_factory { + tsi_ssl_handshaker_factory base; + SSL_CTX* ssl_context; + unsigned char* alpn_protocol_list; + size_t alpn_protocol_list_length; + grpc_core::RefCountedPtr session_cache; +}; + +struct tsi_ssl_server_handshaker_factory { + /* 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. */ + tsi_ssl_handshaker_factory base; + 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; +}; + +typedef struct { + tsi_handshaker base; + SSL* ssl; + BIO* network_io; + tsi_result result; + unsigned char* outgoing_bytes_buffer; + size_t outgoing_bytes_buffer_size; + tsi_ssl_handshaker_factory* factory_ref; +} tsi_ssl_handshaker; + +typedef struct { + tsi_handshaker_result base; + SSL* ssl; + BIO* network_io; + unsigned char* unused_bytes; + size_t unused_bytes_size; +} tsi_ssl_handshaker_result; + +typedef struct { + tsi_frame_protector base; + SSL* ssl; + BIO* network_io; + unsigned char* buffer; + size_t buffer_size; + size_t buffer_offset; +} tsi_ssl_frame_protector; + +/* --- Library Initialization. ---*/ + +static gpr_once g_init_openssl_once = GPR_ONCE_INIT; +static int g_ssl_ctx_ex_factory_index = -1; +static const unsigned char kSslSessionIdContext[] = {'g', 'r', 'p', 'c'}; + +#if OPENSSL_VERSION_NUMBER < 0x10100000 +static gpr_mu* g_openssl_mutexes = nullptr; +static void openssl_locking_cb(int mode, int type, const char* file, + int line) GRPC_UNUSED; +static unsigned long openssl_thread_id_cb(void) GRPC_UNUSED; + +static void openssl_locking_cb(int mode, int type, const char* file, int line) { + if (mode & CRYPTO_LOCK) { + gpr_mu_lock(&g_openssl_mutexes[type]); + } else { + gpr_mu_unlock(&g_openssl_mutexes[type]); + } +} + +static unsigned long openssl_thread_id_cb(void) { + return static_cast(gpr_thd_currentid()); +} +#endif + +static void init_openssl(void) { +#if OPENSSL_API_COMPAT >= 0x10100000L + OPENSSL_init_ssl(0, NULL); +#else + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); +#endif +#if OPENSSL_VERSION_NUMBER < 0x10100000 + if (!CRYPTO_get_locking_callback()) { + int num_locks = CRYPTO_num_locks(); + GPR_ASSERT(num_locks > 0); + g_openssl_mutexes = static_cast( + gpr_malloc(static_cast(num_locks) * sizeof(gpr_mu))); + for (int i = 0; i < num_locks; i++) { + gpr_mu_init(&g_openssl_mutexes[i]); + } + CRYPTO_set_locking_callback(openssl_locking_cb); + CRYPTO_set_id_callback(openssl_thread_id_cb); + } else { + gpr_log(GPR_INFO, "OpenSSL callback has already been set."); + } +#endif + g_ssl_ctx_ex_factory_index = + SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); + GPR_ASSERT(g_ssl_ctx_ex_factory_index != -1); +} + +/* --- 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.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 occurred.\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, and only handles IPv6 in hexadecimal form. */ +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] == ':') { + /* IPv6 Address in hexadecimal form, : is not allowed in DNS names. */ + return 1; + } + 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 = nullptr; + ASN1_STRING* common_name_asn1 = nullptr; + X509_NAME* subject_name = X509_get_subject_name(cert); + int utf8_returned_size = 0; + if (subject_name == nullptr) { + gpr_log(GPR_INFO, "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_INFO, "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 == nullptr) { + 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 == nullptr) { + 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 = static_cast(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 = nullptr; + common_name_size = 0; + } else { + return result; + } + } + result = tsi_construct_string_peer_property( + TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, + common_name == nullptr ? "" : reinterpret_cast(common_name), + common_name_size, property); + OPENSSL_free(common_name); + return result; +} + +/* Gets the X509 cert in PEM format as a tsi_peer_property. */ +static tsi_result add_pem_certificate(X509* cert, tsi_peer_property* property) { + BIO* bio = BIO_new(BIO_s_mem()); + if (!PEM_write_bio_X509(bio, cert)) { + BIO_free(bio); + return TSI_INTERNAL_ERROR; + } + char* contents; + long len = BIO_get_mem_data(bio, &contents); + if (len <= 0) { + BIO_free(bio); + return TSI_INTERNAL_ERROR; + } + tsi_result result = tsi_construct_string_peer_property( + TSI_X509_PEM_CERT_PROPERTY, (const char*)contents, + static_cast(len), property); + BIO_free(bio); + 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, + size_t subject_alt_name_count) { + size_t 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, TSI_SIZE_AS_SIZE(i)); + /* Filter out the non-dns entries names. */ + if (subject_alt_name->type == GEN_DNS) { + unsigned char* name = nullptr; + int name_size; + name_size = ASN1_STRING_to_UTF8(&name, subject_alt_name->d.dNSName); + if (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, + reinterpret_cast(name), static_cast(name_size), + &peer->properties[peer->property_count++]); + OPENSSL_free(name); + } else if (subject_alt_name->type == GEN_IPADD) { + char ntop_buf[INET6_ADDRSTRLEN]; + int af; + + if (subject_alt_name->d.iPAddress->length == 4) { + af = AF_INET; + } else if (subject_alt_name->d.iPAddress->length == 16) { + af = AF_INET6; + } else { + gpr_log(GPR_ERROR, "SAN IP Address contained invalid IP"); + result = TSI_INTERNAL_ERROR; + break; + } + const char* name = inet_ntop(af, subject_alt_name->d.iPAddress->data, + ntop_buf, INET6_ADDRSTRLEN); + if (name == nullptr) { + gpr_log(GPR_ERROR, "Could not get IP string from asn1 octet."); + result = TSI_INTERNAL_ERROR; + break; + } + + result = tsi_construct_string_peer_property_from_cstring( + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, name, + &peer->properties[peer->property_count++]); + } + 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 = static_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); + int subject_alt_name_count = + (subject_alt_names != nullptr) + ? static_cast(sk_GENERAL_NAME_num(subject_alt_names)) + : 0; + size_t property_count; + tsi_result result; + GPR_ASSERT(subject_alt_name_count >= 0); + property_count = (include_certificate_type ? static_cast(1) : 0) + + 2 /* common name, certificate */ + + static_cast(subject_alt_name_count); + 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; + + result = add_pem_certificate( + cert, &peer->properties[include_certificate_type ? 2 : 1]); + if (result != TSI_OK) break; + + if (subject_alt_name_count != 0) { + result = add_subject_alt_names_properties_to_peer( + peer, subject_alt_names, static_cast(subject_alt_name_count)); + if (result != TSI_OK) break; + } + } while (0); + + if (subject_alt_names != nullptr) { + 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(static_cast(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; + GPR_ASSERT(*unprotected_bytes_size <= INT_MAX); + read_from_ssl = SSL_read(ssl, unprotected_bytes, + static_cast(*unprotected_bytes_size)); + if (read_from_ssl <= 0) { + read_from_ssl = SSL_get_error(ssl, read_from_ssl); + switch (read_from_ssl) { + case SSL_ERROR_ZERO_RETURN: /* Received a close_notify alert. */ + 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 = static_cast(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; + GPR_ASSERT(unprotected_bytes_size <= INT_MAX); + ssl_write_result = SSL_write(ssl, unprotected_bytes, + static_cast(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 char* pem_cert_chain, + size_t pem_cert_chain_size) { + tsi_result result = TSI_OK; + X509* certificate = nullptr; + BIO* pem; + GPR_ASSERT(pem_cert_chain_size <= INT_MAX); + pem = BIO_new_mem_buf((void*)pem_cert_chain, + static_cast(pem_cert_chain_size)); + if (pem == nullptr) return TSI_OUT_OF_RESOURCES; + + do { + certificate = PEM_read_bio_X509_AUX(pem, nullptr, nullptr, (void*)""); + if (certificate == nullptr) { + 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, nullptr, nullptr, (void*)""); + if (certificate_authority == nullptr) { + 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 != nullptr) 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 char* pem_key, + size_t pem_key_size) { + tsi_result result = TSI_OK; + EVP_PKEY* private_key = nullptr; + BIO* pem; + GPR_ASSERT(pem_key_size <= INT_MAX); + pem = BIO_new_mem_buf((void*)pem_key, static_cast(pem_key_size)); + if (pem == nullptr) return TSI_OUT_OF_RESOURCES; + do { + private_key = PEM_read_bio_PrivateKey(pem, nullptr, nullptr, (void*)""); + if (private_key == nullptr) { + result = TSI_INVALID_ARGUMENT; + break; + } + if (!SSL_CTX_use_PrivateKey(context, private_key)) { + result = TSI_INVALID_ARGUMENT; + break; + } + } while (0); + if (private_key != nullptr) 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 x509_store_load_certs(X509_STORE* cert_store, + const 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 = nullptr; + X509_NAME* root_name = nullptr; + BIO* pem; + GPR_ASSERT(pem_roots_size <= INT_MAX); + pem = BIO_new_mem_buf((void*)pem_roots, static_cast(pem_roots_size)); + if (cert_store == nullptr) return TSI_INVALID_ARGUMENT; + if (pem == nullptr) return TSI_OUT_OF_RESOURCES; + if (root_names != nullptr) { + *root_names = sk_X509_NAME_new_null(); + if (*root_names == nullptr) return TSI_OUT_OF_RESOURCES; + } + + while (1) { + root = PEM_read_bio_X509_AUX(pem, nullptr, nullptr, (void*)""); + if (root == nullptr) { + ERR_clear_error(); + break; /* We're at the end of stream. */ + } + if (root_names != nullptr) { + root_name = X509_get_subject_name(root); + if (root_name == nullptr) { + 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 == nullptr) { + result = TSI_OUT_OF_RESOURCES; + break; + } + sk_X509_NAME_push(*root_names, root_name); + root_name = nullptr; + } + if (!X509_STORE_add_cert(cert_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 != nullptr) X509_free(root); + if (root_names != nullptr) { + sk_X509_NAME_pop_free(*root_names, X509_NAME_free); + *root_names = nullptr; + if (root_name != nullptr) X509_NAME_free(root_name); + } + } + BIO_free(pem); + return result; +} + +static tsi_result ssl_ctx_load_verification_certs(SSL_CTX* context, + const char* pem_roots, + size_t pem_roots_size, + STACK_OF(X509_NAME) * + *root_name) { + X509_STORE* cert_store = SSL_CTX_get_cert_store(context); + return x509_store_load_certs(cert_store, pem_roots, pem_roots_size, + root_name); +} + +/* 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 tsi_ssl_pem_key_cert_pair* key_cert_pair, + const char* cipher_list) { + tsi_result result = TSI_OK; + if (key_cert_pair != nullptr) { + if (key_cert_pair->cert_chain != nullptr) { + result = ssl_ctx_use_certificate_chain(context, key_cert_pair->cert_chain, + strlen(key_cert_pair->cert_chain)); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Invalid cert chain file."); + return result; + } + } + if (key_cert_pair->private_key != nullptr) { + result = ssl_ctx_use_private_key(context, key_cert_pair->private_key, + strlen(key_cert_pair->private_key)); + 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 != nullptr) && + !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 char* pem_cert, + tsi_peer* peer) { + tsi_result result = TSI_OK; + X509* cert = nullptr; + BIO* pem; + pem = BIO_new_mem_buf((void*)pem_cert, static_cast(strlen(pem_cert))); + if (pem == nullptr) return TSI_OUT_OF_RESOURCES; + + cert = PEM_read_bio_X509(pem, nullptr, nullptr, (void*)""); + if (cert == nullptr) { + gpr_log(GPR_ERROR, "Invalid certificate"); + result = TSI_INVALID_ARGUMENT; + } else { + result = peer_from_x509(cert, 0, peer); + } + if (cert != nullptr) 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 char** alpn_protocols, 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 = nullptr; + *protocol_name_list_length = 0; + if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT; + for (i = 0; i < num_alpn_protocols; i++) { + size_t length = + alpn_protocols[i] == nullptr ? 0 : strlen(alpn_protocols[i]); + if (length == 0 || length > 255) { + gpr_log(GPR_ERROR, "Invalid protocol name length: %d.", + static_cast(length)); + return TSI_INVALID_ARGUMENT; + } + *protocol_name_list_length += length + 1; + } + *protocol_name_list = + static_cast(gpr_malloc(*protocol_name_list_length)); + if (*protocol_name_list == nullptr) return TSI_OUT_OF_RESOURCES; + current = *protocol_name_list; + for (i = 0; i < num_alpn_protocols; i++) { + size_t length = strlen(alpn_protocols[i]); + *(current++) = static_cast(length); /* max checked above. */ + memcpy(current, alpn_protocols[i], length); + current += length; + } + /* Safety check. */ + if ((current < *protocol_name_list) || + (static_cast(current - *protocol_name_list) != + *protocol_name_list_length)) { + return TSI_INTERNAL_ERROR; + } + return TSI_OK; +} + +// The verification callback is used for clients that don't really care about +// the server's certificate, but we need to pull it anyway, in case a higher +// layer wants to look at it. In this case the verification may fail, but +// we don't really care. +static int NullVerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { + return 1; +} + +/* --- tsi_ssl_root_certs_store methods implementation. ---*/ + +tsi_ssl_root_certs_store* tsi_ssl_root_certs_store_create( + const char* pem_roots) { + if (pem_roots == nullptr) { + gpr_log(GPR_ERROR, "The root certificates are empty."); + return nullptr; + } + tsi_ssl_root_certs_store* root_store = static_cast( + gpr_zalloc(sizeof(tsi_ssl_root_certs_store))); + if (root_store == nullptr) { + gpr_log(GPR_ERROR, "Could not allocate buffer for ssl_root_certs_store."); + return nullptr; + } + root_store->store = X509_STORE_new(); + if (root_store->store == nullptr) { + gpr_log(GPR_ERROR, "Could not allocate buffer for X509_STORE."); + gpr_free(root_store); + return nullptr; + } + tsi_result result = x509_store_load_certs(root_store->store, pem_roots, + strlen(pem_roots), nullptr); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Could not load root certificates."); + X509_STORE_free(root_store->store); + gpr_free(root_store); + return nullptr; + } + return root_store; +} + +void tsi_ssl_root_certs_store_destroy(tsi_ssl_root_certs_store* self) { + if (self == nullptr) return; + X509_STORE_free(self->store); + gpr_free(self); +} + +/* --- tsi_ssl_session_cache methods implementation. ---*/ + +tsi_ssl_session_cache* tsi_ssl_session_cache_create_lru(size_t capacity) { + /* Pointer will be dereferenced by unref call. */ + return reinterpret_cast( + tsi::SslSessionLRUCache::Create(capacity).release()); +} + +void tsi_ssl_session_cache_ref(tsi_ssl_session_cache* cache) { + /* Pointer will be dereferenced by unref call. */ + reinterpret_cast(cache)->Ref().release(); +} + +void tsi_ssl_session_cache_unref(tsi_ssl_session_cache* cache) { + reinterpret_cast(cache)->Unref(); +} + +/* --- 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 = + reinterpret_cast(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. */ + int pending_in_ssl = static_cast(BIO_pending(impl->network_io)); + if (pending_in_ssl > 0) { + *unprotected_bytes_size = 0; + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); + read_from_ssl = BIO_read(impl->network_io, protected_output_frames, + static_cast(*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 = static_cast(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; + + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); + read_from_ssl = BIO_read(impl->network_io, protected_output_frames, + static_cast(*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 = static_cast(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 = + reinterpret_cast(self); + int read_from_ssl = 0; + int pending; + + 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; + } + + pending = static_cast(BIO_pending(impl->network_io)); + GPR_ASSERT(pending >= 0); + *still_pending_size = static_cast(pending); + if (*still_pending_size == 0) return TSI_OK; + + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); + read_from_ssl = BIO_read(impl->network_io, protected_output_frames, + static_cast(*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 = static_cast(read_from_ssl); + pending = static_cast(BIO_pending(impl->network_io)); + GPR_ASSERT(pending >= 0); + *still_pending_size = static_cast(pending); + 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 = + reinterpret_cast(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. */ + GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX); + written_into_ssl = BIO_write(impl->network_io, protected_frames_bytes, + static_cast(*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 = static_cast(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 = + reinterpret_cast(self); + if (impl->buffer != nullptr) gpr_free(impl->buffer); + if (impl->ssl != nullptr) SSL_free(impl->ssl); + if (impl->network_io != nullptr) BIO_free(impl->network_io); + gpr_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_server_handshaker_factory methods implementation. --- */ + +static void tsi_ssl_handshaker_factory_destroy( + tsi_ssl_handshaker_factory* self) { + if (self == nullptr) return; + + if (self->vtable != nullptr && self->vtable->destroy != nullptr) { + self->vtable->destroy(self); + } + /* Note, we don't free(self) here because this object is always directly + * embedded in another object. If tsi_ssl_handshaker_factory_init allocates + * any memory, it should be free'd here. */ +} + +static tsi_ssl_handshaker_factory* tsi_ssl_handshaker_factory_ref( + tsi_ssl_handshaker_factory* self) { + if (self == nullptr) return nullptr; + gpr_refn(&self->refcount, 1); + return self; +} + +static void tsi_ssl_handshaker_factory_unref(tsi_ssl_handshaker_factory* self) { + if (self == nullptr) return; + + if (gpr_unref(&self->refcount)) { + tsi_ssl_handshaker_factory_destroy(self); + } +} + +static tsi_ssl_handshaker_factory_vtable handshaker_factory_vtable = {nullptr}; + +/* Initializes a tsi_ssl_handshaker_factory object. Caller is responsible for + * allocating memory for the factory. */ +static void tsi_ssl_handshaker_factory_init( + tsi_ssl_handshaker_factory* factory) { + GPR_ASSERT(factory != nullptr); + + factory->vtable = &handshaker_factory_vtable; + gpr_ref_init(&factory->refcount, 1); +} + +/* --- tsi_handshaker_result methods implementation. ---*/ + +static tsi_result ssl_handshaker_result_extract_peer( + const tsi_handshaker_result* self, tsi_peer* peer) { + tsi_result result = TSI_OK; + const unsigned char* alpn_selected = nullptr; + unsigned int alpn_selected_len; + const tsi_ssl_handshaker_result* impl = + reinterpret_cast(self); + X509* peer_cert = SSL_get_peer_certificate(impl->ssl); + if (peer_cert != nullptr) { + 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 == nullptr) { + /* Try npn. */ + SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected, + &alpn_selected_len); + } + + // 1 is for session reused property. + size_t new_property_count = peer->property_count + 1; + if (alpn_selected != nullptr) new_property_count++; + tsi_peer_property* new_properties = static_cast( + gpr_zalloc(sizeof(*new_properties) * new_property_count)); + for (size_t i = 0; i < peer->property_count; i++) { + new_properties[i] = peer->properties[i]; + } + if (peer->properties != nullptr) gpr_free(peer->properties); + peer->properties = new_properties; + + if (alpn_selected != nullptr) { + result = tsi_construct_string_peer_property( + TSI_SSL_ALPN_SELECTED_PROTOCOL, + reinterpret_cast(alpn_selected), alpn_selected_len, + &peer->properties[peer->property_count]); + if (result != TSI_OK) return result; + peer->property_count++; + } + + const char* session_reused = SSL_session_reused(impl->ssl) ? "true" : "false"; + result = tsi_construct_string_peer_property_from_cstring( + TSI_SSL_SESSION_REUSED_PEER_PROPERTY, session_reused, + &peer->properties[peer->property_count]); + if (result != TSI_OK) return result; + peer->property_count++; + + return result; +} + +static tsi_result ssl_handshaker_result_create_frame_protector( + const tsi_handshaker_result* 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_result* impl = + reinterpret_cast( + const_cast(self)); + tsi_ssl_frame_protector* protector_impl = + static_cast( + gpr_zalloc(sizeof(*protector_impl))); + + if (max_output_protected_frame_size != nullptr) { + 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 = + static_cast(gpr_malloc(protector_impl->buffer_size)); + if (protector_impl->buffer == nullptr) { + gpr_log(GPR_ERROR, + "Could not allocated buffer for tsi_ssl_frame_protector."); + gpr_free(protector_impl); + return TSI_INTERNAL_ERROR; + } + + /* Transfer ownership of ssl and network_io to the frame protector. */ + protector_impl->ssl = impl->ssl; + impl->ssl = nullptr; + protector_impl->network_io = impl->network_io; + impl->network_io = nullptr; + protector_impl->base.vtable = &frame_protector_vtable; + *protector = &protector_impl->base; + return TSI_OK; +} + +static tsi_result ssl_handshaker_result_get_unused_bytes( + const tsi_handshaker_result* self, const unsigned char** bytes, + size_t* bytes_size) { + const tsi_ssl_handshaker_result* impl = + reinterpret_cast(self); + *bytes_size = impl->unused_bytes_size; + *bytes = impl->unused_bytes; + return TSI_OK; +} + +static void ssl_handshaker_result_destroy(tsi_handshaker_result* self) { + tsi_ssl_handshaker_result* impl = + reinterpret_cast(self); + SSL_free(impl->ssl); + BIO_free(impl->network_io); + gpr_free(impl->unused_bytes); + gpr_free(impl); +} + +static const tsi_handshaker_result_vtable handshaker_result_vtable = { + ssl_handshaker_result_extract_peer, + nullptr, /* create_zero_copy_grpc_protector */ + ssl_handshaker_result_create_frame_protector, + ssl_handshaker_result_get_unused_bytes, + ssl_handshaker_result_destroy, +}; + +static tsi_result ssl_handshaker_result_create( + tsi_ssl_handshaker* handshaker, const unsigned char* unused_bytes, + size_t unused_bytes_size, tsi_handshaker_result** handshaker_result) { + if (handshaker == nullptr || handshaker_result == nullptr || + (unused_bytes_size > 0 && unused_bytes == nullptr)) { + return TSI_INVALID_ARGUMENT; + } + tsi_ssl_handshaker_result* result = + static_cast(gpr_zalloc(sizeof(*result))); + result->base.vtable = &handshaker_result_vtable; + /* Transfer ownership of ssl and network_io to the handshaker result. */ + result->ssl = handshaker->ssl; + handshaker->ssl = nullptr; + result->network_io = handshaker->network_io; + handshaker->network_io = nullptr; + if (unused_bytes_size > 0) { + result->unused_bytes = + static_cast(gpr_malloc(unused_bytes_size)); + memcpy(result->unused_bytes, unused_bytes, unused_bytes_size); + } + result->unused_bytes_size = unused_bytes_size; + *handshaker_result = &result->base; + return TSI_OK; +} + +/* --- tsi_handshaker methods implementation. ---*/ + +static tsi_result ssl_handshaker_get_bytes_to_send_to_peer( + tsi_ssl_handshaker* impl, unsigned char* bytes, size_t* bytes_size) { + int bytes_read_from_ssl = 0; + if (bytes == nullptr || bytes_size == nullptr || *bytes_size == 0 || + *bytes_size > INT_MAX) { + return TSI_INVALID_ARGUMENT; + } + GPR_ASSERT(*bytes_size <= INT_MAX); + bytes_read_from_ssl = + BIO_read(impl->network_io, bytes, static_cast(*bytes_size)); + if (bytes_read_from_ssl < 0) { + *bytes_size = 0; + if (!BIO_should_retry(impl->network_io)) { + impl->result = TSI_INTERNAL_ERROR; + return impl->result; + } else { + return TSI_OK; + } + } + *bytes_size = static_cast(bytes_read_from_ssl); + return BIO_pending(impl->network_io) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA; +} + +static tsi_result ssl_handshaker_get_result(tsi_ssl_handshaker* impl) { + 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_ssl_handshaker* impl, const unsigned char* bytes, size_t* bytes_size) { + int bytes_written_into_ssl_size = 0; + if (bytes == nullptr || bytes_size == nullptr || *bytes_size > INT_MAX) { + return TSI_INVALID_ARGUMENT; + } + GPR_ASSERT(*bytes_size <= INT_MAX); + bytes_written_into_ssl_size = + BIO_write(impl->network_io, bytes, static_cast(*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 = static_cast(bytes_written_into_ssl_size); + + if (ssl_handshaker_get_result(impl) != TSI_HANDSHAKE_IN_PROGRESS) { + 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->network_io) == 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 void ssl_handshaker_destroy(tsi_handshaker* self) { + tsi_ssl_handshaker* impl = reinterpret_cast(self); + SSL_free(impl->ssl); + BIO_free(impl->network_io); + gpr_free(impl->outgoing_bytes_buffer); + tsi_ssl_handshaker_factory_unref(impl->factory_ref); + gpr_free(impl); +} + +static tsi_result ssl_handshaker_next( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result, + tsi_handshaker_on_next_done_cb cb, void* user_data) { + /* Input sanity check. */ + if ((received_bytes_size > 0 && received_bytes == nullptr) || + bytes_to_send == nullptr || bytes_to_send_size == nullptr || + handshaker_result == nullptr) { + return TSI_INVALID_ARGUMENT; + } + /* If there are received bytes, process them first. */ + tsi_ssl_handshaker* impl = reinterpret_cast(self); + tsi_result status = TSI_OK; + size_t bytes_consumed = received_bytes_size; + if (received_bytes_size > 0) { + status = ssl_handshaker_process_bytes_from_peer(impl, received_bytes, + &bytes_consumed); + if (status != TSI_OK) return status; + } + /* Get bytes to send to the peer, if available. */ + size_t offset = 0; + do { + size_t to_send_size = impl->outgoing_bytes_buffer_size - offset; + status = ssl_handshaker_get_bytes_to_send_to_peer( + impl, impl->outgoing_bytes_buffer + offset, &to_send_size); + offset += to_send_size; + if (status == TSI_INCOMPLETE_DATA) { + impl->outgoing_bytes_buffer_size *= 2; + impl->outgoing_bytes_buffer = static_cast(gpr_realloc( + impl->outgoing_bytes_buffer, impl->outgoing_bytes_buffer_size)); + } + } while (status == TSI_INCOMPLETE_DATA); + if (status != TSI_OK) return status; + *bytes_to_send = impl->outgoing_bytes_buffer; + *bytes_to_send_size = offset; + /* If handshake completes, create tsi_handshaker_result. */ + if (ssl_handshaker_get_result(impl) == TSI_HANDSHAKE_IN_PROGRESS) { + *handshaker_result = nullptr; + } else { + size_t unused_bytes_size = received_bytes_size - bytes_consumed; + const unsigned char* unused_bytes = + unused_bytes_size == 0 ? nullptr : received_bytes + bytes_consumed; + status = ssl_handshaker_result_create(impl, unused_bytes, unused_bytes_size, + handshaker_result); + if (status == TSI_OK) { + /* Indicates that the handshake has completed and that a handshaker_result + * has been created. */ + self->handshaker_result_created = true; + } + } + return status; +} + +static const tsi_handshaker_vtable handshaker_vtable = { + nullptr, /* get_bytes_to_send_to_peer -- deprecated */ + nullptr, /* process_bytes_from_peer -- deprecated */ + nullptr, /* get_result -- deprecated */ + nullptr, /* extract_peer -- deprecated */ + nullptr, /* create_frame_protector -- deprecated */ + ssl_handshaker_destroy, + ssl_handshaker_next, + nullptr, /* shutdown */ +}; + +/* --- tsi_ssl_handshaker_factory common methods. --- */ + +static void tsi_ssl_handshaker_resume_session( + SSL* ssl, tsi::SslSessionLRUCache* session_cache) { + const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name == nullptr) { + return; + } + tsi::SslSessionPtr session = session_cache->Get(server_name); + if (session != nullptr) { + // SSL_set_session internally increments reference counter. + SSL_set_session(ssl, session.get()); + } +} + +static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, + const char* server_name_indication, + tsi_ssl_handshaker_factory* factory, + tsi_handshaker** handshaker) { + SSL* ssl = SSL_new(ctx); + BIO* network_io = nullptr; + BIO* ssl_io = nullptr; + tsi_ssl_handshaker* impl = nullptr; + *handshaker = nullptr; + if (ctx == nullptr) { + gpr_log(GPR_ERROR, "SSL Context is null. Should never happen."); + return TSI_INTERNAL_ERROR; + } + if (ssl == nullptr) { + return TSI_OUT_OF_RESOURCES; + } + SSL_set_info_callback(ssl, ssl_info_callback); + + if (!BIO_new_bio_pair(&network_io, 0, &ssl_io, 0)) { + gpr_log(GPR_ERROR, "BIO_new_bio_pair failed."); + SSL_free(ssl); + return TSI_OUT_OF_RESOURCES; + } + SSL_set_bio(ssl, ssl_io, ssl_io); + if (is_client) { + int ssl_result; + SSL_set_connect_state(ssl); + if (server_name_indication != nullptr) { + 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); + BIO_free(network_io); + return TSI_INTERNAL_ERROR; + } + } + tsi_ssl_client_handshaker_factory* client_factory = + reinterpret_cast(factory); + if (client_factory->session_cache != nullptr) { + tsi_ssl_handshaker_resume_session(ssl, + client_factory->session_cache.get()); + } + 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); + BIO_free(network_io); + return TSI_INTERNAL_ERROR; + } + } else { + SSL_set_accept_state(ssl); + } + + impl = static_cast(gpr_zalloc(sizeof(*impl))); + impl->ssl = ssl; + impl->network_io = network_io; + impl->result = TSI_HANDSHAKE_IN_PROGRESS; + impl->outgoing_bytes_buffer_size = + TSI_SSL_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE; + impl->outgoing_bytes_buffer = + static_cast(gpr_zalloc(impl->outgoing_bytes_buffer_size)); + impl->base.vtable = &handshaker_vtable; + impl->factory_ref = tsi_ssl_handshaker_factory_ref(factory); + + *handshaker = &impl->base; + return TSI_OK; +} + +static int select_protocol_list(const unsigned char** out, + unsigned char* outlen, + const unsigned char* client_list, + size_t client_list_len, + const unsigned char* server_list, + size_t server_list_len) { + const unsigned char* client_current = client_list; + while (static_cast(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) && + static_cast(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. --- */ + +tsi_result tsi_ssl_client_handshaker_factory_create_handshaker( + tsi_ssl_client_handshaker_factory* self, const char* server_name_indication, + tsi_handshaker** handshaker) { + return create_tsi_ssl_handshaker(self->ssl_context, 1, server_name_indication, + &self->base, handshaker); +} + +void tsi_ssl_client_handshaker_factory_unref( + tsi_ssl_client_handshaker_factory* self) { + if (self == nullptr) return; + tsi_ssl_handshaker_factory_unref(&self->base); +} + +static void tsi_ssl_client_handshaker_factory_destroy( + tsi_ssl_handshaker_factory* factory) { + if (factory == nullptr) return; + tsi_ssl_client_handshaker_factory* self = + reinterpret_cast(factory); + if (self->ssl_context != nullptr) SSL_CTX_free(self->ssl_context); + if (self->alpn_protocol_list != nullptr) gpr_free(self->alpn_protocol_list); + self->session_cache.reset(); + gpr_free(self); +} + +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 = + static_cast(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. --- */ + +tsi_result tsi_ssl_server_handshaker_factory_create_handshaker( + tsi_ssl_server_handshaker_factory* self, tsi_handshaker** handshaker) { + if (self->ssl_context_count == 0) 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(self->ssl_contexts[0], 0, nullptr, + &self->base, handshaker); +} + +void tsi_ssl_server_handshaker_factory_unref( + tsi_ssl_server_handshaker_factory* self) { + if (self == nullptr) return; + tsi_ssl_handshaker_factory_unref(&self->base); +} + +static void tsi_ssl_server_handshaker_factory_destroy( + tsi_ssl_handshaker_factory* factory) { + if (factory == nullptr) return; + tsi_ssl_server_handshaker_factory* self = + reinterpret_cast(factory); + size_t i; + for (i = 0; i < self->ssl_context_count; i++) { + if (self->ssl_contexts[i] != nullptr) { + SSL_CTX_free(self->ssl_contexts[i]); + tsi_peer_destruct(&self->ssl_context_x509_subject_names[i]); + } + } + if (self->ssl_contexts != nullptr) gpr_free(self->ssl_contexts); + if (self->ssl_context_x509_subject_names != nullptr) { + gpr_free(self->ssl_context_x509_subject_names); + } + if (self->alpn_protocol_list != nullptr) gpr_free(self->alpn_protocol_list); + gpr_free(self); +} + +static int does_entry_match_name(const char* entry, size_t entry_length, + const char* name) { + const char* dot; + const char* name_subdomain = nullptr; + 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 == nullptr) 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 == nullptr) || (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 = + static_cast(arg); + size_t i = 0; + const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (servername == nullptr || 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 = + static_cast(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 = + static_cast(arg); + *out = factory->alpn_protocol_list; + GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX); + *outlen = static_cast(factory->alpn_protocol_list_length); + return SSL_TLSEXT_ERR_OK; +} + +/// This callback is called when new \a session is established and ready to +/// be cached. This session can be reused for new connections to similar +/// servers at later point of time. +/// It's intended to be used with SSL_CTX_sess_set_new_cb function. +/// +/// It returns 1 if callback takes ownership over \a session and 0 otherwise. +static int server_handshaker_factory_new_session_callback( + SSL* ssl, SSL_SESSION* session) { + SSL_CTX* ssl_context = SSL_get_SSL_CTX(ssl); + if (ssl_context == nullptr) { + return 0; + } + void* arg = SSL_CTX_get_ex_data(ssl_context, g_ssl_ctx_ex_factory_index); + tsi_ssl_client_handshaker_factory* factory = + static_cast(arg); + const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name == nullptr) { + return 0; + } + factory->session_cache->Put(server_name, tsi::SslSessionPtr(session)); + // Return 1 to indicate transfered ownership over the given session. + return 1; +} + +/* --- tsi_ssl_handshaker_factory constructors. --- */ + +static tsi_ssl_handshaker_factory_vtable client_handshaker_factory_vtable = { + tsi_ssl_client_handshaker_factory_destroy}; + +tsi_result tsi_create_ssl_client_handshaker_factory( + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair, + const char* pem_root_certs, const char* cipher_suites, + const char** alpn_protocols, uint16_t num_alpn_protocols, + tsi_ssl_client_handshaker_factory** factory) { + tsi_ssl_client_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.pem_key_cert_pair = pem_key_cert_pair; + options.pem_root_certs = pem_root_certs; + options.cipher_suites = cipher_suites; + options.alpn_protocols = alpn_protocols; + options.num_alpn_protocols = num_alpn_protocols; + return tsi_create_ssl_client_handshaker_factory_with_options(&options, + factory); +} + +tsi_result tsi_create_ssl_client_handshaker_factory_with_options( + const tsi_ssl_client_handshaker_options* options, + tsi_ssl_client_handshaker_factory** factory) { + SSL_CTX* ssl_context = nullptr; + tsi_ssl_client_handshaker_factory* impl = nullptr; + tsi_result result = TSI_OK; + + gpr_once_init(&g_init_openssl_once, init_openssl); + + if (factory == nullptr) return TSI_INVALID_ARGUMENT; + *factory = nullptr; + if (options->pem_root_certs == nullptr && options->root_store == nullptr) { + return TSI_INVALID_ARGUMENT; + } + +#if defined(OPENSSL_NO_TLS1_2_METHOD) || OPENSSL_API_COMPAT >= 0x10100000L + ssl_context = SSL_CTX_new(TLS_method()); +#else + ssl_context = SSL_CTX_new(TLSv1_2_method()); +#endif + if (ssl_context == nullptr) { + gpr_log(GPR_ERROR, "Could not create ssl context."); + return TSI_INVALID_ARGUMENT; + } + + impl = static_cast( + gpr_zalloc(sizeof(*impl))); + tsi_ssl_handshaker_factory_init(&impl->base); + impl->base.vtable = &client_handshaker_factory_vtable; + impl->ssl_context = ssl_context; + if (options->session_cache != nullptr) { + // Unref is called manually on factory destruction. + impl->session_cache = + reinterpret_cast(options->session_cache) + ->Ref(); + SSL_CTX_set_ex_data(ssl_context, g_ssl_ctx_ex_factory_index, impl); + SSL_CTX_sess_set_new_cb(ssl_context, + server_handshaker_factory_new_session_callback); + SSL_CTX_set_session_cache_mode(ssl_context, SSL_SESS_CACHE_CLIENT); + } + + do { + result = populate_ssl_context(ssl_context, options->pem_key_cert_pair, + options->cipher_suites); + if (result != TSI_OK) break; + +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + // X509_STORE_up_ref is only available since OpenSSL 1.1. + if (options->root_store != nullptr) { + X509_STORE_up_ref(options->root_store->store); + SSL_CTX_set_cert_store(ssl_context, options->root_store->store); + } +#endif + if (OPENSSL_VERSION_NUMBER < 0x10100000 || options->root_store == nullptr) { + result = ssl_ctx_load_verification_certs( + ssl_context, options->pem_root_certs, strlen(options->pem_root_certs), + nullptr); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Cannot load server root certificates."); + break; + } + } + + if (options->num_alpn_protocols != 0) { + result = build_alpn_protocol_name_list( + options->alpn_protocols, options->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 + GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX); + if (SSL_CTX_set_alpn_protos( + ssl_context, impl->alpn_protocol_list, + static_cast(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) { + tsi_ssl_handshaker_factory_unref(&impl->base); + return result; + } + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, nullptr); + /* TODO(jboeuf): Add revocation verification. */ + + *factory = impl; + return TSI_OK; +} + +static tsi_ssl_handshaker_factory_vtable server_handshaker_factory_vtable = { + tsi_ssl_server_handshaker_factory_destroy}; + +tsi_result tsi_create_ssl_server_handshaker_factory( + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs, const char* pem_client_root_certs, + int force_client_auth, const char* cipher_suites, + const char** alpn_protocols, uint16_t num_alpn_protocols, + tsi_ssl_server_handshaker_factory** factory) { + return tsi_create_ssl_server_handshaker_factory_ex( + pem_key_cert_pairs, num_key_cert_pairs, pem_client_root_certs, + force_client_auth ? TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + : TSI_DONT_REQUEST_CLIENT_CERTIFICATE, + cipher_suites, alpn_protocols, num_alpn_protocols, factory); +} + +tsi_result tsi_create_ssl_server_handshaker_factory_ex( + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs, const char* pem_client_root_certs, + tsi_client_certificate_request_type client_certificate_request, + const char* cipher_suites, const char** alpn_protocols, + uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory** factory) { + tsi_ssl_server_handshaker_options options; + memset(&options, 0, sizeof(options)); + options.pem_key_cert_pairs = pem_key_cert_pairs; + options.num_key_cert_pairs = num_key_cert_pairs; + options.pem_client_root_certs = pem_client_root_certs; + options.client_certificate_request = client_certificate_request; + options.cipher_suites = cipher_suites; + options.alpn_protocols = alpn_protocols; + options.num_alpn_protocols = num_alpn_protocols; + return tsi_create_ssl_server_handshaker_factory_with_options(&options, + factory); +} + +tsi_result tsi_create_ssl_server_handshaker_factory_with_options( + const tsi_ssl_server_handshaker_options* options, + tsi_ssl_server_handshaker_factory** factory) { + tsi_ssl_server_handshaker_factory* impl = nullptr; + tsi_result result = TSI_OK; + size_t i = 0; + + gpr_once_init(&g_init_openssl_once, init_openssl); + + if (factory == nullptr) return TSI_INVALID_ARGUMENT; + *factory = nullptr; + if (options->num_key_cert_pairs == 0 || + options->pem_key_cert_pairs == nullptr) { + return TSI_INVALID_ARGUMENT; + } + + impl = static_cast( + gpr_zalloc(sizeof(*impl))); + tsi_ssl_handshaker_factory_init(&impl->base); + impl->base.vtable = &server_handshaker_factory_vtable; + + impl->ssl_contexts = static_cast( + gpr_zalloc(options->num_key_cert_pairs * sizeof(SSL_CTX*))); + impl->ssl_context_x509_subject_names = static_cast( + gpr_zalloc(options->num_key_cert_pairs * sizeof(tsi_peer))); + if (impl->ssl_contexts == nullptr || + impl->ssl_context_x509_subject_names == nullptr) { + tsi_ssl_handshaker_factory_unref(&impl->base); + return TSI_OUT_OF_RESOURCES; + } + impl->ssl_context_count = options->num_key_cert_pairs; + + if (options->num_alpn_protocols > 0) { + result = build_alpn_protocol_name_list( + options->alpn_protocols, options->num_alpn_protocols, + &impl->alpn_protocol_list, &impl->alpn_protocol_list_length); + if (result != TSI_OK) { + tsi_ssl_handshaker_factory_unref(&impl->base); + return result; + } + } + + for (i = 0; i < options->num_key_cert_pairs; i++) { + do { +#if defined(OPENSSL_NO_TLS1_2_METHOD) || OPENSSL_API_COMPAT >= 0x10100000L + impl->ssl_contexts[i] = SSL_CTX_new(TLS_method()); +#else + impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); +#endif + if (impl->ssl_contexts[i] == nullptr) { + gpr_log(GPR_ERROR, "Could not create ssl context."); + result = TSI_OUT_OF_RESOURCES; + break; + } + result = populate_ssl_context(impl->ssl_contexts[i], + &options->pem_key_cert_pairs[i], + options->cipher_suites); + if (result != TSI_OK) break; + + // TODO(elessar): Provide ability to disable session ticket keys. + + // Allow client cache sessions (it's needed for OpenSSL only). + int set_sid_ctx_result = SSL_CTX_set_session_id_context( + impl->ssl_contexts[i], kSslSessionIdContext, + GPR_ARRAY_SIZE(kSslSessionIdContext)); + if (set_sid_ctx_result == 0) { + gpr_log(GPR_ERROR, "Failed to set session id context."); + result = TSI_INTERNAL_ERROR; + break; + } + + if (options->session_ticket_key != nullptr) { + if (SSL_CTX_set_tlsext_ticket_keys( + impl->ssl_contexts[i], + const_cast(options->session_ticket_key), + options->session_ticket_key_size) == 0) { + gpr_log(GPR_ERROR, "Invalid STEK size."); + result = TSI_INVALID_ARGUMENT; + break; + } + } + + if (options->pem_client_root_certs != nullptr) { + STACK_OF(X509_NAME)* root_names = nullptr; + result = ssl_ctx_load_verification_certs( + impl->ssl_contexts[i], options->pem_client_root_certs, + strlen(options->pem_client_root_certs), &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); + } + switch (options->client_certificate_request) { + case TSI_DONT_REQUEST_CLIENT_CERTIFICATE: + SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, nullptr); + break; + case TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, + NullVerifyCallback); + break; + case TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY: + SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, nullptr); + break; + case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + SSL_CTX_set_verify(impl->ssl_contexts[i], + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + NullVerifyCallback); + break; + case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: + SSL_CTX_set_verify(impl->ssl_contexts[i], + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + nullptr); + break; + } + /* TODO(jboeuf): Add revocation verification. */ + + result = extract_x509_subject_names_from_pem_cert( + options->pem_key_cert_pairs[i].cert_chain, + &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_unref(&impl->base); + return result; + } + } + + *factory = impl; + 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 = nullptr; + int like_ip = looks_like_ip_address(name); + + /* Check the SAN first. */ + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property* property = &peer->properties[i]; + if (property->name == nullptr) continue; + if (strcmp(property->name, + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { + san_count++; + + if (!like_ip && does_entry_match_name(property->value.data, + property->value.length, name)) { + return 1; + } else if (like_ip && + strncmp(name, property->value.data, property->value.length) == + 0 && + strlen(name) == property->value.length) { + /* IP Addresses are exact matches only. */ + 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, but only if its not like an IP Address */ + if (san_count == 0 && cn_property != nullptr && !like_ip) { + if (does_entry_match_name(cn_property->value.data, + cn_property->value.length, name)) { + return 1; + } + } + + return 0; /* Not found. */ +} + +/* --- Testing support. --- */ +const tsi_ssl_handshaker_factory_vtable* tsi_ssl_handshaker_factory_swap_vtable( + tsi_ssl_handshaker_factory* factory, + tsi_ssl_handshaker_factory_vtable* new_vtable) { + GPR_ASSERT(factory != nullptr); + GPR_ASSERT(factory->vtable != nullptr); + + const tsi_ssl_handshaker_factory_vtable* orig_vtable = factory->vtable; + factory->vtable = new_vtable; + return orig_vtable; +} diff --git a/third_party/grpc/src/core/tsi/ssl_transport_security.h b/third_party/grpc/src/core/tsi/ssl_transport_security.h index 51c0003a85411d..cabf5830980990 100644 --- a/third_party/grpc/src/core/tsi/ssl_transport_security.h +++ b/third_party/grpc/src/core/tsi/ssl_transport_security.h @@ -1,44 +1,27 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_TSI_SSL_TRANSPORT_SECURITY_H -#define GRPC_INTERNAL_CORE_TSI_SSL_TRANSPORT_SECURITY_H +#ifndef GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H +#define GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H -#include "src/core/tsi/transport_security_interface.h" +#include -#ifdef __cplusplus -extern "C" { -#endif +#include "src/core/tsi/transport_security_interface.h" /* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for X509 certs. */ #define TSI_X509_CERTIFICATE_TYPE "X509" @@ -47,37 +30,75 @@ extern "C" { #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_SESSION_REUSED_PEER_PROPERTY "ssl_session_reused" + +#define TSI_X509_PEM_CERT_PROPERTY "x509_pem_cert" #define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol" -/* --- tsi_ssl_handshaker_factory object --- +/* --- tsi_ssl_root_certs_store object --- - This object creates tsi_handshaker objects implemented in terms of the - TLS 1.2 specificiation. */ + This object stores SSL root certificates. It can be shared by multiple SSL + context. */ +typedef struct tsi_ssl_root_certs_store tsi_ssl_root_certs_store; -typedef struct tsi_ssl_handshaker_factory tsi_ssl_handshaker_factory; +/* Given a NULL-terminated string containing the PEM encoding of the root + certificates, creates a tsi_ssl_root_certs_store object. */ +tsi_ssl_root_certs_store* tsi_ssl_root_certs_store_create( + const char* pem_roots); -/* 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. +/* Destroys the tsi_ssl_root_certs_store object. */ +void tsi_ssl_root_certs_store_destroy(tsi_ssl_root_certs_store* self); + +/* --- tsi_ssl_session_cache object --- + + Cache for SSL sessions for sessions resumption. */ + +typedef struct tsi_ssl_session_cache tsi_ssl_session_cache; + +/* Create LRU cache for SSL sessions with \a capacity. */ +tsi_ssl_session_cache* tsi_ssl_session_cache_create_lru(size_t capacity); + +/* Increment reference counter of \a cache. */ +void tsi_ssl_session_cache_ref(tsi_ssl_session_cache* cache); + +/* Decrement reference counter of \a cache. */ +void tsi_ssl_session_cache_unref(tsi_ssl_session_cache* cache); + +/* --- tsi_ssl_client_handshaker_factory object --- + + This object creates a client tsi_handshaker objects implemented in terms of + the TLS 1.2 specificiation. */ + +typedef struct tsi_ssl_client_handshaker_factory + tsi_ssl_client_handshaker_factory; + +/* 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; +} tsi_ssl_pem_key_cert_pair; + +/* TO BE DEPRECATED. + Creates a client handshaker factory. + - pem_key_cert_pair is a pointer to 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. + - pem_roots_cert is the NULL-terminated string containing the PEM encoding of + the server root certificates. - 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. + - alpn_protocols is an array containing the NULL terminated protocol names + that the handshakers created with this factory support. 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. @@ -85,45 +106,94 @@ typedef struct tsi_ssl_handshaker_factory tsi_ssl_handshaker_factory; - 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); + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair, + const char* pem_root_certs, const char* cipher_suites, + const char** alpn_protocols, uint16_t num_alpn_protocols, + tsi_ssl_client_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 +typedef struct { + /* pem_key_cert_pair is a pointer to 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. */ + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair; + /* pem_roots_cert is the NULL-terminated string containing the PEM encoding of + the client root certificates. */ + const char* pem_root_certs; + /* root_store is a pointer to the ssl_root_certs_store object. If root_store + is not nullptr and SSL implementation permits, root_store will be used as + root certificates. Otherwise, pem_roots_cert will be used to load server + root certificates. */ + const tsi_ssl_root_certs_store* root_store; + /* 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. */ + const char* cipher_suites; + /* alpn_protocols is an array containing the NULL terminated protocol names + that the handshakers created with this factory support. This parameter can + be NULL. */ + const char** alpn_protocols; + /* 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. */ + size_t num_alpn_protocols; + /* ssl_session_cache is a cache for reusable client-side sessions. */ + tsi_ssl_session_cache* session_cache; +} tsi_ssl_client_handshaker_options; + +/* Creates a client handshaker factory. + - options is the options used to create a factory. + - 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_with_options( + const tsi_ssl_client_handshaker_options* options, + tsi_ssl_client_handshaker_factory** factory); + +/* Creates a client 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. + - handshaker 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_client_handshaker_factory_create_handshaker( + tsi_ssl_client_handshaker_factory* self, const char* server_name_indication, + tsi_handshaker** handshaker); + +/* Decrements reference count of the handshaker factory. Handshaker factory will + * be destroyed once no references exist. */ +void tsi_ssl_client_handshaker_factory_unref( + tsi_ssl_client_handshaker_factory* factory); + +/* --- tsi_ssl_server_handshaker_factory object --- + + This object creates a client tsi_handshaker objects implemented in terms of + the TLS 1.2 specificiation. */ + +typedef struct tsi_ssl_server_handshaker_factory + tsi_ssl_server_handshaker_factory; + +/* TO BE DEPRECATED. + Creates a server handshaker factory. + - pem_key_cert_pairs is an array private key / certificate chains of the + server. + - num_key_cert_pairs is the number of items in the pem_key_cert_pairs array. + - pem_root_certs 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. - 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. + - alpn_protocols is an array containing the NULL terminated protocol names + that the handshakers created with this factory support. 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. @@ -131,43 +201,114 @@ tsi_result tsi_create_ssl_client_handshaker_factory( - 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. + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs, const char* pem_client_root_certs, + int force_client_auth, const char* cipher_suites, + const char** alpn_protocols, uint16_t num_alpn_protocols, + tsi_ssl_server_handshaker_factory** factory); + +/* TO BE DEPRECATED. + Same as tsi_create_ssl_server_handshaker_factory method except uses + tsi_client_certificate_request_type to support more ways to handle client + certificate authentication. + - client_certificate_request, 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 */ +tsi_result tsi_create_ssl_server_handshaker_factory_ex( + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs, + size_t num_key_cert_pairs, const char* pem_client_root_certs, + tsi_client_certificate_request_type client_certificate_request, + const char* cipher_suites, const char** alpn_protocols, + uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory** factory); + +typedef struct { + /* pem_key_cert_pairs is an array private key / certificate chains of the + server. */ + const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs; + /* num_key_cert_pairs is the number of items in the pem_key_cert_pairs + array. */ + size_t num_key_cert_pairs; + /* pem_root_certs is the NULL-terminated string containing the PEM encoding + of the server root certificates. This parameter may be NULL if the server + does not want the client to be authenticated with SSL. */ + const char* pem_client_root_certs; + /* client_certificate_request, 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. */ + tsi_client_certificate_request_type client_certificate_request; + /* 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. */ + const char* cipher_suites; + /* alpn_protocols is an array containing the NULL terminated protocol names + that the handshakers created with this factory support. This parameter can + be NULL. */ + const char** alpn_protocols; + /* 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. */ + uint16_t num_alpn_protocols; + /* session_ticket_key is optional key for encrypting session keys. If paramter + is not specified it must be NULL. */ + const char* session_ticket_key; + /* session_ticket_key_size is a size of session ticket encryption key. */ + size_t session_ticket_key_size; +} tsi_ssl_server_handshaker_options; + +/* Creates a server handshaker factory. + - options is the options used to create a factory. + - 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_with_options( + const tsi_ssl_server_handshaker_options* options, + tsi_ssl_server_handshaker_factory** factory); + +/* Creates a server 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. + - handshaker 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); +tsi_result tsi_ssl_server_handshaker_factory_create_handshaker( + tsi_ssl_server_handshaker_factory* self, 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); +/* Decrements reference count of the handshaker factory. Handshaker factory will + * be destroyed once no references exist. */ +void tsi_ssl_server_handshaker_factory_unref( + tsi_ssl_server_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); + - handle public suffix wildchar more strictly (e.g. *.co.uk) */ +int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name); + +/* --- Testing support. --- + + These functions and typedefs are not intended to be used outside of testing. + */ + +/* Base type of client and server handshaker factories. */ +typedef struct tsi_ssl_handshaker_factory tsi_ssl_handshaker_factory; + +/* Function pointer to handshaker_factory destructor. */ +typedef void (*tsi_ssl_handshaker_factory_destructor)( + tsi_ssl_handshaker_factory* factory); + +/* Virtual table for tsi_ssl_handshaker_factory. */ +typedef struct { + tsi_ssl_handshaker_factory_destructor destroy; +} tsi_ssl_handshaker_factory_vtable; -#ifdef __cplusplus -} -#endif +/* Set destructor of handshaker_factory to new_destructor, returns previous + destructor. */ +const tsi_ssl_handshaker_factory_vtable* tsi_ssl_handshaker_factory_swap_vtable( + tsi_ssl_handshaker_factory* factory, + tsi_ssl_handshaker_factory_vtable* new_vtable); -#endif /* GRPC_INTERNAL_CORE_TSI_SSL_TRANSPORT_SECURITY_H */ +#endif /* GRPC_CORE_TSI_SSL_TRANSPORT_SECURITY_H */ diff --git a/third_party/grpc/src/core/tsi/ssl_types.h b/third_party/grpc/src/core/tsi/ssl_types.h index 0d0225e9c7ba83..0ce5e2ee6f3229 100644 --- a/third_party/grpc/src/core/tsi/ssl_types.h +++ b/third_party/grpc/src/core/tsi/ssl_types.h @@ -1,38 +1,23 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_TSI_SSL_TYPES_H -#define GRPC_INTERNAL_CORE_TSI_SSL_TYPES_H +#ifndef GRPC_CORE_TSI_SSL_TYPES_H +#define GRPC_CORE_TSI_SSL_TYPES_H /* A collection of macros to cast between various integer types that are * used differently between BoringSSL and OpenSSL: @@ -42,6 +27,10 @@ * function */ +#include + +#include "src/core/tsi/grpc_shadow_boringssl.h" + #include #ifdef OPENSSL_IS_BORINGSSL @@ -52,4 +41,4 @@ #define TSI_SIZE_AS_SIZE(x) ((int)(x)) #endif -#endif +#endif /* GRPC_CORE_TSI_SSL_TYPES_H */ diff --git a/third_party/grpc/src/core/tsi/test_creds/README b/third_party/grpc/src/core/tsi/test_creds/README deleted file mode 100644 index eb8482d648f735..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/README +++ /dev/null @@ -1,62 +0,0 @@ -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/third_party/grpc/src/core/tsi/test_creds/badclient.key b/third_party/grpc/src/core/tsi/test_creds/badclient.key deleted file mode 100644 index 5832685122afcc..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/badclient.key +++ /dev/null @@ -1,16 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/badclient.pem b/third_party/grpc/src/core/tsi/test_creds/badclient.pem deleted file mode 100644 index 1785970221f71e..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/badclient.pem +++ /dev/null @@ -1,17 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/badserver.key b/third_party/grpc/src/core/tsi/test_creds/badserver.key deleted file mode 100644 index abfbde10ff63cf..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/badserver.key +++ /dev/null @@ -1,16 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/badserver.pem b/third_party/grpc/src/core/tsi/test_creds/badserver.pem deleted file mode 100644 index 983c979f312781..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/badserver.pem +++ /dev/null @@ -1,17 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/ca-openssl.cnf b/third_party/grpc/src/core/tsi/test_creds/ca-openssl.cnf deleted file mode 100644 index e97b945e4bb346..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/ca-openssl.cnf +++ /dev/null @@ -1,17 +0,0 @@ -[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/third_party/grpc/src/core/tsi/test_creds/ca.key b/third_party/grpc/src/core/tsi/test_creds/ca.key deleted file mode 100644 index 03c4f950e38953..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/ca.key +++ /dev/null @@ -1,16 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/ca.pem b/third_party/grpc/src/core/tsi/test_creds/ca.pem deleted file mode 100644 index 6c8511a73c68ac..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/ca.pem +++ /dev/null @@ -1,15 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/client.key b/third_party/grpc/src/core/tsi/test_creds/client.key deleted file mode 100644 index f48d0735d99ad5..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/client.key +++ /dev/null @@ -1,16 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/client.pem b/third_party/grpc/src/core/tsi/test_creds/client.pem deleted file mode 100644 index e332091019bdd9..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/client.pem +++ /dev/null @@ -1,14 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/server0.key b/third_party/grpc/src/core/tsi/test_creds/server0.key deleted file mode 100644 index add153c9aea2a3..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/server0.key +++ /dev/null @@ -1,16 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/server0.pem b/third_party/grpc/src/core/tsi/test_creds/server0.pem deleted file mode 100644 index ade75d8563f782..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/server0.pem +++ /dev/null @@ -1,14 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/server1-openssl.cnf b/third_party/grpc/src/core/tsi/test_creds/server1-openssl.cnf deleted file mode 100644 index 8a021082891c22..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/server1-openssl.cnf +++ /dev/null @@ -1,26 +0,0 @@ -[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/third_party/grpc/src/core/tsi/test_creds/server1.key b/third_party/grpc/src/core/tsi/test_creds/server1.key deleted file mode 100644 index 143a5b87658d5c..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/server1.key +++ /dev/null @@ -1,16 +0,0 @@ ------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/third_party/grpc/src/core/tsi/test_creds/server1.pem b/third_party/grpc/src/core/tsi/test_creds/server1.pem deleted file mode 100644 index f3d43fcc5beadd..00000000000000 --- a/third_party/grpc/src/core/tsi/test_creds/server1.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICnDCCAgWgAwIBAgIBBzANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJBVTET -MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ -dHkgTHRkMQ8wDQYDVQQDEwZ0ZXN0Y2EwHhcNMTUxMTA0MDIyMDI0WhcNMjUxMTAx -MDIyMDI0WjBlMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV -BAcTB0NoaWNhZ28xFTATBgNVBAoTDEV4YW1wbGUsIENvLjEaMBgGA1UEAxQRKi50 -ZXN0Lmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOHDFSco -LCVJpYDDM4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1Bg -zkWF+slf3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd -9N8YwbBYAckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAGjazBpMAkGA1UdEwQCMAAw -CwYDVR0PBAQDAgXgME8GA1UdEQRIMEaCECoudGVzdC5nb29nbGUuZnKCGHdhdGVy -em9vaS50ZXN0Lmdvb2dsZS5iZYISKi50ZXN0LnlvdXR1YmUuY29thwTAqAEDMA0G -CSqGSIb3DQEBCwUAA4GBAJFXVifQNub1LUP4JlnX5lXNlo8FxZ2a12AFQs+bzoJ6 -hM044EDjqyxUqSbVePK0ni3w1fHQB5rY9yYC5f8G7aqqTY1QOhoUk8ZTSTRpnkTh -y4jjdvTZeLDVBlueZUTDRmy2feY5aZIU18vFDK08dTG0A87pppuv1LNIR3loveU8 ------END CERTIFICATE----- diff --git a/third_party/grpc/src/core/tsi/transport_security.c b/third_party/grpc/src/core/tsi/transport_security.c deleted file mode 100644 index db219a50a6798a..00000000000000 --- a/third_party/grpc/src/core/tsi/transport_security.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 || bytes == NULL || bytes_size == 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 || bytes == NULL || bytes_size == 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/third_party/grpc/src/core/tsi/transport_security.cc b/third_party/grpc/src/core/tsi/transport_security.cc new file mode 100644 index 00000000000000..078a917bbacbd1 --- /dev/null +++ b/third_party/grpc/src/core/tsi/transport_security.cc @@ -0,0 +1,357 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/transport_security.h" + +#include +#include + +#include +#include + +/* --- Tracing. --- */ + +grpc_core::TraceFlag tsi_tracing_enabled(false, "tsi"); + +/* --- 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"; + case TSI_ASYNC: + return "TSI_ASYNC"; + 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 == nullptr || self->vtable == nullptr || + unprotected_bytes == nullptr || unprotected_bytes_size == nullptr || + protected_output_frames == nullptr || + protected_output_frames_size == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect == nullptr) return TSI_UNIMPLEMENTED; + 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 == nullptr || self->vtable == nullptr || + protected_output_frames == nullptr || + protected_output_frames_size == nullptr || + still_pending_size == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect_flush == nullptr) return TSI_UNIMPLEMENTED; + 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 == nullptr || self->vtable == nullptr || + protected_frames_bytes == nullptr || + protected_frames_bytes_size == nullptr || unprotected_bytes == nullptr || + unprotected_bytes_size == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->unprotect == nullptr) return TSI_UNIMPLEMENTED; + 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 == nullptr) 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 == nullptr || self->vtable == nullptr || bytes == nullptr || + bytes_size == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; + if (self->vtable->get_bytes_to_send_to_peer == nullptr) + return TSI_UNIMPLEMENTED; + 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 == nullptr || self->vtable == nullptr || bytes == nullptr || + bytes_size == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; + if (self->vtable->process_bytes_from_peer == nullptr) + return TSI_UNIMPLEMENTED; + return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); +} + +tsi_result tsi_handshaker_get_result(tsi_handshaker* self) { + if (self == nullptr || self->vtable == nullptr) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; + if (self->vtable->get_result == nullptr) return TSI_UNIMPLEMENTED; + return self->vtable->get_result(self); +} + +tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer) { + if (self == nullptr || self->vtable == nullptr || peer == nullptr) { + return TSI_INVALID_ARGUMENT; + } + memset(peer, 0, sizeof(tsi_peer)); + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; + if (tsi_handshaker_get_result(self) != TSI_OK) { + return TSI_FAILED_PRECONDITION; + } + if (self->vtable->extract_peer == nullptr) return TSI_UNIMPLEMENTED; + 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 == nullptr || self->vtable == nullptr || protector == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; + if (tsi_handshaker_get_result(self) != TSI_OK) return TSI_FAILED_PRECONDITION; + if (self->vtable->create_frame_protector == nullptr) return TSI_UNIMPLEMENTED; + result = self->vtable->create_frame_protector(self, max_protected_frame_size, + protector); + if (result == TSI_OK) { + self->frame_protector_created = true; + } + return result; +} + +tsi_result tsi_handshaker_next( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result, + tsi_handshaker_on_next_done_cb cb, void* user_data) { + if (self == nullptr || self->vtable == nullptr) return TSI_INVALID_ARGUMENT; + if (self->handshaker_result_created) return TSI_FAILED_PRECONDITION; + if (self->handshake_shutdown) return TSI_HANDSHAKE_SHUTDOWN; + if (self->vtable->next == nullptr) return TSI_UNIMPLEMENTED; + return self->vtable->next(self, received_bytes, received_bytes_size, + bytes_to_send, bytes_to_send_size, + handshaker_result, cb, user_data); +} + +void tsi_handshaker_shutdown(tsi_handshaker* self) { + if (self == nullptr || self->vtable == nullptr) return; + if (self->vtable->shutdown != nullptr) { + self->vtable->shutdown(self); + } + self->handshake_shutdown = true; +} + +void tsi_handshaker_destroy(tsi_handshaker* self) { + if (self == nullptr) return; + self->vtable->destroy(self); +} + +/* --- tsi_handshaker_result implementation. --- */ + +tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result* self, + tsi_peer* peer) { + if (self == nullptr || self->vtable == nullptr || peer == nullptr) { + return TSI_INVALID_ARGUMENT; + } + memset(peer, 0, sizeof(tsi_peer)); + if (self->vtable->extract_peer == nullptr) return TSI_UNIMPLEMENTED; + return self->vtable->extract_peer(self, peer); +} + +tsi_result tsi_handshaker_result_create_frame_protector( + const tsi_handshaker_result* self, size_t* max_protected_frame_size, + tsi_frame_protector** protector) { + if (self == nullptr || self->vtable == nullptr || protector == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->create_frame_protector == nullptr) return TSI_UNIMPLEMENTED; + return self->vtable->create_frame_protector(self, max_protected_frame_size, + protector); +} + +tsi_result tsi_handshaker_result_get_unused_bytes( + const tsi_handshaker_result* self, const unsigned char** bytes, + size_t* bytes_size) { + if (self == nullptr || self->vtable == nullptr || bytes == nullptr || + bytes_size == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->get_unused_bytes == nullptr) return TSI_UNIMPLEMENTED; + return self->vtable->get_unused_bytes(self, bytes, bytes_size); +} + +void tsi_handshaker_result_destroy(tsi_handshaker_result* self) { + if (self == nullptr) 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]); + } + gpr_free(children); +} + +void tsi_peer_property_destruct(tsi_peer_property* property) { + if (property->name != nullptr) { + gpr_free(property->name); + } + if (property->value.data != nullptr) { + gpr_free(property->value.data); + } + *property = tsi_init_peer_property(); /* Reset everything to 0. */ +} + +void tsi_peer_destruct(tsi_peer* self) { + if (self == nullptr) return; + if (self->properties != nullptr) { + tsi_peer_destroy_list_property(self->properties, self->property_count); + self->properties = nullptr; + } + 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 != nullptr) property->name = gpr_strdup(name); + if (value_length > 0) { + property->value.data = static_cast(gpr_zalloc(value_length)); + 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 = static_cast( + gpr_zalloc(property_count * sizeof(tsi_peer_property))); + peer->property_count = property_count; + } + return TSI_OK; +} + +const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* peer, + const char* name) { + size_t i; + if (peer == nullptr) return nullptr; + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property* property = &peer->properties[i]; + if (name == nullptr && property->name == nullptr) { + return property; + } + if (name != nullptr && property->name != nullptr && + strcmp(property->name, name) == 0) { + return property; + } + } + return nullptr; +} diff --git a/third_party/grpc/src/core/tsi/transport_security.h b/third_party/grpc/src/core/tsi/transport_security.h index 4077737473ac5b..482d300a056ab7 100644 --- a/third_party/grpc/src/core/tsi/transport_security.h +++ b/third_party/grpc/src/core/tsi/transport_security.h @@ -1,111 +1,130 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_H -#define GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_H +#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_H +#define GRPC_CORE_TSI_TRANSPORT_SECURITY_H -#include "src/core/tsi/transport_security_interface.h" +#include + +#include -#ifdef __cplusplus -extern "C" { -#endif +#include "src/core/lib/debug/trace.h" +#include "src/core/tsi/transport_security_interface.h" -extern int tsi_tracing_enabled; +extern grpc_core::TraceFlag 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_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; + 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_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_result (*next)(tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, + const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, + tsi_handshaker_result** handshaker_result, + tsi_handshaker_on_next_done_cb cb, void* user_data); + void (*shutdown)(tsi_handshaker* self); } tsi_handshaker_vtable; struct tsi_handshaker { - const tsi_handshaker_vtable *vtable; - int frame_protector_created; + const tsi_handshaker_vtable* vtable; + bool frame_protector_created; + bool handshaker_result_created; + bool handshake_shutdown; +}; + +/* Base for tsi_handshaker_result implementations. + See transport_security_interface.h for documentation. + The exec_ctx parameter in create_zero_copy_grpc_protector is supposed to be + of type grpc_exec_ctx*, but we're using void* instead to avoid making the TSI + API depend on grpc. The create_zero_copy_grpc_protector() method is only used + in grpc, where we do need the exec_ctx passed through, but the API still + needs to compile in other applications, where grpc_exec_ctx is not defined. +*/ +typedef struct { + tsi_result (*extract_peer)(const tsi_handshaker_result* self, tsi_peer* peer); + tsi_result (*create_zero_copy_grpc_protector)( + const tsi_handshaker_result* self, + size_t* max_output_protected_frame_size, + tsi_zero_copy_grpc_protector** protector); + tsi_result (*create_frame_protector)(const tsi_handshaker_result* self, + size_t* max_output_protected_frame_size, + tsi_frame_protector** protector); + tsi_result (*get_unused_bytes)(const tsi_handshaker_result* self, + const unsigned char** bytes, + size_t* bytes_size); + void (*destroy)(tsi_handshaker_result* self); +} tsi_handshaker_result_vtable; + +struct tsi_handshaker_result { + const tsi_handshaker_result_vtable* vtable; }; /* Peer and property construction/destruction functions. */ -tsi_result tsi_construct_peer(size_t property_count, tsi_peer *peer); +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, +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_peer_property* property); tsi_result tsi_construct_allocated_string_peer_property( - const char *name, size_t value_length, tsi_peer_property *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); - + const char* name, const char* value, tsi_peer_property* property); +const tsi_peer_property* tsi_peer_get_property_by_name(const tsi_peer* peer, + const char* name); /* Utils. */ -char *tsi_strdup(const char *src); /* Sadly, no strdup in C89. */ - -#ifdef __cplusplus -} -#endif +char* tsi_strdup(const char* src); /* Sadly, no strdup in C89. */ -#endif /* GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_H */ +#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_H */ diff --git a/third_party/grpc/src/core/tsi/transport_security_grpc.cc b/third_party/grpc/src/core/tsi/transport_security_grpc.cc new file mode 100644 index 00000000000000..c73a6e303e6c33 --- /dev/null +++ b/third_party/grpc/src/core/tsi/transport_security_grpc.cc @@ -0,0 +1,66 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/tsi/transport_security_grpc.h" + +/* This method creates a tsi_zero_copy_grpc_protector object. */ +tsi_result tsi_handshaker_result_create_zero_copy_grpc_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_zero_copy_grpc_protector** protector) { + if (self == nullptr || self->vtable == nullptr || protector == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->create_zero_copy_grpc_protector == nullptr) { + return TSI_UNIMPLEMENTED; + } + return self->vtable->create_zero_copy_grpc_protector( + self, max_output_protected_frame_size, protector); +} + +/* --- tsi_zero_copy_grpc_protector common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_zero_copy_grpc_protector_protect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices) { + if (self == nullptr || self->vtable == nullptr || + unprotected_slices == nullptr || protected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect == nullptr) return TSI_UNIMPLEMENTED; + return self->vtable->protect(self, unprotected_slices, protected_slices); +} + +tsi_result tsi_zero_copy_grpc_protector_unprotect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices) { + if (self == nullptr || self->vtable == nullptr || + protected_slices == nullptr || unprotected_slices == nullptr) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->unprotect == nullptr) return TSI_UNIMPLEMENTED; + return self->vtable->unprotect(self, protected_slices, unprotected_slices); +} + +void tsi_zero_copy_grpc_protector_destroy(tsi_zero_copy_grpc_protector* self) { + if (self == nullptr) return; + self->vtable->destroy(self); +} diff --git a/third_party/grpc/src/core/tsi/transport_security_grpc.h b/third_party/grpc/src/core/tsi/transport_security_grpc.h new file mode 100644 index 00000000000000..d3bb04d07fc3a0 --- /dev/null +++ b/third_party/grpc/src/core/tsi/transport_security_grpc.h @@ -0,0 +1,74 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H +#define GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H + +#include + +#include +#include "src/core/tsi/transport_security.h" + +/* This method creates a tsi_zero_copy_grpc_protector object. It return TSI_OK + assuming there is no fatal error. + The caller is responsible for destroying the protector. */ +tsi_result tsi_handshaker_result_create_zero_copy_grpc_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_zero_copy_grpc_protector** protector); + +/* -- tsi_zero_copy_grpc_protector object -- */ + +/* Outputs protected frames. + - unprotected_slices is the unprotected data to be protected. + - protected_slices is the protected output frames. One or more frames + may be produced in this protect function. + - This method returns TSI_OK in case of success or a specific error code in + case of failure. */ +tsi_result tsi_zero_copy_grpc_protector_protect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices); + +/* Outputs unprotected bytes. + - protected_slices is the bytes of protected frames. + - unprotected_slices is the unprotected output data. + - This method returns TSI_OK in case of success. Success includes cases where + there is not enough data to output in which case unprotected_slices has 0 + bytes. */ +tsi_result tsi_zero_copy_grpc_protector_unprotect( + tsi_zero_copy_grpc_protector* self, grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices); + +/* Destroys the tsi_zero_copy_grpc_protector object. */ +void tsi_zero_copy_grpc_protector_destroy(tsi_zero_copy_grpc_protector* self); + +/* Base for tsi_zero_copy_grpc_protector implementations. */ +typedef struct { + tsi_result (*protect)(tsi_zero_copy_grpc_protector* self, + grpc_slice_buffer* unprotected_slices, + grpc_slice_buffer* protected_slices); + tsi_result (*unprotect)(tsi_zero_copy_grpc_protector* self, + grpc_slice_buffer* protected_slices, + grpc_slice_buffer* unprotected_slices); + void (*destroy)(tsi_zero_copy_grpc_protector* self); +} tsi_zero_copy_grpc_protector_vtable; + +struct tsi_zero_copy_grpc_protector { + const tsi_zero_copy_grpc_protector_vtable* vtable; +}; + +#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_GRPC_H */ diff --git a/third_party/grpc/src/core/tsi/transport_security_interface.h b/third_party/grpc/src/core/tsi/transport_security_interface.h index 69ee17ae917924..7a0cdc3453a07f 100644 --- a/third_party/grpc/src/core/tsi/transport_security_interface.h +++ b/third_party/grpc/src/core/tsi/transport_security_interface.h @@ -1,45 +1,30 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#ifndef GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H -#define GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H +#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H +#define GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H + +#include #include #include -#ifdef __cplusplus -extern "C" { -#endif +#include "src/core/lib/debug/trace.h" /* --- tsi result --- */ @@ -56,15 +41,34 @@ typedef enum { TSI_NOT_FOUND = 9, TSI_PROTOCOL_FAILURE = 10, TSI_HANDSHAKE_IN_PROGRESS = 11, - TSI_OUT_OF_RESOURCES = 12 + TSI_OUT_OF_RESOURCES = 12, + TSI_ASYNC = 13, + TSI_HANDSHAKE_SHUTDOWN = 14, } tsi_result; -const char *tsi_result_to_string(tsi_result result); +typedef enum { + // Default option + TSI_DONT_REQUEST_CLIENT_CERTIFICATE, + TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, + TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY, + TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY, + TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY, +} tsi_client_certificate_request_type; + +const char* tsi_result_to_string(tsi_result result); /* --- tsi tracing --- */ -/* Set this early to avoid races */ -extern int tsi_tracing_enabled; +extern grpc_core::TraceFlag tsi_tracing_enabled; + +/* -- tsi_zero_copy_grpc_protector object -- + + This object protects and unprotects grpc slice buffers with zero or minimized + memory copy once the handshake is done. Implementations of this object must be + thread compatible. This object depends on grpc and the details of this object + is defined in transport_security_grpc.h. */ + +typedef struct tsi_zero_copy_grpc_protector tsi_zero_copy_grpc_protector; /* --- tsi_frame_protector object --- @@ -126,11 +130,11 @@ typedef struct tsi_frame_protector tsi_frame_protector; 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); +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. @@ -141,8 +145,8 @@ tsi_result tsi_frame_protector_protect(tsi_frame_protector *self, - 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); + 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 @@ -167,12 +171,12 @@ tsi_result tsi_frame_protector_protect_flush( 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); + 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); +void tsi_frame_protector_destroy(tsi_frame_protector* self); /* --- tsi_peer objects --- @@ -184,91 +188,155 @@ void tsi_frame_protector_destroy(tsi_frame_protector *self); /* 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; + char* name; struct { - char *data; + char* data; size_t length; } value; } tsi_peer_property; typedef struct { - tsi_peer_property *properties; + tsi_peer_property* properties; size_t property_count; } tsi_peer; /* Destructs the tsi_peer object. */ -void tsi_peer_destruct(tsi_peer *self); +void tsi_peer_destruct(tsi_peer* self); + +/* --- tsi_handshaker_result object --- + + This object contains all necessary handshake results and data such as peer + info, negotiated keys, unused handshake bytes, when the handshake completes. + Implementations of this object must be thread compatible. */ + +typedef struct tsi_handshaker_result tsi_handshaker_result; + +/* This method extracts tsi peer. It returns TSI_OK assuming there is no fatal + error. + The caller is responsible for destructing the peer. */ +tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result* self, + tsi_peer* peer); + +/* This method creates a tsi_frame_protector object. It returns TSI_OK assuming + there is no fatal error. + The caller is responsible for destroying the protector. */ +tsi_result tsi_handshaker_result_create_frame_protector( + const tsi_handshaker_result* self, size_t* max_output_protected_frame_size, + tsi_frame_protector** protector); + +/* This method returns the unused bytes from the handshake. It returns TSI_OK + assuming there is no fatal error. + Ownership of the bytes is retained by the handshaker result. As a + consequence, the caller must not free the bytes. */ +tsi_result tsi_handshaker_result_get_unused_bytes( + const tsi_handshaker_result* self, const unsigned char** bytes, + size_t* byte_size); + +/* This method releases the tsi_handshaker_handshaker object. After this method + is called, no other method can be called on the object. */ +void tsi_handshaker_result_destroy(tsi_handshaker_result* self); /* --- tsi_handshaker objects ---- Implementations of this object must be thread compatible. - A typical usage of this object would be: + ------------------------------------------------------------------------ + + A typical usage supporting both synchronous and asynchronous TSI handshaker + implementations 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; + + typedef struct { + tsi_handshaker *handshaker; + tsi_handshaker_result *handshaker_result; + unsigned char *handshake_buffer; + size_t handshake_buffer_size; + ... + } security_handshaker; + + void do_handshake(security_handshaker *h, ...) { + // Start the handshake by the calling do_handshake_next. + do_handshake_next(h, NULL, 0); + ... } - // 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, ..., ...); - .... + // This method is the callback function when data is received from the + // peer. This method will read bytes into the handshake buffer and call + // do_handshake_next. + void on_handshake_data_received_from_peer(void *user_data) { + security_handshaker *h = (security_handshaker *)user_data; + size_t bytes_received_size = h->handshake_buffer_size; + read_bytes_from_peer(h->handshake_buffer, &bytes_received_size); + do_handshake_next(h, h->handshake_buffer, bytes_received_size); + } + + // This method processes a step of handshake, calling tsi_handshaker_next. + void do_handshake_next(security_handshaker *h, + const unsigned char* bytes_received, + size_t bytes_received_size) { + tsi_result status = TSI_OK; + unsigned char *bytes_to_send = NULL; + size_t bytes_to_send_size = 0; + tsi_handshaker_result *result = NULL; + status = tsi_handshaker_next( + handshaker, bytes_received, bytes_received_size, &bytes_to_send, + &bytes_to_send_size, &result, on_handshake_next_done, h); + // If TSI handshaker is asynchronous, on_handshake_next_done will be + // executed inside tsi_handshaker_next. + if (status == TSI_ASYNC) return; + // If TSI handshaker is synchronous, invoke callback directly in this + // thread. + on_handshake_next_done(status, (void *)h, bytes_to_send, + bytes_to_send_size, result); + } + + // This is the callback function to execute after tsi_handshaker_next. + // It is passed to tsi_handshaker_next as a function parameter. + void on_handshake_next_done( + tsi_result status, void *user_data, const unsigned char *bytes_to_send, + size_t bytes_to_send_size, tsi_handshaker_result *result) { + security_handshaker *h = (security_handshaker *)user_data; + if (status == TSI_INCOMPLETE_DATA) { + // Schedule an asynchronous read from the peer. If handshake data are + // received, on_handshake_data_received_from_peer will be called. + async_read_from_peer(..., ..., on_handshake_data_received_from_peer); + return; + } + if (status != TSI_OK) return; + + if (bytes_to_send_size > 0) { + send_bytes_to_peer(bytes_to_send, bytes_to_send_size); + } + + if (result != NULL) { + // Handshake completed. + h->result = result; + // Check the Peer. + tsi_peer peer; + status = tsi_handshaker_result_extract_peer(result, &peer); + if (status != TSI_OK) return; + status = check_peer(&peer); + tsi_peer_destruct(&peer); + if (status != TSI_OK) return; + + // Create the protector. + tsi_frame_protector* protector = NULL; + status = tsi_handshaker_result_create_frame_protector(result, NULL, + &protector); + if (status != TSI_OK) return; + + // Do not forget to unprotect outstanding data if any. + .... + } } - ... ------------------------------------------------------------------------ */ typedef struct tsi_handshaker tsi_handshaker; -/* Gets bytes that need to be sent to the peer. +/* TODO(jiangtaoli2016): Cleans up deprecated methods when we are ready. */ + +/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead. + 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 @@ -279,11 +347,12 @@ typedef struct tsi_handshaker tsi_handshaker; 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); +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. +/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead. + 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. @@ -292,28 +361,33 @@ tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, 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); +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. +/* TO BE DEPRECATED SOON. + 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); +tsi_result tsi_handshaker_get_result(tsi_handshaker* self); -/* Returns 1 if the handshake is in progress, 0 otherwise. */ +/* TO BE DEPRECATED SOON. + 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 +/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_extract_peer instead. + 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); +tsi_result tsi_handshaker_extract_peer(tsi_handshaker* self, tsi_peer* peer); -/* This method creates a tsi_frame_protector object after the handshake phase +/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_create_frame_protector + instead. + 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 @@ -330,15 +404,61 @@ tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer); 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); + tsi_handshaker* self, size_t* max_output_protected_frame_size, + tsi_frame_protector** protector); + +/* Callback function definition for tsi_handshaker_next. + - status indicates the status of the next operation. + - user_data is the argument to callback function passed from the caller. + - bytes_to_send is the data buffer to be sent to the peer. + - bytes_to_send_size is the size of data buffer to be sent to the peer. + - handshaker_result is the result of handshake when the handshake completes, + is NULL otherwise. */ +typedef void (*tsi_handshaker_on_next_done_cb)( + tsi_result status, void* user_data, const unsigned char* bytes_to_send, + size_t bytes_to_send_size, tsi_handshaker_result* handshaker_result); + +/* Conduct a next step of the handshake. + - received_bytes is the buffer containing the data received from the peer. + - received_bytes_size is the size of the data received from the peer. + - bytes_to_send is the data buffer to be sent to the peer. + - bytes_to_send_size is the size of data buffer to be sent to the peer. + - handshaker_result is the result of handshake if the handshake completes. + - cb is the callback function defined above. It can be NULL for synchronous + TSI handshaker implementation. + - user_data is the argument to callback function passed from the caller. + This method returns TSI_ASYNC if the TSI handshaker implementation is + asynchronous, and in this case, the callback is guaranteed to run in another + thread owned by TSI. It returns TSI_OK if the handshake completes or if + there are data to send to the peer, otherwise returns TSI_INCOMPLETE_DATA + which indicates that this method needs to be called again with more data + from the peer. In case of a fatal error in the handshake, another specific + error code is returned. + The caller is responsible for destroying the handshaker_result. However, + the caller should not free bytes_to_send, as the buffer is owned by the + tsi_handshaker object. */ +tsi_result tsi_handshaker_next( + tsi_handshaker* self, const unsigned char* received_bytes, + size_t received_bytes_size, const unsigned char** bytes_to_send, + size_t* bytes_to_send_size, tsi_handshaker_result** handshaker_result, + tsi_handshaker_on_next_done_cb cb, void* user_data); + +/* This method shuts down a TSI handshake that is in progress. + * + * This method will be invoked when TSI handshake should be terminated before + * being finished in order to free any resources being used. + */ +void tsi_handshaker_shutdown(tsi_handshaker* self); /* 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); +void tsi_handshaker_destroy(tsi_handshaker* self); + +/* This method initializes the necessary shared objects used for tsi + implementation. */ +void tsi_init(); -#ifdef __cplusplus -} -#endif +/* This method destroys the shared objects created by tsi_init. */ +void tsi_destroy(); -#endif /* GRPC_INTERNAL_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H */ +#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_INTERFACE_H */ diff --git a/third_party/grpc/src/cpp/README.md b/third_party/grpc/src/cpp/README.md index baeba083155522..4ec9133c598168 100644 --- a/third_party/grpc/src/cpp/README.md +++ b/third_party/grpc/src/cpp/README.md @@ -1,8 +1,83 @@ -#Overview +# Overview -This directory contains source code for C++ implementation of gRPC. +A C++ implementation of gRPC -#Status +# To start using gRPC C++ -Beta +In the C++ world, there's no universally accepted standard for managing project dependencies. +Therefore, gRPC supports several major build systems, which should satisfy most users. + +## bazel + +We recommend using Bazel for projects that use gRPC as it will give you the best developer experience +(easy handling of dependencies that support bazel & fast builds). + +To add gRPC as a dependency in bazel: +1. determine commit SHA for the grpc release you want to use +2. Use the [http_archive](https://docs.bazel.build/versions/master/be/workspace.html#http_archive) bazel rule to include gRPC source + ``` + http_archive( + name = "com_github_grpc_grpc", + urls = [ + "https://github.com/grpc/grpc/archive/YOUR_GRPC_COMMIT_SHA.tar.gz", + ], + strip_prefix = "grpc-YOUR_GRPC_COMMIT_SHA", + ) + + load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") + + grpc_deps() + ``` + +NOTE: currently bazel is only supported for building gRPC on Linux. + +## make + +Currently the default choice for building on UNIX based systems is `make`. + +To install gRPC for C++ on your system using `make`, follow the [Building gRPC C++](../../BUILDING.md) +instructions to build from source and then install locally using `make install`. +This also installs the protocol buffer compiler `protoc` (if you don't have it already), +and the C++ gRPC plugin for `protoc`. + +WARNING: After installing with `make install` there is no easy way to uninstall, which can cause issues +if you later want to remove the grpc and/or protobuf installation or upgrade to a newer version. + +## cmake + +`cmake` is the default build option on Windows, but also works on Linux, MacOS. `cmake` has good +support for crosscompiling and can be used for targeting Android platform. + +If your project is using cmake, there are several ways to add gRPC dependency. +- install gRPC via cmake first and then locate it with `find_package(gRPC CONFIG)`. [Example](../../examples/cpp/helloworld/CMakeLists.txt) +- via cmake's `ExternalProject_Add` using a technique called "superbuild". [Example](../../examples/cpp/helloworld/cmake_externalproject/CMakeLists.txt) +- add gRPC source tree to your project (preferrably as a git submodule) and add it to your cmake project with `add_subdirectory`. [Example](../../examples/cpp/helloworld/CMakeLists.txt) + +## Packaging systems + +There's no standard packaging system for C++. We've looked into supporting some (e.g. Conan and vcpkg) but we are not there yet. +Contributions and community-maintained packages for popular packaging systems are welcome! + + +## Examples & Additional Documentation + +You can find out how to build and run our simplest gRPC C++ example in our +[C++ quick start](../../examples/cpp). + +For more detailed documentation on using gRPC in C++ , see our main +documentation site at [grpc.io](https://grpc.io), specifically: + +* [Overview](https://grpc.io/docs/): An introduction to gRPC with a simple + Hello World example in all our supported languages, including C++. +* [gRPC Basics - C++](https://grpc.io/docs/tutorials/basic/c.html): + A tutorial that steps you through creating a simple gRPC C++ example + application. +* [Asynchronous Basics - C++](https://grpc.io/docs/tutorials/async/helloasync-cpp.html): + A tutorial that shows you how to use gRPC C++'s asynchronous/non-blocking + APIs. + + +# To start developing gRPC C++ + +For instructions on how to build gRPC C++ from source, follow the [Building gRPC C++](../../BUILDING.md) instructions. diff --git a/third_party/grpc/src/cpp/client/channel.cc b/third_party/grpc/src/cpp/client/channel.cc deleted file mode 100644 index ae20392d1160c7..00000000000000 --- a/third_party/grpc/src/cpp/client/channel.cc +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "src/core/profiling/timers.h" - -namespace grpc { - -static internal::GrpcLibraryInitializer g_gli_initializer; -Channel::Channel(const grpc::string& host, grpc_channel* channel) - : host_(host), c_channel_(channel) { - g_gli_initializer.summon(); -} - -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()); - 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]; - ops->FillOps(cops, &nops); - GPR_ASSERT(GRPC_CALL_OK == - grpc_call_start_batch(call->call(), cops, nops, ops, nullptr)); -} - -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/third_party/grpc/src/cpp/client/channel_cc.cc b/third_party/grpc/src/cpp/client/channel_cc.cc new file mode 100644 index 00000000000000..a31d0b30b159fe --- /dev/null +++ b/third_party/grpc/src/cpp/client/channel_cc.cc @@ -0,0 +1,255 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/memory.h" +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/surface/completion_queue.h" + +namespace grpc { + +static internal::GrpcLibraryInitializer g_gli_initializer; +Channel::Channel( + const grpc::string& host, grpc_channel* channel, + std::vector< + std::unique_ptr> + interceptor_creators) + : host_(host), c_channel_(channel) { + interceptor_creators_ = std::move(interceptor_creators); + g_gli_initializer.summon(); +} + +Channel::~Channel() { + grpc_channel_destroy(c_channel_); + if (callback_cq_ != nullptr) { + callback_cq_->Shutdown(); + } +} + +namespace { + +inline grpc_slice SliceFromArray(const char* arr, size_t len) { + return g_core_codegen_interface->grpc_slice_from_copied_buffer(arr, len); +} + +grpc::string GetChannelInfoField(grpc_channel* channel, + grpc_channel_info* channel_info, + char*** channel_info_field) { + char* value = nullptr; + memset(channel_info, 0, sizeof(*channel_info)); + *channel_info_field = &value; + grpc_channel_get_info(channel, channel_info); + if (value == nullptr) return ""; + grpc::string result = value; + gpr_free(value); + return result; +} + +} // namespace + +grpc::string Channel::GetLoadBalancingPolicyName() const { + grpc_channel_info channel_info; + return GetChannelInfoField(c_channel_, &channel_info, + &channel_info.lb_policy_name); +} + +grpc::string Channel::GetServiceConfigJSON() const { + grpc_channel_info channel_info; + return GetChannelInfoField(c_channel_, &channel_info, + &channel_info.service_config_json); +} + +namespace experimental { + +void ChannelResetConnectionBackoff(Channel* channel) { + grpc_channel_reset_connect_backoff(channel->c_channel_); +} + +} // namespace experimental + +internal::Call Channel::CreateCallInternal(const internal::RpcMethod& method, + ClientContext* context, + CompletionQueue* cq, + size_t interceptor_pos) { + const bool kRegistered = method.channel_tag() && context->authority().empty(); + grpc_call* c_call = nullptr; + 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 string* host_str = nullptr; + if (!context->authority_.empty()) { + host_str = &context->authority_; + } else if (!host_.empty()) { + host_str = &host_; + } + grpc_slice method_slice = + SliceFromArray(method.name(), strlen(method.name())); + grpc_slice host_slice; + if (host_str != nullptr) { + host_slice = SliceFromCopiedString(*host_str); + } + c_call = grpc_channel_create_call( + c_channel_, context->propagate_from_call_, + context->propagation_options_.c_bitmask(), cq->cq(), method_slice, + host_str == nullptr ? nullptr : &host_slice, context->raw_deadline(), + nullptr); + grpc_slice_unref(method_slice); + if (host_str != nullptr) { + grpc_slice_unref(host_slice); + } + } + grpc_census_call_set_context(c_call, context->census_context()); + + // ClientRpcInfo should be set before call because set_call also checks + // whether the call has been cancelled, and if the call was cancelled, we + // should notify the interceptors too/ + auto* info = + context->set_client_rpc_info(method.name(), method.method_type(), this, + interceptor_creators_, interceptor_pos); + context->set_call(c_call, shared_from_this()); + + return internal::Call(c_call, this, cq, info); +} + +internal::Call Channel::CreateCall(const internal::RpcMethod& method, + ClientContext* context, + CompletionQueue* cq) { + return CreateCallInternal(method, context, cq, 0); +} + +void Channel::PerformOpsOnCall(internal::CallOpSetInterface* ops, + internal::Call* call) { + ops->FillOps( + call); // Make a copy of call. It's fine since Call just has pointers +} + +void* Channel::RegisterMethod(const char* method) { + return grpc_channel_register_call( + c_channel_, method, host_.empty() ? nullptr : 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 final : public internal::CompletionQueueTag { + public: + explicit TagSaver(void* tag) : tag_(tag) {} + ~TagSaver() override {} + bool FinalizeResult(void** tag, bool* status) 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 = nullptr; + NotifyOnStateChangeImpl(last_observed, deadline, &cq, nullptr); + cq.Next(&tag, &ok); + GPR_ASSERT(tag == nullptr); + return ok; +} + +namespace { +class ShutdownCallback : public grpc_experimental_completion_queue_functor { + public: + ShutdownCallback() { functor_run = &ShutdownCallback::Run; } + // TakeCQ takes ownership of the cq into the shutdown callback + // so that the shutdown callback will be responsible for destroying it + void TakeCQ(CompletionQueue* cq) { cq_ = cq; } + + // The Run function will get invoked by the completion queue library + // when the shutdown is actually complete + static void Run(grpc_experimental_completion_queue_functor* cb, int) { + auto* callback = static_cast(cb); + delete callback->cq_; + delete callback; + } + + private: + CompletionQueue* cq_ = nullptr; +}; +} // namespace + +CompletionQueue* Channel::CallbackCQ() { + // TODO(vjpai): Consider using a single global CQ for the default CQ + // if there is no explicit per-channel CQ registered + std::lock_guard l(mu_); + if (callback_cq_ == nullptr) { + auto* shutdown_callback = new ShutdownCallback; + callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING, + shutdown_callback}); + + // Transfer ownership of the new cq to its own shutdown callback + shutdown_callback->TakeCQ(callback_cq_); + } + return callback_cq_; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/client/client_context.cc b/third_party/grpc/src/cpp/client/client_context.cc index 73147fd7bb21b3..c9ea3e5f83b37f 100644 --- a/third_party/grpc/src/cpp/client/client_context.cc +++ b/third_party/grpc/src/cpp/client/client_context.cc @@ -1,75 +1,69 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include #include #include #include +#include #include -#include -#include -#include -#include "src/core/channel/compress_filter.h" -#include "src/cpp/common/create_auth_context.h" +#include +#include +#include +#include +#include namespace grpc { -class DefaultGlobalClientCallbacks GRPC_FINAL +class DefaultGlobalClientCallbacks final : public ClientContext::GlobalCallbacks { public: - ~DefaultGlobalClientCallbacks() GRPC_OVERRIDE {} - void DefaultConstructor(ClientContext* context) GRPC_OVERRIDE {} - void Destructor(ClientContext* context) GRPC_OVERRIDE {} + ~DefaultGlobalClientCallbacks() override {} + void DefaultConstructor(ClientContext* context) override {} + void Destructor(ClientContext* context) override {} }; -static DefaultGlobalClientCallbacks g_default_client_callbacks; +static internal::GrpcLibraryInitializer g_gli_initializer; +static DefaultGlobalClientCallbacks* g_default_client_callbacks = + new DefaultGlobalClientCallbacks(); static ClientContext::GlobalCallbacks* g_client_callbacks = - &g_default_client_callbacks; + g_default_client_callbacks; ClientContext::ClientContext() : initial_metadata_received_(false), + wait_for_ready_(false), + wait_for_ready_explicitly_set_(false), + idempotent_(false), + cacheable_(false), call_(nullptr), call_canceled_(false), deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)), - propagate_from_call_(nullptr) { + census_context_(nullptr), + propagate_from_call_(nullptr), + initial_metadata_corked_(false) { g_client_callbacks->DefaultConstructor(this); } ClientContext::~ClientContext() { if (call_) { - grpc_call_destroy(call_); + grpc_call_unref(call_); } g_client_callbacks->Destructor(this); } @@ -89,47 +83,52 @@ void ClientContext::AddMetadata(const grpc::string& meta_key, void ClientContext::set_call(grpc_call* call, const std::shared_ptr& channel) { - grpc::unique_lock lock(mu_); + std::unique_lock lock(mu_); GPR_ASSERT(call_ == nullptr); call_ = call; channel_ = channel; if (creds_ && !creds_->ApplyToCall(call_)) { + // TODO(yashykt): should interceptors also see this status? + SendCancelToInterceptors(); grpc_call_cancel_with_status(call, GRPC_STATUS_CANCELLED, "Failed to set credentials to rpc.", nullptr); } if (call_canceled_) { + SendCancelToInterceptors(); grpc_call_cancel(call_, nullptr); } } void ClientContext::set_compression_algorithm( grpc_compression_algorithm algorithm) { - char* algorithm_name = nullptr; + compression_algorithm_ = algorithm; + const 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_; + AddMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name); } void ClientContext::TryCancel() { - grpc::unique_lock lock(mu_); + std::unique_lock lock(mu_); if (call_) { + SendCancelToInterceptors(); grpc_call_cancel(call_, nullptr); } else { call_canceled_ = true; } } +void ClientContext::SendCancelToInterceptors() { + internal::CancelInterceptorBatchMethods cancel_methods; + for (size_t i = 0; i < rpc_info_.interceptors_.size(); i++) { + rpc_info_.RunInterceptor(&cancel_methods, i); + } +} + grpc::string ClientContext::peer() const { grpc::string peer; if (call_) { @@ -141,9 +140,9 @@ grpc::string ClientContext::peer() const { } void ClientContext::SetGlobalCallbacks(GlobalCallbacks* client_callbacks) { - GPR_ASSERT(g_client_callbacks == &g_default_client_callbacks); - GPR_ASSERT(client_callbacks != NULL); - GPR_ASSERT(client_callbacks != &g_default_client_callbacks); + GPR_ASSERT(g_client_callbacks == g_default_client_callbacks); + GPR_ASSERT(client_callbacks != nullptr); + GPR_ASSERT(client_callbacks != g_default_client_callbacks); g_client_callbacks = client_callbacks; } diff --git a/third_party/grpc/src/cpp/client/client_interceptor.cc b/third_party/grpc/src/cpp/client/client_interceptor.cc new file mode 100644 index 00000000000000..3a5cac9830f843 --- /dev/null +++ b/third_party/grpc/src/cpp/client/client_interceptor.cc @@ -0,0 +1,34 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +namespace grpc { + +namespace internal { +experimental::ClientInterceptorFactoryInterface* + g_global_client_interceptor_factory = nullptr; +} + +namespace experimental { +void RegisterGlobalClientInterceptorFactory( + ClientInterceptorFactoryInterface* factory) { + internal::g_global_client_interceptor_factory = factory; +} +} // namespace experimental +} // namespace grpc diff --git a/third_party/grpc/src/cpp/client/create_channel.cc b/third_party/grpc/src/cpp/client/create_channel.cc index a1b65af71d0b2b..457daa674c7eed 100644 --- a/third_party/grpc/src/cpp/client/create_channel.cc +++ b/third_party/grpc/src/cpp/client/create_channel.cc @@ -1,43 +1,27 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include "src/cpp/client/create_channel_internal.h" @@ -50,29 +34,50 @@ std::shared_ptr CreateChannel( return CreateCustomChannel(target, creds, ChannelArguments()); } -// GCC-specific features -#if (defined(COMPILER_GCC3) || defined(__APPLE__)) && !defined(SWIG) -#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) -#else // Not GCC -#define ATTRIBUTE_UNUSED -#endif // GCC - std::shared_ptr CreateCustomChannel( const grpc::string& target, const std::shared_ptr& creds, const ChannelArguments& args) { - ATTRIBUTE_UNUSED internal::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.")); + GrpcLibraryCodegen init_lib; // We need to call init in case of a bad creds. + return creds ? creds->CreateChannel(target, args) + : CreateChannelInternal( + "", + grpc_lame_client_channel_create( + nullptr, GRPC_STATUS_INVALID_ARGUMENT, + "Invalid credentials."), + std::vector>()); +} + +namespace experimental { +/// Create a new \em custom \a Channel pointing to \a target with \a +/// interceptors being invoked per call. +/// +/// \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 (one on which all operations +/// fail) is returned. +/// \param args Options for channel creation. +std::shared_ptr CreateCustomChannelWithInterceptors( + const grpc::string& target, + const std::shared_ptr& creds, + const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) { + return creds ? creds->CreateChannelWithInterceptors( + target, args, std::move(interceptor_creators)) + : CreateChannelInternal( + "", + grpc_lame_client_channel_create( + nullptr, GRPC_STATUS_INVALID_ARGUMENT, + "Invalid credentials."), + std::vector>()); } +} // namespace experimental } // namespace grpc diff --git a/third_party/grpc/src/cpp/client/create_channel_internal.cc b/third_party/grpc/src/cpp/client/create_channel_internal.cc index 9c5ab038cf1db5..a0efb97f7ef278 100644 --- a/third_party/grpc/src/cpp/client/create_channel_internal.cc +++ b/third_party/grpc/src/cpp/client/create_channel_internal.cc @@ -1,46 +1,35 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include -#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)); +std::shared_ptr CreateChannelInternal( + const grpc::string& host, grpc_channel* c_channel, + std::vector< + std::unique_ptr> + interceptor_creators) { + return std::shared_ptr( + new Channel(host, c_channel, std::move(interceptor_creators))); } } // namespace grpc diff --git a/third_party/grpc/src/cpp/client/create_channel_internal.h b/third_party/grpc/src/cpp/client/create_channel_internal.h index 4385ec701e2640..a90c92c518db0c 100644 --- a/third_party/grpc/src/cpp/client/create_channel_internal.h +++ b/third_party/grpc/src/cpp/client/create_channel_internal.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -36,15 +21,19 @@ #include -#include +#include +#include struct grpc_channel; namespace grpc { class Channel; -std::shared_ptr CreateChannelInternal(const grpc::string& host, - grpc_channel* c_channel); +std::shared_ptr CreateChannelInternal( + const grpc::string& host, grpc_channel* c_channel, + std::vector< + std::unique_ptr> + interceptor_creators); } // namespace grpc diff --git a/third_party/grpc/src/cpp/client/create_channel_posix.cc b/third_party/grpc/src/cpp/client/create_channel_posix.cc new file mode 100644 index 00000000000000..3affc1ef3912b3 --- /dev/null +++ b/third_party/grpc/src/cpp/client/create_channel_posix.cc @@ -0,0 +1,75 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include + +#include "src/cpp/client/create_channel_internal.h" + +namespace grpc { + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +std::shared_ptr CreateInsecureChannelFromFd(const grpc::string& target, + int fd) { + internal::GrpcLibrary init_lib; + init_lib.init(); + return CreateChannelInternal( + "", grpc_insecure_channel_create_from_fd(target.c_str(), fd, nullptr), + std::vector< + std::unique_ptr>()); +} + +std::shared_ptr CreateCustomInsecureChannelFromFd( + const grpc::string& target, int fd, const ChannelArguments& args) { + internal::GrpcLibrary init_lib; + init_lib.init(); + grpc_channel_args channel_args; + args.SetChannelArgs(&channel_args); + return CreateChannelInternal( + "", + grpc_insecure_channel_create_from_fd(target.c_str(), fd, &channel_args), + std::vector< + std::unique_ptr>()); +} + +namespace experimental { + +std::shared_ptr CreateCustomInsecureChannelWithInterceptorsFromFd( + const grpc::string& target, int fd, const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) { + internal::GrpcLibrary init_lib; + init_lib.init(); + grpc_channel_args channel_args; + args.SetChannelArgs(&channel_args); + return CreateChannelInternal( + "", + grpc_insecure_channel_create_from_fd(target.c_str(), fd, &channel_args), + std::move(interceptor_creators)); +} + +} // namespace experimental + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/client/credentials.cc b/third_party/grpc/src/cpp/client/credentials.cc deleted file mode 100644 index 6fb620b0ea8b7c..00000000000000 --- a/third_party/grpc/src/cpp/client/credentials.cc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include -#include - -namespace grpc { - -static internal::GrpcLibraryInitializer g_gli_initializer; -ChannelCredentials::ChannelCredentials() { g_gli_initializer.summon(); } - -ChannelCredentials::~ChannelCredentials() {} - -CallCredentials::CallCredentials() { g_gli_initializer.summon(); } - -CallCredentials::~CallCredentials() {} - -} // namespace grpc diff --git a/third_party/grpc/src/cpp/client/credentials_cc.cc b/third_party/grpc/src/cpp/client/credentials_cc.cc new file mode 100644 index 00000000000000..2a0f06f424e06a --- /dev/null +++ b/third_party/grpc/src/cpp/client/credentials_cc.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +namespace grpc { + +static internal::GrpcLibraryInitializer g_gli_initializer; +ChannelCredentials::ChannelCredentials() { g_gli_initializer.summon(); } + +ChannelCredentials::~ChannelCredentials() {} + +CallCredentials::CallCredentials() { g_gli_initializer.summon(); } + +CallCredentials::~CallCredentials() {} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/client/cronet_credentials.cc b/third_party/grpc/src/cpp/client/cronet_credentials.cc new file mode 100644 index 00000000000000..b2801764f20831 --- /dev/null +++ b/third_party/grpc/src/cpp/client/cronet_credentials.cc @@ -0,0 +1,64 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include "src/cpp/client/create_channel_internal.h" + +namespace grpc { + +class CronetChannelCredentialsImpl final : public ChannelCredentials { + public: + CronetChannelCredentialsImpl(void* engine) : engine_(engine) {} + + std::shared_ptr CreateChannel( + const string& target, const grpc::ChannelArguments& args) override { + return CreateChannelWithInterceptors( + target, args, + std::vector>()); + } + + SecureChannelCredentials* AsSecureCredentials() override { return nullptr; } + + private: + std::shared_ptr CreateChannelWithInterceptors( + const string& target, const grpc::ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) override { + grpc_channel_args channel_args; + args.SetChannelArgs(&channel_args); + return CreateChannelInternal( + "", + grpc_cronet_secure_channel_create(engine_, target.c_str(), + &channel_args, nullptr), + std::move(interceptor_creators)); + } + void* engine_; +}; + +std::shared_ptr CronetChannelCredentials(void* engine) { + return std::shared_ptr( + new CronetChannelCredentialsImpl(engine)); +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/client/generic_stub.cc b/third_party/grpc/src/cpp/client/generic_stub.cc index 7a2fdf941cec7f..f61c1b5317bf7d 100644 --- a/third_party/grpc/src/cpp/client/generic_stub.cc +++ b/third_party/grpc/src/cpp/client/generic_stub.cc @@ -1,50 +1,84 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include -#include +#include +#include +#include namespace grpc { +namespace { +std::unique_ptr CallInternal( + ChannelInterface* channel, ClientContext* context, + const grpc::string& method, CompletionQueue* cq, bool start, void* tag) { + return std::unique_ptr( + internal::ClientAsyncReaderWriterFactory::Create( + channel, cq, + internal::RpcMethod(method.c_str(), + internal::RpcMethod::BIDI_STREAMING), + context, start, tag)); +} + +} // namespace + // 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( + return CallInternal(channel_.get(), context, method, cq, true, tag); +} + +// setup a call to a named method +std::unique_ptr GenericStub::PrepareCall( + ClientContext* context, const grpc::string& method, CompletionQueue* cq) { + return CallInternal(channel_.get(), context, method, cq, false, nullptr); +} + +// setup a unary call to a named method +std::unique_ptr GenericStub::PrepareUnaryCall( + ClientContext* context, const grpc::string& method, + const ByteBuffer& request, CompletionQueue* cq) { + return std::unique_ptr( + internal::ClientAsyncResponseReaderFactory::Create( channel_.get(), cq, - RpcMethod(method.c_str(), RpcMethod::BIDI_STREAMING), context, tag)); + internal::RpcMethod(method.c_str(), internal::RpcMethod::NORMAL_RPC), + context, request, false)); +} + +void GenericStub::experimental_type::UnaryCall( + ClientContext* context, const grpc::string& method, + const ByteBuffer* request, ByteBuffer* response, + std::function on_completion) { + internal::CallbackUnaryCall( + stub_->channel_.get(), + internal::RpcMethod(method.c_str(), internal::RpcMethod::NORMAL_RPC), + context, request, response, std::move(on_completion)); +} + +void GenericStub::experimental_type::PrepareBidiStreamingCall( + ClientContext* context, const grpc::string& method, + experimental::ClientBidiReactor* reactor) { + internal::ClientCallbackReaderWriterFactory::Create( + stub_->channel_.get(), + internal::RpcMethod(method.c_str(), internal::RpcMethod::BIDI_STREAMING), + context, reactor); } } // namespace grpc diff --git a/third_party/grpc/src/cpp/client/insecure_credentials.cc b/third_party/grpc/src/cpp/client/insecure_credentials.cc index 1293203b932cdb..241ce918034caa 100644 --- a/third_party/grpc/src/cpp/client/insecure_credentials.cc +++ b/third_party/grpc/src/cpp/client/insecure_credentials.cc @@ -1,62 +1,57 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include #include #include -#include -#include -#include +#include +#include +#include #include "src/cpp/client/create_channel_internal.h" namespace grpc { namespace { -class InsecureChannelCredentialsImpl GRPC_FINAL : public ChannelCredentials { +class InsecureChannelCredentialsImpl final : public ChannelCredentials { public: std::shared_ptr CreateChannel( - const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE { + const string& target, const grpc::ChannelArguments& args) override { + return CreateChannelWithInterceptors( + target, args, + std::vector>()); + } + + std::shared_ptr CreateChannelWithInterceptors( + const string& target, const grpc::ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) override { grpc_channel_args channel_args; args.SetChannelArgs(&channel_args); return CreateChannelInternal( "", - grpc_insecure_channel_create(target.c_str(), &channel_args, nullptr)); + grpc_insecure_channel_create(target.c_str(), &channel_args, nullptr), + std::move(interceptor_creators)); } - SecureChannelCredentials* AsSecureCredentials() GRPC_OVERRIDE { - return nullptr; - } + SecureChannelCredentials* AsSecureCredentials() override { return nullptr; } }; } // namespace diff --git a/third_party/grpc/src/cpp/client/secure_credentials.cc b/third_party/grpc/src/cpp/client/secure_credentials.cc index 074dae7ca7900c..4d0ed355abaa62 100644 --- a/third_party/grpc/src/cpp/client/secure_credentials.cc +++ b/third_party/grpc/src/cpp/client/secure_credentials.cc @@ -1,42 +1,28 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include -#include -#include +#include "src/cpp/client/secure_credentials.h" #include +#include +#include +#include +#include #include "src/cpp/client/create_channel_internal.h" -#include "src/cpp/client/secure_credentials.h" #include "src/cpp/common/secure_auth_context.h" namespace grpc { @@ -50,18 +36,30 @@ SecureChannelCredentials::SecureChannelCredentials( std::shared_ptr SecureChannelCredentials::CreateChannel( const string& target, const grpc::ChannelArguments& args) { + return CreateChannelWithInterceptors( + target, args, + std::vector< + std::unique_ptr>()); +} + +std::shared_ptr +SecureChannelCredentials::CreateChannelWithInterceptors( + const string& target, const grpc::ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) { 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)); + nullptr), + std::move(interceptor_creators)); } SecureCallCredentials::SecureCallCredentials(grpc_call_credentials* c_creds) : c_creds_(c_creds) { - internal::GrpcLibraryInitializer gli_initializer; - gli_initializer.summon(); + g_gli_initializer.summon(); } bool SecureCallCredentials::ApplyToCall(grpc_call* call) { @@ -71,38 +69,69 @@ bool SecureCallCredentials::ApplyToCall(grpc_call* call) { namespace { std::shared_ptr WrapChannelCredentials( grpc_channel_credentials* creds) { - return creds == nullptr ? nullptr : std::shared_ptr( - new SecureChannelCredentials(creds)); + return creds == nullptr ? nullptr + : std::shared_ptr( + new SecureChannelCredentials(creds)); } std::shared_ptr WrapCallCredentials( grpc_call_credentials* creds) { - return creds == nullptr ? nullptr : std::shared_ptr( - new SecureCallCredentials(creds)); + return creds == nullptr ? nullptr + : std::shared_ptr( + new SecureCallCredentials(creds)); } } // namespace std::shared_ptr GoogleDefaultCredentials() { - GrpcLibrary init; // To call grpc_init(). + GrpcLibraryCodegen init; // To call grpc_init(). return WrapChannelCredentials(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(). + GrpcLibraryCodegen 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_channel_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); + options.pem_private_key.empty() ? nullptr : &pem_key_cert_pair, nullptr, + nullptr); return WrapChannelCredentials(c_creds); } +namespace experimental { + +// Builds ALTS Credentials given ALTS specific options +std::shared_ptr AltsCredentials( + const AltsCredentialsOptions& options) { + GrpcLibraryCodegen init; // To call grpc_init(). + grpc_alts_credentials_options* c_options = + grpc_alts_credentials_client_options_create(); + for (auto service_account = options.target_service_accounts.begin(); + service_account != options.target_service_accounts.end(); + service_account++) { + grpc_alts_credentials_client_options_add_target_service_account( + c_options, service_account->c_str()); + } + grpc_channel_credentials* c_creds = grpc_alts_credentials_create(c_options); + grpc_alts_credentials_options_destroy(c_options); + return WrapChannelCredentials(c_creds); +} + +// Builds Local Credentials +std::shared_ptr LocalCredentials( + grpc_local_connect_type type) { + GrpcLibraryCodegen init; // To call grpc_init(). + return WrapChannelCredentials(grpc_local_credentials_create(type)); +} + +} // namespace experimental + // Builds credentials for use when running in GCE std::shared_ptr GoogleComputeEngineCredentials() { - GrpcLibrary init; // To call grpc_init(). + GrpcLibraryCodegen init; // To call grpc_init(). return WrapCallCredentials( grpc_google_compute_engine_credentials_create(nullptr)); } @@ -110,7 +139,7 @@ std::shared_ptr GoogleComputeEngineCredentials() { // Builds JWT credentials. std::shared_ptr ServiceAccountJWTAccessCredentials( const grpc::string& json_key, long token_lifetime_seconds) { - GrpcLibrary init; // To call grpc_init(). + GrpcLibraryCodegen init; // To call grpc_init(). if (token_lifetime_seconds <= 0) { gpr_log(GPR_ERROR, "Trying to create JWTCredentials with non-positive lifetime"); @@ -125,7 +154,7 @@ std::shared_ptr ServiceAccountJWTAccessCredentials( // Builds refresh token credentials. std::shared_ptr GoogleRefreshTokenCredentials( const grpc::string& json_refresh_token) { - GrpcLibrary init; // To call grpc_init(). + GrpcLibraryCodegen init; // To call grpc_init(). return WrapCallCredentials(grpc_google_refresh_token_credentials_create( json_refresh_token.c_str(), nullptr)); } @@ -133,7 +162,7 @@ std::shared_ptr GoogleRefreshTokenCredentials( // Builds access token credentials. std::shared_ptr AccessTokenCredentials( const grpc::string& access_token) { - GrpcLibrary init; // To call grpc_init(). + GrpcLibraryCodegen init; // To call grpc_init(). return WrapCallCredentials( grpc_access_token_credentials_create(access_token.c_str(), nullptr)); } @@ -142,7 +171,7 @@ std::shared_ptr AccessTokenCredentials( std::shared_ptr GoogleIAMCredentials( const grpc::string& authorization_token, const grpc::string& authority_selector) { - GrpcLibrary init; // To call grpc_init(). + GrpcLibraryCodegen init; // To call grpc_init(). return WrapCallCredentials(grpc_google_iam_credentials_create( authorization_token.c_str(), authority_selector.c_str(), nullptr)); } @@ -166,56 +195,113 @@ std::shared_ptr CompositeChannelCredentials( return nullptr; } +std::shared_ptr CompositeCallCredentials( + const std::shared_ptr& creds1, + const std::shared_ptr& creds2) { + SecureCallCredentials* s_creds1 = creds1->AsSecureCredentials(); + SecureCallCredentials* s_creds2 = creds2->AsSecureCredentials(); + if (s_creds1 != nullptr && s_creds2 != nullptr) { + return WrapCallCredentials(grpc_composite_call_credentials_create( + s_creds1->GetRawCreds(), s_creds2->GetRawCreds(), nullptr)); + } + return nullptr; +} + void MetadataCredentialsPluginWrapper::Destroy(void* wrapper) { if (wrapper == nullptr) return; MetadataCredentialsPluginWrapper* w = - reinterpret_cast(wrapper); + static_cast(wrapper); delete w; } -void MetadataCredentialsPluginWrapper::GetMetadata( +int MetadataCredentialsPluginWrapper::GetMetadata( void* wrapper, grpc_auth_metadata_context context, - grpc_credentials_plugin_metadata_cb cb, void* user_data) { + grpc_credentials_plugin_metadata_cb cb, void* user_data, + grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], + size_t* num_creds_md, grpc_status_code* status, + const char** error_details) { GPR_ASSERT(wrapper); MetadataCredentialsPluginWrapper* w = - reinterpret_cast(wrapper); + static_cast(wrapper); if (!w->plugin_) { - cb(user_data, NULL, 0, GRPC_STATUS_OK, NULL); - return; + *num_creds_md = 0; + *status = GRPC_STATUS_OK; + *error_details = nullptr; + return true; } if (w->plugin_->IsBlocking()) { - w->thread_pool_->Add( - std::bind(&MetadataCredentialsPluginWrapper::InvokePlugin, w, context, - cb, user_data)); + // Asynchronous return. + w->thread_pool_->Add([w, context, cb, user_data] { + w->MetadataCredentialsPluginWrapper::InvokePlugin( + context, cb, user_data, nullptr, nullptr, nullptr, nullptr); + }); + return 0; } else { - w->InvokePlugin(context, cb, user_data); + // Synchronous return. + w->InvokePlugin(context, cb, user_data, creds_md, num_creds_md, status, + error_details); + return 1; } } +namespace { + +void UnrefMetadata(const std::vector& md) { + for (auto it = md.begin(); it != md.end(); ++it) { + grpc_slice_unref(it->key); + grpc_slice_unref(it->value); + } +} + +} // namespace + void MetadataCredentialsPluginWrapper::InvokePlugin( grpc_auth_metadata_context context, grpc_credentials_plugin_metadata_cb cb, - void* user_data) { + void* user_data, grpc_metadata creds_md[4], size_t* num_creds_md, + grpc_status_code* status_code, const char** error_details) { std::multimap metadata; - // const_cast is safe since the SecureAuthContext does not take owndership and - // the object is passed as a const ref to plugin_->GetMetadata. + // const_cast is safe since the SecureAuthContext only inc/dec the refcount + // and the object is passed as a const ref to plugin_->GetMetadata. SecureAuthContext cpp_channel_auth_context( - const_cast(context.channel_auth_context), false); + const_cast(context.channel_auth_context)); Status status = plugin_->GetMetadata(context.service_url, context.method_name, cpp_channel_auth_context, &metadata); std::vector md; for (auto it = metadata.begin(); it != metadata.end(); ++it) { grpc_metadata md_entry; - md_entry.key = it->first.c_str(); - md_entry.value = it->second.data(); - md_entry.value_length = it->second.size(); + md_entry.key = SliceFromCopiedString(it->first); + md_entry.value = SliceFromCopiedString(it->second); md_entry.flags = 0; md.push_back(md_entry); } - cb(user_data, md.empty() ? nullptr : &md[0], md.size(), - static_cast(status.error_code()), - status.error_message().c_str()); + if (creds_md != nullptr) { + // Synchronous return. + if (md.size() > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) { + *num_creds_md = 0; + *status_code = GRPC_STATUS_INTERNAL; + *error_details = gpr_strdup( + "blocking plugin credentials returned too many metadata keys"); + UnrefMetadata(md); + } else { + for (const auto& elem : md) { + creds_md[*num_creds_md].key = elem.key; + creds_md[*num_creds_md].value = elem.value; + creds_md[*num_creds_md].flags = elem.flags; + ++(*num_creds_md); + } + *status_code = static_cast(status.error_code()); + *error_details = + status.ok() ? nullptr : gpr_strdup(status.error_message().c_str()); + } + } else { + // Asynchronous return. + cb(user_data, md.empty() ? nullptr : &md[0], md.size(), + static_cast(status.error_code()), + status.error_message().c_str()); + UnrefMetadata(md); + } } MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper( @@ -224,7 +310,7 @@ MetadataCredentialsPluginWrapper::MetadataCredentialsPluginWrapper( std::shared_ptr MetadataCredentialsFromPlugin( std::unique_ptr plugin) { - GrpcLibrary init; // To call grpc_init(). + GrpcLibraryCodegen init; // To call grpc_init(). const char* type = plugin->GetType(); MetadataCredentialsPluginWrapper* wrapper = new MetadataCredentialsPluginWrapper(std::move(plugin)); diff --git a/third_party/grpc/src/cpp/client/secure_credentials.h b/third_party/grpc/src/cpp/client/secure_credentials.h index 9e841021543a52..4918bd5a4d79d8 100644 --- a/third_party/grpc/src/cpp/client/secure_credentials.h +++ b/third_party/grpc/src/cpp/client/secure_credentials.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -36,53 +21,71 @@ #include -#include -#include +#include +#include +#include "src/core/lib/security/credentials/credentials.h" #include "src/cpp/server/thread_pool_interface.h" namespace grpc { -class SecureChannelCredentials GRPC_FINAL : public ChannelCredentials { +class SecureChannelCredentials final : public ChannelCredentials { public: explicit SecureChannelCredentials(grpc_channel_credentials* c_creds); - ~SecureChannelCredentials() { grpc_channel_credentials_release(c_creds_); } + ~SecureChannelCredentials() { + if (c_creds_ != nullptr) c_creds_->Unref(); + } grpc_channel_credentials* GetRawCreds() { return c_creds_; } std::shared_ptr CreateChannel( - const string& target, const grpc::ChannelArguments& args) GRPC_OVERRIDE; - SecureChannelCredentials* AsSecureCredentials() GRPC_OVERRIDE { return this; } + const string& target, const grpc::ChannelArguments& args) override; + + SecureChannelCredentials* AsSecureCredentials() override { return this; } private: + std::shared_ptr CreateChannelWithInterceptors( + const string& target, const grpc::ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) override; grpc_channel_credentials* const c_creds_; }; -class SecureCallCredentials GRPC_FINAL : public CallCredentials { +class SecureCallCredentials final : public CallCredentials { public: explicit SecureCallCredentials(grpc_call_credentials* c_creds); - ~SecureCallCredentials() { grpc_call_credentials_release(c_creds_); } + ~SecureCallCredentials() { + if (c_creds_ != nullptr) c_creds_->Unref(); + } grpc_call_credentials* GetRawCreds() { return c_creds_; } - bool ApplyToCall(grpc_call* call) GRPC_OVERRIDE; - SecureCallCredentials* AsSecureCredentials() GRPC_OVERRIDE { return this; } + bool ApplyToCall(grpc_call* call) override; + SecureCallCredentials* AsSecureCredentials() override { return this; } private: grpc_call_credentials* const c_creds_; }; -class MetadataCredentialsPluginWrapper GRPC_FINAL { +class MetadataCredentialsPluginWrapper final : private GrpcLibraryCodegen { public: static void Destroy(void* wrapper); - static void GetMetadata(void* wrapper, grpc_auth_metadata_context context, - grpc_credentials_plugin_metadata_cb cb, - void* user_data); + static int GetMetadata( + void* wrapper, grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, void* user_data, + grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], + size_t* num_creds_md, grpc_status_code* status, + const char** error_details); explicit MetadataCredentialsPluginWrapper( std::unique_ptr plugin); private: - void InvokePlugin(grpc_auth_metadata_context context, - grpc_credentials_plugin_metadata_cb cb, void* user_data); + void InvokePlugin( + grpc_auth_metadata_context context, + grpc_credentials_plugin_metadata_cb cb, void* user_data, + grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX], + size_t* num_creds_md, grpc_status_code* status_code, + const char** error_details); std::unique_ptr thread_pool_; std::unique_ptr plugin_; }; diff --git a/third_party/grpc/src/cpp/codegen/codegen_init.cc b/third_party/grpc/src/cpp/codegen/codegen_init.cc new file mode 100644 index 00000000000000..684d7218b938a4 --- /dev/null +++ b/third_party/grpc/src/cpp/codegen/codegen_init.cc @@ -0,0 +1,30 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +/// Null-initializes the global gRPC variables for the codegen library. These +/// stay null in the absence of of grpc++ library. In this case, no gRPC +/// features such as the ability to perform calls will be available. Trying to +/// perform them would result in a segmentation fault when trying to deference +/// the following nulled globals. These should be associated with actual +/// as part of the instantiation of a \a grpc::GrpcLibraryInitializer variable. + +grpc::CoreCodegenInterface* grpc::g_core_codegen_interface; +grpc::GrpcLibraryInterface* grpc::g_glip; diff --git a/third_party/grpc/src/cpp/codegen/grpc_library.cc b/third_party/grpc/src/cpp/codegen/grpc_library.cc deleted file mode 100644 index 48acec3f3d1cee..00000000000000 --- a/third_party/grpc/src/cpp/codegen/grpc_library.cc +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * Copyright 2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -namespace grpc { - -GrpcLibraryInterface *g_glip = nullptr; - -} // namespace grpc diff --git a/third_party/grpc/src/cpp/common/alarm.cc b/third_party/grpc/src/cpp/common/alarm.cc index 807a67df246b4d..148f0b9bc94e83 100644 --- a/third_party/grpc/src/cpp/common/alarm.cc +++ b/third_party/grpc/src/cpp/common/alarm.cc @@ -1,50 +1,149 @@ /* - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2018 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include -#include -#include -#include +#include -namespace grpc { +#include -static internal::GrpcLibraryInitializer g_gli_initializer; -Alarm::Alarm(CompletionQueue* cq, gpr_timespec deadline, void* tag) - : alarm_(grpc_alarm_create(cq->cq(), deadline, tag)) { +#include +#include +#include +#include +#include +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/completion_queue.h" + +#include +#include "src/core/lib/debug/trace.h" + +namespace grpc_impl { + +namespace internal { +class AlarmImpl : public ::grpc::internal::CompletionQueueTag { + public: + AlarmImpl() : cq_(nullptr), tag_(nullptr) { + gpr_ref_init(&refs_, 1); + grpc_timer_init_unset(&timer_); + } + ~AlarmImpl() { + grpc_core::ExecCtx exec_ctx; + if (cq_ != nullptr) { + GRPC_CQ_INTERNAL_UNREF(cq_, "alarm"); + } + } + bool FinalizeResult(void** tag, bool* status) override { + *tag = tag_; + Unref(); + return true; + } + void Set(::grpc::CompletionQueue* cq, gpr_timespec deadline, void* tag) { + grpc_core::ExecCtx exec_ctx; + GRPC_CQ_INTERNAL_REF(cq->cq(), "alarm"); + cq_ = cq->cq(); + tag_ = tag; + GPR_ASSERT(grpc_cq_begin_op(cq_, this)); + GRPC_CLOSURE_INIT(&on_alarm_, + [](void* arg, grpc_error* error) { + // queue the op on the completion queue + AlarmImpl* alarm = static_cast(arg); + alarm->Ref(); + grpc_cq_end_op( + alarm->cq_, alarm, error, + [](void* arg, grpc_cq_completion* completion) {}, + arg, &alarm->completion_); + }, + this, grpc_schedule_on_exec_ctx); + grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline), + &on_alarm_); + } + void Set(gpr_timespec deadline, std::function f) { + grpc_core::ExecCtx exec_ctx; + // Don't use any CQ at all. Instead just use the timer to fire the function + callback_ = std::move(f); + Ref(); + GRPC_CLOSURE_INIT(&on_alarm_, + [](void* arg, grpc_error* error) { + AlarmImpl* alarm = static_cast(arg); + alarm->callback_(error == GRPC_ERROR_NONE); + alarm->Unref(); + }, + this, grpc_schedule_on_exec_ctx); + grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline), + &on_alarm_); + } + void Cancel() { + grpc_core::ExecCtx exec_ctx; + grpc_timer_cancel(&timer_); + } + void Destroy() { + Cancel(); + Unref(); + } + + private: + void Ref() { gpr_ref(&refs_); } + void Unref() { + if (gpr_unref(&refs_)) { + delete this; + } + } + + grpc_timer timer_; + gpr_refcount refs_; + grpc_closure on_alarm_; + grpc_cq_completion completion_; + // completion queue where events about this alarm will be posted + grpc_completion_queue* cq_; + void* tag_; + std::function callback_; +}; +} // namespace internal + +static ::grpc::internal::GrpcLibraryInitializer g_gli_initializer; + +Alarm::Alarm() : alarm_(new internal::AlarmImpl()) { g_gli_initializer.summon(); } -Alarm::~Alarm() { grpc_alarm_destroy(alarm_); } +void Alarm::SetInternal(::grpc::CompletionQueue* cq, gpr_timespec deadline, + void* tag) { + // Note that we know that alarm_ is actually an internal::AlarmImpl + // but we declared it as the base pointer to avoid a forward declaration + // or exposing core data structures in the C++ public headers. + // Thus it is safe to use a static_cast to the subclass here, and the + // C++ style guide allows us to do so in this case + static_cast(alarm_)->Set(cq, deadline, tag); +} -void Alarm::Cancel() { grpc_alarm_cancel(alarm_); } +void Alarm::SetInternal(gpr_timespec deadline, std::function f) { + // Note that we know that alarm_ is actually an internal::AlarmImpl + // but we declared it as the base pointer to avoid a forward declaration + // or exposing core data structures in the C++ public headers. + // Thus it is safe to use a static_cast to the subclass here, and the + // C++ style guide allows us to do so in this case + static_cast(alarm_)->Set(deadline, std::move(f)); +} + +Alarm::~Alarm() { + if (alarm_ != nullptr) { + static_cast(alarm_)->Destroy(); + } +} -} // namespace grpc +void Alarm::Cancel() { static_cast(alarm_)->Cancel(); } +} // namespace grpc_impl diff --git a/third_party/grpc/src/cpp/common/auth_property_iterator.cc b/third_party/grpc/src/cpp/common/auth_property_iterator.cc index a47abaf4b8dc19..fbb18e9915ca73 100644 --- a/third_party/grpc/src/cpp/common/auth_property_iterator.cc +++ b/third_party/grpc/src/cpp/common/auth_property_iterator.cc @@ -1,37 +1,22 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include #include diff --git a/third_party/grpc/src/cpp/common/call.cc b/third_party/grpc/src/cpp/common/call.cc deleted file mode 100644 index 5b87c2a80673c3..00000000000000 --- a/third_party/grpc/src/cpp/common/call.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/cpp/common/channel_arguments.cc b/third_party/grpc/src/cpp/common/channel_arguments.cc index 90cd5136af356c..214d72f853f8d9 100644 --- a/third_party/grpc/src/cpp/common/channel_arguments.cc +++ b/third_party/grpc/src/cpp/common/channel_arguments.cc @@ -1,43 +1,39 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ +#include -#include +#include +#include #include -#include "src/core/channel/channel_args.h" +#include +#include +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/socket_mutator.h" namespace grpc { +ChannelArguments::ChannelArguments() { + // This will be ignored if used on the server side. + SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, "grpc-c++/" + Version()); +} + ChannelArguments::ChannelArguments(const ChannelArguments& other) : strings_(other.strings_) { args_.reserve(other.args_.size()); @@ -62,15 +58,22 @@ ChannelArguments::ChannelArguments(const ChannelArguments& other) break; case GRPC_ARG_POINTER: ap.value.pointer = a->value.pointer; - ap.value.pointer.p = a->value.pointer.copy - ? a->value.pointer.copy(ap.value.pointer.p) - : ap.value.pointer.p; + ap.value.pointer.p = a->value.pointer.vtable->copy(ap.value.pointer.p); break; } args_.push_back(ap); } } +ChannelArguments::~ChannelArguments() { + grpc_core::ExecCtx exec_ctx; + for (auto it = args_.begin(); it != args_.end(); ++it) { + if (it->type == GRPC_ARG_POINTER) { + it->value.pointer.vtable->destroy(it->value.pointer.p); + } + } +} + void ChannelArguments::Swap(ChannelArguments& other) { args_.swap(other.args_); strings_.swap(other.strings_); @@ -78,7 +81,90 @@ void ChannelArguments::Swap(ChannelArguments& other) { void ChannelArguments::SetCompressionAlgorithm( grpc_compression_algorithm algorithm) { - SetInt(GRPC_COMPRESSION_ALGORITHM_ARG, algorithm); + SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, algorithm); +} + +void ChannelArguments::SetGrpclbFallbackTimeout(int fallback_timeout) { + SetInt(GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS, fallback_timeout); +} + +void ChannelArguments::SetSocketMutator(grpc_socket_mutator* mutator) { + if (!mutator) { + return; + } + grpc_arg mutator_arg = grpc_socket_mutator_to_arg(mutator); + bool replaced = false; + grpc_core::ExecCtx exec_ctx; + for (auto it = args_.begin(); it != args_.end(); ++it) { + if (it->type == mutator_arg.type && + grpc::string(it->key) == grpc::string(mutator_arg.key)) { + GPR_ASSERT(!replaced); + it->value.pointer.vtable->destroy(it->value.pointer.p); + it->value.pointer = mutator_arg.value.pointer; + replaced = true; + } + } + + if (!replaced) { + strings_.push_back(grpc::string(mutator_arg.key)); + args_.push_back(mutator_arg); + args_.back().key = const_cast(strings_.back().c_str()); + } +} + +// Note: a second call to this will add in front the result of the first call. +// An example is calling this on a copy of ChannelArguments which already has a +// prefix. The user can build up a prefix string by calling this multiple times, +// each with more significant identifier. +void ChannelArguments::SetUserAgentPrefix( + const grpc::string& user_agent_prefix) { + if (user_agent_prefix.empty()) { + return; + } + bool replaced = false; + auto strings_it = strings_.begin(); + for (auto it = args_.begin(); it != args_.end(); ++it) { + const grpc_arg& arg = *it; + ++strings_it; + if (arg.type == GRPC_ARG_STRING) { + if (grpc::string(arg.key) == GRPC_ARG_PRIMARY_USER_AGENT_STRING) { + GPR_ASSERT(arg.value.string == strings_it->c_str()); + *(strings_it) = user_agent_prefix + " " + arg.value.string; + it->value.string = const_cast(strings_it->c_str()); + replaced = true; + break; + } + ++strings_it; + } + } + if (!replaced) { + SetString(GRPC_ARG_PRIMARY_USER_AGENT_STRING, user_agent_prefix); + } +} + +void ChannelArguments::SetResourceQuota( + const grpc::ResourceQuota& resource_quota) { + SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA, + resource_quota.c_resource_quota(), + grpc_resource_quota_arg_vtable()); +} + +void ChannelArguments::SetMaxReceiveMessageSize(int size) { + SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, size); +} + +void ChannelArguments::SetMaxSendMessageSize(int size) { + SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, size); +} + +void ChannelArguments::SetLoadBalancingPolicyName( + const grpc::string& lb_policy_name) { + SetString(GRPC_ARG_LB_POLICY_NAME, lb_policy_name); +} + +void ChannelArguments::SetServiceConfigJSON( + const grpc::string& service_config_json) { + SetString(GRPC_ARG_SERVICE_CONFIG, service_config_json); } void ChannelArguments::SetInt(const grpc::string& key, int value) { @@ -92,13 +178,21 @@ void ChannelArguments::SetInt(const grpc::string& key, int value) { } void ChannelArguments::SetPointer(const grpc::string& key, void* value) { + static const grpc_arg_pointer_vtable vtable = { + &PointerVtableMembers::Copy, &PointerVtableMembers::Destroy, + &PointerVtableMembers::Compare}; + SetPointerWithVtable(key, value, &vtable); +} + +void ChannelArguments::SetPointerWithVtable( + const grpc::string& key, void* value, + const grpc_arg_pointer_vtable* vtable) { grpc_arg arg; arg.type = GRPC_ARG_POINTER; strings_.push_back(key); arg.key = const_cast(strings_.back().c_str()); - arg.value.pointer.p = value; - arg.value.pointer.copy = nullptr; - arg.value.pointer.destroy = nullptr; + arg.value.pointer.p = vtable->copy(value); + arg.value.pointer.vtable = vtable; args_.push_back(arg); } diff --git a/third_party/grpc/src/cpp/common/channel_filter.cc b/third_party/grpc/src/cpp/common/channel_filter.cc new file mode 100644 index 00000000000000..422e7bb65ee2c1 --- /dev/null +++ b/third_party/grpc/src/cpp/common/channel_filter.cc @@ -0,0 +1,99 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/cpp/common/channel_filter.h" + +#include + +namespace grpc { + +// MetadataBatch + +grpc_linked_mdelem* MetadataBatch::AddMetadata(const string& key, + const string& value) { + grpc_linked_mdelem* storage = new grpc_linked_mdelem; + memset(storage, 0, sizeof(grpc_linked_mdelem)); + storage->md = grpc_mdelem_from_slices(SliceFromCopiedString(key), + SliceFromCopiedString(value)); + GRPC_LOG_IF_ERROR("MetadataBatch::AddMetadata", + grpc_metadata_batch_link_head(batch_, storage)); + return storage; +} + +// ChannelData + +void ChannelData::StartTransportOp(grpc_channel_element* elem, + TransportOp* op) { + grpc_channel_next_op(elem, op->op()); +} + +void ChannelData::GetInfo(grpc_channel_element* elem, + const grpc_channel_info* channel_info) { + grpc_channel_next_get_info(elem, channel_info); +} + +// CallData + +void CallData::StartTransportStreamOpBatch(grpc_call_element* elem, + TransportStreamOpBatch* op) { + grpc_call_next_op(elem, op->op()); +} + +void CallData::SetPollsetOrPollsetSet(grpc_call_element* elem, + grpc_polling_entity* pollent) { + grpc_call_stack_ignore_set_pollset_or_pollset_set(elem, pollent); +} + +// internal code used by RegisterChannelFilter() + +namespace internal { + +// Note: Implicitly initialized to nullptr due to static lifetime. +std::vector* channel_filters; + +namespace { + +bool MaybeAddFilter(grpc_channel_stack_builder* builder, void* arg) { + const FilterRecord& filter = *static_cast(arg); + if (filter.include_filter) { + const grpc_channel_args* args = + grpc_channel_stack_builder_get_channel_arguments(builder); + if (!filter.include_filter(*args)) return true; + } + return grpc_channel_stack_builder_prepend_filter(builder, &filter.filter, + nullptr, nullptr); +} + +} // namespace + +void ChannelFilterPluginInit() { + for (size_t i = 0; i < channel_filters->size(); ++i) { + FilterRecord& filter = (*channel_filters)[i]; + grpc_channel_init_register_stage(filter.stack_type, filter.priority, + MaybeAddFilter, (void*)&filter); + } +} + +void ChannelFilterPluginShutdown() {} + +} // namespace internal + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/common/channel_filter.h b/third_party/grpc/src/cpp/common/channel_filter.h new file mode 100644 index 00000000000000..5e569c97e68dc2 --- /dev/null +++ b/third_party/grpc/src/cpp/common/channel_filter.h @@ -0,0 +1,397 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPCXX_CHANNEL_FILTER_H +#define GRPCXX_CHANNEL_FILTER_H + +#include +#include +#include + +#include +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/metadata_batch.h" + +/// An interface to define filters. +/// +/// To define a filter, implement a subclass of each of \c CallData and +/// \c ChannelData. Then register the filter using something like this: +/// \code{.cpp} +/// RegisterChannelFilter( +/// "name-of-filter", GRPC_SERVER_CHANNEL, INT_MAX, nullptr); +/// \endcode + +namespace grpc { + +/// A C++ wrapper for the \c grpc_metadata_batch struct. +class MetadataBatch { + public: + /// Borrows a pointer to \a batch, but does NOT take ownership. + /// The caller must ensure that \a batch continues to exist for as + /// long as the MetadataBatch object does. + explicit MetadataBatch(grpc_metadata_batch* batch) : batch_(batch) {} + + grpc_metadata_batch* batch() const { return batch_; } + + /// Adds metadata and returns the newly allocated storage. + /// The caller takes ownership of the result, which must exist for the + /// lifetime of the gRPC call. + grpc_linked_mdelem* AddMetadata(const string& key, const string& value); + + class const_iterator : public std::iterator { + public: + const grpc_mdelem& operator*() const { return elem_->md; } + const grpc_mdelem operator->() const { return elem_->md; } + + const_iterator& operator++() { + elem_ = elem_->next; + return *this; + } + const_iterator operator++(int) { + const_iterator tmp(*this); + operator++(); + return tmp; + } + const_iterator& operator--() { + elem_ = elem_->prev; + return *this; + } + const_iterator operator--(int) { + const_iterator tmp(*this); + operator--(); + return tmp; + } + + bool operator==(const const_iterator& other) const { + return elem_ == other.elem_; + } + bool operator!=(const const_iterator& other) const { + return elem_ != other.elem_; + } + + private: + friend class MetadataBatch; + explicit const_iterator(grpc_linked_mdelem* elem) : elem_(elem) {} + + grpc_linked_mdelem* elem_; + }; + + const_iterator begin() const { return const_iterator(batch_->list.head); } + const_iterator end() const { return const_iterator(nullptr); } + + private: + grpc_metadata_batch* batch_; // Not owned. +}; + +/// A C++ wrapper for the \c grpc_transport_op struct. +class TransportOp { + public: + /// Borrows a pointer to \a op, but does NOT take ownership. + /// The caller must ensure that \a op continues to exist for as + /// long as the TransportOp object does. + explicit TransportOp(grpc_transport_op* op) : op_(op) {} + + grpc_transport_op* op() const { return op_; } + + // TODO(roth): Add a C++ wrapper for grpc_error? + grpc_error* disconnect_with_error() const { + return op_->disconnect_with_error; + } + bool send_goaway() const { return op_->goaway_error != GRPC_ERROR_NONE; } + + // TODO(roth): Add methods for additional fields as needed. + + private: + grpc_transport_op* op_; // Not owned. +}; + +/// A C++ wrapper for the \c grpc_transport_stream_op_batch struct. +class TransportStreamOpBatch { + public: + /// Borrows a pointer to \a op, but does NOT take ownership. + /// The caller must ensure that \a op continues to exist for as + /// long as the TransportStreamOpBatch object does. + explicit TransportStreamOpBatch(grpc_transport_stream_op_batch* op) + : op_(op), + send_initial_metadata_( + op->send_initial_metadata + ? op->payload->send_initial_metadata.send_initial_metadata + : nullptr), + send_trailing_metadata_( + op->send_trailing_metadata + ? op->payload->send_trailing_metadata.send_trailing_metadata + : nullptr), + recv_initial_metadata_( + op->recv_initial_metadata + ? op->payload->recv_initial_metadata.recv_initial_metadata + : nullptr), + recv_trailing_metadata_( + op->recv_trailing_metadata + ? op->payload->recv_trailing_metadata.recv_trailing_metadata + : nullptr) {} + + grpc_transport_stream_op_batch* op() const { return op_; } + + grpc_closure* on_complete() const { return op_->on_complete; } + void set_on_complete(grpc_closure* closure) { op_->on_complete = closure; } + + MetadataBatch* send_initial_metadata() { + return op_->send_initial_metadata ? &send_initial_metadata_ : nullptr; + } + MetadataBatch* send_trailing_metadata() { + return op_->send_trailing_metadata ? &send_trailing_metadata_ : nullptr; + } + MetadataBatch* recv_initial_metadata() { + return op_->recv_initial_metadata ? &recv_initial_metadata_ : nullptr; + } + MetadataBatch* recv_trailing_metadata() { + return op_->recv_trailing_metadata ? &recv_trailing_metadata_ : nullptr; + } + + uint32_t* send_initial_metadata_flags() const { + return op_->send_initial_metadata ? &op_->payload->send_initial_metadata + .send_initial_metadata_flags + : nullptr; + } + + grpc_closure* recv_initial_metadata_ready() const { + return op_->recv_initial_metadata + ? op_->payload->recv_initial_metadata.recv_initial_metadata_ready + : nullptr; + } + void set_recv_initial_metadata_ready(grpc_closure* closure) { + op_->payload->recv_initial_metadata.recv_initial_metadata_ready = closure; + } + + grpc_core::OrphanablePtr* send_message() const { + return op_->send_message ? &op_->payload->send_message.send_message + : nullptr; + } + void set_send_message( + grpc_core::OrphanablePtr send_message) { + op_->send_message = true; + op_->payload->send_message.send_message = std::move(send_message); + } + + grpc_core::OrphanablePtr* recv_message() const { + return op_->recv_message ? op_->payload->recv_message.recv_message + : nullptr; + } + void set_recv_message( + grpc_core::OrphanablePtr* recv_message) { + op_->recv_message = true; + op_->payload->recv_message.recv_message = recv_message; + } + + census_context* get_census_context() const { + return static_cast( + op_->payload->context[GRPC_CONTEXT_TRACING].value); + } + + const gpr_atm* get_peer_string() const { + if (op_->send_initial_metadata && + op_->payload->send_initial_metadata.peer_string != nullptr) { + return op_->payload->send_initial_metadata.peer_string; + } else if (op_->recv_initial_metadata && + op_->payload->recv_initial_metadata.peer_string != nullptr) { + return op_->payload->recv_initial_metadata.peer_string; + } else { + return nullptr; + } + } + + private: + grpc_transport_stream_op_batch* op_; // Not owned. + MetadataBatch send_initial_metadata_; + MetadataBatch send_trailing_metadata_; + MetadataBatch recv_initial_metadata_; + MetadataBatch recv_trailing_metadata_; +}; + +/// Represents channel data. +class ChannelData { + public: + ChannelData() {} + virtual ~ChannelData() {} + + // TODO(roth): Come up with a more C++-like API for the channel element. + + /// Initializes the channel data. + virtual grpc_error* Init(grpc_channel_element* elem, + grpc_channel_element_args* args) { + return GRPC_ERROR_NONE; + } + + // Called before destruction. + virtual void Destroy(grpc_channel_element* elem) {} + + virtual void StartTransportOp(grpc_channel_element* elem, TransportOp* op); + + virtual void GetInfo(grpc_channel_element* elem, + const grpc_channel_info* channel_info); +}; + +/// Represents call data. +class CallData { + public: + CallData() {} + virtual ~CallData() {} + + // TODO(roth): Come up with a more C++-like API for the call element. + + /// Initializes the call data. + virtual grpc_error* Init(grpc_call_element* elem, + const grpc_call_element_args* args) { + return GRPC_ERROR_NONE; + } + + // Called before destruction. + virtual void Destroy(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) {} + + /// Starts a new stream operation. + virtual void StartTransportStreamOpBatch(grpc_call_element* elem, + TransportStreamOpBatch* op); + + /// Sets a pollset or pollset set. + virtual void SetPollsetOrPollsetSet(grpc_call_element* elem, + grpc_polling_entity* pollent); +}; + +namespace internal { + +// Defines static members for passing to C core. +// Members of this class correspond to the members of the C +// grpc_channel_filter struct. +template +class ChannelFilter final { + public: + static const size_t channel_data_size = sizeof(ChannelDataType); + + static grpc_error* InitChannelElement(grpc_channel_element* elem, + grpc_channel_element_args* args) { + // Construct the object in the already-allocated memory. + ChannelDataType* channel_data = new (elem->channel_data) ChannelDataType(); + return channel_data->Init(elem, args); + } + + static void DestroyChannelElement(grpc_channel_element* elem) { + ChannelDataType* channel_data = + static_cast(elem->channel_data); + channel_data->Destroy(elem); + channel_data->~ChannelDataType(); + } + + static void StartTransportOp(grpc_channel_element* elem, + grpc_transport_op* op) { + ChannelDataType* channel_data = + static_cast(elem->channel_data); + TransportOp op_wrapper(op); + channel_data->StartTransportOp(elem, &op_wrapper); + } + + static void GetChannelInfo(grpc_channel_element* elem, + const grpc_channel_info* channel_info) { + ChannelDataType* channel_data = + static_cast(elem->channel_data); + channel_data->GetInfo(elem, channel_info); + } + + static const size_t call_data_size = sizeof(CallDataType); + + static grpc_error* InitCallElement(grpc_call_element* elem, + const grpc_call_element_args* args) { + // Construct the object in the already-allocated memory. + CallDataType* call_data = new (elem->call_data) CallDataType(); + return call_data->Init(elem, args); + } + + static void DestroyCallElement(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) { + CallDataType* call_data = static_cast(elem->call_data); + call_data->Destroy(elem, final_info, then_call_closure); + call_data->~CallDataType(); + } + + static void StartTransportStreamOpBatch(grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + CallDataType* call_data = static_cast(elem->call_data); + TransportStreamOpBatch op_wrapper(op); + call_data->StartTransportStreamOpBatch(elem, &op_wrapper); + } + + static void SetPollsetOrPollsetSet(grpc_call_element* elem, + grpc_polling_entity* pollent) { + CallDataType* call_data = static_cast(elem->call_data); + call_data->SetPollsetOrPollsetSet(elem, pollent); + } +}; + +struct FilterRecord { + grpc_channel_stack_type stack_type; + int priority; + std::function include_filter; + grpc_channel_filter filter; +}; +extern std::vector* channel_filters; + +void ChannelFilterPluginInit(); +void ChannelFilterPluginShutdown(); + +} // namespace internal + +/// Registers a new filter. +/// Must be called by only one thread at a time. +/// The \a include_filter argument specifies a function that will be called +/// to determine at run-time whether or not to add the filter. If the +/// value is nullptr, the filter will be added unconditionally. +template +void RegisterChannelFilter( + const char* name, grpc_channel_stack_type stack_type, int priority, + std::function include_filter) { + // If we haven't been called before, initialize channel_filters and + // call grpc_register_plugin(). + if (internal::channel_filters == nullptr) { + grpc_register_plugin(internal::ChannelFilterPluginInit, + internal::ChannelFilterPluginShutdown); + internal::channel_filters = new std::vector(); + } + // Add an entry to channel_filters. The filter will be added when the + // C-core initialization code calls ChannelFilterPluginInit(). + typedef internal::ChannelFilter FilterType; + internal::FilterRecord filter_record = { + stack_type, + priority, + include_filter, + {FilterType::StartTransportStreamOpBatch, FilterType::StartTransportOp, + FilterType::call_data_size, FilterType::InitCallElement, + FilterType::SetPollsetOrPollsetSet, FilterType::DestroyCallElement, + FilterType::channel_data_size, FilterType::InitChannelElement, + FilterType::DestroyChannelElement, FilterType::GetChannelInfo, name}}; + internal::channel_filters->push_back(filter_record); +} + +} // namespace grpc + +#endif // GRPCXX_CHANNEL_FILTER_H diff --git a/third_party/grpc/src/cpp/common/completion_queue.cc b/third_party/grpc/src/cpp/common/completion_queue.cc deleted file mode 100644 index 4f76dfff1d7a73..00000000000000 --- a/third_party/grpc/src/cpp/common/completion_queue.cc +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include - -#include -#include -#include -#include -#include - -namespace grpc { - -static internal::GrpcLibraryInitializer g_gli_initializer; -CompletionQueue::CompletionQueue() { - g_gli_initializer.summon(); - 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/third_party/grpc/src/cpp/common/completion_queue_cc.cc b/third_party/grpc/src/cpp/common/completion_queue_cc.cc new file mode 100644 index 00000000000000..d93a54aed719ad --- /dev/null +++ b/third_party/grpc/src/cpp/common/completion_queue_cc.cc @@ -0,0 +1,99 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include + +namespace grpc { + +static internal::GrpcLibraryInitializer g_gli_initializer; + +// 'CompletionQueue' constructor can safely call GrpcLibraryCodegen(false) here +// i.e not have GrpcLibraryCodegen call grpc_init(). This is because, to create +// a 'grpc_completion_queue' instance (which is being passed as the input to +// this constructor), one must have already called grpc_init(). +CompletionQueue::CompletionQueue(grpc_completion_queue* take) + : GrpcLibraryCodegen(false), cq_(take) { + InitialAvalanching(); +} + +void CompletionQueue::Shutdown() { + g_gli_initializer.summon(); + CompleteAvalanching(); +} + +void CompletionQueue::CompleteAvalanching() { + // Check if this was the last avalanching operation + if (gpr_atm_no_barrier_fetch_add(&avalanches_in_flight_, + static_cast(-1)) == 1) { + 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 core_cq_tag = static_cast(ev.tag); + *ok = ev.success != 0; + *tag = core_cq_tag; + if (core_cq_tag->FinalizeResult(tag, ok)) { + return GOT_EVENT; + } + break; + } + } +} + +CompletionQueue::CompletionQueueTLSCache::CompletionQueueTLSCache( + CompletionQueue* cq) + : cq_(cq), flushed_(false) { + grpc_completion_queue_thread_local_cache_init(cq_->cq_); +} + +CompletionQueue::CompletionQueueTLSCache::~CompletionQueueTLSCache() { + GPR_ASSERT(flushed_); +} + +bool CompletionQueue::CompletionQueueTLSCache::Flush(void** tag, bool* ok) { + int res = 0; + void* res_tag; + flushed_ = true; + if (grpc_completion_queue_thread_local_cache_flush(cq_->cq_, &res_tag, + &res)) { + auto core_cq_tag = static_cast(res_tag); + *ok = res == 1; + if (core_cq_tag->FinalizeResult(tag, ok)) { + return true; + } + } + return false; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/common/core_codegen.cc b/third_party/grpc/src/cpp/common/core_codegen.cc new file mode 100644 index 00000000000000..cfaa2e7b19376b --- /dev/null +++ b/third_party/grpc/src/cpp/common/core_codegen.cc @@ -0,0 +1,228 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/profiling/timers.h" + +struct grpc_byte_buffer; + +namespace grpc { + +const grpc_completion_queue_factory* +CoreCodegen::grpc_completion_queue_factory_lookup( + const grpc_completion_queue_attributes* attributes) { + return ::grpc_completion_queue_factory_lookup(attributes); +} + +grpc_completion_queue* CoreCodegen::grpc_completion_queue_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attributes, void* reserved) { + return ::grpc_completion_queue_create(factory, attributes, reserved); +} + +grpc_completion_queue* CoreCodegen::grpc_completion_queue_create_for_next( + void* reserved) { + return ::grpc_completion_queue_create_for_next(reserved); +} + +grpc_completion_queue* CoreCodegen::grpc_completion_queue_create_for_pluck( + void* reserved) { + return ::grpc_completion_queue_create_for_pluck(reserved); +} + +void CoreCodegen::grpc_completion_queue_destroy(grpc_completion_queue* cq) { + ::grpc_completion_queue_destroy(cq); +} + +grpc_event CoreCodegen::grpc_completion_queue_pluck(grpc_completion_queue* cq, + void* tag, + gpr_timespec deadline, + void* reserved) { + return ::grpc_completion_queue_pluck(cq, tag, deadline, reserved); +} + +void* CoreCodegen::gpr_malloc(size_t size) { return ::gpr_malloc(size); } + +void CoreCodegen::gpr_free(void* p) { return ::gpr_free(p); } + +void CoreCodegen::grpc_init() { ::grpc_init(); } +void CoreCodegen::grpc_shutdown() { ::grpc_shutdown(); } + +void CoreCodegen::gpr_mu_init(gpr_mu* mu) { ::gpr_mu_init(mu); }; +void CoreCodegen::gpr_mu_destroy(gpr_mu* mu) { ::gpr_mu_destroy(mu); } +void CoreCodegen::gpr_mu_lock(gpr_mu* mu) { ::gpr_mu_lock(mu); } +void CoreCodegen::gpr_mu_unlock(gpr_mu* mu) { ::gpr_mu_unlock(mu); } +void CoreCodegen::gpr_cv_init(gpr_cv* cv) { ::gpr_cv_init(cv); } +void CoreCodegen::gpr_cv_destroy(gpr_cv* cv) { ::gpr_cv_destroy(cv); } +int CoreCodegen::gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, + gpr_timespec abs_deadline) { + return ::gpr_cv_wait(cv, mu, abs_deadline); +} +void CoreCodegen::gpr_cv_signal(gpr_cv* cv) { ::gpr_cv_signal(cv); } +void CoreCodegen::gpr_cv_broadcast(gpr_cv* cv) { ::gpr_cv_broadcast(cv); } + +grpc_byte_buffer* CoreCodegen::grpc_byte_buffer_copy(grpc_byte_buffer* bb) { + return ::grpc_byte_buffer_copy(bb); +} + +void CoreCodegen::grpc_byte_buffer_destroy(grpc_byte_buffer* bb) { + ::grpc_byte_buffer_destroy(bb); +} + +size_t CoreCodegen::grpc_byte_buffer_length(grpc_byte_buffer* bb) { + return ::grpc_byte_buffer_length(bb); +} + +grpc_call_error CoreCodegen::grpc_call_start_batch(grpc_call* call, + const grpc_op* ops, + size_t nops, void* tag, + void* reserved) { + return ::grpc_call_start_batch(call, ops, nops, tag, reserved); +} + +grpc_call_error CoreCodegen::grpc_call_cancel_with_status( + grpc_call* call, grpc_status_code status, const char* description, + void* reserved) { + return ::grpc_call_cancel_with_status(call, status, description, reserved); +} +void CoreCodegen::grpc_call_ref(grpc_call* call) { ::grpc_call_ref(call); } +void CoreCodegen::grpc_call_unref(grpc_call* call) { ::grpc_call_unref(call); } +void* CoreCodegen::grpc_call_arena_alloc(grpc_call* call, size_t length) { + return ::grpc_call_arena_alloc(call, length); +} + +int CoreCodegen::grpc_byte_buffer_reader_init(grpc_byte_buffer_reader* reader, + grpc_byte_buffer* buffer) { + return ::grpc_byte_buffer_reader_init(reader, buffer); +} + +void CoreCodegen::grpc_byte_buffer_reader_destroy( + grpc_byte_buffer_reader* reader) { + ::grpc_byte_buffer_reader_destroy(reader); +} + +int CoreCodegen::grpc_byte_buffer_reader_next(grpc_byte_buffer_reader* reader, + grpc_slice* slice) { + return ::grpc_byte_buffer_reader_next(reader, slice); +} + +grpc_byte_buffer* CoreCodegen::grpc_raw_byte_buffer_create(grpc_slice* slice, + size_t nslices) { + return ::grpc_raw_byte_buffer_create(slice, nslices); +} + +grpc_slice CoreCodegen::grpc_slice_new_with_user_data(void* p, size_t len, + void (*destroy)(void*), + void* user_data) { + return ::grpc_slice_new_with_user_data(p, len, destroy, user_data); +} + +grpc_slice CoreCodegen::grpc_slice_new_with_len(void* p, size_t len, + void (*destroy)(void*, + size_t)) { + return ::grpc_slice_new_with_len(p, len, destroy); +} + +grpc_slice CoreCodegen::grpc_empty_slice() { return ::grpc_empty_slice(); } + +grpc_slice CoreCodegen::grpc_slice_malloc(size_t length) { + return ::grpc_slice_malloc(length); +} + +void CoreCodegen::grpc_slice_unref(grpc_slice slice) { + ::grpc_slice_unref(slice); +} + +grpc_slice CoreCodegen::grpc_slice_ref(grpc_slice slice) { + return ::grpc_slice_ref(slice); +} + +grpc_slice CoreCodegen::grpc_slice_split_tail(grpc_slice* s, size_t split) { + return ::grpc_slice_split_tail(s, split); +} + +grpc_slice CoreCodegen::grpc_slice_split_head(grpc_slice* s, size_t split) { + return ::grpc_slice_split_head(s, split); +} + +grpc_slice CoreCodegen::grpc_slice_sub(grpc_slice s, size_t begin, size_t end) { + return ::grpc_slice_sub(s, begin, end); +} + +grpc_slice CoreCodegen::grpc_slice_from_static_buffer(const void* buffer, + size_t length) { + return ::grpc_slice_from_static_buffer(buffer, length); +} + +grpc_slice CoreCodegen::grpc_slice_from_copied_buffer(const void* buffer, + size_t length) { + return ::grpc_slice_from_copied_buffer(static_cast(buffer), + length); +} + +void CoreCodegen::grpc_slice_buffer_add(grpc_slice_buffer* sb, + grpc_slice slice) { + ::grpc_slice_buffer_add(sb, slice); +} + +void CoreCodegen::grpc_slice_buffer_pop(grpc_slice_buffer* sb) { + ::grpc_slice_buffer_pop(sb); +} + +void CoreCodegen::grpc_metadata_array_init(grpc_metadata_array* array) { + ::grpc_metadata_array_init(array); +} + +void CoreCodegen::grpc_metadata_array_destroy(grpc_metadata_array* array) { + ::grpc_metadata_array_destroy(array); +} + +const Status& CoreCodegen::ok() { return grpc::Status::OK; } + +const Status& CoreCodegen::cancelled() { return grpc::Status::CANCELLED; } + +gpr_timespec CoreCodegen::gpr_inf_future(gpr_clock_type type) { + return ::gpr_inf_future(type); +} + +gpr_timespec CoreCodegen::gpr_time_0(gpr_clock_type type) { + return ::gpr_time_0(type); +} + +void CoreCodegen::assert_fail(const char* failed_assertion, const char* file, + int line) { + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "assertion failed: %s", + failed_assertion); + abort(); +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/common/create_auth_context.h b/third_party/grpc/src/cpp/common/create_auth_context.h deleted file mode 100644 index 4f3da397badcb8..00000000000000 --- a/third_party/grpc/src/cpp/common/create_auth_context.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/cpp/common/insecure_create_auth_context.cc b/third_party/grpc/src/cpp/common/insecure_create_auth_context.cc index b2e153229a9d8d..4e5cbd03723873 100644 --- a/third_party/grpc/src/cpp/common/insecure_create_auth_context.cc +++ b/third_party/grpc/src/cpp/common/insecure_create_auth_context.cc @@ -1,39 +1,24 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include #include -#include +#include namespace grpc { diff --git a/third_party/grpc/src/cpp/common/resource_quota_cc.cc b/third_party/grpc/src/cpp/common/resource_quota_cc.cc new file mode 100644 index 00000000000000..276e5f795488c6 --- /dev/null +++ b/third_party/grpc/src/cpp/common/resource_quota_cc.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +namespace grpc { + +ResourceQuota::ResourceQuota() : impl_(grpc_resource_quota_create(nullptr)) {} + +ResourceQuota::ResourceQuota(const grpc::string& name) + : impl_(grpc_resource_quota_create(name.c_str())) {} + +ResourceQuota::~ResourceQuota() { grpc_resource_quota_unref(impl_); } + +ResourceQuota& ResourceQuota::Resize(size_t new_size) { + grpc_resource_quota_resize(impl_, new_size); + return *this; +} + +ResourceQuota& ResourceQuota::SetMaxThreads(int new_max_threads) { + grpc_resource_quota_set_max_threads(impl_, new_max_threads); + return *this; +} +} // namespace grpc diff --git a/third_party/grpc/src/cpp/common/rpc_method.cc b/third_party/grpc/src/cpp/common/rpc_method.cc index 1654d4a262cde5..a47dd3e44440f2 100644 --- a/third_party/grpc/src/cpp/common/rpc_method.cc +++ b/third_party/grpc/src/cpp/common/rpc_method.cc @@ -1,36 +1,21 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include namespace grpc {} // namespace grpc diff --git a/third_party/grpc/src/cpp/common/secure_auth_context.cc b/third_party/grpc/src/cpp/common/secure_auth_context.cc index 8615ac8aebcdfa..7a2b5afed69887 100644 --- a/third_party/grpc/src/cpp/common/secure_auth_context.cc +++ b/third_party/grpc/src/cpp/common/secure_auth_context.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -37,19 +22,12 @@ 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_) { + if (ctx_ == nullptr) { return std::vector(); } - grpc_auth_property_iterator iter = grpc_auth_context_peer_identity(ctx_); + grpc_auth_property_iterator iter = + grpc_auth_context_peer_identity(ctx_.get()); std::vector identity; const grpc_auth_property* property = nullptr; while ((property = grpc_auth_property_iterator_next(&iter))) { @@ -60,20 +38,20 @@ std::vector SecureAuthContext::GetPeerIdentity() const { } grpc::string SecureAuthContext::GetPeerIdentityPropertyName() const { - if (!ctx_) { + if (ctx_ == nullptr) { return ""; } - const char* name = grpc_auth_context_peer_identity_property_name(ctx_); + const char* name = grpc_auth_context_peer_identity_property_name(ctx_.get()); return name == nullptr ? "" : name; } std::vector SecureAuthContext::FindPropertyValues( const grpc::string& name) const { - if (!ctx_) { + if (ctx_ == nullptr) { return std::vector(); } grpc_auth_property_iterator iter = - grpc_auth_context_find_properties_by_name(ctx_, name.c_str()); + grpc_auth_context_find_properties_by_name(ctx_.get(), name.c_str()); const grpc_auth_property* property = nullptr; std::vector values; while ((property = grpc_auth_property_iterator_next(&iter))) { @@ -83,9 +61,9 @@ std::vector SecureAuthContext::FindPropertyValues( } AuthPropertyIterator SecureAuthContext::begin() const { - if (ctx_) { + if (ctx_ != nullptr) { grpc_auth_property_iterator iter = - grpc_auth_context_property_iterator(ctx_); + grpc_auth_context_property_iterator(ctx_.get()); const grpc_auth_property* property = grpc_auth_property_iterator_next(&iter); return AuthPropertyIterator(property, &iter); @@ -100,19 +78,20 @@ AuthPropertyIterator SecureAuthContext::end() const { 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()); + if (ctx_ == nullptr) return; + grpc_auth_context_add_property(ctx_.get(), 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_, + if (ctx_ == nullptr) return false; + return grpc_auth_context_set_peer_identity_property_name(ctx_.get(), name.c_str()) != 0; } bool SecureAuthContext::IsPeerAuthenticated() const { - if (!ctx_) return false; - return grpc_auth_context_peer_is_authenticated(ctx_) != 0; + if (ctx_ == nullptr) return false; + return grpc_auth_context_peer_is_authenticated(ctx_.get()) != 0; } } // namespace grpc diff --git a/third_party/grpc/src/cpp/common/secure_auth_context.h b/third_party/grpc/src/cpp/common/secure_auth_context.h index c9f1dad1319f8f..2e8f7937211002 100644 --- a/third_party/grpc/src/cpp/common/secure_auth_context.h +++ b/third_party/grpc/src/cpp/common/secure_auth_context.h @@ -1,73 +1,58 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H #define GRPC_INTERNAL_CPP_COMMON_SECURE_AUTH_CONTEXT_H -#include +#include -struct grpc_auth_context; +#include "src/core/lib/gprpp/ref_counted_ptr.h" +#include "src/core/lib/security/context/security_context.h" namespace grpc { -class SecureAuthContext GRPC_FINAL : public AuthContext { +class SecureAuthContext final : public AuthContext { public: - SecureAuthContext(grpc_auth_context* ctx, bool take_ownership); + explicit SecureAuthContext(grpc_auth_context* ctx) + : ctx_(ctx != nullptr ? ctx->Ref() : nullptr) {} - ~SecureAuthContext() GRPC_OVERRIDE; + ~SecureAuthContext() override = default; - bool IsPeerAuthenticated() const GRPC_OVERRIDE; + bool IsPeerAuthenticated() const override; - std::vector GetPeerIdentity() const GRPC_OVERRIDE; + std::vector GetPeerIdentity() const override; - grpc::string GetPeerIdentityPropertyName() const GRPC_OVERRIDE; + grpc::string GetPeerIdentityPropertyName() const override; std::vector FindPropertyValues( - const grpc::string& name) const GRPC_OVERRIDE; + const grpc::string& name) const override; - AuthPropertyIterator begin() const GRPC_OVERRIDE; + AuthPropertyIterator begin() const override; - AuthPropertyIterator end() const GRPC_OVERRIDE; + AuthPropertyIterator end() const override; void AddProperty(const grpc::string& key, - const grpc::string_ref& value) GRPC_OVERRIDE; + const grpc::string_ref& value) override; - virtual bool SetPeerIdentityPropertyName(const grpc::string& name) - GRPC_OVERRIDE; + virtual bool SetPeerIdentityPropertyName(const grpc::string& name) override; private: - grpc_auth_context* ctx_; - bool take_ownership_; + grpc_core::RefCountedPtr ctx_; }; } // namespace grpc diff --git a/third_party/grpc/src/cpp/common/secure_channel_arguments.cc b/third_party/grpc/src/cpp/common/secure_channel_arguments.cc index e17d3b58b06257..2fb8ea44fbc80f 100644 --- a/third_party/grpc/src/cpp/common/secure_channel_arguments.cc +++ b/third_party/grpc/src/cpp/common/secure_channel_arguments.cc @@ -1,40 +1,25 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include #include -#include "src/core/channel/channel_args.h" +#include "src/core/lib/channel/channel_args.h" namespace grpc { diff --git a/third_party/grpc/src/cpp/common/secure_create_auth_context.cc b/third_party/grpc/src/cpp/common/secure_create_auth_context.cc index 40bc298b6420ec..908c46629e65f7 100644 --- a/third_party/grpc/src/cpp/common/secure_create_auth_context.cc +++ b/third_party/grpc/src/cpp/common/secure_create_auth_context.cc @@ -1,40 +1,26 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #include #include #include -#include +#include +#include "src/core/lib/gprpp/ref_counted_ptr.h" #include "src/cpp/common/secure_auth_context.h" namespace grpc { @@ -43,8 +29,8 @@ 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)); + grpc_core::RefCountedPtr ctx(grpc_call_auth_context(call)); + return std::make_shared(ctx.get()); } } // namespace grpc diff --git a/third_party/grpc/src/cpp/common/version_cc.cc b/third_party/grpc/src/cpp/common/version_cc.cc new file mode 100644 index 00000000000000..768b0c6c932a9e --- /dev/null +++ b/third_party/grpc/src/cpp/common/version_cc.cc @@ -0,0 +1,26 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* This file is autogenerated from: + templates/src/core/surface/version.c.template */ + +#include + +namespace grpc { +grpc::string Version() { return "1.18.0"; } +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/channel_filter.cc b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.cc new file mode 100644 index 00000000000000..4ac684d27798f5 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.cc @@ -0,0 +1,30 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/channel_filter.h" + +namespace grpc { + +grpc_error* CensusChannelData::Init(grpc_channel_element* elem, + grpc_channel_element_args* args) { + return GRPC_ERROR_NONE; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/channel_filter.h b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.h new file mode 100644 index 00000000000000..0b7c6826816866 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/channel_filter.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H + +#include + +#include "src/cpp/ext/filters/census/context.h" + +namespace grpc { + +class CensusChannelData : public ChannelData { + public: + grpc_error* Init(grpc_channel_element* elem, + grpc_channel_element_args* args) override; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CHANNEL_FILTER_H */ diff --git a/third_party/grpc/src/cpp/ext/filters/census/client_filter.cc b/third_party/grpc/src/cpp/ext/filters/census/client_filter.cc new file mode 100644 index 00000000000000..940d42d1000a73 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/client_filter.cc @@ -0,0 +1,165 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/client_filter.h" + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "opencensus/stats/stats.h" +#include "src/core/lib/surface/call.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" +#include "src/cpp/ext/filters/census/measures.h" + +namespace grpc { + +constexpr uint32_t CensusClientCallData::kMaxTraceContextLen; +constexpr uint32_t CensusClientCallData::kMaxTagsLen; + +namespace { + +void FilterTrailingMetadata(grpc_metadata_batch* b, uint64_t* elapsed_time) { + if (b->idx.named.grpc_server_stats_bin != nullptr) { + ServerStatsDeserialize( + reinterpret_cast(GRPC_SLICE_START_PTR( + GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md))), + GRPC_SLICE_LENGTH(GRPC_MDVALUE(b->idx.named.grpc_server_stats_bin->md)), + elapsed_time); + grpc_metadata_batch_remove(b, b->idx.named.grpc_server_stats_bin); + } +} + +} // namespace + +void CensusClientCallData::OnDoneRecvTrailingMetadataCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast(user_data); + CensusClientCallData* calld = + reinterpret_cast(elem->call_data); + GPR_ASSERT(calld != nullptr); + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(calld->recv_trailing_metadata_ != nullptr); + FilterTrailingMetadata(calld->recv_trailing_metadata_, + &calld->elapsed_time_); + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_trailing_metadata_, + GRPC_ERROR_REF(error)); +} + +void CensusClientCallData::OnDoneRecvMessageCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast(user_data); + CensusClientCallData* calld = + reinterpret_cast(elem->call_data); + CensusChannelData* channeld = + reinterpret_cast(elem->channel_data); + GPR_ASSERT(calld != nullptr); + GPR_ASSERT(channeld != nullptr); + // Stream messages are no longer valid after receiving trailing metadata. + if ((*calld->recv_message_) != nullptr) { + calld->recv_message_count_++; + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_message_, GRPC_ERROR_REF(error)); +} + +void CensusClientCallData::StartTransportStreamOpBatch( + grpc_call_element* elem, TransportStreamOpBatch* op) { + if (op->send_initial_metadata() != nullptr) { + census_context* ctxt = op->get_census_context(); + GenerateClientContext( + qualified_method_, &context_, + (ctxt == nullptr) ? nullptr : reinterpret_cast(ctxt)); + size_t tracing_len = TraceContextSerialize(context_.Context(), tracing_buf_, + kMaxTraceContextLen); + if (tracing_len > 0) { + GRPC_LOG_IF_ERROR( + "census grpc_filter", + grpc_metadata_batch_add_tail( + op->send_initial_metadata()->batch(), &tracing_bin_, + grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_TRACE_BIN, + grpc_slice_from_copied_buffer(tracing_buf_, tracing_len)))); + } + grpc_slice tags = grpc_empty_slice(); + // TODO: Add in tagging serialization. + size_t encoded_tags_len = StatsContextSerialize(kMaxTagsLen, &tags); + if (encoded_tags_len > 0) { + GRPC_LOG_IF_ERROR( + "census grpc_filter", + grpc_metadata_batch_add_tail( + op->send_initial_metadata()->batch(), &stats_bin_, + grpc_mdelem_from_slices(GRPC_MDSTR_GRPC_TAGS_BIN, tags))); + } + } + + if (op->send_message() != nullptr) { + ++sent_message_count_; + } + if (op->recv_message() != nullptr) { + recv_message_ = op->op()->payload->recv_message.recv_message; + initial_on_done_recv_message_ = + op->op()->payload->recv_message.recv_message_ready; + op->op()->payload->recv_message.recv_message_ready = &on_done_recv_message_; + } + if (op->recv_trailing_metadata() != nullptr) { + recv_trailing_metadata_ = op->recv_trailing_metadata()->batch(); + initial_on_done_recv_trailing_metadata_ = + op->op()->payload->recv_trailing_metadata.recv_trailing_metadata_ready; + op->op()->payload->recv_trailing_metadata.recv_trailing_metadata_ready = + &on_done_recv_trailing_metadata_; + } + // Call next op. + grpc_call_next_op(elem, op->op()); +} + +grpc_error* CensusClientCallData::Init(grpc_call_element* elem, + const grpc_call_element_args* args) { + path_ = grpc_slice_ref_internal(args->path); + start_time_ = absl::Now(); + method_ = GetMethod(&path_); + qualified_method_ = absl::StrCat("Sent.", method_); + GRPC_CLOSURE_INIT(&on_done_recv_message_, OnDoneRecvMessageCb, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_done_recv_trailing_metadata_, + OnDoneRecvTrailingMetadataCb, elem, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +void CensusClientCallData::Destroy(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) { + const uint64_t request_size = GetOutgoingDataSize(final_info); + const uint64_t response_size = GetIncomingDataSize(final_info); + double latency_ms = absl::ToDoubleMilliseconds(absl::Now() - start_time_); + ::opencensus::stats::Record( + {{RpcClientSentBytesPerRpc(), static_cast(request_size)}, + {RpcClientReceivedBytesPerRpc(), static_cast(response_size)}, + {RpcClientRoundtripLatency(), latency_ms}, + {RpcClientServerLatency(), + ToDoubleMilliseconds(absl::Nanoseconds(elapsed_time_))}, + {RpcClientSentMessagesPerRpc(), sent_message_count_}, + {RpcClientReceivedMessagesPerRpc(), recv_message_count_}}, + {{ClientMethodTagKey(), method_}, + {ClientStatusTagKey(), StatusCodeToString(final_info->final_status)}}); + grpc_slice_unref_internal(path_); + context_.EndSpan(); +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/client_filter.h b/third_party/grpc/src/cpp/ext/filters/census/client_filter.h new file mode 100644 index 00000000000000..851022873f1984 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/client_filter.h @@ -0,0 +1,104 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H + +#include + +#include "absl/strings/string_view.h" +#include "absl/time/time.h" +#include "src/cpp/ext/filters/census/channel_filter.h" +#include "src/cpp/ext/filters/census/context.h" + +namespace grpc { + +// A CallData class will be created for every grpc call within a channel. It is +// used to store data and methods specific to that call. CensusClientCallData is +// thread-compatible, however typically only 1 thread should be interacting with +// a call at a time. +class CensusClientCallData : public CallData { + public: + // Maximum size of trace context is sent on the wire. + static constexpr uint32_t kMaxTraceContextLen = 64; + // Maximum size of tags that are sent on the wire. + static constexpr uint32_t kMaxTagsLen = 2048; + + CensusClientCallData() + : recv_trailing_metadata_(nullptr), + initial_on_done_recv_trailing_metadata_(nullptr), + initial_on_done_recv_message_(nullptr), + elapsed_time_(0), + recv_message_(nullptr), + recv_message_count_(0), + sent_message_count_(0) { + memset(&stats_bin_, 0, sizeof(grpc_linked_mdelem)); + memset(&tracing_bin_, 0, sizeof(grpc_linked_mdelem)); + memset(&path_, 0, sizeof(grpc_slice)); + memset(&on_done_recv_trailing_metadata_, 0, sizeof(grpc_closure)); + memset(&on_done_recv_message_, 0, sizeof(grpc_closure)); + } + + grpc_error* Init(grpc_call_element* elem, + const grpc_call_element_args* args) override; + + void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) override; + + void StartTransportStreamOpBatch(grpc_call_element* elem, + TransportStreamOpBatch* op) override; + + static void OnDoneRecvTrailingMetadataCb(void* user_data, grpc_error* error); + + static void OnDoneSendInitialMetadataCb(void* user_data, grpc_error* error); + + static void OnDoneRecvMessageCb(void* user_data, grpc_error* error); + + private: + CensusContext context_; + // Metadata elements for tracing and census stats data. + grpc_linked_mdelem stats_bin_; + grpc_linked_mdelem tracing_bin_; + // Client method. + absl::string_view method_; + std::string qualified_method_; + grpc_slice path_; + // The recv trailing metadata callbacks. + grpc_metadata_batch* recv_trailing_metadata_; + grpc_closure* initial_on_done_recv_trailing_metadata_; + grpc_closure on_done_recv_trailing_metadata_; + // recv message + grpc_closure* initial_on_done_recv_message_; + grpc_closure on_done_recv_message_; + // Start time (for measuring latency). + absl::Time start_time_; + // Server elapsed time in nanoseconds. + uint64_t elapsed_time_; + // The received message--may be null. + grpc_core::OrphanablePtr* recv_message_; + // Number of messages in this RPC. + uint64_t recv_message_count_; + uint64_t sent_message_count_; + // Buffer needed for grpc_slice to reference when adding trace context + // metatdata to outgoing message. + char tracing_buf_[kMaxTraceContextLen]; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CLIENT_FILTER_H */ diff --git a/third_party/grpc/src/cpp/ext/filters/census/context.cc b/third_party/grpc/src/cpp/ext/filters/census/context.cc new file mode 100644 index 00000000000000..160590353a714d --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/context.cc @@ -0,0 +1,144 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/context.h" + +namespace grpc { + +using ::opencensus::trace::Span; +using ::opencensus::trace::SpanContext; + +void GenerateServerContext(absl::string_view tracing, absl::string_view stats, + absl::string_view primary_role, + absl::string_view method, CensusContext* context) { + // Destruct the current CensusContext to free the Span memory before + // overwriting it below. + context->~CensusContext(); + GrpcTraceContext trace_ctxt; + if (TraceContextEncoding::Decode(tracing, &trace_ctxt) != + TraceContextEncoding::kEncodeDecodeFailure) { + SpanContext parent_ctx = trace_ctxt.ToSpanContext(); + if (parent_ctx.IsValid()) { + new (context) CensusContext(method, parent_ctx); + return; + } + } + new (context) CensusContext(method); +} + +void GenerateClientContext(absl::string_view method, CensusContext* ctxt, + CensusContext* parent_ctxt) { + // Destruct the current CensusContext to free the Span memory before + // overwriting it below. + ctxt->~CensusContext(); + if (parent_ctxt != nullptr) { + SpanContext span_ctxt = parent_ctxt->Context(); + Span span = parent_ctxt->Span(); + if (span_ctxt.IsValid()) { + new (ctxt) CensusContext(method, &span); + return; + } + } + new (ctxt) CensusContext(method); +} + +size_t TraceContextSerialize(const ::opencensus::trace::SpanContext& context, + char* tracing_buf, size_t tracing_buf_size) { + GrpcTraceContext trace_ctxt(context); + return TraceContextEncoding::Encode(trace_ctxt, tracing_buf, + tracing_buf_size); +} + +size_t StatsContextSerialize(size_t max_tags_len, grpc_slice* tags) { + // TODO: Add implementation. Waiting on stats tagging to be added. + return 0; +} + +size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf, + size_t buf_size) { + return RpcServerStatsEncoding::Encode(server_elapsed_time, buf, buf_size); +} + +size_t ServerStatsDeserialize(const char* buf, size_t buf_size, + uint64_t* server_elapsed_time) { + return RpcServerStatsEncoding::Decode(absl::string_view(buf, buf_size), + server_elapsed_time); +} + +uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info) { + return final_info->stats.transport_stream_stats.incoming.data_bytes; +} + +uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info) { + return final_info->stats.transport_stream_stats.outgoing.data_bytes; +} + +SpanContext SpanContextFromCensusContext(const census_context* ctxt) { + return reinterpret_cast(ctxt)->Context(); +} + +Span SpanFromCensusContext(const census_context* ctxt) { + return reinterpret_cast(ctxt)->Span(); +} + +absl::string_view StatusCodeToString(grpc_status_code code) { + switch (code) { + case GRPC_STATUS_OK: + return "OK"; + case GRPC_STATUS_CANCELLED: + return "CANCELLED"; + case GRPC_STATUS_UNKNOWN: + return "UNKNOWN"; + case GRPC_STATUS_INVALID_ARGUMENT: + return "INVALID_ARGUMENT"; + case GRPC_STATUS_DEADLINE_EXCEEDED: + return "DEADLINE_EXCEEDED"; + case GRPC_STATUS_NOT_FOUND: + return "NOT_FOUND"; + case GRPC_STATUS_ALREADY_EXISTS: + return "ALREADY_EXISTS"; + case GRPC_STATUS_PERMISSION_DENIED: + return "PERMISSION_DENIED"; + case GRPC_STATUS_UNAUTHENTICATED: + return "UNAUTHENTICATED"; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return "RESOURCE_EXHAUSTED"; + case GRPC_STATUS_FAILED_PRECONDITION: + return "FAILED_PRECONDITION"; + case GRPC_STATUS_ABORTED: + return "ABORTED"; + case GRPC_STATUS_OUT_OF_RANGE: + return "OUT_OF_RANGE"; + case GRPC_STATUS_UNIMPLEMENTED: + return "UNIMPLEMENTED"; + case GRPC_STATUS_INTERNAL: + return "INTERNAL"; + case GRPC_STATUS_UNAVAILABLE: + return "UNAVAILABLE"; + case GRPC_STATUS_DATA_LOSS: + return "DATA_LOSS"; + default: + // gRPC wants users of this enum to include a default branch so that + // adding values is not a breaking change. + return "UNKNOWN_STATUS"; + } +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/context.h b/third_party/grpc/src/cpp/ext/filters/census/context.h new file mode 100644 index 00000000000000..1643fdd11be8aa --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/context.h @@ -0,0 +1,126 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H + +#include + +#include +#include "absl/memory/memory.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" +#include "opencensus/trace/span.h" +#include "opencensus/trace/span_context.h" +#include "opencensus/trace/trace_params.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/cpp/common/channel_filter.h" +#include "src/cpp/ext/filters/census/rpc_encoding.h" + +// This is needed because grpc has hardcoded CensusContext with a +// forward declaration of 'struct census_context;' +struct census_context; + +namespace grpc { + +// Thread compatible. +class CensusContext { + public: + CensusContext() : span_(::opencensus::trace::Span::BlankSpan()) {} + + explicit CensusContext(absl::string_view name) + : span_(::opencensus::trace::Span::StartSpan(name)) {} + + CensusContext(absl::string_view name, const ::opencensus::trace::Span* parent) + : span_(::opencensus::trace::Span::StartSpan(name, parent)) {} + + CensusContext(absl::string_view name, + const ::opencensus::trace::SpanContext& parent_ctxt) + : span_(::opencensus::trace::Span::StartSpanWithRemoteParent( + name, parent_ctxt)) {} + + ::opencensus::trace::SpanContext Context() const { return span_.context(); } + ::opencensus::trace::Span Span() const { return span_; } + void EndSpan() { span_.End(); } + + private: + ::opencensus::trace::Span span_; +}; + +// Serializes the outgoing trace context. Field IDs are 1 byte followed by +// field data. A 1 byte version ID is always encoded first. +size_t TraceContextSerialize(const ::opencensus::trace::SpanContext& context, + char* tracing_buf, size_t tracing_buf_size); + +// Serializes the outgoing stats context. Field IDs are 1 byte followed by +// field data. A 1 byte version ID is always encoded first. Tags are directly +// serialized into the given grpc_slice. +size_t StatsContextSerialize(size_t max_tags_len, grpc_slice* tags); + +// Serialize outgoing server stats. Returns the number of bytes serialized. +size_t ServerStatsSerialize(uint64_t server_elapsed_time, char* buf, + size_t buf_size); + +// Deserialize incoming server stats. Returns the number of bytes deserialized. +size_t ServerStatsDeserialize(const char* buf, size_t buf_size, + uint64_t* server_elapsed_time); + +// Deserialize the incoming SpanContext and generate a new server context based +// on that. This new span will never be a root span. This should only be called +// with a blank CensusContext as it overwrites it. +void GenerateServerContext(absl::string_view tracing, absl::string_view stats, + absl::string_view primary_role, + absl::string_view method, CensusContext* context); + +// Creates a new client context that is by default a new root context. +// If the current context is the default context then the newly created +// span automatically becomes a root span. This should only be called with a +// blank CensusContext as it overwrites it. +void GenerateClientContext(absl::string_view method, CensusContext* ctxt, + CensusContext* parent_ctx); + +// Returns the incoming data size from the grpc call final info. +uint64_t GetIncomingDataSize(const grpc_call_final_info* final_info); + +// Returns the outgoing data size from the grpc call final info. +uint64_t GetOutgoingDataSize(const grpc_call_final_info* final_info); + +// These helper functions return the SpanContext and Span, respectively +// associated with the census_context* stored by grpc. The user will need to +// call this for manual propagation of tracing data. +::opencensus::trace::SpanContext SpanContextFromCensusContext( + const census_context* ctxt); +::opencensus::trace::Span SpanFromCensusContext(const census_context* ctxt); + +// Returns a string representation of the StatusCode enum. +absl::string_view StatusCodeToString(grpc_status_code code); + +inline absl::string_view GetMethod(const grpc_slice* path) { + if (GRPC_SLICE_IS_EMPTY(*path)) { + return ""; + } + // Check for leading '/' and trim it if present. + return absl::StripPrefix(absl::string_view(reinterpret_cast( + GRPC_SLICE_START_PTR(*path)), + GRPC_SLICE_LENGTH(*path)), + "/"); +} + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_CONTEXT_H */ diff --git a/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.cc b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.cc new file mode 100644 index 00000000000000..f978ed3bf51b86 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.cc @@ -0,0 +1,130 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/grpc_plugin.h" + +#include + +#include "opencensus/trace/span.h" +#include "src/cpp/ext/filters/census/channel_filter.h" +#include "src/cpp/ext/filters/census/client_filter.h" +#include "src/cpp/ext/filters/census/measures.h" +#include "src/cpp/ext/filters/census/server_filter.h" + +namespace grpc { + +void RegisterOpenCensusPlugin() { + RegisterChannelFilter( + "opencensus_client", GRPC_CLIENT_CHANNEL, INT_MAX /* priority */, + nullptr /* condition function */); + RegisterChannelFilter( + "opencensus_server", GRPC_SERVER_CHANNEL, INT_MAX /* priority */, + nullptr /* condition function */); + + // Access measures to ensure they are initialized. Otherwise, creating a view + // before the first RPC would cause an error. + RpcClientSentBytesPerRpc(); + RpcClientReceivedBytesPerRpc(); + RpcClientRoundtripLatency(); + RpcClientServerLatency(); + RpcClientSentMessagesPerRpc(); + RpcClientReceivedMessagesPerRpc(); + + RpcServerSentBytesPerRpc(); + RpcServerReceivedBytesPerRpc(); + RpcServerServerLatency(); + RpcServerSentMessagesPerRpc(); + RpcServerReceivedMessagesPerRpc(); +} + +::opencensus::trace::Span GetSpanFromServerContext(ServerContext* context) { + return reinterpret_cast(context->census_context()) + ->Span(); +} + +// These measure definitions should be kept in sync across opencensus +// implementations--see +// https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java. +::opencensus::stats::TagKey ClientMethodTagKey() { + static const auto method_tag_key = + ::opencensus::stats::TagKey::Register("grpc_client_method"); + return method_tag_key; +} + +::opencensus::stats::TagKey ClientStatusTagKey() { + static const auto status_tag_key = + ::opencensus::stats::TagKey::Register("grpc_client_status"); + return status_tag_key; +} + +::opencensus::stats::TagKey ServerMethodTagKey() { + static const auto method_tag_key = + ::opencensus::stats::TagKey::Register("grpc_server_method"); + return method_tag_key; +} + +::opencensus::stats::TagKey ServerStatusTagKey() { + static const auto status_tag_key = + ::opencensus::stats::TagKey::Register("grpc_server_status"); + return status_tag_key; +} + +// Client +ABSL_CONST_INIT const absl::string_view + kRpcClientSentMessagesPerRpcMeasureName = + "grpc.io/client/sent_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcClientSentBytesPerRpcMeasureName = + "grpc.io/client/sent_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcClientReceivedMessagesPerRpcMeasureName = + "grpc.io/client/received_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcClientReceivedBytesPerRpcMeasureName = + "grpc.io/client/received_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcClientRoundtripLatencyMeasureName = + "grpc.io/client/roundtrip_latency"; + +ABSL_CONST_INIT const absl::string_view kRpcClientServerLatencyMeasureName = + "grpc.io/client/server_latency"; + +// Server +ABSL_CONST_INIT const absl::string_view + kRpcServerSentMessagesPerRpcMeasureName = + "grpc.io/server/sent_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcServerSentBytesPerRpcMeasureName = + "grpc.io/server/sent_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcServerReceivedMessagesPerRpcMeasureName = + "grpc.io/server/received_messages_per_rpc"; + +ABSL_CONST_INIT const absl::string_view + kRpcServerReceivedBytesPerRpcMeasureName = + "grpc.io/server/received_bytes_per_rpc"; + +ABSL_CONST_INIT const absl::string_view kRpcServerServerLatencyMeasureName = + "grpc.io/server/server_latency"; + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.h b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.h new file mode 100644 index 00000000000000..9e319cb994ec0b --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/grpc_plugin.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H + +#include + +#include "absl/strings/string_view.h" +#include "include/grpcpp/opencensus.h" +#include "opencensus/stats/stats.h" + +namespace grpc { + +class ServerContext; + +// The tag keys set when recording RPC stats. +::opencensus::stats::TagKey ClientMethodTagKey(); +::opencensus::stats::TagKey ClientStatusTagKey(); +::opencensus::stats::TagKey ServerMethodTagKey(); +::opencensus::stats::TagKey ServerStatusTagKey(); + +// Names of measures used by the plugin--users can create views on these +// measures but should not record data for them. +extern const absl::string_view kRpcClientSentMessagesPerRpcMeasureName; +extern const absl::string_view kRpcClientSentBytesPerRpcMeasureName; +extern const absl::string_view kRpcClientReceivedMessagesPerRpcMeasureName; +extern const absl::string_view kRpcClientReceivedBytesPerRpcMeasureName; +extern const absl::string_view kRpcClientRoundtripLatencyMeasureName; +extern const absl::string_view kRpcClientServerLatencyMeasureName; + +extern const absl::string_view kRpcServerSentMessagesPerRpcMeasureName; +extern const absl::string_view kRpcServerSentBytesPerRpcMeasureName; +extern const absl::string_view kRpcServerReceivedMessagesPerRpcMeasureName; +extern const absl::string_view kRpcServerReceivedBytesPerRpcMeasureName; +extern const absl::string_view kRpcServerServerLatencyMeasureName; + +// Canonical gRPC view definitions. +const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ClientReceivedMessagesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ClientReceivedBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientServerLatencyCumulative(); +const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsCumulative(); + +const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ServerReceivedBytesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerServerLatencyCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerStartedCountCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsCumulative(); +const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcCumulative(); +const ::opencensus::stats::ViewDescriptor& +ServerReceivedMessagesPerRpcCumulative(); + +const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyMinute(); +const ::opencensus::stats::ViewDescriptor& ClientServerLatencyMinute(); +const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsMinute(); + +const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedMessagesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedBytesPerRpcMinute(); +const ::opencensus::stats::ViewDescriptor& ServerServerLatencyMinute(); +const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsMinute(); + +const ::opencensus::stats::ViewDescriptor& ClientSentMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientSentBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientReceivedBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ClientRoundtripLatencyHour(); +const ::opencensus::stats::ViewDescriptor& ClientServerLatencyHour(); +const ::opencensus::stats::ViewDescriptor& ClientCompletedRpcsHour(); + +const ::opencensus::stats::ViewDescriptor& ServerSentMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerSentBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedMessagesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerReceivedBytesPerRpcHour(); +const ::opencensus::stats::ViewDescriptor& ServerServerLatencyHour(); +const ::opencensus::stats::ViewDescriptor& ServerStartedCountHour(); +const ::opencensus::stats::ViewDescriptor& ServerCompletedRpcsHour(); + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_GRPC_PLUGIN_H */ diff --git a/third_party/grpc/src/cpp/ext/filters/census/measures.cc b/third_party/grpc/src/cpp/ext/filters/census/measures.cc new file mode 100644 index 00000000000000..b522fae09aec16 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/measures.cc @@ -0,0 +1,129 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/measures.h" + +#include "opencensus/stats/stats.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" + +namespace grpc { + +using ::opencensus::stats::MeasureDouble; +using ::opencensus::stats::MeasureInt64; + +// These measure definitions should be kept in sync across opencensus +// implementations--see +// https://github.com/census-instrumentation/opencensus-java/blob/master/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcMeasureConstants.java. + +namespace { + +// Unit constants +constexpr char kUnitBytes[] = "By"; +constexpr char kUnitMilliseconds[] = "ms"; +constexpr char kCount[] = "1"; + +} // namespace + +// Client +MeasureDouble RpcClientSentBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcClientSentBytesPerRpcMeasureName, + "Total bytes sent across all request messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcClientReceivedBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcClientReceivedBytesPerRpcMeasureName, + "Total bytes received across all response messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcClientRoundtripLatency() { + static const auto measure = MeasureDouble::Register( + kRpcClientRoundtripLatencyMeasureName, + "Time between first byte of request sent to last byte of response " + "received, or terminal error", + kUnitMilliseconds); + return measure; +} + +MeasureDouble RpcClientServerLatency() { + static const auto measure = MeasureDouble::Register( + kRpcClientServerLatencyMeasureName, + "Time between first byte of request received to last byte of response " + "sent, or terminal error (propagated from the server)", + kUnitMilliseconds); + return measure; +} + +MeasureInt64 RpcClientSentMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcClientSentMessagesPerRpcMeasureName, + "Number of messages sent per RPC", kCount); + return measure; +} + +MeasureInt64 RpcClientReceivedMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcClientReceivedMessagesPerRpcMeasureName, + "Number of messages received per RPC", kCount); + return measure; +} + +// Server +MeasureDouble RpcServerSentBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcServerSentBytesPerRpcMeasureName, + "Total bytes sent across all messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcServerReceivedBytesPerRpc() { + static const auto measure = MeasureDouble::Register( + kRpcServerReceivedBytesPerRpcMeasureName, + "Total bytes received across all messages per RPC", kUnitBytes); + return measure; +} + +MeasureDouble RpcServerServerLatency() { + static const auto measure = MeasureDouble::Register( + kRpcServerServerLatencyMeasureName, + "Time between first byte of request received to last byte of response " + "sent, or terminal error", + kUnitMilliseconds); + return measure; +} + +MeasureInt64 RpcServerSentMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcServerSentMessagesPerRpcMeasureName, + "Number of messages sent per RPC", kCount); + return measure; +} + +MeasureInt64 RpcServerReceivedMessagesPerRpc() { + static const auto measure = + MeasureInt64::Register(kRpcServerReceivedMessagesPerRpcMeasureName, + "Number of messages received per RPC", kCount); + return measure; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/measures.h b/third_party/grpc/src/cpp/ext/filters/census/measures.h new file mode 100644 index 00000000000000..8f8e72ace20092 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/measures.h @@ -0,0 +1,46 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H + +#include + +#include "opencensus/stats/stats.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" + +namespace grpc { + +::opencensus::stats::MeasureInt64 RpcClientSentMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcClientSentBytesPerRpc(); +::opencensus::stats::MeasureInt64 RpcClientReceivedMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcClientReceivedBytesPerRpc(); +::opencensus::stats::MeasureDouble RpcClientRoundtripLatency(); +::opencensus::stats::MeasureDouble RpcClientServerLatency(); +::opencensus::stats::MeasureInt64 RpcClientCompletedRpcs(); + +::opencensus::stats::MeasureInt64 RpcServerSentMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcServerSentBytesPerRpc(); +::opencensus::stats::MeasureInt64 RpcServerReceivedMessagesPerRpc(); +::opencensus::stats::MeasureDouble RpcServerReceivedBytesPerRpc(); +::opencensus::stats::MeasureDouble RpcServerServerLatency(); +::opencensus::stats::MeasureInt64 RpcServerCompletedRpcs(); + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_MEASURES_H */ diff --git a/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.cc b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.cc new file mode 100644 index 00000000000000..45a66d9dc82049 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.cc @@ -0,0 +1,39 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/rpc_encoding.h" + +namespace grpc { + +constexpr size_t TraceContextEncoding::kGrpcTraceContextSize; +constexpr size_t TraceContextEncoding::kEncodeDecodeFailure; +constexpr size_t TraceContextEncoding::kVersionIdSize; +constexpr size_t TraceContextEncoding::kFieldIdSize; +constexpr size_t TraceContextEncoding::kVersionIdOffset; +constexpr size_t TraceContextEncoding::kVersionId; + +constexpr size_t RpcServerStatsEncoding::kRpcServerStatsSize; +constexpr size_t RpcServerStatsEncoding::kEncodeDecodeFailure; +constexpr size_t RpcServerStatsEncoding::kVersionIdSize; +constexpr size_t RpcServerStatsEncoding::kFieldIdSize; +constexpr size_t RpcServerStatsEncoding::kVersionIdOffset; +constexpr size_t RpcServerStatsEncoding::kVersionId; + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.h b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.h new file mode 100644 index 00000000000000..ffffa60c468562 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/rpc_encoding.h @@ -0,0 +1,284 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H + +#include + +#include + +#include "absl/base/internal/endian.h" +#include "absl/strings/string_view.h" +#include "opencensus/trace/span_context.h" +#include "opencensus/trace/span_id.h" +#include "opencensus/trace/trace_id.h" + +namespace grpc { + +// TODO: Rename to GrpcTraceContextV0. +struct GrpcTraceContext { + GrpcTraceContext() {} + + explicit GrpcTraceContext(const ::opencensus::trace::SpanContext& ctx) { + ctx.trace_id().CopyTo(trace_id); + ctx.span_id().CopyTo(span_id); + ctx.trace_options().CopyTo(trace_options); + } + + ::opencensus::trace::SpanContext ToSpanContext() const { + return ::opencensus::trace::SpanContext( + ::opencensus::trace::TraceId(trace_id), + ::opencensus::trace::SpanId(span_id), + ::opencensus::trace::TraceOptions(trace_options)); + } + + // TODO: For performance: + // uint8_t version; + // uint8_t trace_id_field_id; + uint8_t trace_id[::opencensus::trace::TraceId::kSize]; + // uint8_t span_id_field_id; + uint8_t span_id[::opencensus::trace::SpanId::kSize]; + // uint8_t trace_options_field_id; + uint8_t trace_options[::opencensus::trace::TraceOptions::kSize]; +}; + +// TraceContextEncoding encapsulates the logic for encoding and decoding of +// trace contexts. +class TraceContextEncoding { + public: + // Size of encoded GrpcTraceContext. (16 + 8 + 1 + 4) + static constexpr size_t kGrpcTraceContextSize = 29; + // Error value. + static constexpr size_t kEncodeDecodeFailure = 0; + + // Deserializes a GrpcTraceContext from the incoming buffer. Returns the + // number of bytes deserialized from the buffer. If the incoming buffer is + // empty or the encoding version is not supported it will return 0 bytes, + // currently only version 0 is supported. If an unknown field ID is + // encountered it will return immediately without parsing the rest of the + // buffer. Inlined for performance reasons. + static size_t Decode(absl::string_view buf, GrpcTraceContext* tc) { + if (buf.empty()) { + return kEncodeDecodeFailure; + } + uint8_t version = buf[kVersionIdOffset]; + // TODO: Support other versions later. Only support version 0 for + // now. + if (version != kVersionId) { + return kEncodeDecodeFailure; + } + + size_t pos = kVersionIdSize; + while (pos < buf.size()) { + size_t bytes_read = + ParseField(absl::string_view(&buf[pos], buf.size() - pos), tc); + if (bytes_read == 0) { + break; + } else { + pos += bytes_read; + } + } + return pos; + } + + // Serializes a GrpcTraceContext into the provided buffer. Returns the number + // of bytes serialized into the buffer. If the buffer is not of sufficient + // size (it must be at least kGrpcTraceContextSize bytes) it will drop + // everything and return 0 bytes serialized. Inlined for performance reasons. + static size_t Encode(const GrpcTraceContext& tc, char* buf, size_t buf_size) { + if (buf_size < kGrpcTraceContextSize) { + return kEncodeDecodeFailure; + } + buf[kVersionIdOffset] = kVersionId; + buf[kTraceIdOffset] = kTraceIdField; + memcpy(&buf[kTraceIdOffset + 1], tc.trace_id, + opencensus::trace::TraceId::kSize); + buf[kSpanIdOffset] = kSpanIdField; + memcpy(&buf[kSpanIdOffset + 1], tc.span_id, + opencensus::trace::SpanId::kSize); + buf[kTraceOptionsOffset] = kTraceOptionsField; + memcpy(&buf[kTraceOptionsOffset + 1], tc.trace_options, + opencensus::trace::TraceOptions::kSize); + return kGrpcTraceContextSize; + } + + private: + // Parses the next field from the incoming buffer and stores the parsed value + // in a GrpcTraceContext struct. If it does not recognize the field ID it + // will return 0, otherwise it returns the number of bytes read. + static size_t ParseField(absl::string_view buf, GrpcTraceContext* tc) { + // TODO: Add support for multi-byte field IDs. + if (buf.empty()) { + return 0; + } + // Field ID is always the first byte in a field. + uint32_t field_id = buf[0]; + size_t bytes_read = kFieldIdSize; + switch (field_id) { + case kTraceIdField: + bytes_read += kTraceIdSize; + if (bytes_read > buf.size()) { + return 0; + } + memcpy(tc->trace_id, &buf[kFieldIdSize], + opencensus::trace::TraceId::kSize); + break; + case kSpanIdField: + bytes_read += kSpanIdSize; + if (bytes_read > buf.size()) { + return 0; + } + memcpy(tc->span_id, &buf[kFieldIdSize], + opencensus::trace::SpanId::kSize); + break; + case kTraceOptionsField: + bytes_read += kTraceOptionsSize; + if (bytes_read > buf.size()) { + return 0; + } + memcpy(tc->trace_options, &buf[kFieldIdSize], + opencensus::trace::TraceOptions::kSize); + break; + default: // Invalid field ID + return 0; + } + + return bytes_read; + } + + // Size of Version ID. + static constexpr size_t kVersionIdSize = 1; + // Size of Field ID. + static constexpr size_t kFieldIdSize = 1; + + // Offset and value for currently supported version ID. + static constexpr size_t kVersionIdOffset = 0; + static constexpr size_t kVersionId = 0; + + // Fixed Field ID values: + enum FieldIdValue { + kTraceIdField = 0, + kSpanIdField = 1, + kTraceOptionsField = 2, + }; + + // Field data sizes in bytes + enum FieldSize { + kTraceIdSize = 16, + kSpanIdSize = 8, + kTraceOptionsSize = 1, + }; + + // Fixed size offsets for field ID start positions during encoding. Field + // data immediately follows. + enum FieldIdOffset { + kTraceIdOffset = kVersionIdSize, + kSpanIdOffset = kTraceIdOffset + kFieldIdSize + kTraceIdSize, + kTraceOptionsOffset = kSpanIdOffset + kFieldIdSize + kSpanIdSize, + }; + + TraceContextEncoding() = delete; + TraceContextEncoding(const TraceContextEncoding&) = delete; + TraceContextEncoding(TraceContextEncoding&&) = delete; + TraceContextEncoding operator=(const TraceContextEncoding&) = delete; + TraceContextEncoding operator=(TraceContextEncoding&&) = delete; +}; + +// TODO: This may not be needed. Check to see if opencensus requires +// a trailing server response. +// RpcServerStatsEncoding encapsulates the logic for encoding and decoding of +// rpc server stats messages. Rpc server stats consists of a uint64_t time +// value (server latency in nanoseconds). +class RpcServerStatsEncoding { + public: + // Size of encoded RPC server stats. + static constexpr size_t kRpcServerStatsSize = 10; + // Error value. + static constexpr size_t kEncodeDecodeFailure = 0; + + // Deserializes rpc server stats from the incoming 'buf' into *time. Returns + // number of bytes decoded. If the buffer is of insufficient size (it must be + // at least kRpcServerStatsSize bytes) or the encoding version or field ID are + // unrecognized, *time will be set to 0 and it will return + // kEncodeDecodeFailure. Inlined for performance reasons. + static size_t Decode(absl::string_view buf, uint64_t* time) { + if (buf.size() < kRpcServerStatsSize) { + *time = 0; + return kEncodeDecodeFailure; + } + + uint8_t version = buf[kVersionIdOffset]; + uint32_t fieldID = buf[kServerElapsedTimeOffset]; + if (version != kVersionId || fieldID != kServerElapsedTimeField) { + *time = 0; + return kEncodeDecodeFailure; + } + *time = absl::little_endian::Load64( + &buf[kServerElapsedTimeOffset + kFieldIdSize]); + return kRpcServerStatsSize; + } + + // Serializes rpc server stats into the provided buffer. It returns the + // number of bytes written to the buffer. If the buffer is smaller than + // kRpcServerStatsSize bytes it will return kEncodeDecodeFailure. Inlined for + // performance reasons. + static size_t Encode(uint64_t time, char* buf, size_t buf_size) { + if (buf_size < kRpcServerStatsSize) { + return kEncodeDecodeFailure; + } + + buf[kVersionIdOffset] = kVersionId; + buf[kServerElapsedTimeOffset] = kServerElapsedTimeField; + absl::little_endian::Store64(&buf[kServerElapsedTimeOffset + kFieldIdSize], + time); + return kRpcServerStatsSize; + } + + private: + // Size of Version ID. + static constexpr size_t kVersionIdSize = 1; + // Size of Field ID. + static constexpr size_t kFieldIdSize = 1; + + // Offset and value for currently supported version ID. + static constexpr size_t kVersionIdOffset = 0; + static constexpr size_t kVersionId = 0; + + enum FieldIdValue { + kServerElapsedTimeField = 0, + }; + + enum FieldSize { + kServerElapsedTimeSize = 8, + }; + + enum FieldIdOffset { + kServerElapsedTimeOffset = kVersionIdSize, + }; + + RpcServerStatsEncoding() = delete; + RpcServerStatsEncoding(const RpcServerStatsEncoding&) = delete; + RpcServerStatsEncoding(RpcServerStatsEncoding&&) = delete; + RpcServerStatsEncoding operator=(const RpcServerStatsEncoding&) = delete; + RpcServerStatsEncoding operator=(RpcServerStatsEncoding&&) = delete; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_RPC_ENCODING_H */ diff --git a/third_party/grpc/src/cpp/ext/filters/census/server_filter.cc b/third_party/grpc/src/cpp/ext/filters/census/server_filter.cc new file mode 100644 index 00000000000000..b5f3d5a13a7824 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/server_filter.cc @@ -0,0 +1,198 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/server_filter.h" + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "opencensus/stats/stats.h" +#include "src/core/lib/surface/call.h" +#include "src/cpp/ext/filters/census/grpc_plugin.h" +#include "src/cpp/ext/filters/census/measures.h" + +namespace grpc { + +constexpr uint32_t CensusServerCallData::kMaxServerStatsLen; + +namespace { + +// server metadata elements +struct ServerMetadataElements { + grpc_slice path; + grpc_slice tracing_slice; + grpc_slice census_proto; +}; + +void FilterInitialMetadata(grpc_metadata_batch* b, + ServerMetadataElements* sml) { + if (b->idx.named.path != nullptr) { + sml->path = grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.path->md)); + } + if (b->idx.named.grpc_trace_bin != nullptr) { + sml->tracing_slice = + grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_trace_bin->md)); + grpc_metadata_batch_remove(b, b->idx.named.grpc_trace_bin); + } + if (b->idx.named.grpc_tags_bin != nullptr) { + sml->census_proto = + grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_tags_bin->md)); + grpc_metadata_batch_remove(b, b->idx.named.grpc_tags_bin); + } +} + +} // namespace + +void CensusServerCallData::OnDoneRecvMessageCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast(user_data); + CensusServerCallData* calld = + reinterpret_cast(elem->call_data); + CensusChannelData* channeld = + reinterpret_cast(elem->channel_data); + GPR_ASSERT(calld != nullptr); + GPR_ASSERT(channeld != nullptr); + // Stream messages are no longer valid after receiving trailing metadata. + if ((*calld->recv_message_) != nullptr) { + ++calld->recv_message_count_; + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_message_, GRPC_ERROR_REF(error)); +} + +void CensusServerCallData::OnDoneRecvInitialMetadataCb(void* user_data, + grpc_error* error) { + grpc_call_element* elem = reinterpret_cast(user_data); + CensusServerCallData* calld = + reinterpret_cast(elem->call_data); + GPR_ASSERT(calld != nullptr); + if (error == GRPC_ERROR_NONE) { + grpc_metadata_batch* initial_metadata = calld->recv_initial_metadata_; + GPR_ASSERT(initial_metadata != nullptr); + ServerMetadataElements sml; + sml.path = grpc_empty_slice(); + sml.tracing_slice = grpc_empty_slice(); + sml.census_proto = grpc_empty_slice(); + FilterInitialMetadata(initial_metadata, &sml); + calld->path_ = grpc_slice_ref_internal(sml.path); + calld->method_ = GetMethod(&calld->path_); + calld->qualified_method_ = absl::StrCat("Recv.", calld->method_); + const char* tracing_str = + GRPC_SLICE_IS_EMPTY(sml.tracing_slice) + ? "" + : reinterpret_cast( + GRPC_SLICE_START_PTR(sml.tracing_slice)); + size_t tracing_str_len = GRPC_SLICE_IS_EMPTY(sml.tracing_slice) + ? 0 + : GRPC_SLICE_LENGTH(sml.tracing_slice); + const char* census_str = GRPC_SLICE_IS_EMPTY(sml.census_proto) + ? "" + : reinterpret_cast( + GRPC_SLICE_START_PTR(sml.census_proto)); + size_t census_str_len = GRPC_SLICE_IS_EMPTY(sml.census_proto) + ? 0 + : GRPC_SLICE_LENGTH(sml.census_proto); + + GenerateServerContext(absl::string_view(tracing_str, tracing_str_len), + absl::string_view(census_str, census_str_len), + /*primary_role*/ "", calld->qualified_method_, + &calld->context_); + + grpc_slice_unref_internal(sml.tracing_slice); + grpc_slice_unref_internal(sml.census_proto); + grpc_slice_unref_internal(sml.path); + grpc_census_call_set_context( + calld->gc_, reinterpret_cast(&calld->context_)); + } + GRPC_CLOSURE_RUN(calld->initial_on_done_recv_initial_metadata_, + GRPC_ERROR_REF(error)); +} + +void CensusServerCallData::StartTransportStreamOpBatch( + grpc_call_element* elem, TransportStreamOpBatch* op) { + if (op->recv_initial_metadata() != nullptr) { + // substitute our callback for the op callback + recv_initial_metadata_ = op->recv_initial_metadata()->batch(); + initial_on_done_recv_initial_metadata_ = op->recv_initial_metadata_ready(); + op->set_recv_initial_metadata_ready(&on_done_recv_initial_metadata_); + } + if (op->send_message() != nullptr) { + ++sent_message_count_; + } + if (op->recv_message() != nullptr) { + recv_message_ = op->op()->payload->recv_message.recv_message; + initial_on_done_recv_message_ = + op->op()->payload->recv_message.recv_message_ready; + op->op()->payload->recv_message.recv_message_ready = &on_done_recv_message_; + } + // We need to record the time when the trailing metadata was sent to mark the + // completeness of the request. + if (op->send_trailing_metadata() != nullptr) { + elapsed_time_ = absl::Now() - start_time_; + size_t len = ServerStatsSerialize(absl::ToInt64Nanoseconds(elapsed_time_), + stats_buf_, kMaxServerStatsLen); + if (len > 0) { + GRPC_LOG_IF_ERROR( + "census grpc_filter", + grpc_metadata_batch_add_tail( + op->send_trailing_metadata()->batch(), &census_bin_, + grpc_mdelem_from_slices( + GRPC_MDSTR_GRPC_SERVER_STATS_BIN, + grpc_slice_from_copied_buffer(stats_buf_, len)))); + } + } + // Call next op. + grpc_call_next_op(elem, op->op()); +} + +grpc_error* CensusServerCallData::Init(grpc_call_element* elem, + const grpc_call_element_args* args) { + start_time_ = absl::Now(); + gc_ = + grpc_call_from_top_element(grpc_call_stack_element(args->call_stack, 0)); + GRPC_CLOSURE_INIT(&on_done_recv_initial_metadata_, + OnDoneRecvInitialMetadataCb, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&on_done_recv_message_, OnDoneRecvMessageCb, elem, + grpc_schedule_on_exec_ctx); + auth_context_ = grpc_call_auth_context(gc_); + return GRPC_ERROR_NONE; +} + +void CensusServerCallData::Destroy(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) { + const uint64_t request_size = GetOutgoingDataSize(final_info); + const uint64_t response_size = GetIncomingDataSize(final_info); + double elapsed_time_ms = absl::ToDoubleMilliseconds(elapsed_time_); + grpc_auth_context_release(auth_context_); + ::opencensus::stats::Record( + {{RpcServerSentBytesPerRpc(), static_cast(response_size)}, + {RpcServerReceivedBytesPerRpc(), static_cast(request_size)}, + {RpcServerServerLatency(), elapsed_time_ms}, + {RpcServerSentMessagesPerRpc(), sent_message_count_}, + {RpcServerReceivedMessagesPerRpc(), recv_message_count_}}, + {{ServerMethodTagKey(), method_}, + {ServerStatusTagKey(), StatusCodeToString(final_info->final_status)}}); + grpc_slice_unref_internal(path_); + context_.EndSpan(); +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/filters/census/server_filter.h b/third_party/grpc/src/cpp/ext/filters/census/server_filter.h new file mode 100644 index 00000000000000..e393ed32839767 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/server_filter.h @@ -0,0 +1,101 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H +#define GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H + +#include + +#include "absl/strings/string_view.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "include/grpc/grpc_security.h" +#include "src/cpp/ext/filters/census/channel_filter.h" +#include "src/cpp/ext/filters/census/context.h" + +namespace grpc { + +// A CallData class will be created for every grpc call within a channel. It is +// used to store data and methods specific to that call. CensusServerCallData is +// thread-compatible, however typically only 1 thread should be interacting with +// a call at a time. +class CensusServerCallData : public CallData { + public: + // Maximum size of server stats that are sent on the wire. + static constexpr uint32_t kMaxServerStatsLen = 16; + + CensusServerCallData() + : gc_(nullptr), + auth_context_(nullptr), + recv_initial_metadata_(nullptr), + initial_on_done_recv_initial_metadata_(nullptr), + initial_on_done_recv_message_(nullptr), + recv_message_(nullptr), + recv_message_count_(0), + sent_message_count_(0) { + memset(&census_bin_, 0, sizeof(grpc_linked_mdelem)); + memset(&path_, 0, sizeof(grpc_slice)); + memset(&on_done_recv_initial_metadata_, 0, sizeof(grpc_closure)); + memset(&on_done_recv_message_, 0, sizeof(grpc_closure)); + } + + grpc_error* Init(grpc_call_element* elem, + const grpc_call_element_args* args) override; + + void Destroy(grpc_call_element* elem, const grpc_call_final_info* final_info, + grpc_closure* then_call_closure) override; + + void StartTransportStreamOpBatch(grpc_call_element* elem, + TransportStreamOpBatch* op) override; + + static void OnDoneRecvInitialMetadataCb(void* user_data, grpc_error* error); + + static void OnDoneRecvMessageCb(void* user_data, grpc_error* error); + + private: + CensusContext context_; + // server method + absl::string_view method_; + std::string qualified_method_; + grpc_slice path_; + // Pointer to the grpc_call element + grpc_call* gc_; + // Authorization context for the call. + grpc_auth_context* auth_context_; + // Metadata element for census stats. + grpc_linked_mdelem census_bin_; + // recv callback + grpc_metadata_batch* recv_initial_metadata_; + grpc_closure* initial_on_done_recv_initial_metadata_; + grpc_closure on_done_recv_initial_metadata_; + // recv message + grpc_closure* initial_on_done_recv_message_; + grpc_closure on_done_recv_message_; + absl::Time start_time_; + absl::Duration elapsed_time_; + grpc_core::OrphanablePtr* recv_message_; + uint64_t recv_message_count_; + uint64_t sent_message_count_; + // Buffer needed for grpc_slice to reference it when adding metatdata to + // response. + char stats_buf_[kMaxServerStatsLen]; +}; + +} // namespace grpc + +#endif /* GRPC_INTERNAL_CPP_EXT_FILTERS_CENSUS_SERVER_FILTER_H */ diff --git a/third_party/grpc/src/cpp/ext/filters/census/views.cc b/third_party/grpc/src/cpp/ext/filters/census/views.cc new file mode 100644 index 00000000000000..2c0c5f72950f04 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/filters/census/views.cc @@ -0,0 +1,491 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/ext/filters/census/grpc_plugin.h" + +#include "absl/time/time.h" +#include "opencensus/stats/internal/aggregation_window.h" +#include "opencensus/stats/internal/set_aggregation_window.h" +#include "opencensus/stats/stats.h" + +namespace grpc { + +using ::opencensus::stats::Aggregation; +using ::opencensus::stats::AggregationWindow; +using ::opencensus::stats::BucketBoundaries; +using ::opencensus::stats::ViewDescriptor; + +// These measure definitions should be kept in sync across opencensus +// implementations. + +namespace { + +Aggregation BytesDistributionAggregation() { + return Aggregation::Distribution(BucketBoundaries::Explicit( + {0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, + 67108864, 268435456, 1073741824, 4294967296})); +} + +Aggregation MillisDistributionAggregation() { + return Aggregation::Distribution(BucketBoundaries::Explicit( + {0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1, 2, 3, 4, + 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, + 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, + 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000})); +} + +Aggregation CountDistributionAggregation() { + return Aggregation::Distribution(BucketBoundaries::Exponential(17, 1.0, 2.0)); +} + +ViewDescriptor MinuteDescriptor() { + auto descriptor = ViewDescriptor(); + SetAggregationWindow(AggregationWindow::Interval(absl::Minutes(1)), + &descriptor); + return descriptor; +} + +ViewDescriptor HourDescriptor() { + auto descriptor = ViewDescriptor(); + SetAggregationWindow(AggregationWindow::Interval(absl::Hours(1)), + &descriptor); + return descriptor; +} + +} // namespace + +void RegisterOpenCensusViewsForExport() { + ClientSentMessagesPerRpcCumulative().RegisterForExport(); + ClientSentBytesPerRpcCumulative().RegisterForExport(); + ClientReceivedMessagesPerRpcCumulative().RegisterForExport(); + ClientReceivedBytesPerRpcCumulative().RegisterForExport(); + ClientRoundtripLatencyCumulative().RegisterForExport(); + ClientServerLatencyCumulative().RegisterForExport(); + + ServerSentMessagesPerRpcCumulative().RegisterForExport(); + ServerSentBytesPerRpcCumulative().RegisterForExport(); + ServerReceivedMessagesPerRpcCumulative().RegisterForExport(); + ServerReceivedBytesPerRpcCumulative().RegisterForExport(); + ServerServerLatencyCumulative().RegisterForExport(); +} + +// client cumulative +const ViewDescriptor& ClientSentBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/sent_bytes_per_rpc/cumulative") + .set_measure(kRpcClientSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/received_bytes_per_rpc/cumulative") + .set_measure(kRpcClientReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientRoundtripLatencyCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/roundtrip_latency/cumulative") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientServerLatencyCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/server_latency/cumulative") + .set_measure(kRpcClientServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientCompletedRpcsCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/completed_rpcs/cumulative") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ClientMethodTagKey()) + .add_column(ClientStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientSentMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/received_messages_per_rpc/cumulative") + .set_measure(kRpcClientSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/client/sent_messages_per_rpc/cumulative") + .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +// server cumulative +const ViewDescriptor& ServerSentBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/received_bytes_per_rpc/cumulative") + .set_measure(kRpcServerSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedBytesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/sent_bytes_per_rpc/cumulative") + .set_measure(kRpcServerReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerServerLatencyCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/elapsed_time/cumulative") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerCompletedRpcsCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/completed_rpcs/cumulative") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ServerMethodTagKey()) + .add_column(ServerStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerSentMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/received_messages_per_rpc/cumulative") + .set_measure(kRpcServerSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedMessagesPerRpcCumulative() { + const static ViewDescriptor descriptor = + ViewDescriptor() + .set_name("grpc.io/server/sent_messages_per_rpc/cumulative") + .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +// client minute +const ViewDescriptor& ClientSentBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/sent_bytes_per_rpc/minute") + .set_measure(kRpcClientSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/received_bytes_per_rpc/minute") + .set_measure(kRpcClientReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientRoundtripLatencyMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/roundtrip_latency/minute") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientServerLatencyMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/server_latency/minute") + .set_measure(kRpcClientServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientCompletedRpcsMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/completed_rpcs/minute") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ClientMethodTagKey()) + .add_column(ClientStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientSentMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/sent_messages_per_rpc/minute") + .set_measure(kRpcClientSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/client/received_messages_per_rpc/minute") + .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +// server minute +const ViewDescriptor& ServerSentBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/sent_bytes_per_rpc/minute") + .set_measure(kRpcServerSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedBytesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/received_bytes_per_rpc/minute") + .set_measure(kRpcServerReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerServerLatencyMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/server_latency/minute") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerCompletedRpcsMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/completed_rpcs/minute") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ServerMethodTagKey()) + .add_column(ServerStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerSentMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/sent_messages_per_rpc/minute") + .set_measure(kRpcServerSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedMessagesPerRpcMinute() { + const static ViewDescriptor descriptor = + MinuteDescriptor() + .set_name("grpc.io/server/received_messages_per_rpc/minute") + .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +// client hour +const ViewDescriptor& ClientSentBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/sent_bytes_per_rpc/hour") + .set_measure(kRpcClientSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/received_bytes_per_rpc/hour") + .set_measure(kRpcClientReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientRoundtripLatencyHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/roundtrip_latency/hour") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientServerLatencyHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/server_latency/hour") + .set_measure(kRpcClientServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientCompletedRpcsHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/completed_rpcs/hour") + .set_measure(kRpcClientRoundtripLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ClientMethodTagKey()) + .add_column(ClientStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientSentMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/sent_messages_per_rpc/hour") + .set_measure(kRpcClientSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ClientReceivedMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/client/received_messages_per_rpc/hour") + .set_measure(kRpcClientReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ClientMethodTagKey()); + return descriptor; +} + +// server hour +const ViewDescriptor& ServerSentBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/sent_bytes_per_rpc/hour") + .set_measure(kRpcServerSentBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedBytesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/received_bytes_per_rpc/hour") + .set_measure(kRpcServerReceivedBytesPerRpcMeasureName) + .set_aggregation(BytesDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerServerLatencyHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/server_latency/hour") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(MillisDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerCompletedRpcsHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/completed_rpcs/hour") + .set_measure(kRpcServerServerLatencyMeasureName) + .set_aggregation(Aggregation::Count()) + .add_column(ServerMethodTagKey()) + .add_column(ServerStatusTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerSentMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/sent_messages_per_rpc/hour") + .set_measure(kRpcServerSentMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +const ViewDescriptor& ServerReceivedMessagesPerRpcHour() { + const static ViewDescriptor descriptor = + HourDescriptor() + .set_name("grpc.io/server/received_messages_per_rpc/hour") + .set_measure(kRpcServerReceivedMessagesPerRpcMeasureName) + .set_aggregation(CountDistributionAggregation()) + .add_column(ServerMethodTagKey()); + return descriptor; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/proto_server_reflection.cc b/third_party/grpc/src/cpp/ext/proto_server_reflection.cc new file mode 100644 index 00000000000000..f26f007c8e1901 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/proto_server_reflection.cc @@ -0,0 +1,212 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include + +#include "src/cpp/ext/proto_server_reflection.h" + +using grpc::Status; +using grpc::StatusCode; +using grpc::reflection::v1alpha::ErrorResponse; +using grpc::reflection::v1alpha::ExtensionNumberResponse; +using grpc::reflection::v1alpha::ExtensionRequest; +using grpc::reflection::v1alpha::FileDescriptorResponse; +using grpc::reflection::v1alpha::ListServiceResponse; +using grpc::reflection::v1alpha::ServerReflectionRequest; +using grpc::reflection::v1alpha::ServerReflectionResponse; +using grpc::reflection::v1alpha::ServiceResponse; + +namespace grpc { + +ProtoServerReflection::ProtoServerReflection() + : descriptor_pool_(protobuf::DescriptorPool::generated_pool()) {} + +void ProtoServerReflection::SetServiceList( + const std::vector* services) { + services_ = services; +} + +Status ProtoServerReflection::ServerReflectionInfo( + ServerContext* context, + ServerReaderWriter* + stream) { + ServerReflectionRequest request; + ServerReflectionResponse response; + Status status; + while (stream->Read(&request)) { + switch (request.message_request_case()) { + case ServerReflectionRequest::MessageRequestCase::kFileByFilename: + status = GetFileByName(context, request.file_by_filename(), &response); + break; + case ServerReflectionRequest::MessageRequestCase::kFileContainingSymbol: + status = GetFileContainingSymbol( + context, request.file_containing_symbol(), &response); + break; + case ServerReflectionRequest::MessageRequestCase:: + kFileContainingExtension: + status = GetFileContainingExtension( + context, &request.file_containing_extension(), &response); + break; + case ServerReflectionRequest::MessageRequestCase:: + kAllExtensionNumbersOfType: + status = GetAllExtensionNumbers( + context, request.all_extension_numbers_of_type(), + response.mutable_all_extension_numbers_response()); + break; + case ServerReflectionRequest::MessageRequestCase::kListServices: + status = + ListService(context, response.mutable_list_services_response()); + break; + default: + status = Status(StatusCode::UNIMPLEMENTED, ""); + } + + if (!status.ok()) { + FillErrorResponse(status, response.mutable_error_response()); + } + response.set_valid_host(request.host()); + response.set_allocated_original_request( + new ServerReflectionRequest(request)); + stream->Write(response); + } + + return Status::OK; +} + +void ProtoServerReflection::FillErrorResponse(const Status& status, + ErrorResponse* error_response) { + error_response->set_error_code(status.error_code()); + error_response->set_error_message(status.error_message()); +} + +Status ProtoServerReflection::ListService(ServerContext* context, + ListServiceResponse* response) { + if (services_ == nullptr) { + return Status(StatusCode::NOT_FOUND, "Services not found."); + } + for (auto it = services_->begin(); it != services_->end(); ++it) { + ServiceResponse* service_response = response->add_service(); + service_response->set_name(*it); + } + return Status::OK; +} + +Status ProtoServerReflection::GetFileByName( + ServerContext* context, const grpc::string& filename, + ServerReflectionResponse* response) { + if (descriptor_pool_ == nullptr) { + return Status::CANCELLED; + } + + const protobuf::FileDescriptor* file_desc = + descriptor_pool_->FindFileByName(filename); + if (file_desc == nullptr) { + return Status(StatusCode::NOT_FOUND, "File not found."); + } + std::unordered_set seen_files; + FillFileDescriptorResponse(file_desc, response, &seen_files); + return Status::OK; +} + +Status ProtoServerReflection::GetFileContainingSymbol( + ServerContext* context, const grpc::string& symbol, + ServerReflectionResponse* response) { + if (descriptor_pool_ == nullptr) { + return Status::CANCELLED; + } + + const protobuf::FileDescriptor* file_desc = + descriptor_pool_->FindFileContainingSymbol(symbol); + if (file_desc == nullptr) { + return Status(StatusCode::NOT_FOUND, "Symbol not found."); + } + std::unordered_set seen_files; + FillFileDescriptorResponse(file_desc, response, &seen_files); + return Status::OK; +} + +Status ProtoServerReflection::GetFileContainingExtension( + ServerContext* context, const ExtensionRequest* request, + ServerReflectionResponse* response) { + if (descriptor_pool_ == nullptr) { + return Status::CANCELLED; + } + + const protobuf::Descriptor* desc = + descriptor_pool_->FindMessageTypeByName(request->containing_type()); + if (desc == nullptr) { + return Status(StatusCode::NOT_FOUND, "Type not found."); + } + + const protobuf::FieldDescriptor* field_desc = + descriptor_pool_->FindExtensionByNumber(desc, + request->extension_number()); + if (field_desc == nullptr) { + return Status(StatusCode::NOT_FOUND, "Extension not found."); + } + std::unordered_set seen_files; + FillFileDescriptorResponse(field_desc->file(), response, &seen_files); + return Status::OK; +} + +Status ProtoServerReflection::GetAllExtensionNumbers( + ServerContext* context, const grpc::string& type, + ExtensionNumberResponse* response) { + if (descriptor_pool_ == nullptr) { + return Status::CANCELLED; + } + + const protobuf::Descriptor* desc = + descriptor_pool_->FindMessageTypeByName(type); + if (desc == nullptr) { + return Status(StatusCode::NOT_FOUND, "Type not found."); + } + + std::vector extensions; + descriptor_pool_->FindAllExtensions(desc, &extensions); + for (auto it = extensions.begin(); it != extensions.end(); it++) { + response->add_extension_number((*it)->number()); + } + response->set_base_type_name(type); + return Status::OK; +} + +void ProtoServerReflection::FillFileDescriptorResponse( + const protobuf::FileDescriptor* file_desc, + ServerReflectionResponse* response, + std::unordered_set* seen_files) { + if (seen_files->find(file_desc->name()) != seen_files->end()) { + return; + } + seen_files->insert(file_desc->name()); + + protobuf::FileDescriptorProto file_desc_proto; + grpc::string data; + file_desc->CopyTo(&file_desc_proto); + file_desc_proto.SerializeToString(&data); + response->mutable_file_descriptor_response()->add_file_descriptor_proto(data); + + for (int i = 0; i < file_desc->dependency_count(); ++i) { + FillFileDescriptorResponse(file_desc->dependency(i), response, seen_files); + } +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/ext/proto_server_reflection.h b/third_party/grpc/src/cpp/ext/proto_server_reflection.h new file mode 100644 index 00000000000000..bf40c3c1807170 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/proto_server_reflection.h @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_EXT_PROTO_SERVER_REFLECTION_H +#define GRPC_INTERNAL_CPP_EXT_PROTO_SERVER_REFLECTION_H + +#include +#include + +#include +#include "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h" + +namespace grpc { + +class ProtoServerReflection final + : public reflection::v1alpha::ServerReflection::Service { + public: + ProtoServerReflection(); + + // Add the full names of registered services + void SetServiceList(const std::vector* services); + + // implementation of ServerReflectionInfo(stream ServerReflectionRequest) rpc + // in ServerReflection service + Status ServerReflectionInfo( + ServerContext* context, + ServerReaderWriter* stream) + override; + + private: + Status ListService(ServerContext* context, + reflection::v1alpha::ListServiceResponse* response); + + Status GetFileByName(ServerContext* context, const grpc::string& file_name, + reflection::v1alpha::ServerReflectionResponse* response); + + Status GetFileContainingSymbol( + ServerContext* context, const grpc::string& symbol, + reflection::v1alpha::ServerReflectionResponse* response); + + Status GetFileContainingExtension( + ServerContext* context, + const reflection::v1alpha::ExtensionRequest* request, + reflection::v1alpha::ServerReflectionResponse* response); + + Status GetAllExtensionNumbers( + ServerContext* context, const grpc::string& type, + reflection::v1alpha::ExtensionNumberResponse* response); + + void FillFileDescriptorResponse( + const protobuf::FileDescriptor* file_desc, + reflection::v1alpha::ServerReflectionResponse* response, + std::unordered_set* seen_files); + + void FillErrorResponse(const Status& status, + reflection::v1alpha::ErrorResponse* error_response); + + const protobuf::DescriptorPool* descriptor_pool_; + const std::vector* services_; +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_EXT_PROTO_SERVER_REFLECTION_H diff --git a/third_party/grpc/src/cpp/ext/proto_server_reflection_plugin.cc b/third_party/grpc/src/cpp/ext/proto_server_reflection_plugin.cc new file mode 100644 index 00000000000000..ee3ac3fee88558 --- /dev/null +++ b/third_party/grpc/src/cpp/ext/proto_server_reflection_plugin.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "src/cpp/ext/proto_server_reflection.h" + +namespace grpc { +namespace reflection { + +ProtoServerReflectionPlugin::ProtoServerReflectionPlugin() + : reflection_service_(new grpc::ProtoServerReflection()) {} + +grpc::string ProtoServerReflectionPlugin::name() { + return "proto_server_reflection"; +} + +void ProtoServerReflectionPlugin::InitServer(grpc::ServerInitializer* si) { + si->RegisterService(reflection_service_); +} + +void ProtoServerReflectionPlugin::Finish(grpc::ServerInitializer* si) { + reflection_service_->SetServiceList(si->GetServiceList()); +} + +void ProtoServerReflectionPlugin::ChangeArguments(const grpc::string& name, + void* value) {} + +bool ProtoServerReflectionPlugin::has_sync_methods() const { + if (reflection_service_) { + return reflection_service_->has_synchronous_methods(); + } + return false; +} + +bool ProtoServerReflectionPlugin::has_async_methods() const { + if (reflection_service_) { + return reflection_service_->has_async_methods(); + } + return false; +} + +static std::unique_ptr< ::grpc::ServerBuilderPlugin> CreateProtoReflection() { + return std::unique_ptr< ::grpc::ServerBuilderPlugin>( + new ProtoServerReflectionPlugin()); +} + +void InitProtoReflectionServerBuilderPlugin() { + static bool already_here = false; + if (already_here) return; + already_here = true; + ::grpc::ServerBuilder::InternalAddPluginFactory(&CreateProtoReflection); +} + +// Force InitProtoReflectionServerBuilderPlugin() to be called at static +// initialization time. +struct StaticProtoReflectionPluginInitializer { + StaticProtoReflectionPluginInitializer() { + InitProtoReflectionServerBuilderPlugin(); + } +} static_proto_reflection_plugin_initializer; + +} // namespace reflection +} // namespace grpc diff --git a/third_party/grpc/src/cpp/proto/proto_utils.cc b/third_party/grpc/src/cpp/proto/proto_utils.cc deleted file mode 100644 index 79e7bf1801d782..00000000000000 --- a/third_party/grpc/src/cpp/proto/proto_utils.cc +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 "src/core/profiling/timers.h" - -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_); - // On win x64, int is only 32bit - GPR_ASSERT(GPR_SLICE_LENGTH(slice_) <= INT_MAX); - byte_count_ += * size = (int)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_; - int64_t 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_; - GPR_ASSERT(backup_count_ <= INT_MAX); - *size = (int)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_); - // On win x64, int is only 32bit - GPR_ASSERT(GPR_SLICE_LENGTH(slice_) <= INT_MAX); - byte_count_ += * size = (int)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: - int64_t byte_count_; - int64_t backup_count_; - grpc_byte_buffer_reader reader_; - gpr_slice slice_; -}; - -namespace grpc { - -Status SerializeProto(const grpc::protobuf::Message& msg, - grpc_byte_buffer** bp) { - GPR_TIMER_SCOPE("SerializeProto", 0); - int byte_size = msg.ByteSize(); - if (byte_size <= kMaxBufferLength) { - gpr_slice slice = gpr_slice_malloc(byte_size); - GPR_ASSERT(GPR_SLICE_END_PTR(slice) == - msg.SerializeWithCachedSizesToArray(GPR_SLICE_START_PTR(slice))); - *bp = grpc_raw_byte_buffer_create(&slice, 1); - gpr_slice_unref(slice); - return Status::OK; - } else { - 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) { - GPR_TIMER_SCOPE("DeserializeProto", 0); - 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/third_party/grpc/src/cpp/server/async_generic_service.cc b/third_party/grpc/src/cpp/server/async_generic_service.cc index 6b9ea532b6d597..306137682953b3 100644 --- a/third_party/grpc/src/cpp/server/async_generic_service.cc +++ b/third_party/grpc/src/cpp/server/async_generic_service.cc @@ -1,39 +1,24 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include -#include +#include namespace grpc { diff --git a/third_party/grpc/src/cpp/server/channel_argument_option.cc b/third_party/grpc/src/cpp/server/channel_argument_option.cc new file mode 100644 index 00000000000000..4d6be9011c1832 --- /dev/null +++ b/third_party/grpc/src/cpp/server/channel_argument_option.cc @@ -0,0 +1,63 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +namespace grpc { + +std::unique_ptr MakeChannelArgumentOption( + const grpc::string& name, const grpc::string& value) { + class StringOption final : public ServerBuilderOption { + public: + StringOption(const grpc::string& name, const grpc::string& value) + : name_(name), value_(value) {} + + virtual void UpdateArguments(ChannelArguments* args) override { + args->SetString(name_, value_); + } + virtual void UpdatePlugins( + std::vector>* plugins) override {} + + private: + const grpc::string name_; + const grpc::string value_; + }; + return std::unique_ptr(new StringOption(name, value)); +} + +std::unique_ptr MakeChannelArgumentOption( + const grpc::string& name, int value) { + class IntOption final : public ServerBuilderOption { + public: + IntOption(const grpc::string& name, int value) + : name_(name), value_(value) {} + + virtual void UpdateArguments(ChannelArguments* args) override { + args->SetInt(name_, value_); + } + virtual void UpdatePlugins( + std::vector>* plugins) override {} + + private: + const grpc::string name_; + const int value_; + }; + return std::unique_ptr(new IntOption(name, value)); +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/channelz/channelz_service.cc b/third_party/grpc/src/cpp/server/channelz/channelz_service.cc new file mode 100644 index 00000000000000..c44a9ac0de6b91 --- /dev/null +++ b/third_party/grpc/src/cpp/server/channelz/channelz_service.cc @@ -0,0 +1,146 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/server/channelz/channelz_service.h" + +#include +#include + +namespace grpc { + +Status ChannelzService::GetTopChannels( + ServerContext* unused, const channelz::v1::GetTopChannelsRequest* request, + channelz::v1::GetTopChannelsResponse* response) { + char* json_str = grpc_channelz_get_top_channels(request->start_channel_id()); + if (json_str == nullptr) { + return Status(StatusCode::INTERNAL, + "grpc_channelz_get_top_channels returned null"); + } + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (!s.ok()) { + return Status(StatusCode::INTERNAL, s.ToString()); + } + return Status::OK; +} + +Status ChannelzService::GetServers( + ServerContext* unused, const channelz::v1::GetServersRequest* request, + channelz::v1::GetServersResponse* response) { + char* json_str = grpc_channelz_get_servers(request->start_server_id()); + if (json_str == nullptr) { + return Status(StatusCode::INTERNAL, + "grpc_channelz_get_servers returned null"); + } + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (!s.ok()) { + return Status(StatusCode::INTERNAL, s.ToString()); + } + return Status::OK; +} + +Status ChannelzService::GetServer(ServerContext* unused, + const channelz::v1::GetServerRequest* request, + channelz::v1::GetServerResponse* response) { + char* json_str = grpc_channelz_get_server(request->server_id()); + if (json_str == nullptr) { + return Status(StatusCode::INTERNAL, + "grpc_channelz_get_server returned null"); + } + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (!s.ok()) { + return Status(StatusCode::INTERNAL, s.ToString()); + } + return Status::OK; +} + +Status ChannelzService::GetServerSockets( + ServerContext* unused, const channelz::v1::GetServerSocketsRequest* request, + channelz::v1::GetServerSocketsResponse* response) { + char* json_str = grpc_channelz_get_server_sockets( + request->server_id(), request->start_socket_id(), request->max_results()); + if (json_str == nullptr) { + return Status(StatusCode::INTERNAL, + "grpc_channelz_get_server_sockets returned null"); + } + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (!s.ok()) { + return Status(StatusCode::INTERNAL, s.ToString()); + } + return Status::OK; +} + +Status ChannelzService::GetChannel( + ServerContext* unused, const channelz::v1::GetChannelRequest* request, + channelz::v1::GetChannelResponse* response) { + char* json_str = grpc_channelz_get_channel(request->channel_id()); + if (json_str == nullptr) { + return Status(StatusCode::NOT_FOUND, "No object found for that ChannelId"); + } + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (!s.ok()) { + return Status(StatusCode::INTERNAL, s.ToString()); + } + return Status::OK; +} + +Status ChannelzService::GetSubchannel( + ServerContext* unused, const channelz::v1::GetSubchannelRequest* request, + channelz::v1::GetSubchannelResponse* response) { + char* json_str = grpc_channelz_get_subchannel(request->subchannel_id()); + if (json_str == nullptr) { + return Status(StatusCode::NOT_FOUND, + "No object found for that SubchannelId"); + } + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (!s.ok()) { + return Status(StatusCode::INTERNAL, s.ToString()); + } + return Status::OK; +} + +Status ChannelzService::GetSocket(ServerContext* unused, + const channelz::v1::GetSocketRequest* request, + channelz::v1::GetSocketResponse* response) { + char* json_str = grpc_channelz_get_socket(request->socket_id()); + if (json_str == nullptr) { + return Status(StatusCode::NOT_FOUND, "No object found for that SocketId"); + } + grpc::protobuf::util::Status s = + grpc::protobuf::json::JsonStringToMessage(json_str, response); + gpr_free(json_str); + if (!s.ok()) { + return Status(StatusCode::INTERNAL, s.ToString()); + } + return Status::OK; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/channelz/channelz_service.h b/third_party/grpc/src/cpp/server/channelz/channelz_service.h new file mode 100644 index 00000000000000..b4a66ba1c66235 --- /dev/null +++ b/third_party/grpc/src/cpp/server/channelz/channelz_service.h @@ -0,0 +1,64 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_SERVER_CHANNELZ_SERVICE_H +#define GRPC_INTERNAL_CPP_SERVER_CHANNELZ_SERVICE_H + +#include + +#include +#include "src/proto/grpc/channelz/channelz.grpc.pb.h" + +namespace grpc { + +class ChannelzService final : public channelz::v1::Channelz::Service { + private: + // implementation of GetTopChannels rpc + Status GetTopChannels( + ServerContext* unused, const channelz::v1::GetTopChannelsRequest* request, + channelz::v1::GetTopChannelsResponse* response) override; + // implementation of GetServers rpc + Status GetServers(ServerContext* unused, + const channelz::v1::GetServersRequest* request, + channelz::v1::GetServersResponse* response) override; + // implementation of GetServer rpc + Status GetServer(ServerContext* unused, + const channelz::v1::GetServerRequest* request, + channelz::v1::GetServerResponse* response) override; + // implementation of GetServerSockets rpc + Status GetServerSockets( + ServerContext* unused, + const channelz::v1::GetServerSocketsRequest* request, + channelz::v1::GetServerSocketsResponse* response) override; + // implementation of GetChannel rpc + Status GetChannel(ServerContext* unused, + const channelz::v1::GetChannelRequest* request, + channelz::v1::GetChannelResponse* response) override; + // implementation of GetSubchannel rpc + Status GetSubchannel(ServerContext* unused, + const channelz::v1::GetSubchannelRequest* request, + channelz::v1::GetSubchannelResponse* response) override; + // implementation of GetSocket rpc + Status GetSocket(ServerContext* unused, + const channelz::v1::GetSocketRequest* request, + channelz::v1::GetSocketResponse* response) override; +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_SERVER_CHANNELZ_SERVICE_H diff --git a/third_party/grpc/src/cpp/server/channelz/channelz_service_plugin.cc b/third_party/grpc/src/cpp/server/channelz/channelz_service_plugin.cc new file mode 100644 index 00000000000000..b93e5b551e1273 --- /dev/null +++ b/third_party/grpc/src/cpp/server/channelz/channelz_service_plugin.cc @@ -0,0 +1,79 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include + +#include "src/cpp/server/channelz/channelz_service.h" + +namespace grpc { +namespace channelz { +namespace experimental { + +class ChannelzServicePlugin : public ::grpc::ServerBuilderPlugin { + public: + ChannelzServicePlugin() : channelz_service_(new grpc::ChannelzService()) {} + + grpc::string name() override { return "channelz_service"; } + + void InitServer(grpc::ServerInitializer* si) override { + si->RegisterService(channelz_service_); + } + + void Finish(grpc::ServerInitializer* si) override {} + + void ChangeArguments(const grpc::string& name, void* value) override {} + + bool has_sync_methods() const override { + if (channelz_service_) { + return channelz_service_->has_synchronous_methods(); + } + return false; + } + + bool has_async_methods() const override { + if (channelz_service_) { + return channelz_service_->has_async_methods(); + } + return false; + } + + private: + std::shared_ptr channelz_service_; +}; + +static std::unique_ptr< ::grpc::ServerBuilderPlugin> +CreateChannelzServicePlugin() { + return std::unique_ptr< ::grpc::ServerBuilderPlugin>( + new ChannelzServicePlugin()); +} + +void InitChannelzService() { + static bool already_here = false; + if (already_here) return; + already_here = true; + ::grpc::ServerBuilder::InternalAddPluginFactory(&CreateChannelzServicePlugin); +} + +} // namespace experimental +} // namespace channelz +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/create_default_thread_pool.cc b/third_party/grpc/src/cpp/server/create_default_thread_pool.cc index f3b07ec8ce8662..8ca3e32c2fb193 100644 --- a/third_party/grpc/src/cpp/server/create_default_thread_pool.cc +++ b/third_party/grpc/src/cpp/server/create_default_thread_pool.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -38,13 +23,22 @@ #ifndef GRPC_CUSTOM_DEFAULT_THREAD_POOL namespace grpc { +namespace { -ThreadPoolInterface* CreateDefaultThreadPool() { +ThreadPoolInterface* CreateDefaultThreadPoolImpl() { int cores = gpr_cpu_num_cores(); if (!cores) cores = 4; return new DynamicThreadPool(cores); } +CreateThreadPoolFunc g_ctp_impl = CreateDefaultThreadPoolImpl; + +} // namespace + +ThreadPoolInterface* CreateDefaultThreadPool() { return g_ctp_impl(); } + +void SetCreateThreadPool(CreateThreadPoolFunc func) { g_ctp_impl = func; } + } // namespace grpc #endif // !GRPC_CUSTOM_DEFAULT_THREAD_POOL diff --git a/third_party/grpc/src/cpp/server/dynamic_thread_pool.cc b/third_party/grpc/src/cpp/server/dynamic_thread_pool.cc index 4b226c2992d482..ef99d6459f78b7 100644 --- a/third_party/grpc/src/cpp/server/dynamic_thread_pool.cc +++ b/third_party/grpc/src/cpp/server/dynamic_thread_pool.cc @@ -1,55 +1,46 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include -#include - #include "src/cpp/server/dynamic_thread_pool.h" +#include + +#include + +#include "src/core/lib/gprpp/thd.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(); + thd_("grpcpp_dynamic_pool", + [](void* th) { + static_cast(th)->ThreadFunc(); + }, + this) { + thd_.Start(); } +DynamicThreadPool::DynamicThread::~DynamicThread() { thd_.Join(); } void DynamicThreadPool::DynamicThread::ThreadFunc() { pool_->ThreadFunc(); // Now that we have killed ourselves, we should reduce the thread count - grpc::unique_lock lock(pool_->mu_); + std::unique_lock lock(pool_->mu_); pool_->nthreads_--; // Move ourselves to dead list pool_->dead_threads_.push_back(this); @@ -62,7 +53,7 @@ void DynamicThreadPool::DynamicThread::ThreadFunc() { void DynamicThreadPool::ThreadFunc() { for (;;) { // Wait until work is available or we are shutting down. - grpc::unique_lock lock(mu_); + std::unique_lock lock(mu_); if (!shutdown_ && callbacks_.empty()) { // If there are too many threads waiting, then quit this thread if (threads_waiting_ >= reserve_threads_) { @@ -91,7 +82,7 @@ DynamicThreadPool::DynamicThreadPool(int reserve_threads) nthreads_(0), threads_waiting_(0) { for (int i = 0; i < reserve_threads_; i++) { - grpc::lock_guard lock(mu_); + std::lock_guard lock(mu_); nthreads_++; new DynamicThread(this); } @@ -104,7 +95,7 @@ void DynamicThreadPool::ReapThreads(std::list* tlist) { } DynamicThreadPool::~DynamicThreadPool() { - grpc::unique_lock lock(mu_); + std::unique_lock lock(mu_); shutdown_ = true; cv_.notify_all(); while (nthreads_ != 0) { @@ -114,7 +105,7 @@ DynamicThreadPool::~DynamicThreadPool() { } void DynamicThreadPool::Add(const std::function& callback) { - grpc::lock_guard lock(mu_); + std::lock_guard lock(mu_); // Add works to the callbacks list callbacks_.push(callback); // Increase pool size or notify as needed diff --git a/third_party/grpc/src/cpp/server/dynamic_thread_pool.h b/third_party/grpc/src/cpp/server/dynamic_thread_pool.h index 5ba7533c05f418..5df8cf2b043a8b 100644 --- a/third_party/grpc/src/cpp/server/dynamic_thread_pool.h +++ b/third_party/grpc/src/cpp/server/dynamic_thread_pool.h @@ -1,57 +1,43 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ #ifndef GRPC_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H #define GRPC_INTERNAL_CPP_DYNAMIC_THREAD_POOL_H +#include #include #include +#include #include -#include -#include -#include +#include +#include "src/core/lib/gprpp/thd.h" #include "src/cpp/server/thread_pool_interface.h" namespace grpc { -class DynamicThreadPool GRPC_FINAL : public ThreadPoolInterface { +class DynamicThreadPool final : public ThreadPoolInterface { public: explicit DynamicThreadPool(int reserve_threads); ~DynamicThreadPool(); - void Add(const std::function& callback) GRPC_OVERRIDE; + void Add(const std::function& callback) override; private: class DynamicThread { @@ -61,12 +47,12 @@ class DynamicThreadPool GRPC_FINAL : public ThreadPoolInterface { private: DynamicThreadPool* pool_; - std::unique_ptr thd_; + grpc_core::Thread thd_; void ThreadFunc(); }; - grpc::mutex mu_; - grpc::condition_variable cv_; - grpc::condition_variable shutdown_cv_; + std::mutex mu_; + std::condition_variable cv_; + std::condition_variable shutdown_cv_; bool shutdown_; std::queue> callbacks_; int reserve_threads_; diff --git a/third_party/grpc/src/cpp/server/fixed_size_thread_pool.cc b/third_party/grpc/src/cpp/server/fixed_size_thread_pool.cc deleted file mode 100644 index 2bdc44be2ea20d..00000000000000 --- a/third_party/grpc/src/cpp/server/fixed_size_thread_pool.cc +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/cpp/server/fixed_size_thread_pool.h b/third_party/grpc/src/cpp/server/fixed_size_thread_pool.h deleted file mode 100644 index 394ae5821ecb3f..00000000000000 --- a/third_party/grpc/src/cpp/server/fixed_size_thread_pool.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/cpp/server/health/default_health_check_service.cc b/third_party/grpc/src/cpp/server/health/default_health_check_service.cc new file mode 100644 index 00000000000000..44aebd2f9d91bd --- /dev/null +++ b/third_party/grpc/src/cpp/server/health/default_health_check_service.cc @@ -0,0 +1,499 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include +#include +#include + +#include "pb_decode.h" +#include "pb_encode.h" +#include "src/core/ext/filters/client_channel/health/health.pb.h" +#include "src/cpp/server/health/default_health_check_service.h" + +namespace grpc { + +// +// DefaultHealthCheckService +// + +DefaultHealthCheckService::DefaultHealthCheckService() { + services_map_[""].SetServingStatus(SERVING); +} + +void DefaultHealthCheckService::SetServingStatus( + const grpc::string& service_name, bool serving) { + std::unique_lock lock(mu_); + if (shutdown_) { + // Set to NOT_SERVING in case service_name is not in the map. + serving = false; + } + services_map_[service_name].SetServingStatus(serving ? SERVING : NOT_SERVING); +} + +void DefaultHealthCheckService::SetServingStatus(bool serving) { + const ServingStatus status = serving ? SERVING : NOT_SERVING; + std::unique_lock lock(mu_); + if (shutdown_) { + return; + } + for (auto& p : services_map_) { + ServiceData& service_data = p.second; + service_data.SetServingStatus(status); + } +} + +void DefaultHealthCheckService::Shutdown() { + std::unique_lock lock(mu_); + if (shutdown_) { + return; + } + shutdown_ = true; + for (auto& p : services_map_) { + ServiceData& service_data = p.second; + service_data.SetServingStatus(NOT_SERVING); + } +} + +DefaultHealthCheckService::ServingStatus +DefaultHealthCheckService::GetServingStatus( + const grpc::string& service_name) const { + std::lock_guard lock(mu_); + auto it = services_map_.find(service_name); + if (it == services_map_.end()) { + return NOT_FOUND; + } + const ServiceData& service_data = it->second; + return service_data.GetServingStatus(); +} + +void DefaultHealthCheckService::RegisterCallHandler( + const grpc::string& service_name, + std::shared_ptr handler) { + std::unique_lock lock(mu_); + ServiceData& service_data = services_map_[service_name]; + service_data.AddCallHandler(handler /* copies ref */); + HealthCheckServiceImpl::CallHandler* h = handler.get(); + h->SendHealth(std::move(handler), service_data.GetServingStatus()); +} + +void DefaultHealthCheckService::UnregisterCallHandler( + const grpc::string& service_name, + const std::shared_ptr& handler) { + std::unique_lock lock(mu_); + auto it = services_map_.find(service_name); + if (it == services_map_.end()) return; + ServiceData& service_data = it->second; + service_data.RemoveCallHandler(handler); + if (service_data.Unused()) { + services_map_.erase(it); + } +} + +DefaultHealthCheckService::HealthCheckServiceImpl* +DefaultHealthCheckService::GetHealthCheckService( + std::unique_ptr cq) { + GPR_ASSERT(impl_ == nullptr); + impl_.reset(new HealthCheckServiceImpl(this, std::move(cq))); + return impl_.get(); +} + +// +// DefaultHealthCheckService::ServiceData +// + +void DefaultHealthCheckService::ServiceData::SetServingStatus( + ServingStatus status) { + status_ = status; + for (auto& call_handler : call_handlers_) { + call_handler->SendHealth(call_handler /* copies ref */, status); + } +} + +void DefaultHealthCheckService::ServiceData::AddCallHandler( + std::shared_ptr handler) { + call_handlers_.insert(std::move(handler)); +} + +void DefaultHealthCheckService::ServiceData::RemoveCallHandler( + const std::shared_ptr& handler) { + call_handlers_.erase(handler); +} + +// +// DefaultHealthCheckService::HealthCheckServiceImpl +// + +namespace { +const char kHealthCheckMethodName[] = "/grpc.health.v1.Health/Check"; +const char kHealthWatchMethodName[] = "/grpc.health.v1.Health/Watch"; +} // namespace + +DefaultHealthCheckService::HealthCheckServiceImpl::HealthCheckServiceImpl( + DefaultHealthCheckService* database, + std::unique_ptr cq) + : database_(database), cq_(std::move(cq)) { + // Add Check() method. + AddMethod(new internal::RpcServiceMethod( + kHealthCheckMethodName, internal::RpcMethod::NORMAL_RPC, nullptr)); + // Add Watch() method. + AddMethod(new internal::RpcServiceMethod( + kHealthWatchMethodName, internal::RpcMethod::SERVER_STREAMING, nullptr)); + // Create serving thread. + thread_ = std::unique_ptr<::grpc_core::Thread>( + new ::grpc_core::Thread("grpc_health_check_service", Serve, this)); +} + +DefaultHealthCheckService::HealthCheckServiceImpl::~HealthCheckServiceImpl() { + // We will reach here after the server starts shutting down. + shutdown_ = true; + { + std::unique_lock lock(cq_shutdown_mu_); + cq_->Shutdown(); + } + thread_->Join(); +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::StartServingThread() { + // Request the calls we're interested in. + // We do this before starting the serving thread, so that we know it's + // done before server startup is complete. + CheckCallHandler::CreateAndStart(cq_.get(), database_, this); + WatchCallHandler::CreateAndStart(cq_.get(), database_, this); + // Start serving thread. + thread_->Start(); +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::Serve(void* arg) { + HealthCheckServiceImpl* service = + reinterpret_cast(arg); + void* tag; + bool ok; + while (true) { + if (!service->cq_->Next(&tag, &ok)) { + // The completion queue is shutting down. + GPR_ASSERT(service->shutdown_); + break; + } + auto* next_step = static_cast(tag); + next_step->Run(ok); + } +} + +bool DefaultHealthCheckService::HealthCheckServiceImpl::DecodeRequest( + const ByteBuffer& request, grpc::string* service_name) { + std::vector slices; + if (!request.Dump(&slices).ok()) return false; + uint8_t* request_bytes = nullptr; + size_t request_size = 0; + grpc_health_v1_HealthCheckRequest request_struct; + request_struct.has_service = false; + if (slices.size() == 1) { + request_bytes = const_cast(slices[0].begin()); + request_size = slices[0].size(); + } else if (slices.size() > 1) { + request_bytes = static_cast(gpr_malloc(request.Length())); + uint8_t* copy_to = request_bytes; + for (size_t i = 0; i < slices.size(); i++) { + memcpy(copy_to, slices[i].begin(), slices[i].size()); + copy_to += slices[i].size(); + } + } + pb_istream_t istream = pb_istream_from_buffer(request_bytes, request_size); + bool decode_status = pb_decode( + &istream, grpc_health_v1_HealthCheckRequest_fields, &request_struct); + if (slices.size() > 1) { + gpr_free(request_bytes); + } + if (!decode_status) return false; + *service_name = request_struct.has_service ? request_struct.service : ""; + return true; +} + +bool DefaultHealthCheckService::HealthCheckServiceImpl::EncodeResponse( + ServingStatus status, ByteBuffer* response) { + grpc_health_v1_HealthCheckResponse response_struct; + response_struct.has_status = true; + response_struct.status = + status == NOT_FOUND + ? grpc_health_v1_HealthCheckResponse_ServingStatus_SERVICE_UNKNOWN + : status == SERVING + ? grpc_health_v1_HealthCheckResponse_ServingStatus_SERVING + : grpc_health_v1_HealthCheckResponse_ServingStatus_NOT_SERVING; + pb_ostream_t ostream; + memset(&ostream, 0, sizeof(ostream)); + pb_encode(&ostream, grpc_health_v1_HealthCheckResponse_fields, + &response_struct); + grpc_slice response_slice = grpc_slice_malloc(ostream.bytes_written); + ostream = pb_ostream_from_buffer(GRPC_SLICE_START_PTR(response_slice), + GRPC_SLICE_LENGTH(response_slice)); + bool encode_status = pb_encode( + &ostream, grpc_health_v1_HealthCheckResponse_fields, &response_struct); + if (!encode_status) return false; + Slice encoded_response(response_slice, Slice::STEAL_REF); + ByteBuffer response_buffer(&encoded_response, 1); + response->Swap(&response_buffer); + return true; +} + +// +// DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler +// + +void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: + CreateAndStart(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service) { + std::shared_ptr self = + std::make_shared(cq, database, service); + CheckCallHandler* handler = static_cast(self.get()); + { + std::unique_lock lock(service->cq_shutdown_mu_); + if (service->shutdown_) return; + // Request a Check() call. + handler->next_ = + CallableTag(std::bind(&CheckCallHandler::OnCallReceived, handler, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + service->RequestAsyncUnary(0, &handler->ctx_, &handler->request_, + &handler->writer_, cq, cq, &handler->next_); + } +} + +DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: + CheckCallHandler(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service) + : cq_(cq), database_(database), service_(service), writer_(&ctx_) {} + +void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: + OnCallReceived(std::shared_ptr self, bool ok) { + if (!ok) { + // The value of ok being false means that the server is shutting down. + return; + } + // Spawn a new handler instance to serve the next new client. Every handler + // instance will deallocate itself when it's done. + CreateAndStart(cq_, database_, service_); + // Process request. + gpr_log(GPR_DEBUG, "[HCS %p] Health check started for handler %p", service_, + this); + grpc::string service_name; + grpc::Status status = Status::OK; + ByteBuffer response; + if (!service_->DecodeRequest(request_, &service_name)) { + status = Status(StatusCode::INVALID_ARGUMENT, "could not parse request"); + } else { + ServingStatus serving_status = database_->GetServingStatus(service_name); + if (serving_status == NOT_FOUND) { + status = Status(StatusCode::NOT_FOUND, "service name unknown"); + } else if (!service_->EncodeResponse(serving_status, &response)) { + status = Status(StatusCode::INTERNAL, "could not encode response"); + } + } + // Send response. + { + std::unique_lock lock(service_->cq_shutdown_mu_); + if (!service_->shutdown_) { + next_ = + CallableTag(std::bind(&CheckCallHandler::OnFinishDone, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + if (status.ok()) { + writer_.Finish(response, status, &next_); + } else { + writer_.FinishWithError(status, &next_); + } + } + } +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::CheckCallHandler:: + OnFinishDone(std::shared_ptr self, bool ok) { + if (ok) { + gpr_log(GPR_DEBUG, "[HCS %p] Health check call finished for handler %p", + service_, this); + } + self.reset(); // To appease clang-tidy. +} + +// +// DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler +// + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + CreateAndStart(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service) { + std::shared_ptr self = + std::make_shared(cq, database, service); + WatchCallHandler* handler = static_cast(self.get()); + { + std::unique_lock lock(service->cq_shutdown_mu_); + if (service->shutdown_) return; + // Request AsyncNotifyWhenDone(). + handler->on_done_notified_ = + CallableTag(std::bind(&WatchCallHandler::OnDoneNotified, handler, + std::placeholders::_1, std::placeholders::_2), + self /* copies ref */); + handler->ctx_.AsyncNotifyWhenDone(&handler->on_done_notified_); + // Request a Watch() call. + handler->next_ = + CallableTag(std::bind(&WatchCallHandler::OnCallReceived, handler, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + service->RequestAsyncServerStreaming(1, &handler->ctx_, &handler->request_, + &handler->stream_, cq, cq, + &handler->next_); + } +} + +DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + WatchCallHandler(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service) + : cq_(cq), database_(database), service_(service), stream_(&ctx_) {} + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + OnCallReceived(std::shared_ptr self, bool ok) { + if (!ok) { + // Server shutting down. + // + // AsyncNotifyWhenDone() needs to be called before the call starts, but the + // tag will not pop out if the call never starts ( + // https://github.com/grpc/grpc/issues/10136). So we need to manually + // release the ownership of the handler in this case. + GPR_ASSERT(on_done_notified_.ReleaseHandler() != nullptr); + return; + } + // Spawn a new handler instance to serve the next new client. Every handler + // instance will deallocate itself when it's done. + CreateAndStart(cq_, database_, service_); + // Parse request. + if (!service_->DecodeRequest(request_, &service_name_)) { + SendFinish(std::move(self), + Status(StatusCode::INVALID_ARGUMENT, "could not parse request")); + return; + } + // Register the call for updates to the service. + gpr_log(GPR_DEBUG, + "[HCS %p] Health watch started for service \"%s\" (handler: %p)", + service_, service_name_.c_str(), this); + database_->RegisterCallHandler(service_name_, std::move(self)); +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + SendHealth(std::shared_ptr self, ServingStatus status) { + std::unique_lock lock(send_mu_); + // If there's already a send in flight, cache the new status, and + // we'll start a new send for it when the one in flight completes. + if (send_in_flight_) { + pending_status_ = status; + return; + } + // Start a send. + SendHealthLocked(std::move(self), status); +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + SendHealthLocked(std::shared_ptr self, ServingStatus status) { + send_in_flight_ = true; + // Construct response. + ByteBuffer response; + bool success = service_->EncodeResponse(status, &response); + // Grab shutdown lock and send response. + std::unique_lock cq_lock(service_->cq_shutdown_mu_); + if (service_->shutdown_) { + SendFinishLocked(std::move(self), Status::CANCELLED); + return; + } + if (!success) { + SendFinishLocked(std::move(self), + Status(StatusCode::INTERNAL, "could not encode response")); + return; + } + next_ = CallableTag(std::bind(&WatchCallHandler::OnSendHealthDone, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + stream_.Write(response, &next_); +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + OnSendHealthDone(std::shared_ptr self, bool ok) { + if (!ok) { + SendFinish(std::move(self), Status::CANCELLED); + return; + } + std::unique_lock lock(send_mu_); + send_in_flight_ = false; + // If we got a new status since we started the last send, start a + // new send for it. + if (pending_status_ != NOT_FOUND) { + auto status = pending_status_; + pending_status_ = NOT_FOUND; + SendHealthLocked(std::move(self), status); + } +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + SendFinish(std::shared_ptr self, const Status& status) { + if (finish_called_) return; + std::unique_lock cq_lock(service_->cq_shutdown_mu_); + if (service_->shutdown_) return; + SendFinishLocked(std::move(self), status); +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + SendFinishLocked(std::shared_ptr self, const Status& status) { + on_finish_done_ = + CallableTag(std::bind(&WatchCallHandler::OnFinishDone, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + stream_.Finish(status, &on_finish_done_); + finish_called_ = true; +} + +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + OnFinishDone(std::shared_ptr self, bool ok) { + if (ok) { + gpr_log(GPR_DEBUG, + "[HCS %p] Health watch call finished (service_name: \"%s\", " + "handler: %p).", + service_, service_name_.c_str(), this); + } + self.reset(); // To appease clang-tidy. +} + +// TODO(roth): This method currently assumes that there will be only one +// thread polling the cq and invoking the corresponding callbacks. If +// that changes, we will need to add synchronization here. +void DefaultHealthCheckService::HealthCheckServiceImpl::WatchCallHandler:: + OnDoneNotified(std::shared_ptr self, bool ok) { + GPR_ASSERT(ok); + gpr_log(GPR_DEBUG, + "[HCS %p] Health watch call is notified done (handler: %p, " + "is_cancelled: %d).", + service_, this, static_cast(ctx_.IsCancelled())); + database_->UnregisterCallHandler(service_name_, self); + SendFinish(std::move(self), Status::CANCELLED); +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/health/default_health_check_service.h b/third_party/grpc/src/cpp/server/health/default_health_check_service.h new file mode 100644 index 00000000000000..9551cd2e2cfa49 --- /dev/null +++ b/third_party/grpc/src/cpp/server/health/default_health_check_service.h @@ -0,0 +1,284 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_SERVER_DEFAULT_HEALTH_CHECK_SERVICE_H +#define GRPC_INTERNAL_CPP_SERVER_DEFAULT_HEALTH_CHECK_SERVICE_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/gprpp/thd.h" + +namespace grpc { + +// Default implementation of HealthCheckServiceInterface. Server will create and +// own it. +class DefaultHealthCheckService final : public HealthCheckServiceInterface { + public: + enum ServingStatus { NOT_FOUND, SERVING, NOT_SERVING }; + + // The service impl to register with the server. + class HealthCheckServiceImpl : public Service { + public: + // Base class for call handlers. + class CallHandler { + public: + virtual ~CallHandler() = default; + virtual void SendHealth(std::shared_ptr self, + ServingStatus status) = 0; + }; + + HealthCheckServiceImpl(DefaultHealthCheckService* database, + std::unique_ptr cq); + + ~HealthCheckServiceImpl(); + + void StartServingThread(); + + private: + // A tag that can be called with a bool argument. It's tailored for + // CallHandler's use. Before being used, it should be constructed with a + // method of CallHandler and a shared pointer to the handler. The + // shared pointer will be moved to the invoked function and the function + // can only be invoked once. That makes ref counting of the handler easier, + // because the shared pointer is not bound to the function and can be gone + // once the invoked function returns (if not used any more). + class CallableTag { + public: + using HandlerFunction = + std::function, bool)>; + + CallableTag() {} + + CallableTag(HandlerFunction func, std::shared_ptr handler) + : handler_function_(std::move(func)), handler_(std::move(handler)) { + GPR_ASSERT(handler_function_ != nullptr); + GPR_ASSERT(handler_ != nullptr); + } + + // Runs the tag. This should be called only once. The handler is no + // longer owned by this tag after this method is invoked. + void Run(bool ok) { + GPR_ASSERT(handler_function_ != nullptr); + GPR_ASSERT(handler_ != nullptr); + handler_function_(std::move(handler_), ok); + } + + // Releases and returns the shared pointer to the handler. + std::shared_ptr ReleaseHandler() { + return std::move(handler_); + } + + private: + HandlerFunction handler_function_ = nullptr; + std::shared_ptr handler_; + }; + + // Call handler for Check method. + // Each handler takes care of one call. It contains per-call data and it + // will access the members of the parent class (i.e., + // DefaultHealthCheckService) for per-service health data. + class CheckCallHandler : public CallHandler { + public: + // Instantiates a CheckCallHandler and requests the next health check + // call. The handler object will manage its own lifetime, so no action is + // needed from the caller any more regarding that object. + static void CreateAndStart(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service); + + // This ctor is public because we want to use std::make_shared<> in + // CreateAndStart(). This ctor shouldn't be used elsewhere. + CheckCallHandler(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service); + + // Not used for Check. + void SendHealth(std::shared_ptr self, + ServingStatus status) override {} + + private: + // Called when we receive a call. + // Spawns a new handler so that we can keep servicing future calls. + void OnCallReceived(std::shared_ptr self, bool ok); + + // Called when Finish() is done. + void OnFinishDone(std::shared_ptr self, bool ok); + + // The members passed down from HealthCheckServiceImpl. + ServerCompletionQueue* cq_; + DefaultHealthCheckService* database_; + HealthCheckServiceImpl* service_; + + ByteBuffer request_; + GenericServerAsyncResponseWriter writer_; + ServerContext ctx_; + + CallableTag next_; + }; + + // Call handler for Watch method. + // Each handler takes care of one call. It contains per-call data and it + // will access the members of the parent class (i.e., + // DefaultHealthCheckService) for per-service health data. + class WatchCallHandler : public CallHandler { + public: + // Instantiates a WatchCallHandler and requests the next health check + // call. The handler object will manage its own lifetime, so no action is + // needed from the caller any more regarding that object. + static void CreateAndStart(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service); + + // This ctor is public because we want to use std::make_shared<> in + // CreateAndStart(). This ctor shouldn't be used elsewhere. + WatchCallHandler(ServerCompletionQueue* cq, + DefaultHealthCheckService* database, + HealthCheckServiceImpl* service); + + void SendHealth(std::shared_ptr self, + ServingStatus status) override; + + private: + // Called when we receive a call. + // Spawns a new handler so that we can keep servicing future calls. + void OnCallReceived(std::shared_ptr self, bool ok); + + // Requires holding send_mu_. + void SendHealthLocked(std::shared_ptr self, + ServingStatus status); + + // When sending a health result finishes. + void OnSendHealthDone(std::shared_ptr self, bool ok); + + void SendFinish(std::shared_ptr self, const Status& status); + + // Requires holding service_->cq_shutdown_mu_. + void SendFinishLocked(std::shared_ptr self, + const Status& status); + + // Called when Finish() is done. + void OnFinishDone(std::shared_ptr self, bool ok); + + // Called when AsyncNotifyWhenDone() notifies us. + void OnDoneNotified(std::shared_ptr self, bool ok); + + // The members passed down from HealthCheckServiceImpl. + ServerCompletionQueue* cq_; + DefaultHealthCheckService* database_; + HealthCheckServiceImpl* service_; + + ByteBuffer request_; + grpc::string service_name_; + GenericServerAsyncWriter stream_; + ServerContext ctx_; + + std::mutex send_mu_; + bool send_in_flight_ = false; // Guarded by mu_. + ServingStatus pending_status_ = NOT_FOUND; // Guarded by mu_. + + bool finish_called_ = false; + CallableTag next_; + CallableTag on_done_notified_; + CallableTag on_finish_done_; + }; + + // Handles the incoming requests and drives the completion queue in a loop. + static void Serve(void* arg); + + // Returns true on success. + static bool DecodeRequest(const ByteBuffer& request, + grpc::string* service_name); + static bool EncodeResponse(ServingStatus status, ByteBuffer* response); + + // Needed to appease Windows compilers, which don't seem to allow + // nested classes to access protected members in the parent's + // superclass. + using Service::RequestAsyncServerStreaming; + using Service::RequestAsyncUnary; + + DefaultHealthCheckService* database_; + std::unique_ptr cq_; + + // To synchronize the operations related to shutdown state of cq_, so that + // we don't enqueue new tags into cq_ after it is already shut down. + std::mutex cq_shutdown_mu_; + std::atomic_bool shutdown_{false}; + std::unique_ptr<::grpc_core::Thread> thread_; + }; + + DefaultHealthCheckService(); + + void SetServingStatus(const grpc::string& service_name, + bool serving) override; + void SetServingStatus(bool serving) override; + + void Shutdown() override; + + ServingStatus GetServingStatus(const grpc::string& service_name) const; + + HealthCheckServiceImpl* GetHealthCheckService( + std::unique_ptr cq); + + private: + // Stores the current serving status of a service and any call + // handlers registered for updates when the service's status changes. + class ServiceData { + public: + void SetServingStatus(ServingStatus status); + ServingStatus GetServingStatus() const { return status_; } + void AddCallHandler( + std::shared_ptr handler); + void RemoveCallHandler( + const std::shared_ptr& handler); + bool Unused() const { + return call_handlers_.empty() && status_ == NOT_FOUND; + } + + private: + ServingStatus status_ = NOT_FOUND; + std::set> + call_handlers_; + }; + + void RegisterCallHandler( + const grpc::string& service_name, + std::shared_ptr handler); + + void UnregisterCallHandler( + const grpc::string& service_name, + const std::shared_ptr& handler); + + mutable std::mutex mu_; + bool shutdown_ = false; // Guarded by mu_. + std::map services_map_; // Guarded by mu_. + std::unique_ptr impl_; +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_SERVER_DEFAULT_HEALTH_CHECK_SERVICE_H diff --git a/third_party/grpc/src/cpp/server/health/health_check_service.cc b/third_party/grpc/src/cpp/server/health/health_check_service.cc new file mode 100644 index 00000000000000..a0fa2d62f58c80 --- /dev/null +++ b/third_party/grpc/src/cpp/server/health/health_check_service.cc @@ -0,0 +1,34 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +namespace grpc { +namespace { +bool g_grpc_default_health_check_service_enabled = false; +} // namespace + +bool DefaultHealthCheckServiceEnabled() { + return g_grpc_default_health_check_service_enabled; +} + +void EnableDefaultHealthCheckService(bool enable) { + g_grpc_default_health_check_service_enabled = enable; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/health/health_check_service_server_builder_option.cc b/third_party/grpc/src/cpp/server/health/health_check_service_server_builder_option.cc new file mode 100644 index 00000000000000..7148265d271ae4 --- /dev/null +++ b/third_party/grpc/src/cpp/server/health/health_check_service_server_builder_option.cc @@ -0,0 +1,35 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +namespace grpc { + +HealthCheckServiceServerBuilderOption::HealthCheckServiceServerBuilderOption( + std::unique_ptr hc) + : hc_(std::move(hc)) {} +// Hand over hc_ to the server. +void HealthCheckServiceServerBuilderOption::UpdateArguments( + ChannelArguments* args) { + args->SetPointer(kHealthCheckServiceInterfaceArg, hc_.release()); +} + +void HealthCheckServiceServerBuilderOption::UpdatePlugins( + std::vector>* plugins) {} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/insecure_server_credentials.cc b/third_party/grpc/src/cpp/server/insecure_server_credentials.cc index ef3cae5fd72061..7d749ddca4da7c 100644 --- a/third_party/grpc/src/cpp/server/insecure_server_credentials.cc +++ b/third_party/grpc/src/cpp/server/insecure_server_credentials.cc @@ -1,51 +1,35 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include #include #include namespace grpc { namespace { -class InsecureServerCredentialsImpl GRPC_FINAL : public ServerCredentials { +class InsecureServerCredentialsImpl final : public ServerCredentials { public: - int AddPortToServer(const grpc::string& addr, - grpc_server* server) GRPC_OVERRIDE { + int AddPortToServer(const grpc::string& addr, grpc_server* server) override { return grpc_server_add_insecure_http2_port(server, addr.c_str()); } void SetAuthMetadataProcessor( - const std::shared_ptr& processor) GRPC_OVERRIDE { + const std::shared_ptr& processor) override { (void)processor; GPR_ASSERT(0); // Should not be called on InsecureServerCredentials. } diff --git a/third_party/grpc/src/cpp/server/load_reporter/constants.h b/third_party/grpc/src/cpp/server/load_reporter/constants.h new file mode 100644 index 00000000000000..00ad794a0498d9 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/constants.h @@ -0,0 +1,81 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H +#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H + +#include + +namespace grpc { +namespace load_reporter { + +// TODO(juanlishen): Update the version number with the PR number every time +// there is any change to the server load reporter. +constexpr uint32_t kVersion = 15853; + +// TODO(juanlishen): This window size is from the internal spec for the load +// reporter. Need to ask the gRPC LB team whether we should make this and the +// fetching interval configurable. +constexpr uint32_t kFeedbackSampleWindowSeconds = 10; +constexpr uint32_t kFetchAndSampleIntervalSeconds = 1; + +constexpr size_t kLbIdLength = 8; +constexpr size_t kIpv4AddressLength = 8; +constexpr size_t kIpv6AddressLength = 32; + +constexpr char kInvalidLbId[] = ""; + +// Call statuses. + +constexpr char kCallStatusOk[] = "OK"; +constexpr char kCallStatusServerError[] = "5XX"; +constexpr char kCallStatusClientError[] = "4XX"; + +// Tag keys. + +constexpr char kTagKeyToken[] = "token"; +constexpr char kTagKeyHost[] = "host"; +constexpr char kTagKeyUserId[] = "user_id"; +constexpr char kTagKeyStatus[] = "status"; +constexpr char kTagKeyMetricName[] = "metric_name"; + +// Measure names. + +constexpr char kMeasureStartCount[] = "grpc.io/lb/start_count"; +constexpr char kMeasureEndCount[] = "grpc.io/lb/end_count"; +constexpr char kMeasureEndBytesSent[] = "grpc.io/lb/bytes_sent"; +constexpr char kMeasureEndBytesReceived[] = "grpc.io/lb/bytes_received"; +constexpr char kMeasureEndLatencyMs[] = "grpc.io/lb/latency_ms"; +constexpr char kMeasureOtherCallMetric[] = "grpc.io/lb/other_call_metric"; + +// View names. + +constexpr char kViewStartCount[] = "grpc.io/lb_view/start_count"; +constexpr char kViewEndCount[] = "grpc.io/lb_view/end_count"; +constexpr char kViewEndBytesSent[] = "grpc.io/lb_view/bytes_sent"; +constexpr char kViewEndBytesReceived[] = "grpc.io/lb_view/bytes_received"; +constexpr char kViewEndLatencyMs[] = "grpc.io/lb_view/latency_ms"; +constexpr char kViewOtherCallMetricCount[] = + "grpc.io/lb_view/other_call_metric_count"; +constexpr char kViewOtherCallMetricValue[] = + "grpc.io/lb_view/other_call_metric_value"; + +} // namespace load_reporter +} // namespace grpc + +#endif // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_UTIL_H diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats.h b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats.h new file mode 100644 index 00000000000000..f514b0752f715e --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats.h @@ -0,0 +1,36 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H +#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H + +#include + +#include + +namespace grpc { +namespace load_reporter { + +// Reads the CPU stats (in a pair of busy and total numbers) from the system. +// The units of the stats should be the same. +std::pair GetCpuStatsImpl(); + +} // namespace load_reporter +} // namespace grpc + +#endif // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_GET_CPU_STATS_H diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_linux.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_linux.cc new file mode 100644 index 00000000000000..9c1fd0cd0b8948 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_linux.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_LINUX + +#include + +#include "src/cpp/server/load_reporter/get_cpu_stats.h" + +namespace grpc { +namespace load_reporter { + +std::pair GetCpuStatsImpl() { + uint64_t busy = 0, total = 0; + FILE* fp; + fp = fopen("/proc/stat", "r"); + uint64_t user, nice, system, idle; + fscanf(fp, "cpu %lu %lu %lu %lu", &user, &nice, &system, &idle); + fclose(fp); + busy = user + nice + system; + total = busy + idle; + return std::make_pair(busy, total); +} + +} // namespace load_reporter +} // namespace grpc + +#endif // GPR_LINUX diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_macos.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_macos.cc new file mode 100644 index 00000000000000..dbdde304c2029b --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_macos.cc @@ -0,0 +1,45 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_APPLE + +#include + +#include "src/cpp/server/load_reporter/get_cpu_stats.h" + +namespace grpc { +namespace load_reporter { + +std::pair GetCpuStatsImpl() { + uint64_t busy = 0, total = 0; + host_cpu_load_info_data_t cpuinfo; + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, + (host_info_t)&cpuinfo, &count) == KERN_SUCCESS) { + for (int i = 0; i < CPU_STATE_MAX; i++) total += cpuinfo.cpu_ticks[i]; + busy = total - cpuinfo.cpu_ticks[CPU_STATE_IDLE]; + } + return std::make_pair(busy, total); +} + +} // namespace load_reporter +} // namespace grpc + +#endif // GPR_APPLE diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc new file mode 100644 index 00000000000000..80fb8b6da1fd14 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_unsupported.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#if !defined(GPR_LINUX) && !defined(GPR_WINDOWS) && !defined(GPR_APPLE) + +#include + +#include "src/cpp/server/load_reporter/get_cpu_stats.h" + +namespace grpc { +namespace load_reporter { + +std::pair GetCpuStatsImpl() { + uint64_t busy = 0, total = 0; + gpr_log(GPR_ERROR, + "Platforms other than Linux, Windows, and MacOS are not supported."); + return std::make_pair(busy, total); +} + +} // namespace load_reporter +} // namespace grpc + +#endif // !defined(GPR_LINUX) && !defined(GPR_WINDOWS) && !defined(GPR_APPLE) diff --git a/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_windows.cc b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_windows.cc new file mode 100644 index 00000000000000..0a98e848a2c647 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/get_cpu_stats_windows.cc @@ -0,0 +1,55 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS + +#include +#include + +#include "src/cpp/server/load_reporter/get_cpu_stats.h" + +namespace grpc { +namespace load_reporter { + +namespace { + +uint64_t FiletimeToInt(const FILETIME& ft) { + ULARGE_INTEGER i; + i.LowPart = ft.dwLowDateTime; + i.HighPart = ft.dwHighDateTime; + return i.QuadPart; +} + +} // namespace + +std::pair GetCpuStatsImpl() { + uint64_t busy = 0, total = 0; + FILETIME idle, kernel, user; + if (GetSystemTimes(&idle, &kernel, &user) != 0) { + total = FiletimeToInt(kernel) + FiletimeToInt(user); + busy = total - FiletimeToInt(idle); + } + return std::make_pair(busy, total); +} + +} // namespace load_reporter +} // namespace grpc + +#endif // GPR_WINDOWS diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_data_store.cc b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.cc new file mode 100644 index 00000000000000..594473f5e7a551 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.cc @@ -0,0 +1,338 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/iomgr/socket_utils.h" +#include "src/cpp/server/load_reporter/load_data_store.h" + +namespace grpc { +namespace load_reporter { + +// Some helper functions. +namespace { + +// Given a map from type K to a set of value type V, finds the set associated +// with the given key and erases the value from the set. If the set becomes +// empty, also erases the key-set pair. Returns true if the value is erased +// successfully. +template +bool UnorderedMapOfSetEraseKeyValue(std::unordered_map>& map, + const K& key, const V& value) { + auto it = map.find(key); + if (it != map.end()) { + size_t erased = it->second.erase(value); + if (it->second.size() == 0) { + map.erase(it); + } + return erased; + } + return false; +}; + +// Given a map from type K to a set of value type V, removes the given key and +// the associated set, and returns the set. Returns an empty set if the key is +// not found. +template +std::set UnorderedMapOfSetExtract(std::unordered_map>& map, + const K& key) { + auto it = map.find(key); + if (it != map.end()) { + auto set = std::move(it->second); + map.erase(it); + return set; + } + return {}; +}; + +// From a non-empty container, returns a pointer to a random element. +template +const typename C::value_type* RandomElement(const C& container) { + GPR_ASSERT(!container.empty()); + auto it = container.begin(); + std::advance(it, std::rand() % container.size()); + return &(*it); +} + +} // namespace + +LoadRecordKey::LoadRecordKey(const grpc::string& client_ip_and_token, + grpc::string user_id) + : user_id_(std::move(user_id)) { + GPR_ASSERT(client_ip_and_token.size() >= 2); + int ip_hex_size; + GPR_ASSERT(sscanf(client_ip_and_token.substr(0, 2).c_str(), "%d", + &ip_hex_size) == 1); + GPR_ASSERT(ip_hex_size == 0 || ip_hex_size == kIpv4AddressLength || + ip_hex_size == kIpv6AddressLength); + size_t cur_pos = 2; + client_ip_hex_ = client_ip_and_token.substr(cur_pos, ip_hex_size); + cur_pos += ip_hex_size; + if (client_ip_and_token.size() - cur_pos < kLbIdLength) { + lb_id_ = kInvalidLbId; + lb_tag_ = ""; + } else { + lb_id_ = client_ip_and_token.substr(cur_pos, kLbIdLength); + lb_tag_ = client_ip_and_token.substr(cur_pos + kLbIdLength); + } +} + +grpc::string LoadRecordKey::GetClientIpBytes() const { + if (client_ip_hex_.empty()) { + return ""; + } else if (client_ip_hex_.size() == kIpv4AddressLength) { + uint32_t ip_bytes; + if (sscanf(client_ip_hex_.c_str(), "%x", &ip_bytes) != 1) { + gpr_log(GPR_ERROR, + "Can't parse client IP (%s) from a hex string to an integer.", + client_ip_hex_.c_str()); + return ""; + } + ip_bytes = grpc_htonl(ip_bytes); + return grpc::string(reinterpret_cast(&ip_bytes), + sizeof(ip_bytes)); + } else if (client_ip_hex_.size() == kIpv6AddressLength) { + uint32_t ip_bytes[4]; + for (size_t i = 0; i < 4; ++i) { + if (sscanf(client_ip_hex_.substr(i * 8, (i + 1) * 8).c_str(), "%x", + ip_bytes + i) != 1) { + gpr_log( + GPR_ERROR, + "Can't parse client IP part (%s) from a hex string to an integer.", + client_ip_hex_.substr(i * 8, (i + 1) * 8).c_str()); + return ""; + } + ip_bytes[i] = grpc_htonl(ip_bytes[i]); + } + return grpc::string(reinterpret_cast(ip_bytes), + sizeof(ip_bytes)); + } else { + GPR_UNREACHABLE_CODE(return ""); + } +} + +LoadRecordValue::LoadRecordValue(grpc::string metric_name, uint64_t num_calls, + double total_metric_value) { + call_metrics_.emplace(std::move(metric_name), + CallMetricValue(num_calls, total_metric_value)); +} + +void PerBalancerStore::MergeRow(const LoadRecordKey& key, + const LoadRecordValue& value) { + // During suspension, the load data received will be dropped. + if (!suspended_) { + load_record_map_[key].MergeFrom(value); + gpr_log(GPR_DEBUG, + "[PerBalancerStore %p] Load data merged (Key: %s, Value: %s).", + this, key.ToString().c_str(), value.ToString().c_str()); + } else { + gpr_log(GPR_DEBUG, + "[PerBalancerStore %p] Load data dropped (Key: %s, Value: %s).", + this, key.ToString().c_str(), value.ToString().c_str()); + } + // We always keep track of num_calls_in_progress_, so that when this + // store is resumed, we still have a correct value of + // num_calls_in_progress_. + GPR_ASSERT(static_cast(num_calls_in_progress_) + + value.GetNumCallsInProgressDelta() >= + 0); + num_calls_in_progress_ += value.GetNumCallsInProgressDelta(); +} + +void PerBalancerStore::Suspend() { + suspended_ = true; + load_record_map_.clear(); + gpr_log(GPR_DEBUG, "[PerBalancerStore %p] Suspended.", this); +} + +void PerBalancerStore::Resume() { + suspended_ = false; + gpr_log(GPR_DEBUG, "[PerBalancerStore %p] Resumed.", this); +} + +uint64_t PerBalancerStore::GetNumCallsInProgressForReport() { + GPR_ASSERT(!suspended_); + last_reported_num_calls_in_progress_ = num_calls_in_progress_; + return num_calls_in_progress_; +} + +void PerHostStore::ReportStreamCreated(const grpc::string& lb_id, + const grpc::string& load_key) { + GPR_ASSERT(lb_id != kInvalidLbId); + SetUpForNewLbId(lb_id, load_key); + // Prior to this one, there was no load balancer receiving report, so we may + // have unassigned orphaned stores to assign to this new balancer. + // TODO(juanlishen): If the load key of this new stream is the same with + // some previously adopted orphan store, we may want to take the orphan to + // this stream. Need to discuss with LB team. + if (assigned_stores_.size() == 1) { + for (const auto& p : per_balancer_stores_) { + const grpc::string& other_lb_id = p.first; + const std::unique_ptr& orphaned_store = p.second; + if (other_lb_id != lb_id) { + orphaned_store->Resume(); + AssignOrphanedStore(orphaned_store.get(), lb_id); + } + } + } + // The first connected balancer will adopt the kInvalidLbId. + if (per_balancer_stores_.size() == 1) { + SetUpForNewLbId(kInvalidLbId, ""); + ReportStreamClosed(kInvalidLbId); + } +} + +void PerHostStore::ReportStreamClosed(const grpc::string& lb_id) { + auto it_store_for_gone_lb = per_balancer_stores_.find(lb_id); + GPR_ASSERT(it_store_for_gone_lb != per_balancer_stores_.end()); + // Remove this closed stream from our records. + GPR_ASSERT(UnorderedMapOfSetEraseKeyValue( + load_key_to_receiving_lb_ids_, it_store_for_gone_lb->second->load_key(), + lb_id)); + std::set orphaned_stores = + UnorderedMapOfSetExtract(assigned_stores_, lb_id); + // The stores that were assigned to this balancer are orphaned now. They + // should be re-assigned to other balancers which are still receiving reports. + for (PerBalancerStore* orphaned_store : orphaned_stores) { + const grpc::string* new_receiver = nullptr; + auto it = load_key_to_receiving_lb_ids_.find(orphaned_store->load_key()); + if (it != load_key_to_receiving_lb_ids_.end()) { + // First, try to pick from the active balancers with the same load key. + new_receiver = RandomElement(it->second); + } else if (!assigned_stores_.empty()) { + // If failed, pick from all the remaining active balancers. + new_receiver = &(RandomElement(assigned_stores_)->first); + } + if (new_receiver != nullptr) { + AssignOrphanedStore(orphaned_store, *new_receiver); + } else { + // Load data for an LB ID that can't be assigned to any stream should + // be dropped. + orphaned_store->Suspend(); + } + } +} + +PerBalancerStore* PerHostStore::FindPerBalancerStore( + const grpc::string& lb_id) const { + return per_balancer_stores_.find(lb_id) != per_balancer_stores_.end() + ? per_balancer_stores_.find(lb_id)->second.get() + : nullptr; +} + +const std::set* PerHostStore::GetAssignedStores( + const grpc::string& lb_id) const { + auto it = assigned_stores_.find(lb_id); + if (it == assigned_stores_.end()) return nullptr; + return &(it->second); +} + +void PerHostStore::AssignOrphanedStore(PerBalancerStore* orphaned_store, + const grpc::string& new_receiver) { + auto it = assigned_stores_.find(new_receiver); + GPR_ASSERT(it != assigned_stores_.end()); + it->second.insert(orphaned_store); + gpr_log(GPR_INFO, + "[PerHostStore %p] Re-assigned orphaned store (%p) with original LB" + " ID of %s to new receiver %s", + this, orphaned_store, orphaned_store->lb_id().c_str(), + new_receiver.c_str()); +} + +void PerHostStore::SetUpForNewLbId(const grpc::string& lb_id, + const grpc::string& load_key) { + // The top-level caller (i.e., LoadReportService) should guarantee the + // lb_id is unique for each reporting stream. + GPR_ASSERT(per_balancer_stores_.find(lb_id) == per_balancer_stores_.end()); + GPR_ASSERT(assigned_stores_.find(lb_id) == assigned_stores_.end()); + load_key_to_receiving_lb_ids_[load_key].insert(lb_id); + std::unique_ptr per_balancer_store( + new PerBalancerStore(lb_id, load_key)); + assigned_stores_[lb_id] = {per_balancer_store.get()}; + per_balancer_stores_[lb_id] = std::move(per_balancer_store); +} + +PerBalancerStore* LoadDataStore::FindPerBalancerStore( + const string& hostname, const string& lb_id) const { + auto it = per_host_stores_.find(hostname); + if (it != per_host_stores_.end()) { + const PerHostStore& per_host_store = it->second; + return per_host_store.FindPerBalancerStore(lb_id); + } else { + return nullptr; + } +} + +void LoadDataStore::MergeRow(const grpc::string& hostname, + const LoadRecordKey& key, + const LoadRecordValue& value) { + PerBalancerStore* per_balancer_store = + FindPerBalancerStore(hostname, key.lb_id()); + if (per_balancer_store != nullptr) { + per_balancer_store->MergeRow(key, value); + return; + } + // Unknown LB ID. Track it until its number of in-progress calls drops to + // zero. + int64_t in_progress_delta = value.GetNumCallsInProgressDelta(); + if (in_progress_delta != 0) { + auto it_tracker = unknown_balancer_id_trackers_.find(key.lb_id()); + if (it_tracker == unknown_balancer_id_trackers_.end()) { + gpr_log( + GPR_DEBUG, + "[LoadDataStore %p] Start tracking unknown balancer (lb_id_: %s).", + this, key.lb_id().c_str()); + unknown_balancer_id_trackers_.insert( + {key.lb_id(), static_cast(in_progress_delta)}); + } else if ((it_tracker->second += in_progress_delta) == 0) { + unknown_balancer_id_trackers_.erase(it_tracker); + gpr_log(GPR_DEBUG, + "[LoadDataStore %p] Stop tracking unknown balancer (lb_id_: %s).", + this, key.lb_id().c_str()); + } + } +} + +const std::set* LoadDataStore::GetAssignedStores( + const grpc::string& hostname, const grpc::string& lb_id) { + auto it = per_host_stores_.find(hostname); + if (it == per_host_stores_.end()) return nullptr; + return it->second.GetAssignedStores(lb_id); +} + +void LoadDataStore::ReportStreamCreated(const grpc::string& hostname, + const grpc::string& lb_id, + const grpc::string& load_key) { + per_host_stores_[hostname].ReportStreamCreated(lb_id, load_key); +} + +void LoadDataStore::ReportStreamClosed(const grpc::string& hostname, + const grpc::string& lb_id) { + auto it_per_host_store = per_host_stores_.find(hostname); + GPR_ASSERT(it_per_host_store != per_host_stores_.end()); + it_per_host_store->second.ReportStreamClosed(lb_id); +} + +} // namespace load_reporter +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_data_store.h b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.h new file mode 100644 index 00000000000000..dc08ecf47942b0 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_data_store.h @@ -0,0 +1,348 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_DATA_STORE_H +#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_DATA_STORE_H + +#include + +#include +#include +#include + +#include +#include + +#include "src/cpp/server/load_reporter/constants.h" + +namespace grpc { +namespace load_reporter { + +// The load data storage is organized in hierarchy. The LoadDataStore is the +// top-level data store. In LoadDataStore, for each host we keep a +// PerHostStore, in which for each balancer we keep a PerBalancerStore. Each +// PerBalancerStore maintains a map of load records, mapping from LoadRecordKey +// to LoadRecordValue. The LoadRecordValue contains a map of customized call +// metrics, mapping from a call metric name to the CallMetricValue. + +// The value of a customized call metric. +class CallMetricValue { + public: + explicit CallMetricValue(uint64_t num_calls = 0, + double total_metric_value = 0) + : num_calls_(num_calls), total_metric_value_(total_metric_value) {} + + void MergeFrom(CallMetricValue other) { + num_calls_ += other.num_calls_; + total_metric_value_ += other.total_metric_value_; + } + + // Getters. + uint64_t num_calls() const { return num_calls_; } + double total_metric_value() const { return total_metric_value_; } + + private: + // The number of calls that finished with this metric. + uint64_t num_calls_ = 0; + // The sum of metric values across all the calls that finished with this + // metric. + double total_metric_value_ = 0; +}; + +// The key of a load record. +class LoadRecordKey { + public: + LoadRecordKey(grpc::string lb_id, grpc::string lb_tag, grpc::string user_id, + grpc::string client_ip_hex) + : lb_id_(std::move(lb_id)), + lb_tag_(std::move(lb_tag)), + user_id_(std::move(user_id)), + client_ip_hex_(std::move(client_ip_hex)) {} + + // Parses the input client_ip_and_token to set client IP, LB ID, and LB tag. + LoadRecordKey(const grpc::string& client_ip_and_token, grpc::string user_id); + + grpc::string ToString() const { + return "[lb_id_=" + lb_id_ + ", lb_tag_=" + lb_tag_ + + ", user_id_=" + user_id_ + ", client_ip_hex_=" + client_ip_hex_ + + "]"; + } + + bool operator==(const LoadRecordKey& other) const { + return lb_id_ == other.lb_id_ && lb_tag_ == other.lb_tag_ && + user_id_ == other.user_id_ && client_ip_hex_ == other.client_ip_hex_; + } + + // Gets the client IP bytes in network order (i.e., big-endian). + grpc::string GetClientIpBytes() const; + + // Getters. + const grpc::string& lb_id() const { return lb_id_; } + const grpc::string& lb_tag() const { return lb_tag_; } + const grpc::string& user_id() const { return user_id_; } + const grpc::string& client_ip_hex() const { return client_ip_hex_; } + + struct Hasher { + void hash_combine(size_t* seed, const grpc::string& k) const { + *seed ^= std::hash()(k) + 0x9e3779b9 + (*seed << 6) + + (*seed >> 2); + } + + size_t operator()(const LoadRecordKey& k) const { + size_t h = 0; + hash_combine(&h, k.lb_id_); + hash_combine(&h, k.lb_tag_); + hash_combine(&h, k.user_id_); + hash_combine(&h, k.client_ip_hex_); + return h; + } + }; + + private: + grpc::string lb_id_; + grpc::string lb_tag_; + grpc::string user_id_; + grpc::string client_ip_hex_; +}; + +// The value of a load record. +class LoadRecordValue { + public: + explicit LoadRecordValue(uint64_t start_count = 0, uint64_t ok_count = 0, + uint64_t error_count = 0, uint64_t bytes_sent = 0, + uint64_t bytes_recv = 0, uint64_t latency_ms = 0) + : start_count_(start_count), + ok_count_(ok_count), + error_count_(error_count), + bytes_sent_(bytes_sent), + bytes_recv_(bytes_recv), + latency_ms_(latency_ms) {} + + LoadRecordValue(grpc::string metric_name, uint64_t num_calls, + double total_metric_value); + + void MergeFrom(const LoadRecordValue& other) { + start_count_ += other.start_count_; + ok_count_ += other.ok_count_; + error_count_ += other.error_count_; + bytes_sent_ += other.bytes_sent_; + bytes_recv_ += other.bytes_recv_; + latency_ms_ += other.latency_ms_; + for (const auto& p : other.call_metrics_) { + const grpc::string& key = p.first; + const CallMetricValue& value = p.second; + call_metrics_[key].MergeFrom(value); + } + } + + int64_t GetNumCallsInProgressDelta() const { + return static_cast(start_count_ - ok_count_ - error_count_); + } + + grpc::string ToString() const { + return "[start_count_=" + grpc::to_string(start_count_) + + ", ok_count_=" + grpc::to_string(ok_count_) + + ", error_count_=" + grpc::to_string(error_count_) + + ", bytes_sent_=" + grpc::to_string(bytes_sent_) + + ", bytes_recv_=" + grpc::to_string(bytes_recv_) + + ", latency_ms_=" + grpc::to_string(latency_ms_) + ", " + + grpc::to_string(call_metrics_.size()) + " other call metric(s)]"; + } + + bool InsertCallMetric(const grpc::string& metric_name, + const CallMetricValue& metric_value) { + return call_metrics_.insert({metric_name, metric_value}).second; + } + + // Getters. + uint64_t start_count() const { return start_count_; } + uint64_t ok_count() const { return ok_count_; } + uint64_t error_count() const { return error_count_; } + uint64_t bytes_sent() const { return bytes_sent_; } + uint64_t bytes_recv() const { return bytes_recv_; } + uint64_t latency_ms() const { return latency_ms_; } + const std::unordered_map& call_metrics() + const { + return call_metrics_; + } + + private: + uint64_t start_count_ = 0; + uint64_t ok_count_ = 0; + uint64_t error_count_ = 0; + uint64_t bytes_sent_ = 0; + uint64_t bytes_recv_ = 0; + uint64_t latency_ms_ = 0; + std::unordered_map call_metrics_; +}; + +// Stores the data associated with a particular LB ID. +class PerBalancerStore { + public: + using LoadRecordMap = + std::unordered_map; + + PerBalancerStore(grpc::string lb_id, grpc::string load_key) + : lb_id_(std::move(lb_id)), load_key_(std::move(load_key)) {} + + // Merge a load record with the given key and value if the store is not + // suspended. + void MergeRow(const LoadRecordKey& key, const LoadRecordValue& value); + + // Suspend this store, so that no detailed load data will be recorded. + void Suspend(); + // Resume this store from suspension. + void Resume(); + // Is this store suspended or not? + bool IsSuspended() const { return suspended_; } + + bool IsNumCallsInProgressChangedSinceLastReport() const { + return num_calls_in_progress_ != last_reported_num_calls_in_progress_; + } + + uint64_t GetNumCallsInProgressForReport(); + + grpc::string ToString() { + return "[PerBalancerStore lb_id_=" + lb_id_ + " load_key_=" + load_key_ + + "]"; + } + + void ClearLoadRecordMap() { load_record_map_.clear(); } + + // Getters. + const grpc::string& lb_id() const { return lb_id_; } + const grpc::string& load_key() const { return load_key_; } + const LoadRecordMap& load_record_map() const { return load_record_map_; } + + private: + grpc::string lb_id_; + // TODO(juanlishen): Use bytestring protobuf type? + grpc::string load_key_; + LoadRecordMap load_record_map_; + uint64_t num_calls_in_progress_ = 0; + uint64_t last_reported_num_calls_in_progress_ = 0; + bool suspended_ = false; +}; + +// Stores the data associated with a particular host. +class PerHostStore { + public: + // When a report stream is created, a PerBalancerStore is created for the + // LB ID (guaranteed unique) associated with that stream. If it is the only + // active store, adopt all the orphaned stores. If it is the first created + // store, adopt the store of kInvalidLbId. + void ReportStreamCreated(const grpc::string& lb_id, + const grpc::string& load_key); + + // When a report stream is closed, the PerBalancerStores assigned to the + // associate LB ID need to be re-assigned to other active balancers, + // ideally with the same load key. If there is no active balancer, we have + // to suspend those stores and drop the incoming load data until they are + // resumed. + void ReportStreamClosed(const grpc::string& lb_id); + + // Returns null if not found. Caller doesn't own the returned store. + PerBalancerStore* FindPerBalancerStore(const grpc::string& lb_id) const; + + // Returns null if lb_id is not found. The returned pointer points to the + // underlying data structure, which is not owned by the caller. + const std::set* GetAssignedStores( + const grpc::string& lb_id) const; + + private: + // Creates a PerBalancerStore for the given LB ID, assigns the store to + // itself, and records the LB ID to the load key. + void SetUpForNewLbId(const grpc::string& lb_id, const grpc::string& load_key); + + void AssignOrphanedStore(PerBalancerStore* orphaned_store, + const grpc::string& new_receiver); + + std::unordered_map> + load_key_to_receiving_lb_ids_; + + // Key: LB ID. The key set includes all the LB IDs that have been + // allocated for reporting streams so far. + // Value: the unique pointer to the PerBalancerStore of the LB ID. + std::unordered_map> + per_balancer_stores_; + + // Key: LB ID. The key set includes the LB IDs of the balancers that are + // currently receiving report. + // Value: the set of raw pointers to the PerBalancerStores assigned to the LB + // ID. Note that the sets in assigned_stores_ form a division of the value set + // of per_balancer_stores_. + std::unordered_map> + assigned_stores_; +}; + +// Thread-unsafe two-level bookkeeper of all the load data. +// Note: We never remove any store objects from this class, as per the +// current spec. That's because premature removal of the store objects +// may lead to loss of critical information, e.g., mapping from lb_id to +// load_key, and the number of in-progress calls. Such loss will cause +// information inconsistency when the balancer is re-connected. Keeping +// all the stores should be fine for PerHostStore, since we assume there +// should only be a few hostnames. But it's a potential problem for +// PerBalancerStore. +class LoadDataStore { + public: + // Returns null if not found. Caller doesn't own the returned store. + PerBalancerStore* FindPerBalancerStore(const grpc::string& hostname, + const grpc::string& lb_id) const; + + // Returns null if hostname or lb_id is not found. The returned pointer points + // to the underlying data structure, which is not owned by the caller. + const std::set* GetAssignedStores(const string& hostname, + const string& lb_id); + + // If a PerBalancerStore can be found by the hostname and LB ID in + // LoadRecordKey, the load data will be merged to that store. Otherwise, + // only track the number of the in-progress calls for this unknown LB ID. + void MergeRow(const grpc::string& hostname, const LoadRecordKey& key, + const LoadRecordValue& value); + + // Is the given lb_id a tracked unknown LB ID (i.e., the LB ID was associated + // with some received load data but unknown to this load data store)? + bool IsTrackedUnknownBalancerId(const grpc::string& lb_id) const { + return unknown_balancer_id_trackers_.find(lb_id) != + unknown_balancer_id_trackers_.end(); + } + + // Wrapper around PerHostStore::ReportStreamCreated. + void ReportStreamCreated(const grpc::string& hostname, + const grpc::string& lb_id, + const grpc::string& load_key); + + // Wrapper around PerHostStore::ReportStreamClosed. + void ReportStreamClosed(const grpc::string& hostname, + const grpc::string& lb_id); + + private: + // Buffered data that was fetched from Census but hasn't been sent to + // balancer. We need to keep this data ourselves because Census will + // delete the data once it's returned. + std::unordered_map per_host_stores_; + + // Tracks the number of in-progress calls for each unknown LB ID. + std::unordered_map unknown_balancer_id_trackers_; +}; + +} // namespace load_reporter +} // namespace grpc + +#endif // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_DATA_STORE_H diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.cc new file mode 100644 index 00000000000000..464063a13ff6e1 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.cc @@ -0,0 +1,509 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "src/cpp/server/load_reporter/constants.h" +#include "src/cpp/server/load_reporter/get_cpu_stats.h" +#include "src/cpp/server/load_reporter/load_reporter.h" + +#include "opencensus/stats/internal/set_aggregation_window.h" + +namespace grpc { +namespace load_reporter { + +CpuStatsProvider::CpuStatsSample CpuStatsProviderDefaultImpl::GetCpuStats() { + return GetCpuStatsImpl(); +} + +CensusViewProvider::CensusViewProvider() + : tag_key_token_(::opencensus::stats::TagKey::Register(kTagKeyToken)), + tag_key_host_(::opencensus::stats::TagKey::Register(kTagKeyHost)), + tag_key_user_id_(::opencensus::stats::TagKey::Register(kTagKeyUserId)), + tag_key_status_(::opencensus::stats::TagKey::Register(kTagKeyStatus)), + tag_key_metric_name_( + ::opencensus::stats::TagKey::Register(kTagKeyMetricName)) { + // One view related to starting a call. + auto vd_start_count = + ::opencensus::stats::ViewDescriptor() + .set_name(kViewStartCount) + .set_measure(kMeasureStartCount) + .set_aggregation(::opencensus::stats::Aggregation::Sum()) + .add_column(tag_key_token_) + .add_column(tag_key_host_) + .add_column(tag_key_user_id_) + .set_description( + "Delta count of calls started broken down by ."); + ::opencensus::stats::SetAggregationWindow( + ::opencensus::stats::AggregationWindow::Delta(), &vd_start_count); + view_descriptor_map_.emplace(kViewStartCount, vd_start_count); + // Four views related to ending a call. + // If this view is set as Count of kMeasureEndBytesSent (in hope of saving one + // measure), it's infeasible to prepare fake data for testing. That's because + // the OpenCensus API to make up view data will add the input data as separate + // measurements instead of setting the data values directly. + auto vd_end_count = + ::opencensus::stats::ViewDescriptor() + .set_name(kViewEndCount) + .set_measure(kMeasureEndCount) + .set_aggregation(::opencensus::stats::Aggregation::Sum()) + .add_column(tag_key_token_) + .add_column(tag_key_host_) + .add_column(tag_key_user_id_) + .add_column(tag_key_status_) + .set_description( + "Delta count of calls ended broken down by ."); + ::opencensus::stats::SetAggregationWindow( + ::opencensus::stats::AggregationWindow::Delta(), &vd_end_count); + view_descriptor_map_.emplace(kViewEndCount, vd_end_count); + auto vd_end_bytes_sent = + ::opencensus::stats::ViewDescriptor() + .set_name(kViewEndBytesSent) + .set_measure(kMeasureEndBytesSent) + .set_aggregation(::opencensus::stats::Aggregation::Sum()) + .add_column(tag_key_token_) + .add_column(tag_key_host_) + .add_column(tag_key_user_id_) + .add_column(tag_key_status_) + .set_description( + "Delta sum of bytes sent broken down by ."); + ::opencensus::stats::SetAggregationWindow( + ::opencensus::stats::AggregationWindow::Delta(), &vd_end_bytes_sent); + view_descriptor_map_.emplace(kViewEndBytesSent, vd_end_bytes_sent); + auto vd_end_bytes_received = + ::opencensus::stats::ViewDescriptor() + .set_name(kViewEndBytesReceived) + .set_measure(kMeasureEndBytesReceived) + .set_aggregation(::opencensus::stats::Aggregation::Sum()) + .add_column(tag_key_token_) + .add_column(tag_key_host_) + .add_column(tag_key_user_id_) + .add_column(tag_key_status_) + .set_description( + "Delta sum of bytes received broken down by ."); + ::opencensus::stats::SetAggregationWindow( + ::opencensus::stats::AggregationWindow::Delta(), &vd_end_bytes_received); + view_descriptor_map_.emplace(kViewEndBytesReceived, vd_end_bytes_received); + auto vd_end_latency_ms = + ::opencensus::stats::ViewDescriptor() + .set_name(kViewEndLatencyMs) + .set_measure(kMeasureEndLatencyMs) + .set_aggregation(::opencensus::stats::Aggregation::Sum()) + .add_column(tag_key_token_) + .add_column(tag_key_host_) + .add_column(tag_key_user_id_) + .add_column(tag_key_status_) + .set_description( + "Delta sum of latency in ms broken down by ."); + ::opencensus::stats::SetAggregationWindow( + ::opencensus::stats::AggregationWindow::Delta(), &vd_end_latency_ms); + view_descriptor_map_.emplace(kViewEndLatencyMs, vd_end_latency_ms); + // Two views related to other call metrics. + auto vd_metric_call_count = + ::opencensus::stats::ViewDescriptor() + .set_name(kViewOtherCallMetricCount) + .set_measure(kMeasureOtherCallMetric) + .set_aggregation(::opencensus::stats::Aggregation::Count()) + .add_column(tag_key_token_) + .add_column(tag_key_host_) + .add_column(tag_key_user_id_) + .add_column(tag_key_metric_name_) + .set_description( + "Delta count of calls broken down by ."); + ::opencensus::stats::SetAggregationWindow( + ::opencensus::stats::AggregationWindow::Delta(), &vd_metric_call_count); + view_descriptor_map_.emplace(kViewOtherCallMetricCount, vd_metric_call_count); + auto vd_metric_value = + ::opencensus::stats::ViewDescriptor() + .set_name(kViewOtherCallMetricValue) + .set_measure(kMeasureOtherCallMetric) + .set_aggregation(::opencensus::stats::Aggregation::Sum()) + .add_column(tag_key_token_) + .add_column(tag_key_host_) + .add_column(tag_key_user_id_) + .add_column(tag_key_metric_name_) + .set_description( + "Delta sum of call metric value broken down " + "by ."); + ::opencensus::stats::SetAggregationWindow( + ::opencensus::stats::AggregationWindow::Delta(), &vd_metric_value); + view_descriptor_map_.emplace(kViewOtherCallMetricValue, vd_metric_value); +} + +double CensusViewProvider::GetRelatedViewDataRowDouble( + const ViewDataMap& view_data_map, const char* view_name, + size_t view_name_len, const std::vector& tag_values) { + auto it_vd = view_data_map.find(grpc::string(view_name, view_name_len)); + GPR_ASSERT(it_vd != view_data_map.end()); + GPR_ASSERT(it_vd->second.type() == + ::opencensus::stats::ViewData::Type::kDouble); + auto it_row = it_vd->second.double_data().find(tag_values); + GPR_ASSERT(it_row != it_vd->second.double_data().end()); + return it_row->second; +} + +uint64_t CensusViewProvider::GetRelatedViewDataRowInt( + const ViewDataMap& view_data_map, const char* view_name, + size_t view_name_len, const std::vector& tag_values) { + auto it_vd = view_data_map.find(grpc::string(view_name, view_name_len)); + GPR_ASSERT(it_vd != view_data_map.end()); + GPR_ASSERT(it_vd->second.type() == + ::opencensus::stats::ViewData::Type::kInt64); + auto it_row = it_vd->second.int_data().find(tag_values); + GPR_ASSERT(it_row != it_vd->second.int_data().end()); + GPR_ASSERT(it_row->second >= 0); + return it_row->second; +} + +CensusViewProviderDefaultImpl::CensusViewProviderDefaultImpl() { + for (const auto& p : view_descriptor_map()) { + const grpc::string& view_name = p.first; + const ::opencensus::stats::ViewDescriptor& vd = p.second; + // We need to use pair's piecewise ctor here, otherwise the deleted copy + // ctor of View will be called. + view_map_.emplace(std::piecewise_construct, + std::forward_as_tuple(view_name), + std::forward_as_tuple(vd)); + } +} + +CensusViewProvider::ViewDataMap CensusViewProviderDefaultImpl::FetchViewData() { + gpr_log(GPR_DEBUG, "[CVP %p] Starts fetching Census view data.", this); + ViewDataMap view_data_map; + for (auto& p : view_map_) { + const grpc::string& view_name = p.first; + ::opencensus::stats::View& view = p.second; + if (view.IsValid()) { + view_data_map.emplace(view_name, view.GetData()); + gpr_log(GPR_DEBUG, "[CVP %p] Fetched view data (view: %s).", this, + view_name.c_str()); + } else { + gpr_log( + GPR_DEBUG, + "[CVP %p] Can't fetch view data because view is invalid (view: %s).", + this, view_name.c_str()); + } + } + return view_data_map; +} + +grpc::string LoadReporter::GenerateLbId() { + while (true) { + if (next_lb_id_ > UINT32_MAX) { + gpr_log(GPR_ERROR, "[LR %p] The LB ID exceeds the max valid value!", + this); + return ""; + } + int64_t lb_id = next_lb_id_++; + // Overflow should never happen. + GPR_ASSERT(lb_id >= 0); + // Convert to padded hex string for a 32-bit LB ID. E.g, "0000ca5b". + char buf[kLbIdLength + 1]; + snprintf(buf, sizeof(buf), "%08lx", lb_id); + grpc::string lb_id_str(buf, kLbIdLength); + // The client may send requests with LB ID that has never been allocated + // by this load reporter. Those IDs are tracked and will be skipped when + // we generate a new ID. + if (!load_data_store_.IsTrackedUnknownBalancerId(lb_id_str)) { + return lb_id_str; + } + } +} + +::grpc::lb::v1::LoadBalancingFeedback +LoadReporter::GenerateLoadBalancingFeedback() { + std::unique_lock lock(feedback_mu_); + auto now = std::chrono::system_clock::now(); + // Discard records outside the window until there is only one record + // outside the window, which is used as the base for difference. + while (feedback_records_.size() > 1 && + !IsRecordInWindow(feedback_records_[1], now)) { + feedback_records_.pop_front(); + } + if (feedback_records_.size() < 2) { + return ::grpc::lb::v1::LoadBalancingFeedback::default_instance(); + } + // Find the longest range with valid ends. + auto oldest = feedback_records_.begin(); + auto newest = feedback_records_.end() - 1; + while (std::distance(oldest, newest) > 0 && + (newest->cpu_limit == 0 || oldest->cpu_limit == 0)) { + // A zero limit means that the system info reading was failed, so these + // records can't be used to calculate CPU utilization. + if (newest->cpu_limit == 0) --newest; + if (oldest->cpu_limit == 0) ++oldest; + } + if (std::distance(oldest, newest) < 1 || + oldest->end_time == newest->end_time || + newest->cpu_limit == oldest->cpu_limit) { + return ::grpc::lb::v1::LoadBalancingFeedback::default_instance(); + } + uint64_t rpcs = 0; + uint64_t errors = 0; + for (auto p = newest; p != oldest; --p) { + // Because these two numbers are counters, the oldest record shouldn't be + // included. + rpcs += p->rpcs; + errors += p->errors; + } + double cpu_usage = newest->cpu_usage - oldest->cpu_usage; + double cpu_limit = newest->cpu_limit - oldest->cpu_limit; + std::chrono::duration duration_seconds = + newest->end_time - oldest->end_time; + lock.unlock(); + ::grpc::lb::v1::LoadBalancingFeedback feedback; + feedback.set_server_utilization(static_cast(cpu_usage / cpu_limit)); + feedback.set_calls_per_second( + static_cast(rpcs / duration_seconds.count())); + feedback.set_errors_per_second( + static_cast(errors / duration_seconds.count())); + return feedback; +} + +::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load> +LoadReporter::GenerateLoads(const grpc::string& hostname, + const grpc::string& lb_id) { + std::lock_guard lock(store_mu_); + auto assigned_stores = load_data_store_.GetAssignedStores(hostname, lb_id); + GPR_ASSERT(assigned_stores != nullptr); + GPR_ASSERT(!assigned_stores->empty()); + ::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load> loads; + for (PerBalancerStore* per_balancer_store : *assigned_stores) { + GPR_ASSERT(!per_balancer_store->IsSuspended()); + if (!per_balancer_store->load_record_map().empty()) { + for (const auto& p : per_balancer_store->load_record_map()) { + const auto& key = p.first; + const auto& value = p.second; + auto load = loads.Add(); + load->set_load_balance_tag(key.lb_tag()); + load->set_user_id(key.user_id()); + load->set_client_ip_address(key.GetClientIpBytes()); + load->set_num_calls_started(static_cast(value.start_count())); + load->set_num_calls_finished_without_error( + static_cast(value.ok_count())); + load->set_num_calls_finished_with_error( + static_cast(value.error_count())); + load->set_total_bytes_sent(static_cast(value.bytes_sent())); + load->set_total_bytes_received( + static_cast(value.bytes_recv())); + load->mutable_total_latency()->set_seconds( + static_cast(value.latency_ms() / 1000)); + load->mutable_total_latency()->set_nanos( + (static_cast(value.latency_ms()) % 1000) * 1000000); + for (const auto& p : value.call_metrics()) { + const grpc::string& metric_name = p.first; + const CallMetricValue& metric_value = p.second; + auto call_metric_data = load->add_metric_data(); + call_metric_data->set_metric_name(metric_name); + call_metric_data->set_num_calls_finished_with_metric( + metric_value.num_calls()); + call_metric_data->set_total_metric_value( + metric_value.total_metric_value()); + } + if (per_balancer_store->lb_id() != lb_id) { + // This per-balancer store is an orphan assigned to this receiving + // balancer. + AttachOrphanLoadId(load, *per_balancer_store); + } + } + per_balancer_store->ClearLoadRecordMap(); + } + if (per_balancer_store->IsNumCallsInProgressChangedSinceLastReport()) { + auto load = loads.Add(); + load->set_num_calls_in_progress( + per_balancer_store->GetNumCallsInProgressForReport()); + if (per_balancer_store->lb_id() != lb_id) { + // This per-balancer store is an orphan assigned to this receiving + // balancer. + AttachOrphanLoadId(load, *per_balancer_store); + } + } + } + return loads; +} + +void LoadReporter::AttachOrphanLoadId( + ::grpc::lb::v1::Load* load, const PerBalancerStore& per_balancer_store) { + if (per_balancer_store.lb_id() == kInvalidLbId) { + load->set_load_key_unknown(true); + } else { + // We shouldn't set load_key_unknown to any value in this case because + // load_key_unknown and orphaned_load_identifier are under an oneof struct. + load->mutable_orphaned_load_identifier()->set_load_key( + per_balancer_store.load_key()); + load->mutable_orphaned_load_identifier()->set_load_balancer_id( + per_balancer_store.lb_id()); + } +} + +void LoadReporter::AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors) { + CpuStatsProvider::CpuStatsSample cpu_stats; + if (cpu_stats_provider_ != nullptr) { + cpu_stats = cpu_stats_provider_->GetCpuStats(); + } else { + // This will make the load balancing feedback generation a no-op. + cpu_stats = {0, 0}; + } + std::unique_lock lock(feedback_mu_); + feedback_records_.emplace_back(std::chrono::system_clock::now(), rpcs, errors, + cpu_stats.first, cpu_stats.second); +} + +void LoadReporter::ReportStreamCreated(const grpc::string& hostname, + const grpc::string& lb_id, + const grpc::string& load_key) { + std::lock_guard lock(store_mu_); + load_data_store_.ReportStreamCreated(hostname, lb_id, load_key); + gpr_log(GPR_INFO, + "[LR %p] Report stream created (host: %s, LB ID: %s, load key: %s).", + this, hostname.c_str(), lb_id.c_str(), load_key.c_str()); +} + +void LoadReporter::ReportStreamClosed(const grpc::string& hostname, + const grpc::string& lb_id) { + std::lock_guard lock(store_mu_); + load_data_store_.ReportStreamClosed(hostname, lb_id); + gpr_log(GPR_INFO, "[LR %p] Report stream closed (host: %s, LB ID: %s).", this, + hostname.c_str(), lb_id.c_str()); +} + +void LoadReporter::ProcessViewDataCallStart( + const CensusViewProvider::ViewDataMap& view_data_map) { + auto it = view_data_map.find(kViewStartCount); + if (it != view_data_map.end()) { + for (const auto& p : it->second.int_data()) { + const std::vector& tag_values = p.first; + const uint64_t start_count = static_cast(p.second); + const grpc::string& client_ip_and_token = tag_values[0]; + const grpc::string& host = tag_values[1]; + const grpc::string& user_id = tag_values[2]; + LoadRecordKey key(client_ip_and_token, user_id); + LoadRecordValue value = LoadRecordValue(start_count); + { + std::unique_lock lock(store_mu_); + load_data_store_.MergeRow(host, key, value); + } + } + } +} + +void LoadReporter::ProcessViewDataCallEnd( + const CensusViewProvider::ViewDataMap& view_data_map) { + uint64_t total_end_count = 0; + uint64_t total_error_count = 0; + auto it = view_data_map.find(kViewEndCount); + if (it != view_data_map.end()) { + for (const auto& p : it->second.int_data()) { + const std::vector& tag_values = p.first; + const uint64_t end_count = static_cast(p.second); + const grpc::string& client_ip_and_token = tag_values[0]; + const grpc::string& host = tag_values[1]; + const grpc::string& user_id = tag_values[2]; + const grpc::string& status = tag_values[3]; + // This is due to a bug reported internally of Java server load reporting + // implementation. + // TODO(juanlishen): Check whether this situation happens in OSS C++. + if (client_ip_and_token.size() == 0) { + gpr_log(GPR_DEBUG, + "Skipping processing Opencensus record with empty " + "client_ip_and_token tag."); + continue; + } + LoadRecordKey key(client_ip_and_token, user_id); + const uint64_t bytes_sent = CensusViewProvider::GetRelatedViewDataRowInt( + view_data_map, kViewEndBytesSent, sizeof(kViewEndBytesSent) - 1, + tag_values); + const uint64_t bytes_received = + CensusViewProvider::GetRelatedViewDataRowInt( + view_data_map, kViewEndBytesReceived, + sizeof(kViewEndBytesReceived) - 1, tag_values); + const uint64_t latency_ms = CensusViewProvider::GetRelatedViewDataRowInt( + view_data_map, kViewEndLatencyMs, sizeof(kViewEndLatencyMs) - 1, + tag_values); + uint64_t ok_count = 0; + uint64_t error_count = 0; + total_end_count += end_count; + if (std::strcmp(status.c_str(), kCallStatusOk) == 0) { + ok_count = end_count; + } else { + error_count = end_count; + total_error_count += end_count; + } + LoadRecordValue value = LoadRecordValue( + 0, ok_count, error_count, bytes_sent, bytes_received, latency_ms); + { + std::unique_lock lock(store_mu_); + load_data_store_.MergeRow(host, key, value); + } + } + } + AppendNewFeedbackRecord(total_end_count, total_error_count); +} + +void LoadReporter::ProcessViewDataOtherCallMetrics( + const CensusViewProvider::ViewDataMap& view_data_map) { + auto it = view_data_map.find(kViewOtherCallMetricCount); + if (it != view_data_map.end()) { + for (const auto& p : it->second.int_data()) { + const std::vector& tag_values = p.first; + const int64_t num_calls = p.second; + const grpc::string& client_ip_and_token = tag_values[0]; + const grpc::string& host = tag_values[1]; + const grpc::string& user_id = tag_values[2]; + const grpc::string& metric_name = tag_values[3]; + LoadRecordKey key(client_ip_and_token, user_id); + const double total_metric_value = + CensusViewProvider::GetRelatedViewDataRowDouble( + view_data_map, kViewOtherCallMetricValue, + sizeof(kViewOtherCallMetricValue) - 1, tag_values); + LoadRecordValue value = LoadRecordValue( + metric_name, static_cast(num_calls), total_metric_value); + { + std::unique_lock lock(store_mu_); + load_data_store_.MergeRow(host, key, value); + } + } + } +} + +void LoadReporter::FetchAndSample() { + gpr_log(GPR_DEBUG, + "[LR %p] Starts fetching Census view data and sampling LB feedback " + "record.", + this); + CensusViewProvider::ViewDataMap view_data_map = + census_view_provider_->FetchViewData(); + ProcessViewDataCallStart(view_data_map); + ProcessViewDataCallEnd(view_data_map); + ProcessViewDataOtherCallMetrics(view_data_map); +} + +} // namespace load_reporter +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter.h b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.h new file mode 100644 index 00000000000000..b2254d56016042 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter.h @@ -0,0 +1,228 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H +#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H + +#include + +#include +#include +#include +#include + +#include +#include + +#include "src/cpp/server/load_reporter/load_data_store.h" +#include "src/proto/grpc/lb/v1/load_reporter.grpc.pb.h" + +#include "opencensus/stats/stats.h" + +namespace grpc { +namespace load_reporter { + +// The interface to get the Census stats. Abstracted for mocking. +class CensusViewProvider { + public: + // Maps from the view name to the view data. + using ViewDataMap = + std::unordered_map; + // Maps from the view name to the view descriptor. + using ViewDescriptorMap = + std::unordered_map; + + CensusViewProvider(); + virtual ~CensusViewProvider() = default; + + // Fetches the view data accumulated since last fetching, and returns it as a + // map from the view name to the view data. + virtual ViewDataMap FetchViewData() = 0; + + // A helper function that gets a row with the input tag values from the view + // data. Only used when we know that row must exist because we have seen a row + // with the same tag values in a related view data. Several ViewData's are + // considered related if their views are based on the measures that are always + // recorded at the same time. + static double GetRelatedViewDataRowDouble( + const ViewDataMap& view_data_map, const char* view_name, + size_t view_name_len, const std::vector& tag_values); + static uint64_t GetRelatedViewDataRowInt( + const ViewDataMap& view_data_map, const char* view_name, + size_t view_name_len, const std::vector& tag_values); + + protected: + const ViewDescriptorMap& view_descriptor_map() const { + return view_descriptor_map_; + } + + private: + ViewDescriptorMap view_descriptor_map_; + // Tag keys. + ::opencensus::stats::TagKey tag_key_token_; + ::opencensus::stats::TagKey tag_key_host_; + ::opencensus::stats::TagKey tag_key_user_id_; + ::opencensus::stats::TagKey tag_key_status_; + ::opencensus::stats::TagKey tag_key_metric_name_; +}; + +// The default implementation fetches the real stats from Census. +class CensusViewProviderDefaultImpl : public CensusViewProvider { + public: + CensusViewProviderDefaultImpl(); + + ViewDataMap FetchViewData() override; + + private: + std::unordered_map view_map_; +}; + +// The interface to get the CPU stats. Abstracted for mocking. +class CpuStatsProvider { + public: + // The used and total amounts of CPU usage. + using CpuStatsSample = std::pair; + + virtual ~CpuStatsProvider() = default; + + // Gets the cumulative used CPU and total CPU resource. + virtual CpuStatsSample GetCpuStats() = 0; +}; + +// The default implementation reads CPU jiffies from the system to calculate CPU +// utilization. +class CpuStatsProviderDefaultImpl : public CpuStatsProvider { + public: + CpuStatsSample GetCpuStats() override; +}; + +// Maintains all the load data and load reporting streams. +class LoadReporter { + public: + // TODO(juanlishen): Allow config for providers from users. + LoadReporter(uint32_t feedback_sample_window_seconds, + std::unique_ptr census_view_provider, + std::unique_ptr cpu_stats_provider) + : feedback_sample_window_seconds_(feedback_sample_window_seconds), + census_view_provider_(std::move(census_view_provider)), + cpu_stats_provider_(std::move(cpu_stats_provider)) { + // Append the initial record so that the next real record can have a base. + AppendNewFeedbackRecord(0, 0); + } + + // Fetches the latest data from Census and merge it into the data store. + // Also adds a new sample to the LB feedback sliding window. + // Thread-unsafe. (1). The access to the load data store and feedback records + // has locking. (2). The access to the Census view provider and CPU stats + // provider lacks locking, but we only access these two members in this method + // (in testing, we also access them when setting up expectation). So the + // invocations of this method must be serialized. + void FetchAndSample(); + + // Generates a report for that host and balancer. The report contains + // all the stats data accumulated between the last report (i.e., the last + // consumption) and the last fetch from Census (i.e., the last production). + // Thread-safe. + ::google::protobuf::RepeatedPtrField<::grpc::lb::v1::Load> GenerateLoads( + const grpc::string& hostname, const grpc::string& lb_id); + + // The feedback is calculated from the stats data recorded in the sliding + // window. Outdated records are discarded. + // Thread-safe. + ::grpc::lb::v1::LoadBalancingFeedback GenerateLoadBalancingFeedback(); + + // Wrapper around LoadDataStore::ReportStreamCreated. + // Thread-safe. + void ReportStreamCreated(const grpc::string& hostname, + const grpc::string& lb_id, + const grpc::string& load_key); + + // Wrapper around LoadDataStore::ReportStreamClosed. + // Thread-safe. + void ReportStreamClosed(const grpc::string& hostname, + const grpc::string& lb_id); + + // Generates a unique LB ID of length kLbIdLength. Returns an empty string + // upon failure. Thread-safe. + grpc::string GenerateLbId(); + + // Accessors only for testing. + CensusViewProvider* census_view_provider() { + return census_view_provider_.get(); + } + CpuStatsProvider* cpu_stats_provider() { return cpu_stats_provider_.get(); } + + private: + struct LoadBalancingFeedbackRecord { + std::chrono::system_clock::time_point end_time; + uint64_t rpcs; + uint64_t errors; + uint64_t cpu_usage; + uint64_t cpu_limit; + + LoadBalancingFeedbackRecord( + const std::chrono::system_clock::time_point& end_time, uint64_t rpcs, + uint64_t errors, uint64_t cpu_usage, uint64_t cpu_limit) + : end_time(end_time), + rpcs(rpcs), + errors(errors), + cpu_usage(cpu_usage), + cpu_limit(cpu_limit) {} + }; + + // Finds the view data about starting call from the view_data_map and merges + // the data to the load data store. + void ProcessViewDataCallStart( + const CensusViewProvider::ViewDataMap& view_data_map); + // Finds the view data about ending call from the view_data_map and merges the + // data to the load data store. + void ProcessViewDataCallEnd( + const CensusViewProvider::ViewDataMap& view_data_map); + // Finds the view data about the customized call metrics from the + // view_data_map and merges the data to the load data store. + void ProcessViewDataOtherCallMetrics( + const CensusViewProvider::ViewDataMap& view_data_map); + + bool IsRecordInWindow(const LoadBalancingFeedbackRecord& record, + std::chrono::system_clock::time_point now) { + return record.end_time > now - feedback_sample_window_seconds_; + } + + void AppendNewFeedbackRecord(uint64_t rpcs, uint64_t errors); + + // Extracts an OrphanedLoadIdentifier from the per-balancer store and attaches + // it to the load. + void AttachOrphanLoadId(::grpc::lb::v1::Load* load, + const PerBalancerStore& per_balancer_store); + + std::atomic next_lb_id_{0}; + const std::chrono::seconds feedback_sample_window_seconds_; + std::mutex feedback_mu_; + std::deque feedback_records_; + // TODO(juanlishen): Lock in finer grain. Locking the whole store may be + // too expensive. + std::mutex store_mu_; + LoadDataStore load_data_store_; + std::unique_ptr census_view_provider_; + std::unique_ptr cpu_stats_provider_; +}; + +} // namespace load_reporter +} // namespace grpc + +#endif // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_LOAD_REPORTER_H diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.cc new file mode 100644 index 00000000000000..d001199b8c5f5d --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.cc @@ -0,0 +1,370 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/server/load_reporter/load_reporter_async_service_impl.h" + +namespace grpc { +namespace load_reporter { + +void LoadReporterAsyncServiceImpl::CallableTag::Run(bool ok) { + GPR_ASSERT(handler_function_ != nullptr); + GPR_ASSERT(handler_ != nullptr); + handler_function_(std::move(handler_), ok); +} + +LoadReporterAsyncServiceImpl::LoadReporterAsyncServiceImpl( + std::unique_ptr cq) + : cq_(std::move(cq)) { + thread_ = std::unique_ptr<::grpc_core::Thread>( + new ::grpc_core::Thread("server_load_reporting", Work, this)); + std::unique_ptr cpu_stats_provider = nullptr; +#if defined(GPR_LINUX) || defined(GPR_WINDOWS) || defined(GPR_APPLE) + cpu_stats_provider.reset(new CpuStatsProviderDefaultImpl()); +#endif + load_reporter_ = std::unique_ptr(new LoadReporter( + kFeedbackSampleWindowSeconds, + std::unique_ptr(new CensusViewProviderDefaultImpl()), + std::move(cpu_stats_provider))); +} + +LoadReporterAsyncServiceImpl::~LoadReporterAsyncServiceImpl() { + // We will reach here after the server starts shutting down. + shutdown_ = true; + { + std::unique_lock lock(cq_shutdown_mu_); + cq_->Shutdown(); + } + if (next_fetch_and_sample_alarm_ != nullptr) + next_fetch_and_sample_alarm_->Cancel(); + thread_->Join(); +} + +void LoadReporterAsyncServiceImpl::ScheduleNextFetchAndSample() { + auto next_fetch_and_sample_time = + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(kFetchAndSampleIntervalSeconds * 1000, + GPR_TIMESPAN)); + { + std::unique_lock lock(cq_shutdown_mu_); + if (shutdown_) return; + // TODO(juanlishen): Improve the Alarm implementation to reuse a single + // instance for multiple events. + next_fetch_and_sample_alarm_.reset(new Alarm); + next_fetch_and_sample_alarm_->Set(cq_.get(), next_fetch_and_sample_time, + this); + } + gpr_log(GPR_DEBUG, "[LRS %p] Next fetch-and-sample scheduled.", this); +} + +void LoadReporterAsyncServiceImpl::FetchAndSample(bool ok) { + if (!ok) { + gpr_log(GPR_INFO, "[LRS %p] Fetch-and-sample is stopped.", this); + return; + } + gpr_log(GPR_DEBUG, "[LRS %p] Starting a fetch-and-sample...", this); + load_reporter_->FetchAndSample(); + ScheduleNextFetchAndSample(); +} + +void LoadReporterAsyncServiceImpl::Work(void* arg) { + LoadReporterAsyncServiceImpl* service = + reinterpret_cast(arg); + service->FetchAndSample(true /* ok */); + // TODO(juanlishen): This is a workaround to wait for the cq to be ready. Need + // to figure out why cq is not ready after service starts. + gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_seconds(1, GPR_TIMESPAN))); + ReportLoadHandler::CreateAndStart(service->cq_.get(), service, + service->load_reporter_.get()); + void* tag; + bool ok; + while (true) { + if (!service->cq_->Next(&tag, &ok)) { + // The completion queue is shutting down. + GPR_ASSERT(service->shutdown_); + break; + } + if (tag == service) { + service->FetchAndSample(ok); + } else { + auto* next_step = static_cast(tag); + next_step->Run(ok); + } + } +} + +void LoadReporterAsyncServiceImpl::StartThread() { thread_->Start(); } + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::CreateAndStart( + ServerCompletionQueue* cq, LoadReporterAsyncServiceImpl* service, + LoadReporter* load_reporter) { + std::shared_ptr handler = + std::make_shared(cq, service, load_reporter); + ReportLoadHandler* p = handler.get(); + { + std::unique_lock lock(service->cq_shutdown_mu_); + if (service->shutdown_) return; + p->on_done_notified_ = + CallableTag(std::bind(&ReportLoadHandler::OnDoneNotified, p, + std::placeholders::_1, std::placeholders::_2), + handler); + p->next_inbound_ = + CallableTag(std::bind(&ReportLoadHandler::OnRequestDelivered, p, + std::placeholders::_1, std::placeholders::_2), + std::move(handler)); + p->ctx_.AsyncNotifyWhenDone(&p->on_done_notified_); + service->RequestReportLoad(&p->ctx_, &p->stream_, cq, cq, + &p->next_inbound_); + } +} + +LoadReporterAsyncServiceImpl::ReportLoadHandler::ReportLoadHandler( + ServerCompletionQueue* cq, LoadReporterAsyncServiceImpl* service, + LoadReporter* load_reporter) + : cq_(cq), + service_(service), + load_reporter_(load_reporter), + stream_(&ctx_), + call_status_(WAITING_FOR_DELIVERY) {} + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnRequestDelivered( + std::shared_ptr self, bool ok) { + if (ok) { + call_status_ = DELIVERED; + } else { + // AsyncNotifyWhenDone() needs to be called before the call starts, but the + // tag will not pop out if the call never starts ( + // https://github.com/grpc/grpc/issues/10136). So we need to manually + // release the ownership of the handler in this case. + GPR_ASSERT(on_done_notified_.ReleaseHandler() != nullptr); + } + if (!ok || shutdown_) { + // The value of ok being false means that the server is shutting down. + Shutdown(std::move(self), "OnRequestDelivered"); + return; + } + // Spawn a new handler instance to serve the next new client. Every handler + // instance will deallocate itself when it's done. + CreateAndStart(cq_, service_, load_reporter_); + { + std::unique_lock lock(service_->cq_shutdown_mu_); + if (service_->shutdown_) { + lock.release()->unlock(); + Shutdown(std::move(self), "OnRequestDelivered"); + return; + } + next_inbound_ = + CallableTag(std::bind(&ReportLoadHandler::OnReadDone, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + stream_.Read(&request_, &next_inbound_); + } + // LB ID is unique for each load reporting stream. + lb_id_ = load_reporter_->GenerateLbId(); + gpr_log(GPR_INFO, + "[LRS %p] Call request delivered (lb_id_: %s, handler: %p). " + "Start reading the initial request...", + service_, lb_id_.c_str(), this); +} + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnReadDone( + std::shared_ptr self, bool ok) { + if (!ok || shutdown_) { + if (!ok && call_status_ < INITIAL_REQUEST_RECEIVED) { + // The client may have half-closed the stream or the stream is broken. + gpr_log(GPR_INFO, + "[LRS %p] Failed reading the initial request from the stream " + "(lb_id_: %s, handler: %p, done_notified: %d, is_cancelled: %d).", + service_, lb_id_.c_str(), this, static_cast(done_notified_), + static_cast(is_cancelled_)); + } + Shutdown(std::move(self), "OnReadDone"); + return; + } + // We only receive one request, which is the initial request. + if (call_status_ < INITIAL_REQUEST_RECEIVED) { + if (!request_.has_initial_request()) { + Shutdown(std::move(self), "OnReadDone+initial_request_not_found"); + } else { + call_status_ = INITIAL_REQUEST_RECEIVED; + const auto& initial_request = request_.initial_request(); + load_balanced_hostname_ = initial_request.load_balanced_hostname(); + load_key_ = initial_request.load_key(); + load_reporter_->ReportStreamCreated(load_balanced_hostname_, lb_id_, + load_key_); + const auto& load_report_interval = initial_request.load_report_interval(); + load_report_interval_ms_ = + static_cast(load_report_interval.seconds() * 1000 + + load_report_interval.nanos() / 1000); + gpr_log( + GPR_INFO, + "[LRS %p] Initial request received. Start load reporting (load " + "balanced host: %s, interval: %lu ms, lb_id_: %s, handler: %p)...", + service_, load_balanced_hostname_.c_str(), load_report_interval_ms_, + lb_id_.c_str(), this); + SendReport(self, true /* ok */); + // Expect this read to fail. + { + std::unique_lock lock(service_->cq_shutdown_mu_); + if (service_->shutdown_) { + lock.release()->unlock(); + Shutdown(std::move(self), "OnReadDone"); + return; + } + next_inbound_ = + CallableTag(std::bind(&ReportLoadHandler::OnReadDone, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + stream_.Read(&request_, &next_inbound_); + } + } + } else { + // Another request received! This violates the spec. + gpr_log(GPR_ERROR, + "[LRS %p] Another request received (lb_id_: %s, handler: %p).", + service_, lb_id_.c_str(), this); + Shutdown(std::move(self), "OnReadDone+second_request"); + } +} + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::ScheduleNextReport( + std::shared_ptr self, bool ok) { + if (!ok || shutdown_) { + Shutdown(std::move(self), "ScheduleNextReport"); + return; + } + auto next_report_time = gpr_time_add( + gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(load_report_interval_ms_, GPR_TIMESPAN)); + { + std::unique_lock lock(service_->cq_shutdown_mu_); + if (service_->shutdown_) { + lock.release()->unlock(); + Shutdown(std::move(self), "ScheduleNextReport"); + return; + } + next_outbound_ = + CallableTag(std::bind(&ReportLoadHandler::SendReport, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + // TODO(juanlishen): Improve the Alarm implementation to reuse a single + // instance for multiple events. + next_report_alarm_.reset(new Alarm); + next_report_alarm_->Set(cq_, next_report_time, &next_outbound_); + } + gpr_log(GPR_DEBUG, + "[LRS %p] Next load report scheduled (lb_id_: %s, handler: %p).", + service_, lb_id_.c_str(), this); +} + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::SendReport( + std::shared_ptr self, bool ok) { + if (!ok || shutdown_) { + Shutdown(std::move(self), "SendReport"); + return; + } + ::grpc::lb::v1::LoadReportResponse response; + auto loads = load_reporter_->GenerateLoads(load_balanced_hostname_, lb_id_); + response.mutable_load()->Swap(&loads); + auto feedback = load_reporter_->GenerateLoadBalancingFeedback(); + response.mutable_load_balancing_feedback()->Swap(&feedback); + if (call_status_ < INITIAL_RESPONSE_SENT) { + auto initial_response = response.mutable_initial_response(); + initial_response->set_load_balancer_id(lb_id_); + initial_response->set_implementation_id( + ::grpc::lb::v1::InitialLoadReportResponse::CPP); + initial_response->set_server_version(kVersion); + call_status_ = INITIAL_RESPONSE_SENT; + } + { + std::unique_lock lock(service_->cq_shutdown_mu_); + if (service_->shutdown_) { + lock.release()->unlock(); + Shutdown(std::move(self), "SendReport"); + return; + } + next_outbound_ = + CallableTag(std::bind(&ReportLoadHandler::ScheduleNextReport, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + stream_.Write(response, &next_outbound_); + gpr_log(GPR_INFO, + "[LRS %p] Sending load report (lb_id_: %s, handler: %p, loads " + "count: %d)...", + service_, lb_id_.c_str(), this, response.load().size()); + } +} + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnDoneNotified( + std::shared_ptr self, bool ok) { + GPR_ASSERT(ok); + done_notified_ = true; + if (ctx_.IsCancelled()) { + is_cancelled_ = true; + } + gpr_log(GPR_INFO, + "[LRS %p] Load reporting call is notified done (handler: %p, " + "is_cancelled: %d).", + service_, this, static_cast(is_cancelled_)); + Shutdown(std::move(self), "OnDoneNotified"); +} + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::Shutdown( + std::shared_ptr self, const char* reason) { + if (!shutdown_) { + gpr_log(GPR_INFO, + "[LRS %p] Shutting down the handler (lb_id_: %s, handler: %p, " + "reason: %s).", + service_, lb_id_.c_str(), this, reason); + shutdown_ = true; + if (call_status_ >= INITIAL_REQUEST_RECEIVED) { + load_reporter_->ReportStreamClosed(load_balanced_hostname_, lb_id_); + next_report_alarm_->Cancel(); + } + } + // OnRequestDelivered() may be called after OnDoneNotified(), so we need to + // try to Finish() every time we are in Shutdown(). + if (call_status_ >= DELIVERED && call_status_ < FINISH_CALLED) { + std::unique_lock lock(service_->cq_shutdown_mu_); + if (!service_->shutdown_) { + on_finish_done_ = + CallableTag(std::bind(&ReportLoadHandler::OnFinishDone, this, + std::placeholders::_1, std::placeholders::_2), + std::move(self)); + // TODO(juanlishen): Maybe add a message proto for the client to + // explicitly cancel the stream so that we can return OK status in such + // cases. + stream_.Finish(Status::CANCELLED, &on_finish_done_); + call_status_ = FINISH_CALLED; + } + } +} + +void LoadReporterAsyncServiceImpl::ReportLoadHandler::OnFinishDone( + std::shared_ptr self, bool ok) { + if (ok) { + gpr_log(GPR_INFO, + "[LRS %p] Load reporting finished (lb_id_: %s, handler: %p).", + service_, lb_id_.c_str(), this); + } +} + +} // namespace load_reporter +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.h b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.h new file mode 100644 index 00000000000000..6fc577ff4931a9 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporter_async_service_impl.h @@ -0,0 +1,194 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SRC_CPP_SERVER_LOAD_REPORTER_ASYNC_SERVICE_IMPL_H +#define GRPC_SRC_CPP_SERVER_LOAD_REPORTER_ASYNC_SERVICE_IMPL_H + +#include + +#include +#include +#include + +#include "src/core/lib/gprpp/thd.h" +#include "src/cpp/server/load_reporter/load_reporter.h" + +namespace grpc { +namespace load_reporter { + +// Async load reporting service. It's mainly responsible for controlling the +// procedure of incoming requests. The real business logic is handed off to the +// LoadReporter. There should be at most one instance of this service on a +// server to avoid spreading the load data into multiple places. +class LoadReporterAsyncServiceImpl + : public grpc::lb::v1::LoadReporter::AsyncService { + public: + explicit LoadReporterAsyncServiceImpl( + std::unique_ptr cq); + ~LoadReporterAsyncServiceImpl(); + + // Starts the working thread. + void StartThread(); + + // Not copyable nor movable. + LoadReporterAsyncServiceImpl(const LoadReporterAsyncServiceImpl&) = delete; + LoadReporterAsyncServiceImpl& operator=(const LoadReporterAsyncServiceImpl&) = + delete; + + private: + class ReportLoadHandler; + + // A tag that can be called with a bool argument. It's tailored for + // ReportLoadHandler's use. Before being used, it should be constructed with a + // method of ReportLoadHandler and a shared pointer to the handler. The + // shared pointer will be moved to the invoked function and the function can + // only be invoked once. That makes ref counting of the handler easier, + // because the shared pointer is not bound to the function and can be gone + // once the invoked function returns (if not used any more). + class CallableTag { + public: + using HandlerFunction = + std::function, bool)>; + + CallableTag() {} + + CallableTag(HandlerFunction func, + std::shared_ptr handler) + : handler_function_(std::move(func)), handler_(std::move(handler)) { + GPR_ASSERT(handler_function_ != nullptr); + GPR_ASSERT(handler_ != nullptr); + } + + // Runs the tag. This should be called only once. The handler is no longer + // owned by this tag after this method is invoked. + void Run(bool ok); + + // Releases and returns the shared pointer to the handler. + std::shared_ptr ReleaseHandler() { + return std::move(handler_); + } + + private: + HandlerFunction handler_function_ = nullptr; + std::shared_ptr handler_; + }; + + // Each handler takes care of one load reporting stream. It contains + // per-stream data and it will access the members of the parent class (i.e., + // LoadReporterAsyncServiceImpl) for service-wide data (e.g., the load data). + class ReportLoadHandler { + public: + // Instantiates a ReportLoadHandler and requests the next load reporting + // call. The handler object will manage its own lifetime, so no action is + // needed from the caller any more regarding that object. + static void CreateAndStart(ServerCompletionQueue* cq, + LoadReporterAsyncServiceImpl* service, + LoadReporter* load_reporter); + + // This ctor is public because we want to use std::make_shared<> in + // CreateAndStart(). This ctor shouldn't be used elsewhere. + ReportLoadHandler(ServerCompletionQueue* cq, + LoadReporterAsyncServiceImpl* service, + LoadReporter* load_reporter); + + private: + // After the handler has a call request delivered, it starts reading the + // initial request. Also, a new handler is spawned so that we can keep + // servicing future calls. + void OnRequestDelivered(std::shared_ptr self, bool ok); + + // The first Read() is expected to succeed, after which the handler starts + // sending load reports back to the balancer. The second Read() is + // expected to fail, which happens when the balancer half-closes the + // stream to signal that it's no longer interested in the load reports. For + // the latter case, the handler will then close the stream. + void OnReadDone(std::shared_ptr self, bool ok); + + // The report sending operations are sequential as: send report -> send + // done, schedule the next send -> waiting for the alarm to fire -> alarm + // fires, send report -> ... + void SendReport(std::shared_ptr self, bool ok); + void ScheduleNextReport(std::shared_ptr self, bool ok); + + // Called when Finish() is done. + void OnFinishDone(std::shared_ptr self, bool ok); + + // Called when AsyncNotifyWhenDone() notifies us. + void OnDoneNotified(std::shared_ptr self, bool ok); + + void Shutdown(std::shared_ptr self, const char* reason); + + // The key fields of the stream. + grpc::string lb_id_; + grpc::string load_balanced_hostname_; + grpc::string load_key_; + uint64_t load_report_interval_ms_; + + // The data for RPC communication with the load reportee. + ServerContext ctx_; + ::grpc::lb::v1::LoadReportRequest request_; + + // The members passed down from LoadReporterAsyncServiceImpl. + ServerCompletionQueue* cq_; + LoadReporterAsyncServiceImpl* service_; + LoadReporter* load_reporter_; + ServerAsyncReaderWriter<::grpc::lb::v1::LoadReportResponse, + ::grpc::lb::v1::LoadReportRequest> + stream_; + + // The status of the RPC progress. + enum CallStatus { + WAITING_FOR_DELIVERY, + DELIVERED, + INITIAL_REQUEST_RECEIVED, + INITIAL_RESPONSE_SENT, + FINISH_CALLED + } call_status_; + bool shutdown_{false}; + bool done_notified_{false}; + bool is_cancelled_{false}; + CallableTag on_done_notified_; + CallableTag on_finish_done_; + CallableTag next_inbound_; + CallableTag next_outbound_; + std::unique_ptr next_report_alarm_; + }; + + // Handles the incoming requests and drives the completion queue in a loop. + static void Work(void* arg); + + // Schedules the next data fetching from Census and LB feedback sampling. + void ScheduleNextFetchAndSample(); + + // Fetches data from Census and samples LB feedback. + void FetchAndSample(bool ok); + + std::unique_ptr cq_; + // To synchronize the operations related to shutdown state of cq_, so that we + // don't enqueue new tags into cq_ after it is already shut down. + std::mutex cq_shutdown_mu_; + std::atomic_bool shutdown_{false}; + std::unique_ptr<::grpc_core::Thread> thread_; + std::unique_ptr load_reporter_; + std::unique_ptr next_fetch_and_sample_alarm_; +}; + +} // namespace load_reporter +} // namespace grpc + +#endif // GRPC_SRC_CPP_SERVER_LOAD_REPORTER_ASYNC_SERVICE_IMPL_H diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc new file mode 100644 index 00000000000000..81cf6ac562de05 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_option.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h" + +namespace grpc { +namespace load_reporter { +namespace experimental { + +void LoadReportingServiceServerBuilderOption::UpdateArguments( + ::grpc::ChannelArguments* args) { + args->SetInt(GRPC_ARG_ENABLE_LOAD_REPORTING, true); +} + +void LoadReportingServiceServerBuilderOption::UpdatePlugins( + std::vector>* plugins) { + plugins->emplace_back(new LoadReportingServiceServerBuilderPlugin()); +} + +} // namespace experimental +} // namespace load_reporter +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc new file mode 100644 index 00000000000000..c2c5dd5ed5cbcb --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.cc @@ -0,0 +1,60 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h" + +#include + +namespace grpc { +namespace load_reporter { + +bool LoadReportingServiceServerBuilderPlugin::has_sync_methods() const { + if (service_ != nullptr) { + return service_->has_synchronous_methods(); + } + return false; +} + +bool LoadReportingServiceServerBuilderPlugin::has_async_methods() const { + if (service_ != nullptr) { + return service_->has_async_methods(); + } + return false; +} + +void LoadReportingServiceServerBuilderPlugin::UpdateServerBuilder( + grpc::ServerBuilder* builder) { + auto cq = builder->AddCompletionQueue(); + service_ = std::make_shared(std::move(cq)); +} + +void LoadReportingServiceServerBuilderPlugin::InitServer( + grpc::ServerInitializer* si) { + si->RegisterService(service_); +} + +void LoadReportingServiceServerBuilderPlugin::Finish( + grpc::ServerInitializer* si) { + service_->StartThread(); + service_.reset(); +} + +} // namespace load_reporter +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h new file mode 100644 index 00000000000000..1f098591d4d7f6 --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/load_reporting_service_server_builder_plugin.h @@ -0,0 +1,62 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_SRC_CPP_LOAD_REPORTING_SERVICE_SERVER_BUILDER_PLUGIN_H +#define GRPC_SRC_CPP_LOAD_REPORTING_SERVICE_SERVER_BUILDER_PLUGIN_H + +#include + +#include + +#include "src/cpp/server/load_reporter/load_reporter_async_service_impl.h" + +namespace grpc { +namespace load_reporter { + +// The plugin that registers and starts load reporting service when starting a +// server. +class LoadReportingServiceServerBuilderPlugin : public ServerBuilderPlugin { + public: + ~LoadReportingServiceServerBuilderPlugin() override = default; + grpc::string name() override { return "load_reporting_service"; } + + // Creates a load reporting service. + void UpdateServerBuilder(grpc::ServerBuilder* builder) override; + + // Registers the load reporter service. + void InitServer(grpc::ServerInitializer* si) override; + + // Starts the load reporter service. + void Finish(grpc::ServerInitializer* si) override; + + void ChangeArguments(const grpc::string& name, void* value) override {} + void UpdateChannelArguments(grpc::ChannelArguments* args) override {} + bool has_sync_methods() const override; + bool has_async_methods() const override; + + private: + std::shared_ptr service_; +}; + +std::unique_ptr +CreateLoadReportingServiceServerBuilderPlugin(); + +} // namespace load_reporter +} // namespace grpc + +#endif // GRPC_SRC_CPP_LOAD_REPORTING_SERVICE_SERVER_BUILDER_PLUGIN_H diff --git a/third_party/grpc/src/cpp/server/load_reporter/util.cc b/third_party/grpc/src/cpp/server/load_reporter/util.cc new file mode 100644 index 00000000000000..89bdf57049c72b --- /dev/null +++ b/third_party/grpc/src/cpp/server/load_reporter/util.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2018 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include + +namespace grpc { +namespace load_reporter { +namespace experimental { + +void AddLoadReportingCost(grpc::ServerContext* ctx, + const grpc::string& cost_name, double cost_value) { + if (std::isnormal(cost_value)) { + grpc::string buf; + buf.resize(sizeof(cost_value) + cost_name.size()); + memcpy(&(*buf.begin()), &cost_value, sizeof(cost_value)); + memcpy(&(*buf.begin()) + sizeof(cost_value), cost_name.data(), + cost_name.size()); + ctx->AddTrailingMetadata(GRPC_LB_COST_MD_KEY, buf); + } else { + gpr_log(GPR_ERROR, "Call metric value is not normal."); + } +} + +} // namespace experimental +} // namespace load_reporter +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/secure_server_credentials.cc b/third_party/grpc/src/cpp/server/secure_server_credentials.cc index d472667a7eec32..453e76eb25dfb9 100644 --- a/third_party/grpc/src/cpp/server/secure_server_credentials.cc +++ b/third_party/grpc/src/cpp/server/secure_server_credentials.cc @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -35,31 +20,33 @@ #include #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); + auto* w = static_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); + auto* w = static_cast(wrapper); if (!w->processor_) { // 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)); + w->thread_pool_->Add([w, context, md, num_md, cb, user_data] { + w->AuthMetadataProcessorAyncWrapper::InvokeProcessor(context, md, num_md, + cb, user_data); + }); } else { // invoke directly. w->InvokeProcessor(context, md, num_md, cb, user_data); @@ -71,10 +58,10 @@ void AuthMetadataProcessorAyncWrapper::InvokeProcessor( 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))); + metadata.insert(std::make_pair(StringRefFromSlice(&md[i].key), + StringRefFromSlice(&md[i].value))); } - SecureAuthContext context(ctx, false); + SecureAuthContext context(ctx); AuthMetadataProcessor::OutputMetadata consumed_metadata; AuthMetadataProcessor::OutputMetadata response_metadata; @@ -85,9 +72,8 @@ void AuthMetadataProcessorAyncWrapper::InvokeProcessor( for (auto it = consumed_metadata.begin(); it != consumed_metadata.end(); ++it) { grpc_metadata md_entry; - md_entry.key = it->first.c_str(); - md_entry.value = it->second.data(); - md_entry.value_length = it->second.size(); + md_entry.key = SliceReferencingString(it->first); + md_entry.value = SliceReferencingString(it->second); md_entry.flags = 0; consumed_md.push_back(md_entry); } @@ -95,9 +81,8 @@ void AuthMetadataProcessorAyncWrapper::InvokeProcessor( for (auto it = response_metadata.begin(); it != response_metadata.end(); ++it) { grpc_metadata md_entry; - md_entry.key = it->first.c_str(); - md_entry.value = it->second.data(); - md_entry.value_length = it->second.size(); + md_entry.key = SliceReferencingString(it->first); + md_entry.value = SliceReferencingString(it->second); md_entry.flags = 0; response_md.push_back(md_entry); } @@ -130,12 +115,36 @@ std::shared_ptr SslServerCredentials( key_cert_pair->cert_chain.c_str()}; pem_key_cert_pairs.push_back(p); } - grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create( + grpc_server_credentials* c_creds = grpc_ssl_server_credentials_create_ex( 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); + pem_key_cert_pairs.size(), + options.force_client_auth + ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + : options.client_certificate_request, + nullptr); + return std::shared_ptr( + new SecureServerCredentials(c_creds)); +} + +namespace experimental { + +std::shared_ptr AltsServerCredentials( + const AltsServerCredentialsOptions& options) { + grpc_alts_credentials_options* c_options = + grpc_alts_credentials_server_options_create(); + grpc_server_credentials* c_creds = + grpc_alts_server_credentials_create(c_options); + grpc_alts_credentials_options_destroy(c_options); return std::shared_ptr( new SecureServerCredentials(c_creds)); } +std::shared_ptr LocalServerCredentials( + grpc_local_connect_type type) { + return std::shared_ptr( + new SecureServerCredentials(grpc_local_server_credentials_create(type))); +} + +} // namespace experimental } // namespace grpc diff --git a/third_party/grpc/src/cpp/server/secure_server_credentials.h b/third_party/grpc/src/cpp/server/secure_server_credentials.h index 5460f4a02c3034..8a81af2f59155a 100644 --- a/third_party/grpc/src/cpp/server/secure_server_credentials.h +++ b/third_party/grpc/src/cpp/server/secure_server_credentials.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -36,7 +21,7 @@ #include -#include +#include #include @@ -44,7 +29,7 @@ namespace grpc { -class AuthMetadataProcessorAyncWrapper GRPC_FINAL { +class AuthMetadataProcessorAyncWrapper final { public: static void Destroy(void* wrapper); @@ -64,19 +49,18 @@ class AuthMetadataProcessorAyncWrapper GRPC_FINAL { std::shared_ptr processor_; }; -class SecureServerCredentials GRPC_FINAL : public ServerCredentials { +class SecureServerCredentials final : public ServerCredentials { public: explicit SecureServerCredentials(grpc_server_credentials* creds) : creds_(creds) {} - ~SecureServerCredentials() GRPC_OVERRIDE { + ~SecureServerCredentials() override { grpc_server_credentials_release(creds_); } - int AddPortToServer(const grpc::string& addr, - grpc_server* server) GRPC_OVERRIDE; + int AddPortToServer(const grpc::string& addr, grpc_server* server) override; void SetAuthMetadataProcessor( - const std::shared_ptr& processor) GRPC_OVERRIDE; + const std::shared_ptr& processor) override; private: grpc_server_credentials* creds_; diff --git a/third_party/grpc/src/cpp/server/server.cc b/third_party/grpc/src/cpp/server/server.cc deleted file mode 100644 index 0d31140924bfcc..00000000000000 --- a/third_party/grpc/src/cpp/server/server.cc +++ /dev/null @@ -1,588 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#include - -#include - -#include -#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 DefaultGlobalCallbacks GRPC_FINAL : public Server::GlobalCallbacks { - public: - ~DefaultGlobalCallbacks() GRPC_OVERRIDE {} - void PreSynchronousRequest(ServerContext* context) GRPC_OVERRIDE {} - void PostSynchronousRequest(ServerContext* context) GRPC_OVERRIDE {} -}; - -static std::shared_ptr g_callbacks = nullptr; -static gpr_once g_once_init_callbacks = GPR_ONCE_INIT; - -static void InitGlobalCallbacks() { - if (g_callbacks == nullptr) { - g_callbacks.reset(new DefaultGlobalCallbacks()); - } -} - -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_UNREACHABLE_CODE(return false); - } - - 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(std::shared_ptr global_callbacks) { - ctx_.BeginCompletionOp(&call_); - global_callbacks->PreSynchronousRequest(&ctx_); - method_->handler()->RunHandler(MethodHandler::HandlerParameter( - &call_, &ctx_, request_payload_, call_.max_message_size())); - global_callbacks->PostSynchronousRequest(&ctx_); - 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(const ChannelArguments& args) { - grpc_channel_args channel_args; - args.SetChannelArgs(&channel_args); - return grpc_server_create(&channel_args, nullptr); -} - -static internal::GrpcLibraryInitializer g_gli_initializer; -Server::Server(ThreadPoolInterface* thread_pool, bool thread_pool_owned, - int max_message_size, const ChannelArguments& args) - : 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(args)), - thread_pool_(thread_pool), - thread_pool_owned_(thread_pool_owned) { - g_gli_initializer.summon(); - gpr_once_init(&g_once_init_callbacks, InitGlobalCallbacks); - global_callbacks_ = g_callbacks; - grpc_server_register_completion_queue(server_, cq_.cq(), nullptr); -} - -Server::~Server() { - { - grpc::unique_lock lock(mu_); - if (started_ && !shutdown_) { - lock.unlock(); - Shutdown(); - } else if (!started_) { - cq_.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_; -} - -void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) { - GPR_ASSERT(g_callbacks == nullptr); - GPR_ASSERT(callbacks != nullptr); - g_callbacks.reset(callbacks); -} - -bool Server::RegisterService(const grpc::string* host, Service* service) { - bool has_async_methods = service->has_async_methods(); - if (has_async_methods) { - GPR_ASSERT(service->server_ == nullptr && - "Can only register an asynchronous service against one server."); - service->server_ = this; - } - for (auto it = service->methods_.begin(); it != service->methods_.end(); - ++it) { - if (it->get() == nullptr) { // Handled by generic service if any. - continue; - } - RpcServiceMethod* method = it->get(); - void* tag = grpc_server_register_method(server_, method->name(), - host ? host->c_str() : nullptr); - if (tag == nullptr) { - gpr_log(GPR_DEBUG, "Attempt to register %s multiple times", - method->name()); - return false; - } - if (method->handler() == nullptr) { - method->set_server_tag(tag); - } else { - sync_methods_->emplace_back(method, 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(); - lock.unlock(); - // 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); - } - } - lock.lock(); - - // 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); -} - -ServerInterface::BaseAsyncRequest::BaseAsyncRequest( - ServerInterface* 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_)); -} - -bool ServerInterface::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; -} - -ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest( - ServerInterface* server, ServerContext* context, - ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, void* tag) - : BaseAsyncRequest(server, context, stream, call_cq, tag, true) {} - -void ServerInterface::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); -} - -ServerInterface::GenericAsyncRequest::GenericAsyncRequest( - ServerInterface* 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 ServerInterface::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; - GPR_TIMER_SCOPE("Server::RunRpc", 0); - 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(); - } - } - GPR_TIMER_SCOPE("cd.Run()", 0); - cd.Run(global_callbacks_); - } - } - - { - grpc::unique_lock lock(mu_); - num_running_cb_--; - if (shutdown_) { - callback_cv_.notify_all(); - } - } -} - -} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/server_builder.cc b/third_party/grpc/src/cpp/server/server_builder.cc index a8c188e5a55dd2..0dc03b687689c5 100644 --- a/third_party/grpc/src/cpp/server/server_builder.cc +++ b/third_party/grpc/src/cpp/server/server_builder.cc @@ -1,120 +1,346 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015-2016 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include #include #include -#include -#include +#include +#include +#include + +#include + +#include "src/core/lib/gpr/useful.h" #include "src/cpp/server/thread_pool_interface.h" -#include "src/cpp/server/fixed_size_thread_pool.h" namespace grpc { +static std::vector (*)()>* + g_plugin_factory_list; +static gpr_once once_init_plugin_list = GPR_ONCE_INIT; + +static void do_plugin_list_init(void) { + g_plugin_factory_list = + new std::vector (*)()>(); +} + ServerBuilder::ServerBuilder() - : max_message_size_(-1), generic_service_(nullptr) { - grpc_compression_options_init(&compression_options_); + : max_receive_message_size_(INT_MIN), + max_send_message_size_(INT_MIN), + sync_server_settings_(SyncServerSettings()), + resource_quota_(nullptr), + generic_service_(nullptr) { + gpr_once_init(&once_init_plugin_list, do_plugin_list_init); + for (auto it = g_plugin_factory_list->begin(); + it != g_plugin_factory_list->end(); it++) { + auto& factory = *it; + plugins_.emplace_back(factory()); + } + + // all compression algorithms enabled by default. + enabled_compression_algorithms_bitset_ = + (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; + memset(&maybe_default_compression_level_, 0, + sizeof(maybe_default_compression_level_)); + memset(&maybe_default_compression_algorithm_, 0, + sizeof(maybe_default_compression_algorithm_)); +} + +ServerBuilder::~ServerBuilder() { + if (resource_quota_ != nullptr) { + grpc_resource_quota_unref(resource_quota_); + } } -std::unique_ptr ServerBuilder::AddCompletionQueue() { - ServerCompletionQueue* cq = new ServerCompletionQueue(); +std::unique_ptr ServerBuilder::AddCompletionQueue( + bool is_frequently_polled) { + ServerCompletionQueue* cq = new ServerCompletionQueue( + GRPC_CQ_NEXT, + is_frequently_polled ? GRPC_CQ_DEFAULT_POLLING : GRPC_CQ_NON_LISTENING, + nullptr); cqs_.push_back(cq); return std::unique_ptr(cq); } -void ServerBuilder::RegisterService(Service* service) { +ServerBuilder& ServerBuilder::RegisterService(Service* service) { services_.emplace_back(new NamedService(service)); + return *this; } -void ServerBuilder::RegisterService(const grpc::string& addr, - Service* service) { +ServerBuilder& ServerBuilder::RegisterService(const grpc::string& addr, + Service* service) { services_.emplace_back(new NamedService(addr, service)); + return *this; } -void ServerBuilder::RegisterAsyncGenericService(AsyncGenericService* service) { +ServerBuilder& ServerBuilder::RegisterAsyncGenericService( + AsyncGenericService* service) { if (generic_service_) { gpr_log(GPR_ERROR, "Adding multiple AsyncGenericService is unsupported for now. " "Dropping the service %p", - service); - return; + (void*)service); + } else { + generic_service_ = service; } - generic_service_ = service; + return *this; } -void ServerBuilder::SetOption(std::unique_ptr option) { +ServerBuilder& ServerBuilder::SetOption( + std::unique_ptr option) { options_.push_back(std::move(option)); + return *this; +} + +ServerBuilder& ServerBuilder::SetSyncServerOption( + ServerBuilder::SyncServerOption option, int val) { + switch (option) { + case NUM_CQS: + sync_server_settings_.num_cqs = val; + break; + case MIN_POLLERS: + sync_server_settings_.min_pollers = val; + break; + case MAX_POLLERS: + sync_server_settings_.max_pollers = val; + break; + case CQ_TIMEOUT_MSEC: + sync_server_settings_.cq_timeout_msec = val; + break; + } + return *this; +} + +ServerBuilder& ServerBuilder::SetCompressionAlgorithmSupportStatus( + grpc_compression_algorithm algorithm, bool enabled) { + if (enabled) { + GPR_BITSET(&enabled_compression_algorithms_bitset_, algorithm); + } else { + GPR_BITCLEAR(&enabled_compression_algorithms_bitset_, algorithm); + } + return *this; } -void ServerBuilder::AddListeningPort(const grpc::string& addr, - std::shared_ptr creds, - int* selected_port) { - Port port = {addr, creds, selected_port}; +ServerBuilder& ServerBuilder::SetDefaultCompressionLevel( + grpc_compression_level level) { + maybe_default_compression_level_.level = level; + return *this; +} + +ServerBuilder& ServerBuilder::SetDefaultCompressionAlgorithm( + grpc_compression_algorithm algorithm) { + maybe_default_compression_algorithm_.is_set = true; + maybe_default_compression_algorithm_.algorithm = algorithm; + return *this; +} + +ServerBuilder& ServerBuilder::SetResourceQuota( + const grpc::ResourceQuota& resource_quota) { + if (resource_quota_ != nullptr) { + grpc_resource_quota_unref(resource_quota_); + } + resource_quota_ = resource_quota.c_resource_quota(); + grpc_resource_quota_ref(resource_quota_); + return *this; +} + +ServerBuilder& ServerBuilder::AddListeningPort( + const grpc::string& addr_uri, std::shared_ptr creds, + int* selected_port) { + const grpc::string uri_scheme = "dns:"; + grpc::string addr = addr_uri; + if (addr_uri.compare(0, uri_scheme.size(), uri_scheme) == 0) { + size_t pos = uri_scheme.size(); + while (addr_uri[pos] == '/') ++pos; // Skip slashes. + addr = addr_uri.substr(pos); + } + Port port = {addr, std::move(creds), selected_port}; ports_.push_back(port); + return *this; } std::unique_ptr ServerBuilder::BuildAndStart() { - std::unique_ptr thread_pool; + ChannelArguments args; + for (auto option = options_.begin(); option != options_.end(); ++option) { + (*option)->UpdateArguments(&args); + (*option)->UpdatePlugins(&plugins_); + } + + for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) { + (*plugin)->UpdateServerBuilder(this); + (*plugin)->UpdateChannelArguments(&args); + } + + if (max_receive_message_size_ >= -1) { + args.SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, max_receive_message_size_); + } + + // The default message size is -1 (max), so no need to explicitly set it for + // -1. + if (max_send_message_size_ >= 0) { + args.SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, max_send_message_size_); + } + + args.SetInt(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, + enabled_compression_algorithms_bitset_); + if (maybe_default_compression_level_.is_set) { + args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL, + maybe_default_compression_level_.level); + } + if (maybe_default_compression_algorithm_.is_set) { + args.SetInt(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, + maybe_default_compression_algorithm_.algorithm); + } + + if (resource_quota_ != nullptr) { + args.SetPointerWithVtable(GRPC_ARG_RESOURCE_QUOTA, resource_quota_, + grpc_resource_quota_arg_vtable()); + } + + // == Determine if the server has any syncrhonous methods == + bool has_sync_methods = false; for (auto it = services_.begin(); it != services_.end(); ++it) { if ((*it)->service->has_synchronous_methods()) { - if (thread_pool == nullptr) { - thread_pool.reset(CreateDefaultThreadPool()); + has_sync_methods = true; + break; + } + } + + if (!has_sync_methods) { + for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) { + if ((*plugin)->has_sync_methods()) { + has_sync_methods = true; break; } } } - ChannelArguments args; - for (auto option = options_.begin(); option != options_.end(); ++option) { - (*option)->UpdateArguments(&args); + + // If this is a Sync server, i.e a server expositing sync API, then the server + // needs to create some completion queues to listen for incoming requests. + // 'sync_server_cqs' are those internal completion queues. + // + // This is different from the completion queues added to the server via + // ServerBuilder's AddCompletionQueue() method (those completion queues + // are in 'cqs_' member variable of ServerBuilder object) + std::shared_ptr>> + sync_server_cqs(std::make_shared< + std::vector>>()); + + int num_frequently_polled_cqs = 0; + for (auto it = cqs_.begin(); it != cqs_.end(); ++it) { + if ((*it)->IsFrequentlyPolled()) { + num_frequently_polled_cqs++; + } + } + + const bool is_hybrid_server = + has_sync_methods && num_frequently_polled_cqs > 0; + + if (has_sync_methods) { + grpc_cq_polling_type polling_type = + is_hybrid_server ? GRPC_CQ_NON_POLLING : GRPC_CQ_DEFAULT_POLLING; + + // Create completion queues to listen to incoming rpc requests + for (int i = 0; i < sync_server_settings_.num_cqs; i++) { + sync_server_cqs->emplace_back( + new ServerCompletionQueue(GRPC_CQ_NEXT, polling_type, nullptr)); + } + } + + // == Determine if the server has any callback methods == + bool has_callback_methods = false; + for (auto it = services_.begin(); it != services_.end(); ++it) { + if ((*it)->service->has_callback_methods()) { + has_callback_methods = true; + break; + } + } + + // TODO(vjpai): Add a section here for plugins once they can support callback + // methods + + if (has_sync_methods) { + // This is a Sync server + gpr_log(GPR_INFO, + "Synchronous server. Num CQs: %d, Min pollers: %d, Max Pollers: " + "%d, CQ timeout (msec): %d", + sync_server_settings_.num_cqs, sync_server_settings_.min_pollers, + sync_server_settings_.max_pollers, + sync_server_settings_.cq_timeout_msec); + } + + if (has_callback_methods) { + gpr_log(GPR_INFO, "Callback server."); + } + + std::unique_ptr server(new Server( + max_receive_message_size_, &args, sync_server_cqs, + sync_server_settings_.min_pollers, sync_server_settings_.max_pollers, + sync_server_settings_.cq_timeout_msec, resource_quota_, + std::move(interceptor_creators_))); + + ServerInitializer* initializer = server->initializer(); + + // Register all the completion queues with the server. i.e + // 1. sync_server_cqs: internal completion queues created IF this is a sync + // server + // 2. cqs_: Completion queues added via AddCompletionQueue() call + + for (auto it = sync_server_cqs->begin(); it != sync_server_cqs->end(); ++it) { + grpc_server_register_completion_queue(server->server_, (*it)->cq(), + nullptr); + num_frequently_polled_cqs++; } - if (max_message_size_ > 0) { - args.SetInt(GRPC_ARG_MAX_MESSAGE_LENGTH, max_message_size_); + + if (has_callback_methods) { + auto* cq = server->CallbackCQ(); + grpc_server_register_completion_queue(server->server_, cq->cq(), nullptr); + num_frequently_polled_cqs++; } - args.SetInt(GRPC_COMPRESSION_ALGORITHM_STATE_ARG, - compression_options_.enabled_algorithms_bitset); - std::unique_ptr server( - new Server(thread_pool.release(), true, max_message_size_, args)); - for (auto cq = cqs_.begin(); cq != cqs_.end(); ++cq) { - grpc_server_register_completion_queue(server->server_, (*cq)->cq(), + + // cqs_ contains the completion queue added by calling the ServerBuilder's + // AddCompletionQueue() API. Some of them may not be frequently polled (i.e by + // calling Next() or AsyncNext()) and hence are not safe to be used for + // listening to incoming channels. Such completion queues must be registered + // as non-listening queues + for (auto it = cqs_.begin(); it != cqs_.end(); ++it) { + grpc_server_register_completion_queue(server->server_, (*it)->cq(), nullptr); } + + if (num_frequently_polled_cqs == 0) { + gpr_log(GPR_ERROR, + "At least one of the completion queues must be frequently polled"); + return nullptr; + } + for (auto service = services_.begin(); service != services_.end(); service++) { if (!server->RegisterService((*service)->host.get(), (*service)->service)) { return nullptr; } } + + for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) { + (*plugin)->InitServer(initializer); + } + if (generic_service_) { server->RegisterAsyncGenericService(generic_service_); } else { @@ -127,18 +353,44 @@ std::unique_ptr ServerBuilder::BuildAndStart() { } } } + + bool added_port = false; for (auto port = ports_.begin(); port != ports_.end(); port++) { int r = server->AddListeningPort(port->addr, port->creds.get()); - if (!r) return nullptr; + if (!r) { + if (added_port) server->Shutdown(); + return nullptr; + } + added_port = true; 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; + server->Start(cqs_data, cqs_.size()); + + for (auto plugin = plugins_.begin(); plugin != plugins_.end(); plugin++) { + (*plugin)->Finish(initializer); } + return server; } +void ServerBuilder::InternalAddPluginFactory( + std::unique_ptr (*CreatePlugin)()) { + gpr_once_init(&once_init_plugin_list, do_plugin_list_init); + (*g_plugin_factory_list).push_back(CreatePlugin); +} + +ServerBuilder& ServerBuilder::EnableWorkaround(grpc_workaround_list id) { + switch (id) { + case GRPC_WORKAROUND_ID_CRONET_COMPRESSION: + return AddChannelArgument(GRPC_ARG_WORKAROUND_CRONET_COMPRESSION, 1); + default: + gpr_log(GPR_ERROR, "Workaround %u does not exist or is obsolete.", id); + return *this; + } +} + } // namespace grpc diff --git a/third_party/grpc/src/cpp/server/server_cc.cc b/third_party/grpc/src/cpp/server/server_cc.cc new file mode 100644 index 00000000000000..1e3c57446f45f2 --- /dev/null +++ b/third_party/grpc/src/cpp/server/server_cc.cc @@ -0,0 +1,1166 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/ext/transport/inproc/inproc_transport.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/cpp/client/create_channel_internal.h" +#include "src/cpp/server/health/default_health_check_service.h" +#include "src/cpp/thread_manager/thread_manager.h" + +namespace grpc { +namespace { + +// The default value for maximum number of threads that can be created in the +// sync server. This value of INT_MAX is chosen to match the default behavior if +// no ResourceQuota is set. To modify the max number of threads in a sync +// server, pass a custom ResourceQuota object (with the desired number of +// max-threads set) to the server builder. +#define DEFAULT_MAX_SYNC_SERVER_THREADS INT_MAX + +// How many callback requests of each method should we pre-register at start +#define DEFAULT_CALLBACK_REQS_PER_METHOD 32 + +class DefaultGlobalCallbacks final : public Server::GlobalCallbacks { + public: + ~DefaultGlobalCallbacks() override {} + void PreSynchronousRequest(ServerContext* context) override {} + void PostSynchronousRequest(ServerContext* context) override {} +}; + +std::shared_ptr g_callbacks = nullptr; +gpr_once g_once_init_callbacks = GPR_ONCE_INIT; + +void InitGlobalCallbacks() { + if (!g_callbacks) { + g_callbacks.reset(new DefaultGlobalCallbacks()); + } +} + +class ShutdownTag : public internal::CompletionQueueTag { + public: + bool FinalizeResult(void** tag, bool* status) { return false; } +}; + +class DummyTag : public internal::CompletionQueueTag { + public: + bool FinalizeResult(void** tag, bool* status) { return true; } +}; + +class UnimplementedAsyncRequestContext { + protected: + UnimplementedAsyncRequestContext() : generic_stream_(&server_context_) {} + + GenericServerContext server_context_; + GenericServerAsyncReaderWriter generic_stream_; +}; + +} // namespace + +/// Use private inheritance rather than composition only to establish order +/// of construction, since the public base class should be constructed after the +/// elements belonging to the private base class are constructed. This is not +/// possible using true composition. +class Server::UnimplementedAsyncRequest final + : private UnimplementedAsyncRequestContext, + public GenericAsyncRequest { + public: + UnimplementedAsyncRequest(Server* server, ServerCompletionQueue* cq) + : GenericAsyncRequest(server, &server_context_, &generic_stream_, cq, cq, + nullptr, false), + server_(server), + cq_(cq) {} + + bool FinalizeResult(void** tag, bool* status) override; + + ServerContext* context() { return &server_context_; } + GenericServerAsyncReaderWriter* stream() { return &generic_stream_; } + + private: + Server* const server_; + ServerCompletionQueue* const cq_; +}; + +/// UnimplementedAsyncResponse should not post user-visible completions to the +/// C++ completion queue, but is generated as a CQ event by the core +class Server::UnimplementedAsyncResponse final + : public internal::CallOpSet { + public: + UnimplementedAsyncResponse(UnimplementedAsyncRequest* request); + ~UnimplementedAsyncResponse() { delete request_; } + + bool FinalizeResult(void** tag, bool* status) override { + if (internal::CallOpSet< + internal::CallOpSendInitialMetadata, + internal::CallOpServerSendStatus>::FinalizeResult(tag, status)) { + delete this; + } else { + // The tag was swallowed due to interception. We will see it again. + } + return false; + } + + private: + UnimplementedAsyncRequest* const request_; +}; + +class Server::SyncRequest final : public internal::CompletionQueueTag { + public: + SyncRequest(internal::RpcServiceMethod* method, void* method_tag) + : method_(method), + method_tag_(method_tag), + in_flight_(false), + has_request_payload_( + method->method_type() == internal::RpcMethod::NORMAL_RPC || + method->method_type() == internal::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_); + } + + void SetupRequest() { cq_ = grpc_completion_queue_create_for_pluck(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 (method_tag_) { + if (GRPC_CALL_OK != + grpc_server_request_registered_call( + server, method_tag_, &call_, &deadline_, &request_metadata_, + has_request_payload_ ? &request_payload_ : nullptr, cq_, + notify_cq, this)) { + TeardownRequest(); + return; + } + } else { + if (!call_details_) { + call_details_ = new grpc_call_details; + grpc_call_details_init(call_details_); + } + if (grpc_server_request_call(server, &call_, call_details_, + &request_metadata_, cq_, notify_cq, + this) != GRPC_CALL_OK) { + TeardownRequest(); + return; + } + } + } + + void PostShutdownCleanup() { + if (call_) { + grpc_call_unref(call_); + call_ = nullptr; + } + if (cq_) { + grpc_completion_queue_destroy(cq_); + cq_ = nullptr; + } + } + + bool FinalizeResult(void** tag, bool* status) override { + if (!*status) { + grpc_completion_queue_destroy(cq_); + cq_ = nullptr; + } + if (call_details_) { + deadline_ = call_details_->deadline; + grpc_call_details_destroy(call_details_); + grpc_call_details_init(call_details_); + } + return true; + } + + // The CallData class represents a call that is "active" as opposed + // to just being requested. It wraps and takes ownership of the cq from + // the call request + class CallData final { + public: + explicit CallData(Server* server, SyncRequest* mrd) + : cq_(mrd->cq_), + ctx_(mrd->deadline_, &mrd->request_metadata_), + has_request_payload_(mrd->has_request_payload_), + request_payload_(has_request_payload_ ? mrd->request_payload_ + : nullptr), + request_(nullptr), + method_(mrd->method_), + call_( + mrd->call_, server, &cq_, server->max_receive_message_size(), + ctx_.set_server_rpc_info(method_->name(), method_->method_type(), + server->interceptor_creators_)), + server_(server), + global_callbacks_(nullptr), + resources_(false) { + 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(const std::shared_ptr& global_callbacks, + bool resources) { + global_callbacks_ = global_callbacks; + resources_ = resources; + + interceptor_methods_.SetCall(&call_); + interceptor_methods_.SetReverse(); + // Set interception point for RECV INITIAL METADATA + interceptor_methods_.AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA); + interceptor_methods_.SetRecvInitialMetadata(&ctx_.client_metadata_); + + if (has_request_payload_) { + // Set interception point for RECV MESSAGE + auto* handler = resources_ ? method_->handler() + : server_->resource_exhausted_handler_.get(); + request_ = handler->Deserialize(call_.call(), request_payload_, + &request_status_); + + request_payload_ = nullptr; + interceptor_methods_.AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_MESSAGE); + interceptor_methods_.SetRecvMessage(request_); + } + + if (interceptor_methods_.RunInterceptors( + [this]() { ContinueRunAfterInterception(); })) { + ContinueRunAfterInterception(); + } else { + // There were interceptors to be run, so ContinueRunAfterInterception + // will be run when interceptors are done. + } + } + + void ContinueRunAfterInterception() { + { + ctx_.BeginCompletionOp(&call_, nullptr, nullptr); + global_callbacks_->PreSynchronousRequest(&ctx_); + auto* handler = resources_ ? method_->handler() + : server_->resource_exhausted_handler_.get(); + handler->RunHandler(internal::MethodHandler::HandlerParameter( + &call_, &ctx_, request_, request_status_, nullptr)); + request_ = nullptr; + global_callbacks_->PostSynchronousRequest(&ctx_); + + cq_.Shutdown(); + + internal::CompletionQueueTag* op_tag = ctx_.GetCompletionOpTag(); + cq_.TryPluck(op_tag, gpr_inf_future(GPR_CLOCK_REALTIME)); + + /* Ensure the cq_ is shutdown */ + DummyTag ignored_tag; + GPR_ASSERT(cq_.Pluck(&ignored_tag) == false); + } + delete this; + } + + private: + CompletionQueue cq_; + ServerContext ctx_; + const bool has_request_payload_; + grpc_byte_buffer* request_payload_; + void* request_; + Status request_status_; + internal::RpcServiceMethod* const method_; + internal::Call call_; + Server* server_; + std::shared_ptr global_callbacks_; + bool resources_; + internal::InterceptorBatchMethodsImpl interceptor_methods_; + }; + + private: + internal::RpcServiceMethod* const method_; + void* const method_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_; +}; + +class Server::CallbackRequest final : public internal::CompletionQueueTag { + public: + CallbackRequest(Server* server, internal::RpcServiceMethod* method, + void* method_tag) + : server_(server), + method_(method), + method_tag_(method_tag), + has_request_payload_( + method->method_type() == internal::RpcMethod::NORMAL_RPC || + method->method_type() == internal::RpcMethod::SERVER_STREAMING), + cq_(server->CallbackCQ()), + tag_(this) { + Setup(); + } + + ~CallbackRequest() { Clear(); } + + void Request() { + if (method_tag_) { + if (GRPC_CALL_OK != + grpc_server_request_registered_call( + server_->c_server(), method_tag_, &call_, &deadline_, + &request_metadata_, + has_request_payload_ ? &request_payload_ : nullptr, cq_->cq(), + cq_->cq(), static_cast(&tag_))) { + return; + } + } else { + if (!call_details_) { + call_details_ = new grpc_call_details; + grpc_call_details_init(call_details_); + } + if (grpc_server_request_call(server_->c_server(), &call_, call_details_, + &request_metadata_, cq_->cq(), cq_->cq(), + static_cast(&tag_)) != GRPC_CALL_OK) { + return; + } + } + } + + bool FinalizeResult(void** tag, bool* status) override { return false; } + + private: + class CallbackCallTag : public grpc_experimental_completion_queue_functor { + public: + CallbackCallTag(Server::CallbackRequest* req) : req_(req) { + functor_run = &CallbackCallTag::StaticRun; + } + + // force_run can not be performed on a tag if operations using this tag + // have been sent to PerformOpsOnCall. It is intended for error conditions + // that are detected before the operations are internally processed. + void force_run(bool ok) { Run(ok); } + + private: + Server::CallbackRequest* req_; + internal::Call* call_; + + static void StaticRun(grpc_experimental_completion_queue_functor* cb, + int ok) { + static_cast(cb)->Run(static_cast(ok)); + } + void Run(bool ok) { + void* ignored = req_; + bool new_ok = ok; + GPR_ASSERT(!req_->FinalizeResult(&ignored, &new_ok)); + GPR_ASSERT(ignored == req_); + + if (!ok) { + // The call has been shutdown + req_->Clear(); + return; + } + + // Bind the call, deadline, and metadata from what we got + req_->ctx_.set_call(req_->call_); + req_->ctx_.cq_ = req_->cq_; + req_->ctx_.BindDeadlineAndMetadata(req_->deadline_, + &req_->request_metadata_); + req_->request_metadata_.count = 0; + + // Create a C++ Call to control the underlying core call + call_ = new (grpc_call_arena_alloc(req_->call_, sizeof(internal::Call))) + internal::Call( + req_->call_, req_->server_, req_->cq_, + req_->server_->max_receive_message_size(), + req_->ctx_.set_server_rpc_info( + req_->method_->name(), req_->method_->method_type(), + req_->server_->interceptor_creators_)); + + req_->interceptor_methods_.SetCall(call_); + req_->interceptor_methods_.SetReverse(); + // Set interception point for RECV INITIAL METADATA + req_->interceptor_methods_.AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA); + req_->interceptor_methods_.SetRecvInitialMetadata( + &req_->ctx_.client_metadata_); + + if (req_->has_request_payload_) { + // Set interception point for RECV MESSAGE + req_->request_ = req_->method_->handler()->Deserialize( + req_->call_, req_->request_payload_, &req_->request_status_); + req_->request_payload_ = nullptr; + req_->interceptor_methods_.AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_MESSAGE); + req_->interceptor_methods_.SetRecvMessage(req_->request_); + } + + if (req_->interceptor_methods_.RunInterceptors( + [this] { ContinueRunAfterInterception(); })) { + ContinueRunAfterInterception(); + } else { + // There were interceptors to be run, so ContinueRunAfterInterception + // will be run when interceptors are done. + } + } + void ContinueRunAfterInterception() { + req_->method_->handler()->RunHandler( + internal::MethodHandler::HandlerParameter( + call_, &req_->ctx_, req_->request_, req_->request_status_, + [this] { + req_->Reset(); + req_->Request(); + })); + } + }; + + void Reset() { + Clear(); + Setup(); + } + + void Clear() { + if (call_details_) { + delete call_details_; + call_details_ = nullptr; + } + grpc_metadata_array_destroy(&request_metadata_); + if (has_request_payload_ && request_payload_) { + grpc_byte_buffer_destroy(request_payload_); + } + ctx_.Clear(); + interceptor_methods_.ClearState(); + } + + void Setup() { + grpc_metadata_array_init(&request_metadata_); + ctx_.Setup(gpr_inf_future(GPR_CLOCK_REALTIME)); + request_payload_ = nullptr; + request_ = nullptr; + request_status_ = Status(); + } + + Server* const server_; + internal::RpcServiceMethod* const method_; + void* const method_tag_; + const bool has_request_payload_; + grpc_byte_buffer* request_payload_; + void* request_; + Status request_status_; + grpc_call_details* call_details_ = nullptr; + grpc_call* call_; + gpr_timespec deadline_; + grpc_metadata_array request_metadata_; + CompletionQueue* cq_; + CallbackCallTag tag_; + ServerContext ctx_; + internal::InterceptorBatchMethodsImpl interceptor_methods_; +}; + +// Implementation of ThreadManager. Each instance of SyncRequestThreadManager +// manages a pool of threads that poll for incoming Sync RPCs and call the +// appropriate RPC handlers +class Server::SyncRequestThreadManager : public ThreadManager { + public: + SyncRequestThreadManager(Server* server, CompletionQueue* server_cq, + std::shared_ptr global_callbacks, + grpc_resource_quota* rq, int min_pollers, + int max_pollers, int cq_timeout_msec) + : ThreadManager("SyncServer", rq, min_pollers, max_pollers), + server_(server), + server_cq_(server_cq), + cq_timeout_msec_(cq_timeout_msec), + global_callbacks_(std::move(global_callbacks)) {} + + WorkStatus PollForWork(void** tag, bool* ok) override { + *tag = nullptr; + // TODO(ctiller): workaround for GPR_TIMESPAN based deadlines not working + // right now + gpr_timespec deadline = + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(cq_timeout_msec_, GPR_TIMESPAN)); + + switch (server_cq_->AsyncNext(tag, ok, deadline)) { + case CompletionQueue::TIMEOUT: + return TIMEOUT; + case CompletionQueue::SHUTDOWN: + return SHUTDOWN; + case CompletionQueue::GOT_EVENT: + return WORK_FOUND; + } + + GPR_UNREACHABLE_CODE(return TIMEOUT); + } + + void DoWork(void* tag, bool ok, bool resources) override { + SyncRequest* sync_req = static_cast(tag); + + if (!sync_req) { + // No tag. Nothing to work on. This is an unlikley scenario and possibly a + // bug in RPC Manager implementation. + gpr_log(GPR_ERROR, "Sync server. DoWork() was called with NULL tag"); + return; + } + + if (ok) { + // Calldata takes ownership of the completion queue and interceptors + // inside sync_req + auto* cd = new SyncRequest::CallData(server_, sync_req); + // Prepare for the next request + if (!IsShutdown()) { + sync_req->SetupRequest(); // Create new completion queue for sync_req + sync_req->Request(server_->c_server(), server_cq_->cq()); + } + + GPR_TIMER_SCOPE("cd.Run()", 0); + cd->Run(global_callbacks_, resources); + } + // TODO (sreek) If ok is false here (which it isn't in case of + // grpc_request_registered_call), we should still re-queue the request + // object + } + + void AddSyncMethod(internal::RpcServiceMethod* method, void* tag) { + sync_requests_.emplace_back(new SyncRequest(method, tag)); + } + + void AddUnknownSyncMethod() { + if (!sync_requests_.empty()) { + unknown_method_.reset(new internal::RpcServiceMethod( + "unknown", internal::RpcMethod::BIDI_STREAMING, + new internal::UnknownMethodHandler)); + sync_requests_.emplace_back( + new SyncRequest(unknown_method_.get(), nullptr)); + } + } + + void Shutdown() override { + ThreadManager::Shutdown(); + server_cq_->Shutdown(); + } + + void Wait() override { + ThreadManager::Wait(); + // Drain any pending items from the queue + void* tag; + bool ok; + while (server_cq_->Next(&tag, &ok)) { + if (ok) { + // If a request was pulled off the queue, it means that the thread + // handling the request added it to the completion queue after shutdown + // was called - because the thread had already started and checked the + // shutdown flag before shutdown was called. In this case, we simply + // clean it up here, *after* calling wait on all the worker threads, at + // which point we are certain no in-flight requests will add more to the + // queue. This fixes an intermittent memory leak on shutdown. + SyncRequest* sync_req = static_cast(tag); + sync_req->PostShutdownCleanup(); + } + } + } + + void Start() { + if (!sync_requests_.empty()) { + for (auto m = sync_requests_.begin(); m != sync_requests_.end(); m++) { + (*m)->SetupRequest(); + (*m)->Request(server_->c_server(), server_cq_->cq()); + } + + Initialize(); // ThreadManager's Initialize() + } + } + + private: + Server* server_; + CompletionQueue* server_cq_; + int cq_timeout_msec_; + std::vector> sync_requests_; + std::unique_ptr unknown_method_; + std::shared_ptr global_callbacks_; +}; + +static internal::GrpcLibraryInitializer g_gli_initializer; +Server::Server( + int max_receive_message_size, ChannelArguments* args, + std::shared_ptr>> + sync_server_cqs, + int min_pollers, int max_pollers, int sync_cq_timeout_msec, + grpc_resource_quota* server_rq, + std::vector< + std::unique_ptr> + interceptor_creators) + : interceptor_creators_(std::move(interceptor_creators)), + max_receive_message_size_(max_receive_message_size), + sync_server_cqs_(std::move(sync_server_cqs)), + started_(false), + shutdown_(false), + shutdown_notified_(false), + has_generic_service_(false), + server_(nullptr), + server_initializer_(new ServerInitializer(this)), + health_check_service_disabled_(false) { + g_gli_initializer.summon(); + gpr_once_init(&g_once_init_callbacks, InitGlobalCallbacks); + global_callbacks_ = g_callbacks; + global_callbacks_->UpdateArguments(args); + + if (sync_server_cqs_ != nullptr) { + bool default_rq_created = false; + if (server_rq == nullptr) { + server_rq = grpc_resource_quota_create("SyncServer-default-rq"); + grpc_resource_quota_set_max_threads(server_rq, + DEFAULT_MAX_SYNC_SERVER_THREADS); + default_rq_created = true; + } + + for (const auto& it : *sync_server_cqs_) { + sync_req_mgrs_.emplace_back(new SyncRequestThreadManager( + this, it.get(), global_callbacks_, server_rq, min_pollers, + max_pollers, sync_cq_timeout_msec)); + } + + if (default_rq_created) { + grpc_resource_quota_unref(server_rq); + } + } + + grpc_channel_args channel_args; + args->SetChannelArgs(&channel_args); + + for (size_t i = 0; i < channel_args.num_args; i++) { + if (0 == + strcmp(channel_args.args[i].key, kHealthCheckServiceInterfaceArg)) { + if (channel_args.args[i].value.pointer.p == nullptr) { + health_check_service_disabled_ = true; + } else { + health_check_service_.reset(static_cast( + channel_args.args[i].value.pointer.p)); + } + break; + } + } + + server_ = grpc_server_create(&channel_args, nullptr); +} + +Server::~Server() { + { + std::unique_lock lock(mu_); + if (callback_cq_ != nullptr) { + callback_cq_->Shutdown(); + } + if (started_ && !shutdown_) { + lock.unlock(); + Shutdown(); + } else if (!started_) { + // Shutdown the completion queues + for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) { + (*it)->Shutdown(); + } + } + } + + grpc_server_destroy(server_); +} + +void Server::SetGlobalCallbacks(GlobalCallbacks* callbacks) { + GPR_ASSERT(!g_callbacks); + GPR_ASSERT(callbacks); + g_callbacks.reset(callbacks); +} + +grpc_server* Server::c_server() { return server_; } + +std::shared_ptr Server::InProcessChannel( + const ChannelArguments& args) { + grpc_channel_args channel_args = args.c_channel_args(); + return CreateChannelInternal( + "inproc", grpc_inproc_channel_create(server_, &channel_args, nullptr), + std::vector< + std::unique_ptr>()); +} + +std::shared_ptr +Server::experimental_type::InProcessChannelWithInterceptors( + const ChannelArguments& args, + std::vector< + std::unique_ptr> + interceptor_creators) { + grpc_channel_args channel_args = args.c_channel_args(); + return CreateChannelInternal( + "inproc", + grpc_inproc_channel_create(server_->server_, &channel_args, nullptr), + std::move(interceptor_creators)); +} + +static grpc_server_register_method_payload_handling PayloadHandlingForMethod( + internal::RpcServiceMethod* method) { + switch (method->method_type()) { + case internal::RpcMethod::NORMAL_RPC: + case internal::RpcMethod::SERVER_STREAMING: + return GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER; + case internal::RpcMethod::CLIENT_STREAMING: + case internal::RpcMethod::BIDI_STREAMING: + return GRPC_SRM_PAYLOAD_NONE; + } + GPR_UNREACHABLE_CODE(return GRPC_SRM_PAYLOAD_NONE;); +} + +bool Server::RegisterService(const grpc::string* host, Service* service) { + bool has_async_methods = service->has_async_methods(); + if (has_async_methods) { + GPR_ASSERT(service->server_ == nullptr && + "Can only register an asynchronous service against one server."); + service->server_ = this; + } + + const char* method_name = nullptr; + for (auto it = service->methods_.begin(); it != service->methods_.end(); + ++it) { + if (it->get() == nullptr) { // Handled by generic service if any. + continue; + } + + internal::RpcServiceMethod* method = it->get(); + void* method_registration_tag = grpc_server_register_method( + server_, method->name(), host ? host->c_str() : nullptr, + PayloadHandlingForMethod(method), 0); + if (method_registration_tag == nullptr) { + gpr_log(GPR_DEBUG, "Attempt to register %s multiple times", + method->name()); + return false; + } + + if (method->handler() == nullptr) { // Async method without handler + method->set_server_tag(method_registration_tag); + } else if (method->api_type() == + internal::RpcServiceMethod::ApiType::SYNC) { + for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) { + (*it)->AddSyncMethod(method, method_registration_tag); + } + } else { + // a callback method. Register at least some callback requests + // TODO(vjpai): Register these dynamically based on need + for (int i = 0; i < DEFAULT_CALLBACK_REQS_PER_METHOD; i++) { + auto* req = new CallbackRequest(this, method, method_registration_tag); + callback_reqs_.emplace_back(req); + } + // Enqueue it so that it will be Request'ed later once + // all request matchers are created at core server startup + } + + method_name = method->name(); + } + + // Parse service name. + if (method_name != nullptr) { + std::stringstream ss(method_name); + grpc::string service_name; + if (std::getline(ss, service_name, '/') && + std::getline(ss, service_name, '/')) { + services_.push_back(service_name); + } + } + 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_); + int port = creds->AddPortToServer(addr, server_); + global_callbacks_->AddPort(this, addr, creds, port); + return port; +} + +void Server::Start(ServerCompletionQueue** cqs, size_t num_cqs) { + GPR_ASSERT(!started_); + global_callbacks_->PreServerStart(this); + started_ = true; + + // Only create default health check service when user did not provide an + // explicit one. + ServerCompletionQueue* health_check_cq = nullptr; + DefaultHealthCheckService::HealthCheckServiceImpl* + default_health_check_service_impl = nullptr; + if (health_check_service_ == nullptr && !health_check_service_disabled_ && + DefaultHealthCheckServiceEnabled()) { + auto* default_hc_service = new DefaultHealthCheckService; + health_check_service_.reset(default_hc_service); + // We create a non-polling CQ to avoid impacting application + // performance. This ensures that we don't introduce thread hops + // for application requests that wind up on this CQ, which is polled + // in its own thread. + health_check_cq = + new ServerCompletionQueue(GRPC_CQ_NEXT, GRPC_CQ_NON_POLLING, nullptr); + grpc_server_register_completion_queue(server_, health_check_cq->cq(), + nullptr); + default_health_check_service_impl = + default_hc_service->GetHealthCheckService( + std::unique_ptr(health_check_cq)); + RegisterService(nullptr, default_health_check_service_impl); + } + + grpc_server_start(server_); + + if (!has_generic_service_) { + for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) { + (*it)->AddUnknownSyncMethod(); + } + + for (size_t i = 0; i < num_cqs; i++) { + if (cqs[i]->IsFrequentlyPolled()) { + new UnimplementedAsyncRequest(this, cqs[i]); + } + } + if (health_check_cq != nullptr) { + new UnimplementedAsyncRequest(this, health_check_cq); + } + } + + // If this server has any support for synchronous methods (has any sync + // server CQs), make sure that we have a ResourceExhausted handler + // to deal with the case of thread exhaustion + if (sync_server_cqs_ != nullptr && !sync_server_cqs_->empty()) { + resource_exhausted_handler_.reset(new internal::ResourceExhaustedHandler); + } + + for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) { + (*it)->Start(); + } + + for (auto& cbreq : callback_reqs_) { + cbreq->Request(); + } + + if (default_health_check_service_impl != nullptr) { + default_health_check_service_impl->StartServingThread(); + } +} + +void Server::ShutdownInternal(gpr_timespec deadline) { + std::unique_lock lock(mu_); + if (!shutdown_) { + shutdown_ = true; + + /// The completion queue to use for server shutdown completion notification + CompletionQueue shutdown_cq; + ShutdownTag shutdown_tag; // Dummy shutdown tag + grpc_server_shutdown_and_notify(server_, shutdown_cq.cq(), &shutdown_tag); + + shutdown_cq.Shutdown(); + + void* tag; + bool ok; + CompletionQueue::NextStatus status = + shutdown_cq.AsyncNext(&tag, &ok, deadline); + + // If this timed out, it means we are done with the grace period for a clean + // shutdown. We should force a shutdown now by cancelling all inflight calls + if (status == CompletionQueue::NextStatus::TIMEOUT) { + grpc_server_cancel_all_calls(server_); + } + // Else in case of SHUTDOWN or GOT_EVENT, it means that the server has + // successfully shutdown + + // Shutdown all ThreadManagers. This will try to gracefully stop all the + // threads in the ThreadManagers (once they process any inflight requests) + for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) { + (*it)->Shutdown(); // ThreadManager's Shutdown() + } + + // Wait for threads in all ThreadManagers to terminate + for (auto it = sync_req_mgrs_.begin(); it != sync_req_mgrs_.end(); it++) { + (*it)->Wait(); + } + + // Drain the shutdown queue (if the previous call to AsyncNext() timed out + // and we didn't remove the tag from the queue yet) + while (shutdown_cq.Next(&tag, &ok)) { + // Nothing to be done here. Just ignore ok and tag values + } + + shutdown_notified_ = true; + shutdown_cv_.notify_all(); + } +} + +void Server::Wait() { + std::unique_lock lock(mu_); + while (started_ && !shutdown_notified_) { + shutdown_cv_.wait(lock); + } +} + +void Server::PerformOpsOnCall(internal::CallOpSetInterface* ops, + internal::Call* call) { + ops->FillOps(call); +} + +ServerInterface::BaseAsyncRequest::BaseAsyncRequest( + ServerInterface* server, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize) + : server_(server), + context_(context), + stream_(stream), + call_cq_(call_cq), + notification_cq_(notification_cq), + tag_(tag), + delete_on_finalize_(delete_on_finalize), + call_(nullptr), + done_intercepting_(false) { + /* Set up interception state partially for the receive ops. call_wrapper_ is + * not filled at this point, but it will be filled before the interceptors are + * run. */ + interceptor_methods_.SetCall(&call_wrapper_); + interceptor_methods_.SetReverse(); + call_cq_->RegisterAvalanching(); // This op will trigger more ops +} + +ServerInterface::BaseAsyncRequest::~BaseAsyncRequest() { + call_cq_->CompleteAvalanching(); +} + +bool ServerInterface::BaseAsyncRequest::FinalizeResult(void** tag, + bool* status) { + if (done_intercepting_) { + *tag = tag_; + if (delete_on_finalize_) { + delete this; + } + return true; + } + context_->set_call(call_); + context_->cq_ = call_cq_; + if (call_wrapper_.call() == nullptr) { + // Fill it since it is empty. + call_wrapper_ = internal::Call( + call_, server_, call_cq_, server_->max_receive_message_size(), nullptr); + } + + // just the pointers inside call are copied here + stream_->BindCall(&call_wrapper_); + + if (*status && call_ && call_wrapper_.server_rpc_info()) { + done_intercepting_ = true; + // Set interception point for RECV INITIAL METADATA + interceptor_methods_.AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA); + interceptor_methods_.SetRecvInitialMetadata(&context_->client_metadata_); + if (interceptor_methods_.RunInterceptors( + [this]() { ContinueFinalizeResultAfterInterception(); })) { + // There are no interceptors to run. Continue + } else { + // There were interceptors to be run, so + // ContinueFinalizeResultAfterInterception will be run when interceptors + // are done. + return false; + } + } + if (*status && call_) { + context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr); + } + *tag = tag_; + if (delete_on_finalize_) { + delete this; + } + return true; +} + +void ServerInterface::BaseAsyncRequest:: + ContinueFinalizeResultAfterInterception() { + context_->BeginCompletionOp(&call_wrapper_, nullptr, nullptr); + // Queue a tag which will be returned immediately + grpc_core::ExecCtx exec_ctx; + grpc_cq_begin_op(notification_cq_->cq(), this); + grpc_cq_end_op( + notification_cq_->cq(), this, GRPC_ERROR_NONE, + [](void* arg, grpc_cq_completion* completion) { delete completion; }, + nullptr, new grpc_cq_completion()); +} + +ServerInterface::RegisteredAsyncRequest::RegisteredAsyncRequest( + ServerInterface* server, ServerContext* context, + internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, const char* name, + internal::RpcMethod::RpcType type) + : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag, + true), + name_(name), + type_(type) {} + +void ServerInterface::RegisteredAsyncRequest::IssueRequest( + void* registered_method, grpc_byte_buffer** payload, + ServerCompletionQueue* notification_cq) { + GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_registered_call( + server_->server(), registered_method, &call_, + &context_->deadline_, + context_->client_metadata_.arr(), payload, + call_cq_->cq(), notification_cq->cq(), this)); +} + +ServerInterface::GenericAsyncRequest::GenericAsyncRequest( + ServerInterface* server, GenericServerContext* context, + internal::ServerAsyncStreamingInterface* stream, CompletionQueue* call_cq, + ServerCompletionQueue* notification_cq, void* tag, bool delete_on_finalize) + : BaseAsyncRequest(server, context, stream, call_cq, notification_cq, tag, + delete_on_finalize) { + grpc_call_details_init(&call_details_); + GPR_ASSERT(notification_cq); + GPR_ASSERT(call_cq); + GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call( + server->server(), &call_, &call_details_, + context->client_metadata_.arr(), call_cq->cq(), + notification_cq->cq(), this)); +} + +bool ServerInterface::GenericAsyncRequest::FinalizeResult(void** tag, + bool* status) { + // If we are done intercepting, there is nothing more for us to do + if (done_intercepting_) { + return BaseAsyncRequest::FinalizeResult(tag, status); + } + // TODO(yangg) remove the copy here. + if (*status) { + static_cast(context_)->method_ = + StringFromCopiedSlice(call_details_.method); + static_cast(context_)->host_ = + StringFromCopiedSlice(call_details_.host); + context_->deadline_ = call_details_.deadline; + } + grpc_slice_unref(call_details_.method); + grpc_slice_unref(call_details_.host); + call_wrapper_ = internal::Call( + call_, server_, call_cq_, server_->max_receive_message_size(), + context_->set_server_rpc_info( + static_cast(context_)->method_.c_str(), + internal::RpcMethod::BIDI_STREAMING, + *server_->interceptor_creators())); + return BaseAsyncRequest::FinalizeResult(tag, status); +} + +bool Server::UnimplementedAsyncRequest::FinalizeResult(void** tag, + bool* status) { + if (GenericAsyncRequest::FinalizeResult(tag, status)) { + // We either had no interceptors run or we are done intercepting + if (*status) { + new UnimplementedAsyncRequest(server_, cq_); + new UnimplementedAsyncResponse(this); + } else { + delete this; + } + } else { + // The tag was swallowed due to interception. We will see it again. + } + return false; +} + +Server::UnimplementedAsyncResponse::UnimplementedAsyncResponse( + UnimplementedAsyncRequest* request) + : request_(request) { + Status status(StatusCode::UNIMPLEMENTED, ""); + internal::UnknownMethodHandler::FillOps(request_->context(), this); + request_->stream()->call_.PerformOps(this); +} + +ServerInitializer* Server::initializer() { return server_initializer_.get(); } + +namespace { +class ShutdownCallback : public grpc_experimental_completion_queue_functor { + public: + ShutdownCallback() { functor_run = &ShutdownCallback::Run; } + // TakeCQ takes ownership of the cq into the shutdown callback + // so that the shutdown callback will be responsible for destroying it + void TakeCQ(CompletionQueue* cq) { cq_ = cq; } + + // The Run function will get invoked by the completion queue library + // when the shutdown is actually complete + static void Run(grpc_experimental_completion_queue_functor* cb, int) { + auto* callback = static_cast(cb); + delete callback->cq_; + delete callback; + } + + private: + CompletionQueue* cq_ = nullptr; +}; +} // namespace + +CompletionQueue* Server::CallbackCQ() { + // TODO(vjpai): Consider using a single global CQ for the default CQ + // if there is no explicit per-server CQ registered + std::lock_guard l(mu_); + if (callback_cq_ == nullptr) { + auto* shutdown_callback = new ShutdownCallback; + callback_cq_ = new CompletionQueue(grpc_completion_queue_attributes{ + GRPC_CQ_CURRENT_VERSION, GRPC_CQ_CALLBACK, GRPC_CQ_DEFAULT_POLLING, + shutdown_callback}); + + // Transfer ownership of the new cq to its own shutdown callback + shutdown_callback->TakeCQ(callback_cq_); + } + return callback_cq_; +}; + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/server_context.cc b/third_party/grpc/src/cpp/server/server_context.cc index e205a1969b3d77..1b524bc3e83ff8 100644 --- a/third_party/grpc/src/cpp/server/server_context.cc +++ b/third_party/grpc/src/cpp/server/server_context.cc @@ -1,168 +1,309 @@ /* * - * Copyright 2015-2016, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include +#include + +#include +#include +#include -#include -#include -#include -#include #include #include +#include #include #include +#include +#include +#include -#include "src/core/channel/compress_filter.h" -#include "src/cpp/common/create_auth_context.h" +#include "src/core/lib/surface/call.h" namespace grpc { // CompletionOp -class ServerContext::CompletionOp GRPC_FINAL : public CallOpSetInterface { +class ServerContext::CompletionOp final : public internal::CallOpSetInterface { public: // initial refs: one in the server context, one in the cq - CompletionOp() - : has_tag_(false), + // must ref the call before calling constructor and after deleting this + CompletionOp(internal::Call* call, internal::ServerReactor* reactor) + : call_(*call), + reactor_(reactor), + has_tag_(false), tag_(nullptr), + core_cq_tag_(this), refs_(2), finalized_(false), - cancelled_(0) {} + cancelled_(0), + done_intercepting_(false) {} + + // CompletionOp isn't copyable or movable + CompletionOp(const CompletionOp&) = delete; + CompletionOp& operator=(const CompletionOp&) = delete; + CompletionOp(CompletionOp&&) = delete; + CompletionOp& operator=(CompletionOp&&) = delete; + + ~CompletionOp() { + if (call_.server_rpc_info()) { + call_.server_rpc_info()->Unref(); + } + } - void FillOps(grpc_op* ops, size_t* nops) GRPC_OVERRIDE; - bool FinalizeResult(void** tag, bool* status) GRPC_OVERRIDE; + void FillOps(internal::Call* call) override; - bool CheckCancelled(CompletionQueue* cq); + // This should always be arena allocated in the call, so override delete. + // But this class is not trivially destructible, so must actually call delete + // before allowing the arena to be freed + static void operator delete(void* ptr, std::size_t size) { + assert(size == sizeof(CompletionOp)); + } + + // This operator should never be called as the memory should be freed as part + // of the arena destruction. It only exists to provide a matching operator + // delete to the operator new so that some compilers will not complain (see + // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this + // there are no tests catching the compiler warning. + static void operator delete(void*, void*) { assert(0); } + + bool FinalizeResult(void** tag, bool* status) override; + + bool CheckCancelled(CompletionQueue* cq) { + cq->TryPluck(this); + return CheckCancelledNoPluck(); + } + bool CheckCancelledAsync() { return CheckCancelledNoPluck(); } void set_tag(void* tag) { has_tag_ = true; tag_ = tag; } + void set_core_cq_tag(void* core_cq_tag) { core_cq_tag_ = core_cq_tag; } + + void* core_cq_tag() override { return core_cq_tag_; } + void Unref(); + // This will be called while interceptors are run if the RPC is a hijacked + // RPC. This should set hijacking state for each of the ops. + void SetHijackingState() override { + /* Servers don't allow hijacking */ + GPR_CODEGEN_ASSERT(false); + } + + /* Should be called after interceptors are done running */ + void ContinueFillOpsAfterInterception() override {} + + /* Should be called after interceptors are done running on the finalize result + * path */ + void ContinueFinalizeResultAfterInterception() override { + done_intercepting_ = true; + if (!has_tag_) { + /* We don't have a tag to return. */ + std::unique_lock lock(mu_); + if (--refs_ == 0) { + lock.unlock(); + grpc_call* call = call_.call(); + delete this; + grpc_call_unref(call); + } + return; + } + /* Start a dummy op so that we can return the tag */ + GPR_CODEGEN_ASSERT( + GRPC_CALL_OK == + grpc_call_start_batch(call_.call(), nullptr, 0, core_cq_tag_, nullptr)); + } + private: + bool CheckCancelledNoPluck() { + std::lock_guard g(mu_); + return finalized_ ? (cancelled_ != 0) : false; + } + + internal::Call call_; + internal::ServerReactor* reactor_; bool has_tag_; void* tag_; - grpc::mutex mu_; + void* core_cq_tag_; + std::mutex mu_; int refs_; bool finalized_; - int cancelled_; + int cancelled_; // This is an int (not bool) because it is passed to core + bool done_intercepting_; + internal::InterceptorBatchMethodsImpl interceptor_methods_; }; void ServerContext::CompletionOp::Unref() { - grpc::unique_lock lock(mu_); + std::unique_lock lock(mu_); if (--refs_ == 0) { lock.unlock(); + grpc_call* call = call_.call(); delete this; + grpc_call_unref(call); } } -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; +void ServerContext::CompletionOp::FillOps(internal::Call* call) { + grpc_op ops; + ops.op = GRPC_OP_RECV_CLOSE_ON_SERVER; + ops.data.recv_close_on_server.cancelled = &cancelled_; + ops.flags = 0; + ops.reserved = nullptr; + interceptor_methods_.SetCall(&call_); + interceptor_methods_.SetReverse(); + interceptor_methods_.SetCallOpSetInterface(this); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(call->call(), &ops, 1, + core_cq_tag_, nullptr)); + /* No interceptors to run here */ } 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; + std::unique_lock lock(mu_); + if (done_intercepting_) { + /* We are done intercepting. */ + if (has_tag_) { + *tag = tag_; + ret = true; + } + if (--refs_ == 0) { + lock.unlock(); + grpc_call* call = call_.call(); + delete this; + grpc_call_unref(call); + } + return ret; } - if (!*status) cancelled_ = 1; - if (--refs_ == 0) { - lock.unlock(); - delete this; + finalized_ = true; + + // If for some reason the incoming status is false, mark that as a + // cancellation. + // TODO(vjpai): does this ever happen? + if (!*status) { + cancelled_ = 1; + } + + if (cancelled_ && (reactor_ != nullptr)) { + reactor_->OnCancel(); + } + /* Release the lock since we are going to be running through interceptors now + */ + lock.unlock(); + /* Add interception point and run through interceptors */ + interceptor_methods_.AddInterceptionHookPoint( + experimental::InterceptionHookPoints::POST_RECV_CLOSE); + if (interceptor_methods_.RunInterceptors()) { + /* No interceptors were run */ + if (has_tag_) { + *tag = tag_; + ret = true; + } + lock.lock(); + if (--refs_ == 0) { + lock.unlock(); + grpc_call* call = call_.call(); + delete this; + grpc_call_unref(call); + } + return ret; } - return ret; + /* There are interceptors to be run. Return false for now */ + return false; } // ServerContext body -ServerContext::ServerContext() - : completion_op_(nullptr), - has_notify_when_done_tag_(false), - async_notify_when_done_tag_(nullptr), - deadline_(gpr_inf_future(GPR_CLOCK_REALTIME)), - 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_); - } +ServerContext::ServerContext() { Setup(gpr_inf_future(GPR_CLOCK_REALTIME)); } + +ServerContext::ServerContext(gpr_timespec deadline, grpc_metadata_array* arr) { + Setup(deadline); + std::swap(*client_metadata_.arr(), *arr); +} + +void ServerContext::Setup(gpr_timespec deadline) { + 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; + compression_level_set_ = false; + has_pending_ops_ = false; + rpc_info_ = nullptr; +} + +void ServerContext::BindDeadlineAndMetadata(gpr_timespec deadline, + grpc_metadata_array* arr) { + deadline_ = deadline; + std::swap(*client_metadata_.arr(), *arr); +} + +ServerContext::~ServerContext() { Clear(); } + +void ServerContext::Clear() { + auth_context_.reset(); + initial_metadata_.clear(); + trailing_metadata_.clear(); + client_metadata_.Reset(); if (completion_op_) { completion_op_->Unref(); + completion_op_ = nullptr; + completion_tag_.Clear(); + } + if (rpc_info_) { + rpc_info_->Unref(); + rpc_info_ = nullptr; + } + if (call_) { + auto* call = call_; + call_ = nullptr; + grpc_call_unref(call); } } -void ServerContext::BeginCompletionOp(Call* call) { +void ServerContext::BeginCompletionOp(internal::Call* call, + std::function callback, + internal::ServerReactor* reactor) { GPR_ASSERT(!completion_op_); - completion_op_ = new CompletionOp(); - if (has_notify_when_done_tag_) { + if (rpc_info_) { + rpc_info_->Ref(); + } + grpc_call_ref(call->call()); + completion_op_ = + new (grpc_call_arena_alloc(call->call(), sizeof(CompletionOp))) + CompletionOp(call, reactor); + if (callback != nullptr) { + completion_tag_.Set(call->call(), std::move(callback), completion_op_); + completion_op_->set_core_cq_tag(&completion_tag_); + completion_op_->set_tag(completion_op_); + } else if (has_notify_when_done_tag_) { completion_op_->set_tag(async_notify_when_done_tag_); } call->PerformOps(completion_op_); } +internal::CompletionQueueTag* ServerContext::GetCompletionOpTag() { + return static_cast(completion_op_); +} + void ServerContext::AddInitialMetadata(const grpc::string& key, const grpc::string& value) { initial_metadata_.insert(std::make_pair(key, value)); @@ -174,45 +315,44 @@ void ServerContext::AddTrailingMetadata(const grpc::string& key, } void ServerContext::TryCancel() const { + internal::CancelInterceptorBatchMethods cancel_methods; + if (rpc_info_) { + for (size_t i = 0; i < rpc_info_->interceptors_.size(); i++) { + rpc_info_->RunInterceptor(&cancel_methods, i); + } + } grpc_call_error err = grpc_call_cancel_with_status( - call_, GRPC_STATUS_CANCELLED, "Cancelled on the server side", NULL); + call_, GRPC_STATUS_CANCELLED, "Cancelled on the server side", nullptr); if (err != GRPC_CALL_OK) { gpr_log(GPR_ERROR, "TryCancel failed with: %d", err); } } 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); + if (completion_tag_) { + // When using callback API, this result is always valid. + return completion_op_->CheckCancelledAsync(); + } else if (has_notify_when_done_tag_) { + // When using async API, the result is only valid + // if the tag has already been delivered at the completion queue + return completion_op_ && completion_op_->CheckCancelledAsync(); + } else { + // when using sync API, the result is always valid + return completion_op_ && completion_op_->CheckCancelled(cq_); + } } void ServerContext::set_compression_algorithm( grpc_compression_algorithm algorithm) { - char* algorithm_name = NULL; + compression_algorithm_ = algorithm; + const 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 != 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_; + GPR_ASSERT(algorithm_name != nullptr); + AddInitialMetadata(GRPC_COMPRESSION_REQUEST_ALGORITHM_MD_KEY, algorithm_name); } grpc::string ServerContext::peer() const { @@ -229,4 +369,12 @@ const struct census_context* ServerContext::census_context() const { return grpc_census_call_get_context(call_); } +void ServerContext::SetLoadReportingCosts( + const std::vector& cost_data) { + if (call_ == nullptr) return; + for (const auto& cost_datum : cost_data) { + AddTrailingMetadata(GRPC_LB_COST_MD_KEY, cost_datum); + } +} + } // namespace grpc diff --git a/third_party/grpc/src/cpp/server/server_credentials.cc b/third_party/grpc/src/cpp/server/server_credentials.cc index 84959161781eb1..c3b3a8b3793fce 100644 --- a/third_party/grpc/src/cpp/server/server_credentials.cc +++ b/third_party/grpc/src/cpp/server/server_credentials.cc @@ -1,37 +1,22 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include namespace grpc { diff --git a/third_party/grpc/src/cpp/server/server_posix.cc b/third_party/grpc/src/cpp/server/server_posix.cc new file mode 100644 index 00000000000000..7c221ed036a06a --- /dev/null +++ b/third_party/grpc/src/cpp/server/server_posix.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +namespace grpc { + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +void AddInsecureChannelFromFd(Server* server, int fd) { + grpc_server_add_insecure_channel_from_fd(server->c_server(), nullptr, fd); +} + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/server/thread_pool_interface.h b/third_party/grpc/src/cpp/server/thread_pool_interface.h index 1ebe30fe2a5c74..028842a776f8ac 100644 --- a/third_party/grpc/src/cpp/server/thread_pool_interface.h +++ b/third_party/grpc/src/cpp/server/thread_pool_interface.h @@ -1,33 +1,18 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ @@ -47,6 +32,10 @@ class ThreadPoolInterface { virtual void Add(const std::function& callback) = 0; }; +// Allows different codebases to use their own thread pool impls +typedef ThreadPoolInterface* (*CreateThreadPoolFunc)(void); +void SetCreateThreadPool(CreateThreadPoolFunc func); + ThreadPoolInterface* CreateDefaultThreadPool(); } // namespace grpc diff --git a/third_party/grpc/src/cpp/thread_manager/thread_manager.cc b/third_party/grpc/src/cpp/thread_manager/thread_manager.cc new file mode 100644 index 00000000000000..3e8606a76fda99 --- /dev/null +++ b/third_party/grpc/src/cpp/thread_manager/thread_manager.cc @@ -0,0 +1,252 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/cpp/thread_manager/thread_manager.h" + +#include +#include + +#include +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/exec_ctx.h" + +namespace grpc { + +ThreadManager::WorkerThread::WorkerThread(ThreadManager* thd_mgr) + : thd_mgr_(thd_mgr) { + // Make thread creation exclusive with respect to its join happening in + // ~WorkerThread(). + thd_ = grpc_core::Thread( + "grpcpp_sync_server", + [](void* th) { static_cast(th)->Run(); }, + this); + thd_.Start(); +} + +void ThreadManager::WorkerThread::Run() { + thd_mgr_->MainWorkLoop(); + thd_mgr_->MarkAsCompleted(this); +} + +ThreadManager::WorkerThread::~WorkerThread() { + // Don't join until the thread is fully constructed. + thd_.Join(); +} + +ThreadManager::ThreadManager(const char* name, + grpc_resource_quota* resource_quota, + int min_pollers, int max_pollers) + : shutdown_(false), + num_pollers_(0), + min_pollers_(min_pollers), + max_pollers_(max_pollers == -1 ? INT_MAX : max_pollers), + num_threads_(0), + max_active_threads_sofar_(0) { + resource_user_ = grpc_resource_user_create(resource_quota, name); +} + +ThreadManager::~ThreadManager() { + { + std::lock_guard lock(mu_); + GPR_ASSERT(num_threads_ == 0); + } + + grpc_core::ExecCtx exec_ctx; // grpc_resource_user_unref needs an exec_ctx + grpc_resource_user_unref(resource_user_); + CleanupCompletedThreads(); +} + +void ThreadManager::Wait() { + std::unique_lock lock(mu_); + while (num_threads_ != 0) { + shutdown_cv_.wait(lock); + } +} + +void ThreadManager::Shutdown() { + std::lock_guard lock(mu_); + shutdown_ = true; +} + +bool ThreadManager::IsShutdown() { + std::lock_guard lock(mu_); + return shutdown_; +} + +int ThreadManager::GetMaxActiveThreadsSoFar() { + std::lock_guard list_lock(list_mu_); + return max_active_threads_sofar_; +} + +void ThreadManager::MarkAsCompleted(WorkerThread* thd) { + { + std::lock_guard list_lock(list_mu_); + completed_threads_.push_back(thd); + } + + { + std::lock_guard lock(mu_); + num_threads_--; + if (num_threads_ == 0) { + shutdown_cv_.notify_one(); + } + } + + // Give a thread back to the resource quota + grpc_resource_user_free_threads(resource_user_, 1); +} + +void ThreadManager::CleanupCompletedThreads() { + std::list completed_threads; + { + // swap out the completed threads list: allows other threads to clean up + // more quickly + std::unique_lock lock(list_mu_); + completed_threads.swap(completed_threads_); + } + for (auto thd : completed_threads) delete thd; +} + +void ThreadManager::Initialize() { + if (!grpc_resource_user_allocate_threads(resource_user_, min_pollers_)) { + gpr_log(GPR_ERROR, + "No thread quota available to even create the minimum required " + "polling threads (i.e %d). Unable to start the thread manager", + min_pollers_); + abort(); + } + + { + std::unique_lock lock(mu_); + num_pollers_ = min_pollers_; + num_threads_ = min_pollers_; + max_active_threads_sofar_ = min_pollers_; + } + + for (int i = 0; i < min_pollers_; i++) { + new WorkerThread(this); + } +} + +void ThreadManager::MainWorkLoop() { + while (true) { + void* tag; + bool ok; + WorkStatus work_status = PollForWork(&tag, &ok); + + std::unique_lock lock(mu_); + // Reduce the number of pollers by 1 and check what happened with the poll + num_pollers_--; + bool done = false; + switch (work_status) { + case TIMEOUT: + // If we timed out and we have more pollers than we need (or we are + // shutdown), finish this thread + if (shutdown_ || num_pollers_ > max_pollers_) done = true; + break; + case SHUTDOWN: + // If the thread manager is shutdown, finish this thread + done = true; + break; + case WORK_FOUND: + // If we got work and there are now insufficient pollers and there is + // quota available to create a new thread, start a new poller thread + bool resource_exhausted = false; + if (!shutdown_ && num_pollers_ < min_pollers_) { + if (grpc_resource_user_allocate_threads(resource_user_, 1)) { + // We can allocate a new poller thread + num_pollers_++; + num_threads_++; + if (num_threads_ > max_active_threads_sofar_) { + max_active_threads_sofar_ = num_threads_; + } + // Drop lock before spawning thread to avoid contention + lock.unlock(); + new WorkerThread(this); + } else if (num_pollers_ > 0) { + // There is still at least some thread polling, so we can go on + // even though we are below the number of pollers that we would + // like to have (min_pollers_) + lock.unlock(); + } else { + // There are no pollers to spare and we couldn't allocate + // a new thread, so resources are exhausted! + lock.unlock(); + resource_exhausted = true; + } + } else { + // There are a sufficient number of pollers available so we can do + // the work and continue polling with our existing poller threads + lock.unlock(); + } + // Lock is always released at this point - do the application work + // or return resource exhausted if there is new work but we couldn't + // get a thread in which to do it. + DoWork(tag, ok, !resource_exhausted); + // Take the lock again to check post conditions + lock.lock(); + // If we're shutdown, we should finish at this point. + if (shutdown_) done = true; + break; + } + // If we decided to finish the thread, break out of the while loop + if (done) break; + + // Otherwise go back to polling as long as it doesn't exceed max_pollers_ + // + // **WARNING**: + // There is a possibility of threads thrashing here (i.e excessive thread + // shutdowns and creations than the ideal case). This happens if max_poller_ + // count is small and the rate of incoming requests is also small. In such + // scenarios we can possibly configure max_pollers_ to a higher value and/or + // increase the cq timeout. + // + // However, not doing this check here and unconditionally incrementing + // num_pollers (and hoping that the system will eventually settle down) has + // far worse consequences i.e huge number of threads getting created to the + // point of thread-exhaustion. For example: if the incoming request rate is + // very high, all the polling threads will return very quickly from + // PollForWork() with WORK_FOUND. They all briefly decrement num_pollers_ + // counter thereby possibly - and briefly - making it go below min_pollers; + // This will most likely result in the creation of a new poller since + // num_pollers_ dipped below min_pollers_. + // + // Now, If we didn't do the max_poller_ check here, all these threads will + // go back to doing PollForWork() and the whole cycle repeats (with a new + // thread being added in each cycle). Once the total number of threads in + // the system crosses a certain threshold (around ~1500), there is heavy + // contention on mutexes (the mu_ here or the mutexes in gRPC core like the + // pollset mutex) that makes DoWork() take longer to finish thereby causing + // new poller threads to be created even faster. This results in a thread + // avalanche. + if (num_pollers_ < max_pollers_) { + num_pollers_++; + } else { + break; + } + }; + + // This thread is exiting. Do some cleanup work i.e delete already completed + // worker threads + CleanupCompletedThreads(); + + // If we are here, either ThreadManager is shutting down or it already has + // enough threads. +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/thread_manager/thread_manager.h b/third_party/grpc/src/cpp/thread_manager/thread_manager.h new file mode 100644 index 00000000000000..6f0bd17c5fe10c --- /dev/null +++ b/third_party/grpc/src/cpp/thread_manager/thread_manager.h @@ -0,0 +1,178 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_THREAD_MANAGER_H +#define GRPC_INTERNAL_CPP_THREAD_MANAGER_H + +#include +#include +#include +#include + +#include + +#include "src/core/lib/gprpp/thd.h" +#include "src/core/lib/iomgr/resource_quota.h" + +namespace grpc { + +class ThreadManager { + public: + explicit ThreadManager(const char* name, grpc_resource_quota* resource_quota, + int min_pollers, int max_pollers); + virtual ~ThreadManager(); + + // Initializes and Starts the Rpc Manager threads + void Initialize(); + + // The return type of PollForWork() function + enum WorkStatus { WORK_FOUND, SHUTDOWN, TIMEOUT }; + + // "Polls" for new work. + // If the return value is WORK_FOUND: + // - The implementaion of PollForWork() MAY set some opaque identifier to + // (identify the work item found) via the '*tag' parameter + // - The implementaion MUST set the value of 'ok' to 'true' or 'false'. A + // value of 'false' indicates some implemenation specific error (that is + // neither SHUTDOWN nor TIMEOUT) + // - ThreadManager does not interpret the values of 'tag' and 'ok' + // - ThreadManager WILL call DoWork() and pass '*tag' and 'ok' as input to + // DoWork() + // + // If the return value is SHUTDOWN:, + // - ThreadManager WILL NOT call DoWork() and terminates the thead + // + // If the return value is TIMEOUT:, + // - ThreadManager WILL NOT call DoWork() + // - ThreadManager MAY terminate the thread depending on the current number + // of active poller threads and mix_pollers/max_pollers settings + // - Also, the value of timeout is specific to the derived class + // implementation + virtual WorkStatus PollForWork(void** tag, bool* ok) = 0; + + // The implementation of DoWork() is supposed to perform the work found by + // PollForWork(). The tag and ok parameters are the same as returned by + // PollForWork(). The resources parameter indicates that the call actually + // has the resources available for performing the RPC's work. If it doesn't, + // the implementation should fail it appropriately. + // + // The implementation of DoWork() should also do any setup needed to ensure + // that the next call to PollForWork() (not necessarily by the current thread) + // actually finds some work + virtual void DoWork(void* tag, bool ok, bool resources) = 0; + + // Mark the ThreadManager as shutdown and begin draining the work. This is a + // non-blocking call and the caller should call Wait(), a blocking call which + // returns only once the shutdown is complete + virtual void Shutdown(); + + // Has Shutdown() been called + bool IsShutdown(); + + // A blocking call that returns only after the ThreadManager has shutdown and + // all the threads have drained all the outstanding work + virtual void Wait(); + + // Max number of concurrent threads that were ever active in this thread + // manager so far. This is useful for debugging purposes (and in unit tests) + // to check if resource_quota is properly being enforced. + int GetMaxActiveThreadsSoFar(); + + private: + // Helper wrapper class around grpc_core::Thread. Takes a ThreadManager object + // and starts a new grpc_core::Thread to calls the Run() function. + // + // The Run() function calls ThreadManager::MainWorkLoop() function and once + // that completes, it marks the WorkerThread completed by calling + // ThreadManager::MarkAsCompleted() + // + // WHY IS THIS NEEDED?: + // When a thread terminates, some other thread *must* call Join() on that + // thread so that the resources are released. Having a WorkerThread wrapper + // will make this easier. Once Run() completes, each thread calls the + // following two functions: + // ThreadManager::CleanupCompletedThreads() + // ThreadManager::MarkAsCompleted() + // + // - MarkAsCompleted() puts the WorkerThread object in the ThreadManger's + // completed_threads_ list + // - CleanupCompletedThreads() calls "Join()" on the threads that are already + // in the completed_threads_ list (since a thread cannot call Join() on + // itself, it calls CleanupCompletedThreads() *before* calling + // MarkAsCompleted()) + // + // TODO(sreek): Consider creating the threads 'detached' so that Join() need + // not be called (and the need for this WorkerThread class is eliminated) + class WorkerThread { + public: + WorkerThread(ThreadManager* thd_mgr); + ~WorkerThread(); + + private: + // Calls thd_mgr_->MainWorkLoop() and once that completes, calls + // thd_mgr_>MarkAsCompleted(this) to mark the thread as completed + void Run(); + + ThreadManager* const thd_mgr_; + grpc_core::Thread thd_; + }; + + // The main funtion in ThreadManager + void MainWorkLoop(); + + void MarkAsCompleted(WorkerThread* thd); + void CleanupCompletedThreads(); + + // Protects shutdown_, num_pollers_, num_threads_ and + // max_active_threads_sofar_ + std::mutex mu_; + + bool shutdown_; + std::condition_variable shutdown_cv_; + + // The resource user object to use when requesting quota to create threads + // + // Note: The user of this ThreadManager object must create grpc_resource_quota + // object (that contains the actual max thread quota) and a grpc_resource_user + // object through which quota is requested whenver new threads need to be + // created + grpc_resource_user* resource_user_; + + // Number of threads doing polling + int num_pollers_; + + // The minimum and maximum number of threads that should be doing polling + int min_pollers_; + int max_pollers_; + + // The total number of threads currently active (includes threads includes the + // threads that are currently polling i.e num_pollers_) + int num_threads_; + + // See GetMaxActiveThreadsSoFar()'s description. + // To be more specific, this variable tracks the max value num_threads_ was + // ever set so far + int max_active_threads_sofar_; + + std::mutex list_mu_; + std::list completed_threads_; +}; + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_THREAD_MANAGER_H diff --git a/third_party/grpc/src/cpp/util/byte_buffer.cc b/third_party/grpc/src/cpp/util/byte_buffer.cc deleted file mode 100644 index 3a2318d1a60f57..00000000000000 --- a/third_party/grpc/src/cpp/util/byte_buffer.cc +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright 2015-2016, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#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)); - } - grpc_byte_buffer_reader_destroy(&reader); -} - -size_t ByteBuffer::Length() const { - if (buffer_) { - return grpc_byte_buffer_length(buffer_); - } else { - return 0; - } -} - -ByteBuffer::ByteBuffer(const ByteBuffer& buf) - : buffer_(grpc_byte_buffer_copy(buf.buffer_)) {} - -ByteBuffer& ByteBuffer::operator=(const ByteBuffer& buf) { - Clear(); // first remove existing data - if (buf.buffer_) { - buffer_ = grpc_byte_buffer_copy(buf.buffer_); // then copy - } - return *this; -} - -} // namespace grpc diff --git a/third_party/grpc/src/cpp/util/byte_buffer_cc.cc b/third_party/grpc/src/cpp/util/byte_buffer_cc.cc new file mode 100644 index 00000000000000..a7e164543525d2 --- /dev/null +++ b/third_party/grpc/src/cpp/util/byte_buffer_cc.cc @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +namespace grpc { + +static internal::GrpcLibraryInitializer g_gli_initializer; + +Status ByteBuffer::Dump(std::vector* slices) const { + slices->clear(); + if (!buffer_) { + return Status(StatusCode::FAILED_PRECONDITION, "Buffer not initialized"); + } + grpc_byte_buffer_reader reader; + if (!grpc_byte_buffer_reader_init(&reader, buffer_)) { + return Status(StatusCode::INTERNAL, + "Couldn't initialize byte buffer reader"); + } + grpc_slice s; + while (grpc_byte_buffer_reader_next(&reader, &s)) { + slices->push_back(Slice(s, Slice::STEAL_REF)); + } + grpc_byte_buffer_reader_destroy(&reader); + return Status::OK; +} + +ByteBuffer::ByteBuffer(const ByteBuffer& buf) : buffer_(nullptr) { + operator=(buf); +} + +ByteBuffer& ByteBuffer::operator=(const ByteBuffer& buf) { + if (this != &buf) { + Clear(); // first remove existing data + } + if (buf.buffer_) { + buffer_ = grpc_byte_buffer_copy(buf.buffer_); // then copy + } + return *this; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/util/core_stats.cc b/third_party/grpc/src/cpp/util/core_stats.cc new file mode 100644 index 00000000000000..edf0b1bb67838c --- /dev/null +++ b/third_party/grpc/src/cpp/util/core_stats.cc @@ -0,0 +1,90 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/cpp/util/core_stats.h" + +#include + +using grpc::core::Bucket; +using grpc::core::Histogram; +using grpc::core::Metric; +using grpc::core::Stats; + +namespace grpc { + +void CoreStatsToProto(const grpc_stats_data& core, Stats* proto) { + for (int i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + Metric* m = proto->add_metrics(); + m->set_name(grpc_stats_counter_name[i]); + m->set_count(core.counters[i]); + } + for (int i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { + Metric* m = proto->add_metrics(); + m->set_name(grpc_stats_histogram_name[i]); + Histogram* h = m->mutable_histogram(); + for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { + Bucket* b = h->add_buckets(); + b->set_start(grpc_stats_histo_bucket_boundaries[i][j]); + b->set_count(core.histograms[grpc_stats_histo_start[i] + j]); + } + } +} + +void ProtoToCoreStats(const grpc::core::Stats& proto, grpc_stats_data* core) { + memset(core, 0, sizeof(*core)); + for (const auto& m : proto.metrics()) { + switch (m.value_case()) { + case Metric::VALUE_NOT_SET: + break; + case Metric::kCount: + for (int i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + if (m.name() == grpc_stats_counter_name[i]) { + core->counters[i] = m.count(); + break; + } + } + break; + case Metric::kHistogram: + for (int i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { + if (m.name() == grpc_stats_histogram_name[i]) { + const auto& h = m.histogram(); + bool valid = true; + if (grpc_stats_histo_buckets[i] != h.buckets_size()) valid = false; + for (int j = 0; valid && j < h.buckets_size(); j++) { + if (grpc_stats_histo_bucket_boundaries[i][j] != + h.buckets(j).start()) { + valid = false; + } + } + if (!valid) { + gpr_log(GPR_ERROR, + "Found histogram %s but shape is different from proto", + m.name().c_str()); + } + for (int j = 0; valid && j < h.buckets_size(); j++) { + core->histograms[grpc_stats_histo_start[i] + j] = + h.buckets(j).count(); + } + } + } + break; + } + } +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/util/core_stats.h b/third_party/grpc/src/cpp/util/core_stats.h new file mode 100644 index 00000000000000..6366d7d06e7fb3 --- /dev/null +++ b/third_party/grpc/src/cpp/util/core_stats.h @@ -0,0 +1,33 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef GRPC_INTERNAL_CPP_UTIL_CORE_STATS_H +#define GRPC_INTERNAL_CPP_UTIL_CORE_STATS_H + +#include "src/proto/grpc/core/stats.pb.h" + +#include "src/core/lib/debug/stats.h" + +namespace grpc { + +void CoreStatsToProto(const grpc_stats_data& core, grpc::core::Stats* proto); +void ProtoToCoreStats(const grpc::core::Stats& proto, grpc_stats_data* core); + +} // namespace grpc + +#endif // GRPC_INTERNAL_CPP_UTIL_CORE_STATS_H diff --git a/third_party/grpc/src/cpp/util/error_details.cc b/third_party/grpc/src/cpp/util/error_details.cc new file mode 100644 index 00000000000000..42c887aed73e38 --- /dev/null +++ b/third_party/grpc/src/cpp/util/error_details.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/proto/grpc/status/status.pb.h" + +namespace grpc { + +Status ExtractErrorDetails(const Status& from, ::google::rpc::Status* to) { + if (to == nullptr) { + return Status(StatusCode::FAILED_PRECONDITION, ""); + } + if (!to->ParseFromString(from.error_details())) { + return Status(StatusCode::INVALID_ARGUMENT, ""); + } + return Status::OK; +} + +Status SetErrorDetails(const ::google::rpc::Status& from, Status* to) { + if (to == nullptr) { + return Status(StatusCode::FAILED_PRECONDITION, ""); + } + StatusCode code = StatusCode::UNKNOWN; + if (from.code() >= StatusCode::OK && + from.code() <= StatusCode::UNAUTHENTICATED) { + code = static_cast(from.code()); + } + *to = Status(code, from.message(), from.SerializeAsString()); + return Status::OK; +} + +} // namespace grpc diff --git a/third_party/grpc/src/cpp/util/slice.cc b/third_party/grpc/src/cpp/util/slice.cc deleted file mode 100644 index 7e88423b6c5304..00000000000000 --- a/third_party/grpc/src/cpp/util/slice.cc +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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/third_party/grpc/src/cpp/util/status.cc b/third_party/grpc/src/cpp/util/status.cc index ad9850cf074ec3..93696d81268d61 100644 --- a/third_party/grpc/src/cpp/util/status.cc +++ b/third_party/grpc/src/cpp/util/status.cc @@ -1,37 +1,22 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include +#include namespace grpc { diff --git a/third_party/grpc/src/cpp/util/string_ref.cc b/third_party/grpc/src/cpp/util/string_ref.cc index 66c79a1818431d..8b09a82a634bb9 100644 --- a/third_party/grpc/src/cpp/util/string_ref.cc +++ b/third_party/grpc/src/cpp/util/string_ref.cc @@ -1,104 +1,25 @@ /* * - * Copyright 2015, Google Inc. - * All rights reserved. + * Copyright 2015 gRPC authors. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * http://www.apache.org/licenses/LICENSE-2.0 * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * */ -#include - -#include - -#include -#include +#include namespace grpc { const size_t string_ref::npos = size_t(-1); -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/third_party/grpc/src/cpp/util/time.cc b/third_party/grpc/src/cpp/util/time.cc deleted file mode 100644 index 2685e31ee60038..00000000000000 --- a/third_party/grpc/src/cpp/util/time.cc +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (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 = (int64_t)secs.count(); - to->tv_nsec = (int32_t)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 = (int64_t)secs.count(); - to->tv_nsec = (int32_t)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/third_party/grpc/src/cpp/util/time_cc.cc b/third_party/grpc/src/cpp/util/time_cc.cc new file mode 100644 index 00000000000000..6c9c228d7cd184 --- /dev/null +++ b/third_party/grpc/src/cpp/util/time_cc.cc @@ -0,0 +1,75 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +using std::chrono::duration_cast; +using std::chrono::high_resolution_clock; +using std::chrono::nanoseconds; +using std::chrono::seconds; +using std::chrono::system_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 = static_cast(secs.count()); + to->tv_nsec = static_cast(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 = static_cast(secs.count()); + to->tv_nsec = static_cast(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 diff --git a/third_party/nanopb/CHANGELOG.txt b/third_party/nanopb/CHANGELOG.txt index b81b847eac99fe..8437688fc525d1 100644 --- a/third_party/nanopb/CHANGELOG.txt +++ b/third_party/nanopb/CHANGELOG.txt @@ -1,3 +1,27 @@ +nanopb-0.3.6 (2016-06-19) + Protect against corrupted _count fields in pb_release (#205) + Fix error in STATIC_ASSERT with multiple files (#203) + Add -D option to specify output directory (#193) + Generate MIN/MAX/ARRAYSIZE defines for enums (#194) + Generate comments about uncalculable message sizes (#195) + Documentation updates (#196, #201) + Improvements to test cases. + +nanopb-0.3.5 (2016-02-13) + NOTE: If you are using pb_syshdr.h, you will need to add uint_least8_t + definition. See docs/migration.rst for details. + + Fix generator crash with Enum inside Oneof (#188) + Fix some generator regressions related to .options file path (#172) + Add support for platforms without uint8_t (#191) + Allow const parameter to pb_istream_from_buffer (#152) + Ignore null pointers in pb_release() (#183) + Add support for anonymous unions (#184) + Add Python3 support to the generator (#169) + Add code generator insertion points to generated files (#178) + Improvements to CMake script (#181) + Improvements to test cases. + nanopb-0.3.4 (2015-09-26) Fix handling of unsigned 8- and 16-bit enums (issue 164) Fix generator on systems where python = python3. (issue 155) diff --git a/third_party/nanopb/CONTRIBUTING.md b/third_party/nanopb/CONTRIBUTING.md new file mode 100644 index 00000000000000..4041bc3caf78c1 --- /dev/null +++ b/third_party/nanopb/CONTRIBUTING.md @@ -0,0 +1,32 @@ +Contributing to Nanopb development +================================== + +Reporting issues and requesting features +---------------------------------------- + +Feel free to report any issues you see or features you would like +to see in the future to the Github issue tracker. Using the templates +below is preferred: + +* [Report a bug](https://github.com/nanopb/nanopb/issues/new?body=**Steps%20to%20reproduce%20the%20issue**%0a%0a1.%0a2.%0a3.%0a%0a**What%20happens?**%0A%0A**What%20should%20happen?**&labels=Type-Defect) +* [Request a feature](https://github.com/nanopb/nanopb/issues/new?body=**What%20should%20the%20feature%20do?**%0A%0A**In%20what%20situation%20would%20the%20feature%20be%20useful?**&labels=Type-Enhancement) + +Requesting help +--------------- + +If there is something strange going on, but you do not know if +it is actually a bug in nanopb, try asking first on the +[discussion forum](https://groups.google.com/forum/#!forum/nanopb). + +Pull requests +------------- + +Pull requests are welcome! + +If it is not obvious from the commit message, please indicate the +same information as you would for an issue report: + +* What functionality it fixes/adds. +* How can the problem be reproduced / when would the feature be useful. + + diff --git a/third_party/nanopb/README.bazel.md b/third_party/nanopb/README.bazel.md index deab3e6de7dc95..03ec26d49e1dd6 100644 --- a/third_party/nanopb/README.bazel.md +++ b/third_party/nanopb/README.bazel.md @@ -1,5 +1,9 @@ To reproduce this tree, to: -`git clone https://github.com/nanopb/nanopb.git && rm -fr nanopb/tests` - -This tree was cloned at commit `f8ac4637`. +1. `git clone https://github.com/nanopb/nanopb.git` in a convenient directory +2. `git checkout ` (current is `0.3.6`, commithash `eb0e73ca`) + Note that we want the same nanopb release used by gRPC upstream for the + version in third_party/grpc. +3. `rm -fr /tests` +4. `cp -R /* third_party/nanopb +5. Update BUILD and README.bazel.md if necessary. diff --git a/third_party/nanopb/docs/concepts.rst b/third_party/nanopb/docs/concepts.rst index b0fd43aab75e9f..b4f657e2aad6ee 100644 --- a/third_party/nanopb/docs/concepts.rst +++ b/third_party/nanopb/docs/concepts.rst @@ -255,6 +255,61 @@ generates this field description array for the structure *Person_PhoneNumber*:: PB_LAST_FIELD }; +Oneof +===== +Protocol Buffers supports `oneof`_ sections. Here is an example of ``oneof`` usage:: + + message MsgType1 { + required int32 value = 1; + } + + message MsgType2 { + required bool value = 1; + } + + message MsgType3 { + required int32 value1 = 1; + required int32 value2 = 2; + } + + message MyMessage { + required uint32 uid = 1; + required uint32 pid = 2; + required uint32 utime = 3; + + oneof payload { + MsgType1 msg1 = 4; + MsgType2 msg2 = 5; + MsgType3 msg3 = 6; + } + } + +Nanopb will generate ``payload`` as a C union and add an additional field ``which_payload``:: + + typedef struct _MyMessage { + uint32_t uid; + uint32_t pid; + uint32_t utime; + pb_size_t which_payload; + union { + MsgType1 msg1; + MsgType2 msg2; + MsgType3 msg3; + } payload; + /* @@protoc_insertion_point(struct:MyMessage) */ + } MyMessage; + +``which_payload`` indicates which of the ``oneof`` fields is actually set. +The user is expected to set the filed manually using the correct field tag:: + + MyMessage msg = MyMessage_init_zero; + msg.payload.msg2.value = true; + msg.which_payload = MyMessage_msg2_tag; + +Notice that neither ``which_payload`` field nor the unused fileds in ``payload`` +will consume any space in the resulting encoded message. + +.. _`oneof`: https://developers.google.com/protocol-buffers/docs/reference/proto2-spec#oneof_and_oneof_field Extension fields ================ diff --git a/third_party/nanopb/docs/index.rst b/third_party/nanopb/docs/index.rst index d49abc0708d915..24328574e2e9db 100644 --- a/third_party/nanopb/docs/index.rst +++ b/third_party/nanopb/docs/index.rst @@ -23,6 +23,7 @@ So a typical project might include these files: 1) Nanopb runtime library: - pb.h + - pb_common.h and pb_common.c (always needed) - pb_decode.h and pb_decode.c (needed for decoding messages) - pb_encode.h and pb_encode.c (needed for encoding messages) 2) Protocol description (you can have many): @@ -39,9 +40,9 @@ Features and limitations #) Small code size (2–10 kB depending on processor, plus any message definitions) #) Small ram usage (typically ~300 bytes, plus any message structs) #) Allows specifying maximum size for strings and arrays, so that they can be allocated statically. -#) No malloc needed: everything can be allocated statically or on the stack. +#) No malloc needed: everything can be allocated statically or on the stack. Optional malloc support available. #) You can use either encoder or decoder alone to cut the code size in half. -#) Support for most protobuf features, including: all data types, nested submessages, default values, repeated and optional fields, packed arrays, extension fields. +#) Support for most protobuf features, including: all data types, nested submessages, default values, repeated and optional fields, oneofs, packed arrays, extension fields. #) Callback mechanism for handling messages larger than can fit in available RAM. #) Extensive set of tests. @@ -53,8 +54,8 @@ Features and limitations #) Fields in the generated structs are ordered by the tag number, instead of the natural ordering in .proto file. #) Unknown fields are not preserved when decoding and re-encoding a message. #) Reflection (runtime introspection) is not supported. E.g. you can't request a field by giving its name in a string. -#) Numeric arrays are always encoded as packed, even if not marked as packed in .proto. This causes incompatibility with decoders that do not support packed format. -#) Cyclic references between messages are supported only in callback mode. +#) Numeric arrays are always encoded as packed, even if not marked as packed in .proto.. +#) Cyclic references between messages are supported only in callback and malloc mode. Getting started =============== diff --git a/third_party/nanopb/docs/migration.rst b/third_party/nanopb/docs/migration.rst index ac92db88d47f07..cd5911f5714163 100644 --- a/third_party/nanopb/docs/migration.rst +++ b/third_party/nanopb/docs/migration.rst @@ -11,6 +11,24 @@ are included, in order to make it easier to find this document. .. contents :: +Nanopb-0.3.5 (2016-02-13) +========================= + +Add support for platforms without uint8_t +----------------------------------------- +**Rationale:** Some platforms cannot access 8-bit sized values directly, and +do not define *uint8_t*. Nanopb previously didn't support these platforms. + +**Changes:** References to *uint8_t* were replaced with several alternatives, +one of them being a new *pb_byte_t* typedef. This in turn uses *uint_least8_t* +which means the smallest available type. + +**Required actions:** If your platform does not have a standards-compliant +*stdint.h*, it may lack the definition for *[u]int_least8_t*. This must be +added manually, example can be found in *extra/pb_syshdr.h*. + +**Error indications:** Compiler error: "unknown type name 'uint_least8_t'". + Nanopb-0.3.2 (2015-01-24) ========================= diff --git a/third_party/nanopb/docs/reference.rst b/third_party/nanopb/docs/reference.rst index 296bc78c2af186..7d27a5158d8056 100644 --- a/third_party/nanopb/docs/reference.rst +++ b/third_party/nanopb/docs/reference.rst @@ -20,10 +20,6 @@ You must have the same settings for the nanopb library and all code that includes pb.h. ============================ ================================================ -__BIG_ENDIAN__ Set this if your platform stores integers and - floats in big-endian format. Mixed-endian - systems (different layout for ints and floats) - are currently not supported. PB_NO_PACKED_STRUCTS Disable packed structs. Increases RAM usage but is necessary on some platforms that do not support unaligned memory access. @@ -95,6 +91,7 @@ no_unions Generate 'oneof' fields as optional fields instead of C unions. msgid Specifies a unique id for this message type. Can be used by user code as an identifier. +anonymous_oneof Generate 'oneof' fields as anonymous unions. ============================ ================================================ These options can be defined for the .proto files before they are converted @@ -202,11 +199,20 @@ The options can be defined in file, message and field scopes:: pb.h ==== +pb_byte_t +--------- +Type used for storing byte-sized data, such as raw binary input and bytes-type fields. :: + + typedef uint_least8_t pb_byte_t; + +For most platforms this is equivalent to `uint8_t`. Some platforms however do not support +8-bit variables, and on those platforms 16 or 32 bits need to be used for each byte. + pb_type_t --------- -Defines the encoder/decoder behaviour that should be used for a field. :: +Type used to store the type of each field, to control the encoder/decoder behaviour. :: - typedef uint8_t pb_type_t; + typedef uint_least8_t pb_type_t; The low-order nibble of the enumeration values defines the function that can be used for encoding and decoding the field data: @@ -252,14 +258,14 @@ pb_field_t ---------- Describes a single structure field with memory position in relation to others. The descriptions are usually autogenerated. :: - typedef struct _pb_field_t pb_field_t; - struct _pb_field_t { - uint8_t tag; + typedef struct pb_field_s pb_field_t; + struct pb_field_s { + pb_size_t tag; pb_type_t type; - uint8_t data_offset; - int8_t size_offset; - uint8_t data_size; - uint8_t array_size; + pb_size_t data_offset; + pb_ssize_t size_offset; + pb_size_t data_size; + pb_size_t array_size; const void *ptr; } pb_packed; @@ -278,8 +284,8 @@ pb_bytes_array_t An byte array with a field for storing the length:: typedef struct { - size_t size; - uint8_t bytes[1]; + pb_size_t size; + pb_byte_t bytes[1]; } pb_bytes_array_t; In an actual array, the length of *bytes* may be different. @@ -343,12 +349,14 @@ Ties together the extension field type and the storage for the field value:: const pb_extension_type_t *type; void *dest; pb_extension_t *next; + bool found; } pb_extension_t; :type: Pointer to the structure that defines the callback functions. :dest: Pointer to the variable that stores the field value (as used by the default extension callback functions.) :next: Pointer to the next extension handler, or *NULL*. +:found: Decoder sets this to true if the extension was found. PB_GET_ERROR ------------ @@ -392,7 +400,7 @@ pb_ostream_from_buffer ---------------------- Constructs an output stream for writing into a memory buffer. This is just a helper function, it doesn't do anything you couldn't do yourself in a callback function. It uses an internal callback that stores the pointer in stream *state* field. :: - pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize); + pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); :buf: Memory buffer to write into. :bufsize: Maximum number of bytes to write. @@ -404,7 +412,7 @@ pb_write -------- Writes data to an output stream. Always use this function, instead of trying to call stream callback manually. :: - bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count); + bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); :stream: Output stream to write to. :buf: Pointer to buffer with the data to be written. @@ -445,11 +453,22 @@ This function does this, and it is compatible with *parseDelimitedFrom* in Googl Writing packed arrays is a little bit more involved: you need to use `pb_encode_tag` and specify `PB_WT_STRING` as the wire type. Then you need to know exactly how much data you are going to write, and use `pb_encode_varint`_ to write out the number of bytes before writing the actual data. Substreams can be used to determine the number of bytes beforehand; see `pb_encode_submessage`_ source code for an example. +pb_get_encoded_size +------------------- +Calculates the length of the encoded message. :: + + bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct); + +:size: Calculated size of the encoded message. +:fields: A field description array, usually autogenerated. +:src_struct: Pointer to the data that will be serialized. +:returns: True on success, false on detectable errors in field description or if a field encoder returns false. + pb_encode_tag ------------- Starts a field in the Protocol Buffers binary format: encodes the field number and the wire type of the data. :: - bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, int field_number); + bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); :stream: Output stream to write to. 1-5 bytes will be written. :wiretype: PB_WT_VARINT, PB_WT_64BIT, PB_WT_STRING or PB_WT_32BIT @@ -503,7 +522,7 @@ pb_encode_string ---------------- Writes the length of a string as varint and then contents of the string. Works for fields of type `bytes` and `string`:: - bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size); + bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); :stream: Output stream to write to. :buffer: Pointer to string data. @@ -563,7 +582,7 @@ pb_istream_from_buffer ---------------------- Helper function for creating an input stream that reads data from a memory buffer. :: - pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize); + pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize); :buf: Pointer to byte array to read from. :bufsize: Size of the byte array. @@ -573,7 +592,7 @@ pb_read ------- Read data from input stream. Always use this function, don't try to call the stream callback directly. :: - bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count); + bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); :stream: Input stream to read from. :buf: Buffer to store the data to, or NULL to just read data without storing it anywhere. @@ -632,39 +651,21 @@ This function is compatible with *writeDelimitedTo* in the Google's Protocol Buf pb_release ---------- -Releases any dynamically allocated fields. +Releases any dynamically allocated fields:: void pb_release(const pb_field_t fields[], void *dest_struct); :fields: A field description array. Usually autogenerated. -:dest_struct: Pointer to structure where data will be stored. +:dest_struct: Pointer to structure where data is stored. If NULL, function does nothing. This function is only available if *PB_ENABLE_MALLOC* is defined. It will release any pointer type fields in the structure and set the pointers to NULL. -pb_skip_varint --------------- -Skip a varint_ encoded integer without decoding it. :: - - bool pb_skip_varint(pb_istream_t *stream); - -:stream: Input stream to read from. Will read 1 byte at a time until the MSB is clear. -:returns: True on success, false on IO error. - -pb_skip_string --------------- -Skip a varint-length-prefixed string. This means skipping a value with wire type PB_WT_STRING. :: - - bool pb_skip_string(pb_istream_t *stream); - -:stream: Input stream to read from. -:returns: True on success, false on IO error or length exceeding uint32_t. - pb_decode_tag ------------- Decode the tag that comes before field in the protobuf encoding:: - bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, int *tag, bool *eof); + bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); :stream: Input stream to read from. :wire_type: Pointer to variable where to store the wire type of the field. @@ -731,10 +732,9 @@ pb_decode_fixed64 ----------------- Decode a *fixed64*, *sfixed64* or *double* value. :: - bool pb_dec_fixed(pb_istream_t *stream, const pb_field_t *field, void *dest); + bool pb_decode_fixed64(pb_istream_t *stream, void *dest); :stream: Input stream to read from. 8 bytes will be read. -:field: Not used. :dest: Pointer to destination *int64_t*, *uint64_t* or *double*. :returns: True on success, false on IO errors. diff --git a/third_party/nanopb/docs/security.rst b/third_party/nanopb/docs/security.rst index 2d0affc51a58d8..d85461229d0dc3 100644 --- a/third_party/nanopb/docs/security.rst +++ b/third_party/nanopb/docs/security.rst @@ -38,8 +38,11 @@ these will cause "garbage in, garbage out" behaviour. It will not cause buffer overflows, information disclosure or other security problems: 1. All data read from *pb_istream_t*. -2. All fields in message structures, except callbacks, pointers and extensions. - (Beginning with nanopb-0.2.4, in earlier versions the field sizes are partially unchecked.) +2. All fields in message structures, except: + + - callbacks (*pb_callback_t* structures) + - pointer fields (malloc support) and *_count* fields for pointers + - extensions (*pb_extension_t* structures) Invariants ========== diff --git a/third_party/nanopb/extra/pb_syshdr.h b/third_party/nanopb/extra/pb_syshdr.h index 1ff48230a2d958..55d06a3acc065a 100644 --- a/third_party/nanopb/extra/pb_syshdr.h +++ b/third_party/nanopb/extra/pb_syshdr.h @@ -24,6 +24,14 @@ typedef signed int int32_t; typedef unsigned int uint32_t; typedef signed long long int64_t; typedef unsigned long long uint64_t; + +/* These are ok for most platforms, unless uint8_t is actually not available, + * in which case you should give the smallest available type. */ +typedef int8_t int_least8_t; +typedef uint8_t uint_least8_t; +typedef uint8_t uint_fast8_t; +typedef int16_t int_least16_t; +typedef uint16_t uint_least16_t; #endif /* stddef.h subset */ diff --git a/third_party/nanopb/generator/nanopb_generator.py b/third_party/nanopb/generator/nanopb_generator.py index 2dad4ec1cfad20..2f1d92f48df3ea 100755 --- a/third_party/nanopb/generator/nanopb_generator.py +++ b/third_party/nanopb/generator/nanopb_generator.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals '''Generate header file for nanopb from a ProtoBuf FileDescriptorSet.''' -nanopb_version = "nanopb-0.3.5-dev" +nanopb_version = "nanopb-0.3.6" import sys import re @@ -125,11 +125,15 @@ class EncodedSize: '''Class used to represent the encoded size of a field or a message. Consists of a combination of symbolic sizes and integer sizes.''' def __init__(self, value = 0, symbols = []): - if isinstance(value, strtypes + (Names,)): - symbols = [str(value)] - value = 0 - self.value = value - self.symbols = symbols + if isinstance(value, EncodedSize): + self.value = value.value + self.symbols = value.symbols + elif isinstance(value, strtypes + (Names,)): + self.symbols = [str(value)] + self.value = 0 + else: + self.value = value + self.symbols = symbols def __add__(self, other): if isinstance(other, int): @@ -193,6 +197,10 @@ def __str__(self): result += ' %s;' % self.names + result += '\n#define _%s_MIN %s' % (self.names, self.values[0][0]) + result += '\n#define _%s_MAX %s' % (self.names, self.values[-1][0]) + result += '\n#define _%s_ARRAYSIZE ((%s)(%s+1))' % (self.names, self.names, self.values[-1][0]) + if not self.options.long_names: # Define the long names always so that enum value references # from other files work properly. @@ -209,7 +217,7 @@ def __init__(self, worst = 0, checks = [], field_name = 'undefined'): self.worst = worst self.worst_field = field_name - self.checks = checks + self.checks = list(checks) def extend(self, extend, field_name = None): self.worst = max(self.worst, extend.worst) @@ -463,7 +471,10 @@ def pb_field_t(self, prev_field_name): ''' if self.rules == 'ONEOF': - result = ' PB_ONEOF_FIELD(%s, ' % self.union_name + if self.anonymous: + result = ' PB_ANONYMOUS_ONEOF_FIELD(%s, ' % self.union_name + else: + result = ' PB_ONEOF_FIELD(%s, ' % self.union_name else: result = ' PB_FIELD(' @@ -489,6 +500,9 @@ def pb_field_t(self, prev_field_name): return result + def get_last_field_name(self): + return self.name + def largest_field_value(self): '''Determine if this field needs 16bit or 32bit pb_field_t structure to compile properly. Returns numeric value or a C-expression for assert.''' @@ -497,7 +511,10 @@ def largest_field_value(self): if self.rules == 'REPEATED' and self.allocation == 'STATIC': check.append('pb_membersize(%s, %s[0])' % (self.struct_name, self.name)) elif self.rules == 'ONEOF': - check.append('pb_membersize(%s, %s.%s)' % (self.struct_name, self.union_name, self.name)) + if self.anonymous: + check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name)) + else: + check.append('pb_membersize(%s, %s.%s)' % (self.struct_name, self.union_name, self.name)) else: check.append('pb_membersize(%s, %s)' % (self.struct_name, self.name)) @@ -653,6 +670,7 @@ def __init__(self, struct_name, oneof_desc): self.allocation = 'ONEOF' self.default = None self.rules = 'ONEOF' + self.anonymous = False def add_field(self, field): if field.allocation == 'CALLBACK': @@ -661,6 +679,7 @@ def add_field(self, field): field.union_name = self.name field.rules = 'ONEOF' + field.anonymous = self.anonymous self.fields.append(field) self.fields.sort(key = lambda f: f.tag) @@ -674,7 +693,10 @@ def __str__(self): result += ' union {\n' for f in self.fields: result += ' ' + str(f).replace('\n', '\n ') + '\n' - result += ' } ' + self.name + ';' + if self.anonymous: + result += ' };' + else: + result += ' } ' + self.name + ';' return result def types(self): @@ -693,12 +715,18 @@ def default_decl(self, declaration_only = False): return None def tags(self): - return '\n'.join([f.tags() for f in self.fields]) + return ''.join([f.tags() for f in self.fields]) def pb_field_t(self, prev_field_name): result = ',\n'.join([f.pb_field_t(prev_field_name) for f in self.fields]) return result + def get_last_field_name(self): + if self.anonymous: + return self.fields[-1].name + else: + return self.name + '.' + self.fields[-1].name + def largest_field_value(self): largest = FieldMaxSize() for f in self.fields: @@ -706,10 +734,11 @@ def largest_field_value(self): return largest def encoded_size(self, dependencies): + '''Returns the size of the largest oneof field.''' largest = EncodedSize(0) for f in self.fields: - size = f.encoded_size(dependencies) - if size is None: + size = EncodedSize(f.encoded_size(dependencies)) + if size.value is None: return None elif size.symbols: return None # Cannot resolve maximum of symbols @@ -742,6 +771,8 @@ def __init__(self, names, desc, message_options): pass # No union and skip fields also else: oneof = OneOf(self.name, f) + if oneof_options.anonymous_oneof: + oneof.anonymous = True self.oneofs[i] = oneof self.fields.append(oneof) @@ -782,9 +813,10 @@ def __str__(self): if not self.ordered_fields: # Empty structs are not allowed in C standard. # Therefore add a dummy field if an empty message occurs. - result += ' uint8_t dummy_field;' + result += ' char dummy_field;' result += '\n'.join([str(f) for f in self.ordered_fields]) + result += '\n/* @@protoc_insertion_point(struct:%s) */' % self.name result += '\n}' if self.packed: @@ -847,10 +879,7 @@ def fields_definition(self): for field in self.ordered_fields: result += field.pb_field_t(prev) result += ',\n' - if isinstance(field, OneOf): - prev = field.name + '.' + field.fields[-1].name - else: - prev = field.name + prev = field.get_last_field_name() result += ' PB_LAST_FIELD\n};' return result @@ -1031,6 +1060,8 @@ def generate_header(self, includes, headername, options): yield options.genformat % (noext + options.extension + '.h') yield '\n' + yield '/* @@protoc_insertion_point(includes) */\n' + yield '#if PB_PROTO_HEADER_VERSION != 30\n' yield '#error Regenerate this file with the current version of nanopb generator.\n' yield '#endif\n' @@ -1088,9 +1119,11 @@ def generate_header(self, includes, headername, options): yield '/* Maximum encoded size of messages (where known) */\n' for msg in self.messages: msize = msg.encoded_size(self.dependencies) + identifier = '%s_size' % msg.name if msize is not None: - identifier = '%s_size' % msg.name yield '#define %-40s %s\n' % (identifier, msize) + else: + yield '/* %s depends on runtime parameters */\n' % identifier yield '\n' yield '/* Message IDs (where set with "msgid" option) */\n' @@ -1125,6 +1158,7 @@ def generate_header(self, includes, headername, options): yield '#endif\n' # End of header + yield '/* @@protoc_insertion_point(eof) */\n' yield '\n#endif\n' def generate_source(self, headername, options): @@ -1137,6 +1171,7 @@ def generate_source(self, headername, options): yield '/* Generated by %s at %s. */\n\n' % (nanopb_version, time.asctime()) yield options.genformat % (headername) yield '\n' + yield '/* @@protoc_insertion_point(includes) */\n' yield '#if PB_PROTO_HEADER_VERSION != 30\n' yield '#error Regenerate this file with the current version of nanopb generator.\n' @@ -1229,6 +1264,7 @@ def generate_source(self, headername, options): yield 'PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES)\n' yield '\n' + yield '/* @@protoc_insertion_point(eof) */\n' # --------------------------------------------------------------------------- # Options parsing for the .proto files @@ -1333,6 +1369,9 @@ def get_nanopb_suboptions(subdesc, options, name): optparser.add_option("-I", "--options-path", dest="options_path", metavar="DIR", action="append", default = [], help="Search for .options files additionally in this path") +optparser.add_option("-D", "--output-dir", dest="output_dir", + metavar="OUTPUTDIR", default=None, + help="Output directory of .pb.h and .pb.c files") optparser.add_option("-Q", "--generated-include-format", dest="genformat", metavar="FORMAT", default='#include "%s"\n', help="Set format string to use for including other .pb.h files. [default: %default]") @@ -1449,17 +1488,29 @@ def main_cli(): if options.quiet: options.verbose = False - Globals.verbose_options = options.verbose + if options.output_dir and not os.path.exists(options.output_dir): + optparser.print_help() + sys.stderr.write("\noutput_dir does not exist: %s\n" % options.output_dir) + sys.exit(1) + + Globals.verbose_options = options.verbose for filename in filenames: results = process_file(filename, None, options) + base_dir = options.output_dir or '' + to_write = [ + (os.path.join(base_dir, results['headername']), results['headerdata']), + (os.path.join(base_dir, results['sourcename']), results['sourcedata']), + ] + if not options.quiet: - sys.stderr.write("Writing to " + results['headername'] + " and " - + results['sourcename'] + "\n") + paths = " and ".join([x[0] for x in to_write]) + sys.stderr.write("Writing to %s\n" % paths) - open(results['headername'], 'w').write(results['headerdata']) - open(results['sourcename'], 'w').write(results['sourcedata']) + for path, data in to_write: + with open(path, 'w') as f: + f.write(data) def main_plugin(): '''Main function when invoked as a protoc plugin.''' @@ -1523,4 +1574,3 @@ def main_plugin(): main_plugin() else: main_cli() - diff --git a/third_party/nanopb/generator/proto/nanopb.proto b/third_party/nanopb/generator/proto/nanopb.proto index b8671bbdc931ea..9b2f0fbce0005a 100644 --- a/third_party/nanopb/generator/proto/nanopb.proto +++ b/third_party/nanopb/generator/proto/nanopb.proto @@ -62,6 +62,9 @@ message NanoPBOptions { // integer type tag for a message optional uint32 msgid = 9; + + // decode oneof as anonymous union + optional bool anonymous_oneof = 11 [default = false]; } // Extensions to protoc 'Descriptor' type in order to define options diff --git a/third_party/nanopb/pb.h b/third_party/nanopb/pb.h index 98613a06ac8273..5e5d07bb2095e0 100644 --- a/third_party/nanopb/pb.h +++ b/third_party/nanopb/pb.h @@ -13,10 +13,6 @@ /* Enable support for dynamically allocated fields */ /* #define PB_ENABLE_MALLOC 1 */ -/* Define this if your CPU architecture is big endian, i.e. it - * stores the most-significant byte first. */ -/* #define __BIG_ENDIAN__ 1 */ - /* Define this if your CPU / compiler combination does not support * unaligned memory access to packed structures. */ /* #define PB_NO_PACKED_STRUCTS 1 */ @@ -50,12 +46,12 @@ /* Version of the nanopb library. Just in case you want to check it in * your own program. */ -#define NANOPB_VERSION nanopb-0.3.5-dev +#define NANOPB_VERSION nanopb-0.3.6 /* Include all the system headers needed by nanopb. You will need the * definitions of the following: * - strlen, memcpy, memset functions - * - [u]int8_t, [u]int16_t, [u]int32_t, [u]int64_t + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t * - size_t * - bool * @@ -144,7 +140,7 @@ * Most-significant 4 bits specify repeated/required/packed etc. */ -typedef uint8_t pb_type_t; +typedef uint_least8_t pb_type_t; /**** Field data types ****/ @@ -201,18 +197,22 @@ typedef uint8_t pb_type_t; * and array counts. */ #if defined(PB_FIELD_32BIT) -#define PB_SIZE_MAX ((uint32_t)-1) typedef uint32_t pb_size_t; typedef int32_t pb_ssize_t; #elif defined(PB_FIELD_16BIT) -#define PB_SIZE_MAX ((uint16_t)-1) - typedef uint16_t pb_size_t; - typedef int16_t pb_ssize_t; + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; #else -#define PB_SIZE_MAX ((uint8_t)-1) - typedef uint8_t pb_size_t; - typedef int8_t pb_ssize_t; + typedef uint_least8_t pb_size_t; + typedef int_least8_t pb_ssize_t; #endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +typedef uint_least8_t pb_byte_t; /* This structure is used in auto-generated constants * to specify struct fields. @@ -240,30 +240,24 @@ struct pb_field_s { PB_PACKED_STRUCT_END /* Make sure that the standard integer types are of the expected sizes. - * All kinds of things may break otherwise.. atleast all fixed* types. + * Otherwise fixed32/fixed64 fields can break. * * If you get errors here, it probably means that your stdint.h is not * correct for your platform. */ -PB_STATIC_ASSERT(sizeof(int8_t) == 1, INT8_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(uint8_t) == 1, UINT8_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(int16_t) == 2, INT16_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(uint16_t) == 2, UINT16_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(int32_t) == 4, INT32_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(uint32_t) == 4, UINT32_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(int64_t) == 8, INT64_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(uint64_t) == 8, UINT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) /* This structure is used for 'bytes' arrays. * It has the number of bytes in the beginning, and after that an array. * Note that actual structs used will have a different length of bytes array. */ -#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; uint8_t bytes[n]; } +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } #define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) struct pb_bytes_array_s { pb_size_t size; - uint8_t bytes[1]; + pb_byte_t bytes[1]; }; typedef struct pb_bytes_array_s pb_bytes_array_t; @@ -518,10 +512,25 @@ struct pb_extension_s { pb_membersize(st, u.m[0]), 0, ptr} #define PB_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \ - PB_ ## rules ## _ ## allocation(union_name, tag, message, field, \ + PB_ONEOF_ ## allocation(union_name, tag, message, field, \ PB_DATAOFFSET_ ## placement(message, union_name.field, prevfield), \ PB_LTYPE_MAP_ ## type, ptr) +#define PB_ANONYMOUS_ONEOF_STATIC(u, tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_STATIC | PB_HTYPE_ONEOF | ltype, \ + fd, pb_delta(st, which_ ## u, m), \ + pb_membersize(st, m), 0, ptr} + +#define PB_ANONYMOUS_ONEOF_POINTER(u, tag, st, m, fd, ltype, ptr) \ + {tag, PB_ATYPE_POINTER | PB_HTYPE_ONEOF | ltype, \ + fd, pb_delta(st, which_ ## u, m), \ + pb_membersize(st, m[0]), 0, ptr} + +#define PB_ANONYMOUS_ONEOF_FIELD(union_name, tag, type, rules, allocation, placement, message, field, prevfield, ptr) \ + PB_ANONYMOUS_ONEOF_ ## allocation(union_name, tag, message, field, \ + PB_DATAOFFSET_ ## placement(message, field, prevfield), \ + PB_LTYPE_MAP_ ## type, ptr) + /* These macros are used for giving out error messages. * They are mostly a debugging aid; the main error information * is the true/false return value from functions. diff --git a/third_party/nanopb/pb_decode.c b/third_party/nanopb/pb_decode.c index 5cdcbcfb10c97d..78911e7bbfe692 100644 --- a/third_party/nanopb/pb_decode.c +++ b/third_party/nanopb/pb_decode.c @@ -23,9 +23,9 @@ typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn; -static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count); +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); -static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *iter); @@ -72,10 +72,10 @@ static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { * pb_istream_t implementation * *******************************/ -static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count) +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { - uint8_t *source = (uint8_t*)stream->state; - stream->state = source + count; + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; if (buf != NULL) { @@ -86,13 +86,13 @@ static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t coun return true; } -bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) { #ifndef PB_BUFFER_ONLY if (buf == NULL && stream->callback != buf_read) { /* Skip input bytes */ - uint8_t tmp[16]; + pb_byte_t tmp[16]; while (count > 16) { if (!pb_read(stream, tmp, 16)) @@ -122,7 +122,7 @@ bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) /* Read a single byte from input stream. buf may not be NULL. * This is an optimization for the varint decoding. */ -static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) { if (stream->bytes_left == 0) PB_RETURN_ERROR(stream, "end-of-stream"); @@ -131,8 +131,8 @@ static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) if (!stream->callback(stream, buf, 1)) PB_RETURN_ERROR(stream, "io error"); #else - *buf = *(uint8_t*)stream->state; - stream->state = (uint8_t*)stream->state + 1; + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; #endif stream->bytes_left--; @@ -140,15 +140,23 @@ static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) return true; } -pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize) +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize) { pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; #ifdef PB_BUFFER_ONLY stream.callback = NULL; #else stream.callback = &buf_read; #endif - stream.state = buf; + state.c_state = buf; + stream.state = state.state; stream.bytes_left = bufsize; #ifndef PB_NO_ERRMSG stream.errmsg = NULL; @@ -162,7 +170,7 @@ pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize) static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) { - uint8_t byte; + pb_byte_t byte; uint32_t result; if (!pb_readbyte(stream, &byte)) @@ -176,7 +184,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) else { /* Multibyte case */ - uint8_t bitpos = 7; + uint_fast8_t bitpos = 7; result = byte & 0x7F; do @@ -188,7 +196,7 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) return false; result |= (uint32_t)(byte & 0x7F) << bitpos; - bitpos = (uint8_t)(bitpos + 7); + bitpos = (uint_fast8_t)(bitpos + 7); } while (byte & 0x80); } @@ -198,8 +206,8 @@ static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) { - uint8_t byte; - uint8_t bitpos = 0; + pb_byte_t byte; + uint_fast8_t bitpos = 0; uint64_t result = 0; do @@ -211,7 +219,7 @@ bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) return false; result |= (uint64_t)(byte & 0x7F) << bitpos; - bitpos = (uint8_t)(bitpos + 7); + bitpos = (uint_fast8_t)(bitpos + 7); } while (byte & 0x80); *dest = result; @@ -220,7 +228,7 @@ bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) bool checkreturn pb_skip_varint(pb_istream_t *stream) { - uint8_t byte; + pb_byte_t byte; do { if (!pb_read(stream, &byte, 1)) @@ -279,7 +287,7 @@ bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) /* Read a raw value to buffer, for the purpose of passing it to callback as * a substream. Size is maximum size on call, and actual size on return. */ -static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size) +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) { size_t max_size = *size; switch (wire_type) @@ -367,7 +375,7 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t while (substream.bytes_left > 0 && *size < iter->pos->array_size) { - void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size); + void *pItem = (char*)iter->pData + iter->pos->data_size * (*size); if (!func(&substream, iter->pos, pItem)) { status = false; @@ -386,7 +394,7 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t { /* Repeated field */ pb_size_t *size = (pb_size_t*)iter->pSize; - void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size); + void *pItem = (char*)iter->pData + iter->pos->data_size * (*size); if (*size >= iter->pos->array_size) PB_RETURN_ERROR(stream, "array overflow"); @@ -540,7 +548,7 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ } /* Decode the array entry */ - pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size); + pItem = *(char**)iter->pData + iter->pos->data_size * (*size); initialize_pointer_field(pItem, iter); if (!func(&substream, iter->pos, pItem)) { @@ -576,7 +584,7 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_ if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size)) return false; - pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size - 1); + pItem = *(char**)iter->pData + iter->pos->data_size * (*size - 1); initialize_pointer_field(pItem, iter); return func(stream, iter->pos, pItem); } @@ -623,7 +631,7 @@ static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type * which in turn allows to use same callback for packed and * not-packed fields. */ pb_istream_t substream; - uint8_t buffer[10]; + pb_byte_t buffer[10]; size_t size = sizeof(buffer); if (!read_raw_value(stream, wire_type, buffer, &size)) @@ -830,7 +838,8 @@ static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_str bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) { - uint8_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 7) / 8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 31) / 32] = {0, 0}; + const uint32_t allbits = ~(uint32_t)0; uint32_t extension_range_start = 0; pb_field_iter_t iter; @@ -886,8 +895,8 @@ bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[ if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) { - uint8_t tmp = (uint8_t)(1 << (iter.required_field_index & 7)); - fields_seen[iter.required_field_index >> 3] |= tmp; + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen[iter.required_field_index >> 5] |= tmp; } if (!decode_field(stream, wire_type, &iter)) @@ -912,16 +921,19 @@ bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[ if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0) req_field_count++; - /* Check the whole bytes */ - for (i = 0; i < (req_field_count >> 3); i++) + if (req_field_count > 0) { - if (fields_seen[i] != 0xFF) + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits */ + if (fields_seen[req_field_count >> 5] != (allbits >> (32 - (req_field_count & 31)))) PB_RETURN_ERROR(stream, "missing required field"); } - - /* Check the remaining bits */ - if (fields_seen[req_field_count >> 3] != (0xFF >> (8 - (req_field_count & 7)))) - PB_RETURN_ERROR(stream, "missing required field"); } return true; @@ -1023,6 +1035,12 @@ static void pb_release_single_field(const pb_field_iter_t *iter) if (PB_HTYPE(type) == PB_HTYPE_REPEATED) { count = *(pb_size_t*)iter->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > iter->pos->array_size) + { + /* Protect against corrupted _count fields */ + count = iter->pos->array_size; + } } if (pItem) @@ -1030,7 +1048,7 @@ static void pb_release_single_field(const pb_field_iter_t *iter) while (count--) { pb_release((const pb_field_t*)iter->pos->ptr, pItem); - pItem = (uint8_t*)pItem + iter->pos->data_size; + pItem = (char*)pItem + iter->pos->data_size; } } } @@ -1067,6 +1085,9 @@ void pb_release(const pb_field_t fields[], void *dest_struct) { pb_field_iter_t iter; + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + if (!pb_field_iter_begin(&iter, fields, dest_struct)) return; /* Empty message type */ @@ -1095,44 +1116,35 @@ bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest) bool pb_decode_fixed32(pb_istream_t *stream, void *dest) { - #ifdef __BIG_ENDIAN__ - uint8_t *bytes = (uint8_t*)dest; - uint8_t lebytes[4]; - - if (!pb_read(stream, lebytes, 4)) + pb_byte_t bytes[4]; + + if (!pb_read(stream, bytes, 4)) return false; - bytes[0] = lebytes[3]; - bytes[1] = lebytes[2]; - bytes[2] = lebytes[1]; - bytes[3] = lebytes[0]; + *(uint32_t*)dest = ((uint32_t)bytes[0] << 0) | + ((uint32_t)bytes[1] << 8) | + ((uint32_t)bytes[2] << 16) | + ((uint32_t)bytes[3] << 24); return true; - #else - return pb_read(stream, (uint8_t*)dest, 4); - #endif } bool pb_decode_fixed64(pb_istream_t *stream, void *dest) { - #ifdef __BIG_ENDIAN__ - uint8_t *bytes = (uint8_t*)dest; - uint8_t lebytes[8]; - - if (!pb_read(stream, lebytes, 8)) + pb_byte_t bytes[8]; + + if (!pb_read(stream, bytes, 8)) return false; - bytes[0] = lebytes[7]; - bytes[1] = lebytes[6]; - bytes[2] = lebytes[5]; - bytes[3] = lebytes[4]; - bytes[4] = lebytes[3]; - bytes[5] = lebytes[2]; - bytes[6] = lebytes[1]; - bytes[7] = lebytes[0]; + *(uint64_t*)dest = ((uint64_t)bytes[0] << 0) | + ((uint64_t)bytes[1] << 8) | + ((uint64_t)bytes[2] << 16) | + ((uint64_t)bytes[3] << 24) | + ((uint64_t)bytes[4] << 32) | + ((uint64_t)bytes[5] << 40) | + ((uint64_t)bytes[6] << 48) | + ((uint64_t)bytes[7] << 56); + return true; - #else - return pb_read(stream, (uint8_t*)dest, 8); - #endif } static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) @@ -1149,19 +1161,22 @@ static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *fi * not break decoding of such messages, we cast <=32 bit fields to * int32_t first to get the sign correct. */ - if (field->data_size == 8) + if (field->data_size == sizeof(int64_t)) svalue = (int64_t)value; else svalue = (int32_t)value; - switch (field->data_size) - { - case 1: clamped = *(int8_t*)dest = (int8_t)svalue; break; - case 2: clamped = *(int16_t*)dest = (int16_t)svalue; break; - case 4: clamped = *(int32_t*)dest = (int32_t)svalue; break; - case 8: clamped = *(int64_t*)dest = svalue; break; - default: PB_RETURN_ERROR(stream, "invalid data_size"); - } + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(int64_t)) + clamped = *(int64_t*)dest = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)dest = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)dest = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)dest = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); if (clamped != svalue) PB_RETURN_ERROR(stream, "integer too large"); @@ -1175,14 +1190,17 @@ static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *f if (!pb_decode_varint(stream, &value)) return false; - switch (field->data_size) - { - case 1: clamped = *(uint8_t*)dest = (uint8_t)value; break; - case 2: clamped = *(uint16_t*)dest = (uint16_t)value; break; - case 4: clamped = *(uint32_t*)dest = (uint32_t)value; break; - case 8: clamped = *(uint64_t*)dest = value; break; - default: PB_RETURN_ERROR(stream, "invalid data_size"); - } + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(uint64_t)) + clamped = *(uint64_t*)dest = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)dest = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)dest = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)dest = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); if (clamped != value) PB_RETURN_ERROR(stream, "integer too large"); @@ -1196,14 +1214,17 @@ static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *f if (!pb_decode_svarint(stream, &value)) return false; - switch (field->data_size) - { - case 1: clamped = *(int8_t*)dest = (int8_t)value; break; - case 2: clamped = *(int16_t*)dest = (int16_t)value; break; - case 4: clamped = *(int32_t*)dest = (int32_t)value; break; - case 8: clamped = *(int64_t*)dest = value; break; - default: PB_RETURN_ERROR(stream, "invalid data_size"); - } + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(int64_t)) + clamped = *(int64_t*)dest = value; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)dest = (int32_t)value; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)dest = (int_least16_t)value; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)dest = (int_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); if (clamped != value) PB_RETURN_ERROR(stream, "integer too large"); @@ -1290,8 +1311,8 @@ static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *fi PB_RETURN_ERROR(stream, "string overflow"); } - status = pb_read(stream, (uint8_t*)dest, size); - *((uint8_t*)dest + size) = 0; + status = pb_read(stream, (pb_byte_t*)dest, size); + *((pb_byte_t*)dest + size) = 0; return status; } diff --git a/third_party/nanopb/pb_decode.h b/third_party/nanopb/pb_decode.h index 3d433155b81e20..1d9bb1945c1f52 100644 --- a/third_party/nanopb/pb_decode.h +++ b/third_party/nanopb/pb_decode.h @@ -34,7 +34,7 @@ struct pb_istream_s */ int *callback; #else - bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count); + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); #endif void *state; /* Free field for use by callback implementation */ @@ -103,12 +103,12 @@ void pb_release(const pb_field_t fields[], void *dest_struct); * Alternatively, you can use a custom stream that reads directly from e.g. * a file or a network socket. */ -pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize); +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize); /* Function to read from a pb_istream_t. You can use this if you need to * read some custom header data, or to read data in field callbacks. */ -bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count); +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); /************************************************ diff --git a/third_party/nanopb/pb_encode.c b/third_party/nanopb/pb_encode.c index cc372b8f577149..9f91c9d5f4473d 100644 --- a/third_party/nanopb/pb_encode.c +++ b/third_party/nanopb/pb_encode.c @@ -22,7 +22,7 @@ **************************************/ typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn; -static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count); +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func); static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData); static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); @@ -56,9 +56,9 @@ static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = { * pb_ostream_t implementation * *******************************/ -static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count) +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { - uint8_t *dest = (uint8_t*)stream->state; + pb_byte_t *dest = (pb_byte_t*)stream->state; stream->state = dest + count; while (count--) @@ -67,7 +67,7 @@ static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size return true; } -pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize) +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) { pb_ostream_t stream; #ifdef PB_BUFFER_ONLY @@ -84,7 +84,7 @@ pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize) return stream; } -bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count) +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) { if (stream->callback != NULL) { @@ -413,15 +413,18 @@ bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *sr ********************/ bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value) { - uint8_t buffer[10]; + pb_byte_t buffer[10]; size_t i = 0; - if (value == 0) - return pb_write(stream, (uint8_t*)&value, 1); + if (value <= 0x7F) + { + pb_byte_t v = (pb_byte_t)value; + return pb_write(stream, &v, 1); + } while (value) { - buffer[i] = (uint8_t)((value & 0x7F) | 0x80); + buffer[i] = (pb_byte_t)((value & 0x7F) | 0x80); value >>= 7; i++; } @@ -443,36 +446,28 @@ bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value) bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) { - #ifdef __BIG_ENDIAN__ - const uint8_t *bytes = value; - uint8_t lebytes[4]; - lebytes[0] = bytes[3]; - lebytes[1] = bytes[2]; - lebytes[2] = bytes[1]; - lebytes[3] = bytes[0]; - return pb_write(stream, lebytes, 4); - #else - return pb_write(stream, (const uint8_t*)value, 4); - #endif + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); } bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) { - #ifdef __BIG_ENDIAN__ - const uint8_t *bytes = value; - uint8_t lebytes[8]; - lebytes[0] = bytes[7]; - lebytes[1] = bytes[6]; - lebytes[2] = bytes[5]; - lebytes[3] = bytes[4]; - lebytes[4] = bytes[3]; - lebytes[5] = bytes[2]; - lebytes[6] = bytes[1]; - lebytes[7] = bytes[0]; - return pb_write(stream, lebytes, 8); - #else - return pb_write(stream, (const uint8_t*)value, 8); - #endif + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); } bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) @@ -513,7 +508,7 @@ bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t return pb_encode_tag(stream, wiretype, field->tag); } -bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size) +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) { if (!pb_encode_varint(stream, (uint64_t)size)) return false; @@ -577,16 +572,16 @@ static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *fi { int64_t value = 0; - /* Cases 1 and 2 are for compilers that have smaller types for bool - * or enums, and for int_size option. */ - switch (field->data_size) - { - case 1: value = *(const int8_t*)src; break; - case 2: value = *(const int16_t*)src; break; - case 4: value = *(const int32_t*)src; break; - case 8: value = *(const int64_t*)src; break; - default: PB_RETURN_ERROR(stream, "invalid data_size"); - } + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)src; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)src; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)src; + else if (field->data_size == sizeof(int64_t)) + value = *(const int64_t*)src; + else + PB_RETURN_ERROR(stream, "invalid data_size"); return pb_encode_varint(stream, (uint64_t)value); } @@ -595,14 +590,16 @@ static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *f { uint64_t value = 0; - switch (field->data_size) - { - case 1: value = *(const uint8_t*)src; break; - case 2: value = *(const uint16_t*)src; break; - case 4: value = *(const uint32_t*)src; break; - case 8: value = *(const uint64_t*)src; break; - default: PB_RETURN_ERROR(stream, "invalid data_size"); - } + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)src; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)src; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)src; + else if (field->data_size == sizeof(uint64_t)) + value = *(const uint64_t*)src; + else + PB_RETURN_ERROR(stream, "invalid data_size"); return pb_encode_varint(stream, value); } @@ -611,14 +608,16 @@ static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *f { int64_t value = 0; - switch (field->data_size) - { - case 1: value = *(const int8_t*)src; break; - case 2: value = *(const int16_t*)src; break; - case 4: value = *(const int32_t*)src; break; - case 8: value = *(const int64_t*)src; break; - default: PB_RETURN_ERROR(stream, "invalid data_size"); - } + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)src; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)src; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)src; + else if (field->data_size == sizeof(int64_t)) + value = *(const int64_t*)src; + else + PB_RETURN_ERROR(stream, "invalid data_size"); return pb_encode_svarint(stream, value); } @@ -677,7 +676,7 @@ static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *fi } } - return pb_encode_string(stream, (const uint8_t*)src, size); + return pb_encode_string(stream, (const pb_byte_t*)src, size); } static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src) diff --git a/third_party/nanopb/pb_encode.h b/third_party/nanopb/pb_encode.h index e992c8dca18a1a..d9909fb013778a 100644 --- a/third_party/nanopb/pb_encode.h +++ b/third_party/nanopb/pb_encode.h @@ -35,7 +35,7 @@ struct pb_ostream_s */ int *callback; #else - bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count); + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); #endif void *state; /* Free field for use by callback implementation. */ size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */ @@ -86,7 +86,7 @@ bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *sr * Alternatively, you can use a custom stream that writes directly to e.g. * a file or a network socket. */ -pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize); +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); /* Pseudo-stream for measuring the size of a message without actually storing * the encoded data. @@ -106,7 +106,7 @@ pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize); /* Function to write into a pb_ostream_t stream. You can use this if you need * to append or prepend some custom headers to the message. */ -bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count); +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); /************************************************ @@ -130,7 +130,7 @@ bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); /* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ -bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size); +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); /* Encode a fixed32, sfixed32 or float value. * You need to pass a pointer to a 4-byte wide C variable. */ diff --git a/third_party/nanopb/tools/make_windows_package.sh b/third_party/nanopb/tools/make_windows_package.sh index 4010a316169f3c..72de6f3394a40a 100755 --- a/third_party/nanopb/tools/make_windows_package.sh +++ b/third_party/nanopb/tools/make_windows_package.sh @@ -47,8 +47,8 @@ cp `which MSVCR100.DLL` $DEST/generator-bin/ cp `which MSVCP100.DLL` $DEST/generator-bin/ # Convert line breaks for convenience -find $DEST \( -name '*.c' -o -name '*.h' -o -name '*.txt' \ - -o -name '*.proto' -o -name '*.py' -o -name '*.options' \) \ +find $DEST -name '*.c' -o -name '*.h' -o -name '*.txt' \ + -o -name '*.proto' -o -name '*.py' -o -name '*.options' \ -exec unix2dos '{}' \; # Zip it all up diff --git a/third_party/protobuf/BUILD b/third_party/protobuf/BUILD index e8d495c03cc1c3..6a435c68135a53 100644 --- a/third_party/protobuf/BUILD +++ b/third_party/protobuf/BUILD @@ -26,6 +26,8 @@ proto_alias("protobuf_python", PROTOBUF_VERSION) proto_alias("protobuf", PROTOBUF_VERSION) +proto_alias("protobuf_headers", PROTOBUF_VERSION) + proto_alias("protoc_lib", PROTOBUF_VERSION) proto_alias("java_toolchain", PROTOBUF_VERSION)