aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/lib
diff options
context:
space:
mode:
authorGravatar Nicolas Noble <nicolasnoble@users.noreply.github.com>2016-03-28 09:21:56 -0700
committerGravatar Nicolas Noble <nicolasnoble@users.noreply.github.com>2016-03-28 09:21:56 -0700
commite5cc05b5c6cee7026a0d28d39925621451506820 (patch)
tree5a680b950edfbc64dd1edf939c572947deb1e416 /src/core/lib
parent94f908ae84ab6d280a41e09245925ecfa612dce8 (diff)
parent97e0ebc7ce45da32aca4bad799148eb3fc1b2033 (diff)
Merge pull request #5964 from ctiller/move_the_things
Begin core componentization
Diffstat (limited to 'src/core/lib')
-rw-r--r--src/core/lib/census/README.md76
-rw-r--r--src/core/lib/census/aggregation.h66
-rw-r--r--src/core/lib/census/context.c509
-rw-r--r--src/core/lib/census/grpc_context.c53
-rw-r--r--src/core/lib/census/grpc_filter.c198
-rw-r--r--src/core/lib/census/grpc_filter.h44
-rw-r--r--src/core/lib/census/grpc_plugin.c70
-rw-r--r--src/core/lib/census/grpc_plugin.h40
-rw-r--r--src/core/lib/census/initialize.c54
-rw-r--r--src/core/lib/census/mlog.c600
-rw-r--r--src/core/lib/census/mlog.h95
-rw-r--r--src/core/lib/census/operation.c63
-rw-r--r--src/core/lib/census/placeholders.c109
-rw-r--r--src/core/lib/census/rpc_metric_id.h51
-rw-r--r--src/core/lib/census/tracing.c45
-rw-r--r--src/core/lib/channel/channel_args.c271
-rw-r--r--src/core/lib/channel/channel_args.h94
-rw-r--r--src/core/lib/channel/channel_stack.c262
-rw-r--r--src/core/lib/channel/channel_stack.h260
-rw-r--r--src/core/lib/channel/channel_stack_builder.c258
-rw-r--r--src/core/lib/channel/channel_stack_builder.h155
-rw-r--r--src/core/lib/channel/client_channel.c526
-rw-r--r--src/core/lib/channel/client_channel.h63
-rw-r--r--src/core/lib/channel/compress_filter.c304
-rw-r--r--src/core/lib/channel/compress_filter.h65
-rw-r--r--src/core/lib/channel/connected_channel.c176
-rw-r--r--src/core/lib/channel/connected_channel.h42
-rw-r--r--src/core/lib/channel/context.h49
-rw-r--r--src/core/lib/channel/http_client_filter.c255
-rw-r--r--src/core/lib/channel/http_client_filter.h44
-rw-r--r--src/core/lib/channel/http_server_filter.c240
-rw-r--r--src/core/lib/channel/http_server_filter.h42
-rw-r--r--src/core/lib/channel/subchannel_call_holder.c259
-rw-r--r--src/core/lib/channel/subchannel_call_holder.h97
-rw-r--r--src/core/lib/client_config/README.md66
-rw-r--r--src/core/lib/client_config/client_config.c74
-rw-r--r--src/core/lib/client_config/client_config.h53
-rw-r--r--src/core/lib/client_config/connector.c55
-rw-r--r--src/core/lib/client_config/connector.h92
-rw-r--r--src/core/lib/client_config/default_initial_connect_string.c39
-rw-r--r--src/core/lib/client_config/initial_connect_string.c53
-rw-r--r--src/core/lib/client_config/initial_connect_string.h50
-rw-r--r--src/core/lib/client_config/lb_policies/load_balancer_api.c163
-rw-r--r--src/core/lib/client_config/lb_policies/load_balancer_api.h85
-rw-r--r--src/core/lib/client_config/lb_policies/pick_first.c421
-rw-r--r--src/core/lib/client_config/lb_policies/pick_first.h43
-rw-r--r--src/core/lib/client_config/lb_policies/round_robin.c542
-rw-r--r--src/core/lib/client_config/lb_policies/round_robin.h46
-rw-r--r--src/core/lib/client_config/lb_policy.c134
-rw-r--r--src/core/lib/client_config/lb_policy.h144
-rw-r--r--src/core/lib/client_config/lb_policy_factory.c48
-rw-r--r--src/core/lib/client_config/lb_policy_factory.h73
-rw-r--r--src/core/lib/client_config/lb_policy_registry.c88
-rw-r--r--src/core/lib/client_config/lb_policy_registry.h54
-rw-r--r--src/core/lib/client_config/resolver.c82
-rw-r--r--src/core/lib/client_config/resolver.h94
-rw-r--r--src/core/lib/client_config/resolver_factory.c55
-rw-r--r--src/core/lib/client_config/resolver_factory.h82
-rw-r--r--src/core/lib/client_config/resolver_registry.c137
-rw-r--r--src/core/lib/client_config/resolver_registry.h65
-rw-r--r--src/core/lib/client_config/resolvers/dns_resolver.c309
-rw-r--r--src/core/lib/client_config/resolvers/dns_resolver.h42
-rw-r--r--src/core/lib/client_config/resolvers/sockaddr_resolver.c372
-rw-r--r--src/core/lib/client_config/resolvers/sockaddr_resolver.h50
-rw-r--r--src/core/lib/client_config/resolvers/zookeeper_resolver.c520
-rw-r--r--src/core/lib/client_config/resolvers/zookeeper_resolver.h42
-rw-r--r--src/core/lib/client_config/subchannel.c678
-rw-r--r--src/core/lib/client_config/subchannel.h174
-rw-r--r--src/core/lib/client_config/subchannel_factory.c49
-rw-r--r--src/core/lib/client_config/subchannel_factory.h66
-rw-r--r--src/core/lib/client_config/subchannel_index.c262
-rw-r--r--src/core/lib/client_config/subchannel_index.h77
-rw-r--r--src/core/lib/client_config/uri_parser.c242
-rw-r--r--src/core/lib/client_config/uri_parser.h51
-rw-r--r--src/core/lib/compression/algorithm_metadata.h53
-rw-r--r--src/core/lib/compression/compression_algorithm.c203
-rw-r--r--src/core/lib/compression/message_compress.c198
-rw-r--r--src/core/lib/compression/message_compress.h52
-rw-r--r--src/core/lib/debug/trace.c136
-rw-r--r--src/core/lib/debug/trace.h43
-rw-r--r--src/core/lib/http/format_request.c120
-rw-r--r--src/core/lib/http/format_request.h45
-rw-r--r--src/core/lib/http/httpcli.c293
-rw-r--r--src/core/lib/http/httpcli.h144
-rw-r--r--src/core/lib/http/httpcli_security_connector.c188
-rw-r--r--src/core/lib/http/parser.c313
-rw-r--r--src/core/lib/http/parser.h116
-rw-r--r--src/core/lib/iomgr/closure.c98
-rw-r--r--src/core/lib/iomgr/closure.h98
-rw-r--r--src/core/lib/iomgr/endpoint.c67
-rw-r--r--src/core/lib/iomgr/endpoint.h102
-rw-r--r--src/core/lib/iomgr/endpoint_pair.h47
-rw-r--r--src/core/lib/iomgr/endpoint_pair_posix.c83
-rw-r--r--src/core/lib/iomgr/endpoint_pair_windows.c97
-rw-r--r--src/core/lib/iomgr/exec_ctx.c151
-rw-r--r--src/core/lib/iomgr/exec_ctx.h98
-rw-r--r--src/core/lib/iomgr/executor.c143
-rw-r--r--src/core/lib/iomgr/executor.h53
-rw-r--r--src/core/lib/iomgr/fd_posix.c454
-rw-r--r--src/core/lib/iomgr/fd_posix.h192
-rw-r--r--src/core/lib/iomgr/iocp_windows.c208
-rw-r--r--src/core/lib/iomgr/iocp_windows.h63
-rw-r--r--src/core/lib/iomgr/iomgr.c175
-rw-r--r--src/core/lib/iomgr/iomgr.h43
-rw-r--r--src/core/lib/iomgr/iomgr_internal.h62
-rw-r--r--src/core/lib/iomgr/iomgr_posix.c52
-rw-r--r--src/core/lib/iomgr/iomgr_posix.h39
-rw-r--r--src/core/lib/iomgr/iomgr_windows.c73
-rw-r--r--src/core/lib/iomgr/pollset.h94
-rw-r--r--src/core/lib/iomgr/pollset_multipoller_with_epoll.c324
-rw-r--r--src/core/lib/iomgr/pollset_multipoller_with_poll_posix.c234
-rw-r--r--src/core/lib/iomgr/pollset_posix.c633
-rw-r--r--src/core/lib/iomgr/pollset_posix.h153
-rw-r--r--src/core/lib/iomgr/pollset_set.h61
-rw-r--r--src/core/lib/iomgr/pollset_set_posix.c202
-rw-r--r--src/core/lib/iomgr/pollset_set_posix.h45
-rw-r--r--src/core/lib/iomgr/pollset_set_windows.c60
-rw-r--r--src/core/lib/iomgr/pollset_set_windows.h39
-rw-r--r--src/core/lib/iomgr/pollset_windows.c240
-rw-r--r--src/core/lib/iomgr/pollset_windows.h75
-rw-r--r--src/core/lib/iomgr/resolve_address.h72
-rw-r--r--src/core/lib/iomgr/resolve_address_posix.c178
-rw-r--r--src/core/lib/iomgr/resolve_address_windows.c169
-rw-r--r--src/core/lib/iomgr/sockaddr.h47
-rw-r--r--src/core/lib/iomgr/sockaddr_posix.h44
-rw-r--r--src/core/lib/iomgr/sockaddr_utils.c227
-rw-r--r--src/core/lib/iomgr/sockaddr_utils.h89
-rw-r--r--src/core/lib/iomgr/sockaddr_win32.h43
-rw-r--r--src/core/lib/iomgr/socket_utils_common_posix.c208
-rw-r--r--src/core/lib/iomgr/socket_utils_linux.c51
-rw-r--r--src/core/lib/iomgr/socket_utils_posix.c70
-rw-r--r--src/core/lib/iomgr/socket_utils_posix.h113
-rw-r--r--src/core/lib/iomgr/socket_windows.c100
-rw-r--r--src/core/lib/iomgr/socket_windows.h111
-rw-r--r--src/core/lib/iomgr/tcp_client.h53
-rw-r--r--src/core/lib/iomgr/tcp_client_posix.c306
-rw-r--r--src/core/lib/iomgr/tcp_client_windows.c221
-rw-r--r--src/core/lib/iomgr/tcp_posix.c493
-rw-r--r--src/core/lib/iomgr/tcp_posix.h71
-rw-r--r--src/core/lib/iomgr/tcp_server.h103
-rw-r--r--src/core/lib/iomgr/tcp_server_posix.c607
-rw-r--r--src/core/lib/iomgr/tcp_server_windows.c557
-rw-r--r--src/core/lib/iomgr/tcp_windows.c402
-rw-r--r--src/core/lib/iomgr/tcp_windows.h57
-rw-r--r--src/core/lib/iomgr/time_averaged_stats.c77
-rw-r--r--src/core/lib/iomgr/time_averaged_stats.h88
-rw-r--r--src/core/lib/iomgr/timer.c356
-rw-r--r--src/core/lib/iomgr/timer.h108
-rw-r--r--src/core/lib/iomgr/timer_heap.c146
-rw-r--r--src/core/lib/iomgr/timer_heap.h57
-rw-r--r--src/core/lib/iomgr/udp_server.c418
-rw-r--r--src/core/lib/iomgr/udp_server.h77
-rw-r--r--src/core/lib/iomgr/unix_sockets_posix.c103
-rw-r--r--src/core/lib/iomgr/unix_sockets_posix.h61
-rw-r--r--src/core/lib/iomgr/unix_sockets_posix_noop.c61
-rw-r--r--src/core/lib/iomgr/wakeup_fd_eventfd.c85
-rw-r--r--src/core/lib/iomgr/wakeup_fd_nospecial.c51
-rw-r--r--src/core/lib/iomgr/wakeup_fd_pipe.c102
-rw-r--r--src/core/lib/iomgr/wakeup_fd_pipe.h41
-rw-r--r--src/core/lib/iomgr/wakeup_fd_posix.c72
-rw-r--r--src/core/lib/iomgr/wakeup_fd_posix.h101
-rw-r--r--src/core/lib/iomgr/workqueue.h83
-rw-r--r--src/core/lib/iomgr/workqueue_posix.c144
-rw-r--r--src/core/lib/iomgr/workqueue_posix.h53
-rw-r--r--src/core/lib/iomgr/workqueue_windows.c40
-rw-r--r--src/core/lib/iomgr/workqueue_windows.h37
-rw-r--r--src/core/lib/json/json.c64
-rw-r--r--src/core/lib/json/json.h88
-rw-r--r--src/core/lib/json/json_common.h49
-rw-r--r--src/core/lib/json/json_reader.c659
-rw-r--r--src/core/lib/json/json_reader.h160
-rw-r--r--src/core/lib/json/json_string.c379
-rw-r--r--src/core/lib/json/json_writer.c258
-rw-r--r--src/core/lib/json/json_writer.h97
-rw-r--r--src/core/lib/profiling/basic_timers.c274
-rw-r--r--src/core/lib/profiling/stap_probes.d7
-rw-r--r--src/core/lib/profiling/stap_timers.c65
-rw-r--r--src/core/lib/profiling/timers.h119
-rw-r--r--src/core/lib/proto/grpc/lb/v0/load_balancer.pb.c119
-rw-r--r--src/core/lib/proto/grpc/lb/v0/load_balancer.pb.h182
-rw-r--r--src/core/lib/security/auth_filters.h42
-rw-r--r--src/core/lib/security/b64.c233
-rw-r--r--src/core/lib/security/b64.h52
-rw-r--r--src/core/lib/security/client_auth_filter.c336
-rw-r--r--src/core/lib/security/credentials.c1281
-rw-r--r--src/core/lib/security/credentials.h377
-rw-r--r--src/core/lib/security/credentials_metadata.c101
-rw-r--r--src/core/lib/security/credentials_posix.c61
-rw-r--r--src/core/lib/security/credentials_win32.c61
-rw-r--r--src/core/lib/security/google_default_credentials.c266
-rw-r--r--src/core/lib/security/handshake.c336
-rw-r--r--src/core/lib/security/handshake.h51
-rw-r--r--src/core/lib/security/json_token.c411
-rw-r--r--src/core/lib/security/json_token.h118
-rw-r--r--src/core/lib/security/jwt_verifier.c843
-rw-r--r--src/core/lib/security/jwt_verifier.h136
-rw-r--r--src/core/lib/security/secure_endpoint.c384
-rw-r--r--src/core/lib/security/secure_endpoint.h49
-rw-r--r--src/core/lib/security/security_connector.c812
-rw-r--r--src/core/lib/security/security_connector.h266
-rw-r--r--src/core/lib/security/security_context.c347
-rw-r--r--src/core/lib/security/security_context.h114
-rw-r--r--src/core/lib/security/server_auth_filter.c264
-rw-r--r--src/core/lib/security/server_secure_chttp2.c264
-rw-r--r--src/core/lib/statistics/census_init.c48
-rw-r--r--src/core/lib/statistics/census_interface.h76
-rw-r--r--src/core/lib/statistics/census_log.c603
-rw-r--r--src/core/lib/statistics/census_log.h91
-rw-r--r--src/core/lib/statistics/census_rpc_stats.c253
-rw-r--r--src/core/lib/statistics/census_rpc_stats.h101
-rw-r--r--src/core/lib/statistics/census_tracing.c241
-rw-r--r--src/core/lib/statistics/census_tracing.h96
-rw-r--r--src/core/lib/statistics/hash_table.c303
-rw-r--r--src/core/lib/statistics/hash_table.h131
-rw-r--r--src/core/lib/statistics/window_stats.c316
-rw-r--r--src/core/lib/statistics/window_stats.h173
-rw-r--r--src/core/lib/support/alloc.c90
-rw-r--r--src/core/lib/support/avl.c288
-rw-r--r--src/core/lib/support/backoff.c76
-rw-r--r--src/core/lib/support/backoff.h68
-rw-r--r--src/core/lib/support/block_annotate.h48
-rw-r--r--src/core/lib/support/cmdline.c347
-rw-r--r--src/core/lib/support/cpu_iphone.c49
-rw-r--r--src/core/lib/support/cpu_linux.c78
-rw-r--r--src/core/lib/support/cpu_posix.c77
-rw-r--r--src/core/lib/support/cpu_windows.c47
-rw-r--r--src/core/lib/support/env.h60
-rw-r--r--src/core/lib/support/env_linux.c89
-rw-r--r--src/core/lib/support/env_posix.c57
-rw-r--r--src/core/lib/support/env_win32.c73
-rw-r--r--src/core/lib/support/histogram.c244
-rw-r--r--src/core/lib/support/host_port.c110
-rw-r--r--src/core/lib/support/load_file.c91
-rw-r--r--src/core/lib/support/load_file.h55
-rw-r--r--src/core/lib/support/log.c66
-rw-r--r--src/core/lib/support/log_android.c87
-rw-r--r--src/core/lib/support/log_linux.c105
-rw-r--r--src/core/lib/support/log_posix.c102
-rw-r--r--src/core/lib/support/log_win32.c126
-rw-r--r--src/core/lib/support/murmur_hash.c96
-rw-r--r--src/core/lib/support/murmur_hash.h44
-rw-r--r--src/core/lib/support/slice.c343
-rw-r--r--src/core/lib/support/slice_buffer.c282
-rw-r--r--src/core/lib/support/stack_lockfree.c185
-rw-r--r--src/core/lib/support/stack_lockfree.h53
-rw-r--r--src/core/lib/support/string.c296
-rw-r--r--src/core/lib/support/string.h121
-rw-r--r--src/core/lib/support/string_posix.c86
-rw-r--r--src/core/lib/support/string_win32.c109
-rw-r--r--src/core/lib/support/string_win32.h47
-rw-r--r--src/core/lib/support/subprocess_posix.c112
-rw-r--r--src/core/lib/support/subprocess_windows.c141
-rw-r--r--src/core/lib/support/sync.c127
-rw-r--r--src/core/lib/support/sync_posix.c104
-rw-r--r--src/core/lib/support/sync_win32.c133
-rw-r--r--src/core/lib/support/thd.c64
-rw-r--r--src/core/lib/support/thd_internal.h39
-rw-r--r--src/core/lib/support/thd_posix.c94
-rw-r--r--src/core/lib/support/thd_win32.c117
-rw-r--r--src/core/lib/support/time.c304
-rw-r--r--src/core/lib/support/time_posix.c170
-rw-r--r--src/core/lib/support/time_precise.c89
-rw-r--r--src/core/lib/support/time_precise.h42
-rw-r--r--src/core/lib/support/time_win32.c110
-rw-r--r--src/core/lib/support/tls_pthread.c45
-rw-r--r--src/core/lib/support/tmpfile.h55
-rw-r--r--src/core/lib/support/tmpfile_posix.c85
-rw-r--r--src/core/lib/support/tmpfile_win32.c84
-rw-r--r--src/core/lib/support/wrap_memcpy.c53
-rw-r--r--src/core/lib/surface/alarm.c84
-rw-r--r--src/core/lib/surface/api_trace.c36
-rw-r--r--src/core/lib/surface/api_trace.h65
-rw-r--r--src/core/lib/surface/byte_buffer.c97
-rw-r--r--src/core/lib/surface/byte_buffer_reader.c123
-rw-r--r--src/core/lib/surface/call.c1491
-rw-r--r--src/core/lib/surface/call.h116
-rw-r--r--src/core/lib/surface/call_details.c50
-rw-r--r--src/core/lib/surface/call_log_batch.c118
-rw-r--r--src/core/lib/surface/call_test_only.h64
-rw-r--r--src/core/lib/surface/channel.c324
-rw-r--r--src/core/lib/surface/channel.h75
-rw-r--r--src/core/lib/surface/channel_connectivity.c207
-rw-r--r--src/core/lib/surface/channel_create.c223
-rw-r--r--src/core/lib/surface/channel_init.c146
-rw-r--r--src/core/lib/surface/channel_init.h86
-rw-r--r--src/core/lib/surface/channel_ping.c79
-rw-r--r--src/core/lib/surface/channel_stack_type.c54
-rw-r--r--src/core/lib/surface/channel_stack_type.h58
-rw-r--r--src/core/lib/surface/completion_queue.c512
-rw-r--r--src/core/lib/surface/completion_queue.h91
-rw-r--r--src/core/lib/surface/event_string.c81
-rw-r--r--src/core/lib/surface/event_string.h42
-rw-r--r--src/core/lib/surface/init.c239
-rw-r--r--src/core/lib/surface/init.h41
-rw-r--r--src/core/lib/surface/init_secure.c89
-rw-r--r--src/core/lib/surface/init_unsecure.c38
-rw-r--r--src/core/lib/surface/lame_client.c155
-rw-r--r--src/core/lib/surface/lame_client.h41
-rw-r--r--src/core/lib/surface/metadata_array.c49
-rw-r--r--src/core/lib/surface/secure_channel_create.c319
-rw-r--r--src/core/lib/surface/server.c1319
-rw-r--r--src/core/lib/surface/server.h62
-rw-r--r--src/core/lib/surface/server_chttp2.c146
-rw-r--r--src/core/lib/surface/surface_trace.h48
-rw-r--r--src/core/lib/surface/validate_metadata.c73
-rw-r--r--src/core/lib/surface/version.c39
-rw-r--r--src/core/lib/transport/byte_stream.c78
-rw-r--r--src/core/lib/transport/byte_stream.h89
-rw-r--r--src/core/lib/transport/chttp2/alpn.c56
-rw-r--r--src/core/lib/transport/chttp2/alpn.h49
-rw-r--r--src/core/lib/transport/chttp2/bin_encoder.c233
-rw-r--r--src/core/lib/transport/chttp2/bin_encoder.h54
-rw-r--r--src/core/lib/transport/chttp2/frame.h69
-rw-r--r--src/core/lib/transport/chttp2/frame_data.c248
-rw-r--r--src/core/lib/transport/chttp2/frame_data.h101
-rw-r--r--src/core/lib/transport/chttp2/frame_goaway.c193
-rw-r--r--src/core/lib/transport/chttp2/frame_goaway.h77
-rw-r--r--src/core/lib/transport/chttp2/frame_ping.c97
-rw-r--r--src/core/lib/transport/chttp2/frame_ping.h56
-rw-r--r--src/core/lib/transport/chttp2/frame_rst_stream.c99
-rw-r--r--src/core/lib/transport/chttp2/frame_rst_stream.h55
-rw-r--r--src/core/lib/transport/chttp2/frame_settings.c259
-rw-r--r--src/core/lib/transport/chttp2/frame_settings.h103
-rw-r--r--src/core/lib/transport/chttp2/frame_window_update.c113
-rw-r--r--src/core/lib/transport/chttp2/frame_window_update.h56
-rw-r--r--src/core/lib/transport/chttp2/hpack_encoder.c568
-rw-r--r--src/core/lib/transport/chttp2/hpack_encoder.h95
-rw-r--r--src/core/lib/transport/chttp2/hpack_parser.c1449
-rw-r--r--src/core/lib/transport/chttp2/hpack_parser.h116
-rw-r--r--src/core/lib/transport/chttp2/hpack_table.c361
-rw-r--r--src/core/lib/transport/chttp2/hpack_table.h108
-rw-r--r--src/core/lib/transport/chttp2/hpack_tables.txt66
-rw-r--r--src/core/lib/transport/chttp2/http2_errors.h56
-rw-r--r--src/core/lib/transport/chttp2/huffsyms.c105
-rw-r--r--src/core/lib/transport/chttp2/huffsyms.h48
-rw-r--r--src/core/lib/transport/chttp2/incoming_metadata.c96
-rw-r--r--src/core/lib/transport/chttp2/incoming_metadata.h60
-rw-r--r--src/core/lib/transport/chttp2/internal.h780
-rw-r--r--src/core/lib/transport/chttp2/parsing.c866
-rw-r--r--src/core/lib/transport/chttp2/status_conversion.c109
-rw-r--r--src/core/lib/transport/chttp2/status_conversion.h50
-rw-r--r--src/core/lib/transport/chttp2/stream_lists.c442
-rw-r--r--src/core/lib/transport/chttp2/stream_map.c197
-rw-r--r--src/core/lib/transport/chttp2/stream_map.h84
-rw-r--r--src/core/lib/transport/chttp2/timeout_encoding.c188
-rw-r--r--src/core/lib/transport/chttp2/timeout_encoding.h47
-rw-r--r--src/core/lib/transport/chttp2/varint.c65
-rw-r--r--src/core/lib/transport/chttp2/varint.h75
-rw-r--r--src/core/lib/transport/chttp2/writing.c350
-rw-r--r--src/core/lib/transport/chttp2_transport.c1785
-rw-r--r--src/core/lib/transport/chttp2_transport.h51
-rw-r--r--src/core/lib/transport/connectivity_state.c164
-rw-r--r--src/core/lib/transport/connectivity_state.h85
-rw-r--r--src/core/lib/transport/metadata.c698
-rw-r--r--src/core/lib/transport/metadata.h156
-rw-r--r--src/core/lib/transport/metadata_batch.c194
-rw-r--r--src/core/lib/transport/metadata_batch.h125
-rw-r--r--src/core/lib/transport/static_metadata.c160
-rw-r--r--src/core/lib/transport/static_metadata.h408
-rw-r--r--src/core/lib/transport/transport.c184
-rw-r--r--src/core/lib/transport/transport.h242
-rw-r--r--src/core/lib/transport/transport_impl.h81
-rw-r--r--src/core/lib/transport/transport_op_string.c140
-rw-r--r--src/core/lib/tsi/fake_transport_security.c527
-rw-r--r--src/core/lib/tsi/fake_transport_security.h61
-rw-r--r--src/core/lib/tsi/ssl_transport_security.c1536
-rw-r--r--src/core/lib/tsi/ssl_transport_security.h174
-rw-r--r--src/core/lib/tsi/ssl_types.h55
-rw-r--r--src/core/lib/tsi/test_creds/README62
-rw-r--r--src/core/lib/tsi/test_creds/badclient.key16
-rw-r--r--src/core/lib/tsi/test_creds/badclient.pem17
-rw-r--r--src/core/lib/tsi/test_creds/badserver.key16
-rw-r--r--src/core/lib/tsi/test_creds/badserver.pem17
-rw-r--r--src/core/lib/tsi/test_creds/ca-openssl.cnf17
-rw-r--r--src/core/lib/tsi/test_creds/ca.key16
-rw-r--r--src/core/lib/tsi/test_creds/ca.pem15
-rw-r--r--src/core/lib/tsi/test_creds/client.key16
-rw-r--r--src/core/lib/tsi/test_creds/client.pem14
-rw-r--r--src/core/lib/tsi/test_creds/server0.key16
-rw-r--r--src/core/lib/tsi/test_creds/server0.pem14
-rw-r--r--src/core/lib/tsi/test_creds/server1-openssl.cnf26
-rw-r--r--src/core/lib/tsi/test_creds/server1.key16
-rw-r--r--src/core/lib/tsi/test_creds/server1.pem16
-rw-r--r--src/core/lib/tsi/transport_security.c284
-rw-r--r--src/core/lib/tsi/transport_security.h111
-rw-r--r--src/core/lib/tsi/transport_security_interface.h344
386 files changed, 65947 insertions, 0 deletions
diff --git a/src/core/lib/census/README.md b/src/core/lib/census/README.md
new file mode 100644
index 0000000000..fb615a2194
--- /dev/null
+++ b/src/core/lib/census/README.md
@@ -0,0 +1,76 @@
+<!---
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+# Census - a resource measurement and tracing system
+
+This directory contains code for Census, which will ultimately provide the
+following features for any gRPC-using system:
+* A [dapper](http://research.google.com/pubs/pub36356.html)-like tracing
+ system, enabling tracing across a distributed infrastructure.
+* RPC statistics and measurements for key metrics, such as latency, bytes
+ transferred, number of errors etc.
+* Resource measurement framework which can be used for measuring custom
+ metrics. Through the use of [tags](#Tags), these can be broken down across
+ the entire distributed stack.
+* Easy integration of the above with
+ [Google Cloud Trace](https://cloud.google.com/tools/cloud-trace) and
+ [Google Cloud Monitoring](https://cloud.google.com/monitoring/).
+
+## Concepts
+
+### Context
+
+### Operations
+
+### Tags
+
+### Metrics
+
+## API
+
+### Internal/RPC API
+
+### External/Client API
+
+### RPC API
+
+## Files in this directory
+
+Note that files and functions in this directory can be split into two
+categories:
+* Files that define core census library functions. Functions etc. in these
+ files are named census\_\*, and constitute the core census library
+ functionality. At some time in the future, these will become a standalone
+ library.
+* Files that define functions etc. that provide a convenient interface between
+ grpc and the core census functionality. These files are all named
+ grpc\_\*.{c,h}, and define function names beginning with grpc\_census\_\*.
+
diff --git a/src/core/lib/census/aggregation.h b/src/core/lib/census/aggregation.h
new file mode 100644
index 0000000000..f353368b97
--- /dev/null
+++ b/src/core/lib/census/aggregation.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * 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 <stddef.h>
+
+#ifndef GRPC_CORE_LIB_CENSUS_AGGREGATION_H
+#define GRPC_CORE_LIB_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_CORE_LIB_CENSUS_AGGREGATION_H */
diff --git a/src/core/lib/census/context.c b/src/core/lib/census/context.c
new file mode 100644
index 0000000000..5a118f46a9
--- /dev/null
+++ b/src/core/lib/census/context.c
@@ -0,0 +1,509 @@
+/*
+ *
+ * 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 <grpc/census.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/useful.h>
+#include <stdbool.h>
+#include <string.h>
+#include "src/core/lib/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.
+
+// min and max valid chars in tag keys and values. All printable ASCII is OK.
+#define MIN_VALID_TAG_CHAR 32 // ' '
+#define MAX_VALID_TAG_CHAR 126 // '~'
+
+// 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, including trailing 0 (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 representation of a context. Composed of 2 underlying tag_set
+// structs, one each for propagated and local (non-propagated) tags. This is
+// to efficiently support tag encoding/decoding.
+// TODO(aveitch): need to add tracing id's/structure.
+struct census_context {
+ struct tag_set tags[2];
+ census_context_status status;
+};
+
+// Indices into the tags member of census_context
+#define PROPAGATED_TAGS 0
+#define LOCAL_TAGS 1
+
+// Validate (check all characters are in range and size is less than limit) a
+// key or value string. Returns 0 if the string is invalid, or the length
+// (including terminator) if valid.
+static size_t validate_tag(const char *kv) {
+ size_t len = 1;
+ char ch;
+ while ((ch = *kv++) != 0) {
+ if (ch < MIN_VALID_TAG_CHAR || ch > MAX_VALID_TAG_CHAR) {
+ return 0;
+ }
+ len++;
+ }
+ if (len > CENSUS_MAX_TAG_KV_LEN) {
+ return 0;
+ }
+ return len;
+}
+
+// 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));
+}
+
+// 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, size_t value_len) {
+ if (tags->ntags == CENSUS_MAX_PROPAGATED_TAGS) {
+ return false;
+ }
+ const size_t tag_size = key_len + 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)value_len;
+ // ensure reserved flags are not used.
+ *kvp++ = (char)(tag->flags & (CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS));
+ memcpy(kvp, tag->key, key_len);
+ kvp += key_len;
+ memcpy(kvp, tag->value, 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, size_t value_len) {
+ // First delete the tag if it is already present.
+ bool deleted = context_delete_tag(context, tag, key_len);
+ bool added = false;
+ if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) {
+ added = tag_set_add_tag(&context->tags[PROPAGATED_TAGS], tag, key_len,
+ value_len);
+ } else {
+ added =
+ tag_set_add_tag(&context->tags[LOCAL_TAGS], tag, key_len, value_len);
+ }
+
+ if (deleted) {
+ context->status.n_modified_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[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 = validate_tag(tag->key);
+ // ignore the tag if it is invalid or too short.
+ if (key_len <= 1) {
+ context->status.n_invalid_tags++;
+ } else {
+ if (tag->value != NULL) {
+ size_t value_len = validate_tag(tag->value);
+ if (value_len != 0) {
+ context_modify_tag(context, tag, key_len, value_len);
+ } else {
+ context->status.n_invalid_tags++;
+ }
+ } else {
+ if (context_delete_tag(context, tag, key_len)) {
+ context->status.n_deleted_tags++;
+ }
+ }
+ }
+ }
+ // Remove any deleted tags, update status if needed, and return.
+ tag_set_flatten(&context->tags[PROPAGATED_TAGS]);
+ tag_set_flatten(&context->tags[LOCAL_TAGS]);
+ context->status.n_propagated_tags = context->tags[PROPAGATED_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[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[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->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->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[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;
+}
+
+size_t census_context_encode(const census_context *context, char *buffer,
+ size_t buf_size) {
+ return tag_set_encode(&context->tags[PROPAGATED_TAGS], buffer, buf_size);
+}
+
+// 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) {
+ 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);
+ }
+ memset(&context->status, 0, sizeof(context->status));
+ context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags;
+ return context;
+}
diff --git a/src/core/lib/census/grpc_context.c b/src/core/lib/census/grpc_context.c
new file mode 100644
index 0000000000..457c176355
--- /dev/null
+++ b/src/core/lib/census/grpc_context.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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 <grpc/census.h>
+#include <grpc/grpc.h>
+#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 (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/src/core/lib/census/grpc_filter.c b/src/core/lib/census/grpc_filter.c
new file mode 100644
index 0000000000..d27d789aa1
--- /dev/null
+++ b/src/core/lib/census/grpc_filter.c
@@ -0,0 +1,198 @@
+/*
+ *
+ * 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/lib/census/grpc_filter.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/census.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/statistics/census_interface.h"
+#include "src/core/lib/statistics/census_rpc_stats.h"
+#include "src/core/lib/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->recv_initial_metadata_ready;
+ op->recv_initial_metadata_ready = &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/src/core/lib/census/grpc_filter.h b/src/core/lib/census/grpc_filter.h
new file mode 100644
index 0000000000..7ceafe56e4
--- /dev/null
+++ b/src/core/lib/census/grpc_filter.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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_CORE_LIB_CENSUS_GRPC_FILTER_H
+#define GRPC_CORE_LIB_CENSUS_GRPC_FILTER_H
+
+#include "src/core/lib/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_CORE_LIB_CENSUS_GRPC_FILTER_H */
diff --git a/src/core/lib/census/grpc_plugin.c b/src/core/lib/census/grpc_plugin.c
new file mode 100644
index 0000000000..12aca76745
--- /dev/null
+++ b/src/core/lib/census/grpc_plugin.c
@@ -0,0 +1,70 @@
+/*
+ *
+ * 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/lib/census/grpc_plugin.h"
+
+#include <limits.h>
+
+#include <grpc/census.h>
+
+#include "src/core/lib/census/grpc_filter.h"
+#include "src/core/lib/channel/channel_stack_builder.h"
+#include "src/core/lib/surface/channel_init.h"
+
+static bool maybe_add_census_filter(grpc_channel_stack_builder *builder,
+ void *arg_must_be_null) {
+ const grpc_channel_args *args =
+ grpc_channel_stack_builder_get_channel_arguments(builder);
+ if (grpc_channel_args_is_census_enabled(args)) {
+ return grpc_channel_stack_builder_prepend_filter(
+ builder, &grpc_client_census_filter, NULL, NULL);
+ }
+ return true;
+}
+
+void census_grpc_plugin_init(void) {
+ /* 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.");
+ }
+ }
+ grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX,
+ maybe_add_census_filter, NULL);
+ grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
+ maybe_add_census_filter, NULL);
+}
+
+void census_grpc_plugin_destroy(void) { census_shutdown(); }
diff --git a/src/core/lib/census/grpc_plugin.h b/src/core/lib/census/grpc_plugin.h
new file mode 100644
index 0000000000..33e5f0b701
--- /dev/null
+++ b/src/core/lib/census/grpc_plugin.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * 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_CORE_LIB_CENSUS_GRPC_PLUGIN_H
+#define GRPC_CORE_LIB_CENSUS_GRPC_PLUGIN_H
+
+void census_grpc_plugin_init(void);
+void census_grpc_plugin_destroy(void);
+
+#endif /* GRPC_CORE_LIB_CENSUS_GRPC_PLUGIN_H */
diff --git a/src/core/lib/census/initialize.c b/src/core/lib/census/initialize.c
new file mode 100644
index 0000000000..ce7ec09b89
--- /dev/null
+++ b/src/core/lib/census/initialize.c
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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 <grpc/census.h>
+
+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/src/core/lib/census/mlog.c b/src/core/lib/census/mlog.c
new file mode 100644
index 0000000000..9d47e80297
--- /dev/null
+++ b/src/core/lib/census/mlog.c
@@ -0,0 +1,600 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+// Implements an efficient in-memory log, optimized for multiple writers and
+// a single reader. 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 the
+// 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 the 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 operating 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. Readers 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/lib/census/mlog.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
+#include <stdbool.h>
+#include <string.h>
+
+// 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.
+ size_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
+// size_t 4 8
+// 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
+ uint32_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;
+ 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,
+ size_t bytes_committed) {
+ gpr_atm_rel_store(&block->bytes_committed, (gpr_atm)bytes_committed);
+}
+
+static size_t cl_block_get_bytes_committed(cl_block* block) {
+ return (size_t)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 bool cl_block_try_disable_access(cl_block* block, int discard_data) {
+ if (!cl_try_lock(&block->writer_lock)) {
+ return false;
+ }
+ if (!cl_try_lock(&block->reader_lock)) {
+ cl_unlock(&block->writer_lock);
+ return false;
+ }
+ if (!discard_data &&
+ (block->bytes_read != cl_block_get_bytes_committed(block))) {
+ cl_unlock(&block->reader_lock);
+ cl_unlock(&block->writer_lock);
+ return false;
+ }
+ cl_block_set_bytes_committed(block, 0);
+ block->bytes_read = 0;
+ return true;
+}
+
+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) {
+ if (!cl_try_lock(&block->writer_lock)) {
+ return NULL;
+ }
+ size_t 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) {
+ if (!cl_try_lock(&block->reader_lock)) {
+ return NULL;
+ }
+ // bytes_committed may change from under us. Use bytes_available to update
+ // bytes_read below.
+ size_t bytes_committed = cl_block_get_bytes_committed(block);
+ GPR_ASSERT(bytes_committed >= block->bytes_read);
+ *bytes_available = bytes_committed - block->bytes_read;
+ if (*bytes_available == 0) {
+ cl_unlock(&block->reader_lock);
+ return NULL;
+ }
+ void* 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 true if:
+// - allocated a new block OR
+// - 'core_id' => 'old_block' mapping changed (another thread allocated a
+// block before lock was acquired).
+static bool cl_allocate_core_local_block(uint32_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 true;
+ }
+ 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) {
+ return false;
+ }
+ cl_core_local_block_set_block(core_local_block, block);
+ cl_block_enable_access(block);
+ return true;
+}
+
+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);
+ }
+ } 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;
+}
+
+#define CL_LOG_2_MB 20 // 2^20 = 1MB
+
+// External functions: primary stats_log interface
+void census_log_initialize(size_t size_in_mb, int discard_old_records) {
+ // 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 that we will not get any overflow in calaculating num_blocks
+ GPR_ASSERT(CL_LOG_2_MB >= CENSUS_LOG_2_MAX_RECORD_SIZE);
+ GPR_ASSERT(size_in_mb < 1000);
+ // Ensure at least 2x as many blocks as there are cores.
+ g_log.num_blocks =
+ (uint32_t)GPR_MAX(2 * g_log.num_cores, (size_in_mb << CL_LOG_2_MB) >>
+ CENSUS_LOG_2_MAX_RECORD_SIZE);
+ gpr_mu_init(&g_log.lock);
+ g_log.read_iterator_state = 0;
+ g_log.block_being_read = NULL;
+ 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 (uint32_t i = 0; i < g_log.num_blocks; ++i) {
+ cl_block* block = g_log.blocks + i;
+ cl_block_initialize(block, g_log.buffer + (CENSUS_LOG_MAX_RECORD_SIZE * i));
+ cl_block_try_disable_access(block, 1 /* discard data */);
+ cl_block_list_insert_at_tail(&g_log.free_block_list, block);
+ }
+ gpr_atm_rel_store(&g_log.out_of_space_count, 0);
+ g_log.initialized = 1;
+}
+
+void census_log_shutdown(void) {
+ GPR_ASSERT(g_log.initialized);
+ gpr_mu_destroy(&g_log.lock);
+ gpr_free_aligned(g_log.core_local_blocks);
+ g_log.core_local_blocks = NULL;
+ gpr_free_aligned(g_log.blocks);
+ g_log.blocks = NULL;
+ gpr_free(g_log.buffer);
+ g_log.buffer = NULL;
+ g_log.initialized = 0;
+}
+
+void* census_log_start_write(size_t size) {
+ // Used to bound number of times block allocation is attempted.
+ GPR_ASSERT(size > 0);
+ GPR_ASSERT(g_log.initialized);
+ if (size > CENSUS_LOG_MAX_RECORD_SIZE) {
+ return NULL;
+ }
+ uint32_t attempts_remaining = g_log.num_blocks;
+ uint32_t core_id = gpr_cpu_current_cpu();
+ do {
+ 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
+ gpr_mu_lock(&g_log.lock);
+ bool 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) {
+ GPR_ASSERT(g_log.initialized);
+ size_t space = 0;
+ 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 {
+ GPR_ASSERT(g_log.free_block_list.count >= 0);
+ space = (size_t)g_log.free_block_list.count * CENSUS_LOG_MAX_RECORD_SIZE;
+ }
+ gpr_mu_unlock(&g_log.lock);
+ return space;
+}
+
+int64_t census_log_out_of_space_count(void) {
+ GPR_ASSERT(g_log.initialized);
+ return gpr_atm_acq_load(&g_log.out_of_space_count);
+}
diff --git a/src/core/lib/census/mlog.h b/src/core/lib/census/mlog.h
new file mode 100644
index 0000000000..7fbdeda986
--- /dev/null
+++ b/src/core/lib/census/mlog.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* A very fast in-memory log, optimized for multiple writers. */
+
+#ifndef GRPC_CORE_LIB_CENSUS_MLOG_H
+#define GRPC_CORE_LIB_CENSUS_MLOG_H
+
+#include <grpc/support/port_platform.h>
+#include <stddef.h>
+
+/* 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);
+
+void census_log_init_reader(void);
+
+/* 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.
+*/
+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. */
+int64_t census_log_out_of_space_count(void);
+
+#endif /* GRPC_CORE_LIB_CENSUS_MLOG_H */
diff --git a/src/core/lib/census/operation.c b/src/core/lib/census/operation.c
new file mode 100644
index 0000000000..315f9c3534
--- /dev/null
+++ b/src/core/lib/census/operation.c
@@ -0,0 +1,63 @@
+/*
+ * 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 <grpc/census.h>
+
+/* 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/src/core/lib/census/placeholders.c b/src/core/lib/census/placeholders.c
new file mode 100644
index 0000000000..fe23d13971
--- /dev/null
+++ b/src/core/lib/census/placeholders.c
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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 <grpc/census.h>
+
+#include <grpc/support/log.h>
+
+/* 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/src/core/lib/census/rpc_metric_id.h b/src/core/lib/census/rpc_metric_id.h
new file mode 100644
index 0000000000..aad0588fb3
--- /dev/null
+++ b/src/core/lib/census/rpc_metric_id.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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_CORE_LIB_CENSUS_RPC_METRIC_ID_H
+#define GRPC_CORE_LIB_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 /* GRPC_CORE_LIB_CENSUS_RPC_METRIC_ID_H */
diff --git a/src/core/lib/census/tracing.c b/src/core/lib/census/tracing.c
new file mode 100644
index 0000000000..e508996af3
--- /dev/null
+++ b/src/core/lib/census/tracing.c
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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 <grpc/census.h>
+
+/* 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/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c
new file mode 100644
index 0000000000..1a02f1f4aa
--- /dev/null
+++ b/src/core/lib/channel/channel_args.c
@@ -0,0 +1,271 @@
+/*
+ *
+ * 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/lib/channel/channel_args.h"
+#include <grpc/grpc.h>
+#include "src/core/lib/support/string.h"
+
+#include <grpc/census.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+#include <string.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) {
+ 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);
+}
+
+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 = ap;
+ const grpc_arg *const *b = 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 = gpr_malloc(sizeof(grpc_arg *) * a->num_args);
+ for (size_t i = 0; i < a->num_args; i++) {
+ args[i] = &a->args[i];
+ }
+ qsort(args, a->num_args, sizeof(grpc_arg *), cmp_key_stable);
+
+ grpc_channel_args *b = gpr_malloc(sizeof(grpc_channel_args));
+ b->num_args = a->num_args;
+ b->args = 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;
+ 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);
+}
+
+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 */
+ }
+}
+
+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;
+}
diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h
new file mode 100644
index 0000000000..67d287ec6b
--- /dev/null
+++ b/src/core/lib/channel/channel_args.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+
+/* Copy some arguments */
+grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src);
+
+/* Copy some arguments, stably sorting keys */
+grpc_channel_args *grpc_channel_args_normalize(const grpc_channel_args *a);
+
+/** 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);
+
+int grpc_channel_args_compare(const grpc_channel_args *a,
+ const grpc_channel_args *b);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */
diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c
new file mode 100644
index 0000000000..52283e35fa
--- /dev/null
+++ b/src/core/lib/channel/channel_stack.c
@@ -0,0 +1,262 @@
+/*
+ *
+ * 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/lib/channel/channel_stack.h"
+#include <grpc/support/log.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+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/src/core/lib/channel/channel_stack.h b/src/core/lib/channel/channel_stack.h
new file mode 100644
index 0000000000..b29bee411d
--- /dev/null
+++ b/src/core/lib/channel/channel_stack.h
@@ -0,0 +1,260 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_CHANNEL_STACK_H
+#define GRPC_CORE_LIB_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 <stddef.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/debug/trace.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;
+ 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_CORE_LIB_CHANNEL_CHANNEL_STACK_H */
diff --git a/src/core/lib/channel/channel_stack_builder.c b/src/core/lib/channel/channel_stack_builder.c
new file mode 100644
index 0000000000..1ce0c4e07f
--- /dev/null
+++ b/src/core/lib/channel/channel_stack_builder.c
@@ -0,0 +1,258 @@
+/*
+ *
+ * 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 "src/core/lib/channel/channel_stack_builder.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+int grpc_trace_channel_stack_builder = 0;
+
+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
+ const grpc_channel_args *args;
+ grpc_transport *transport;
+ 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 = gpr_malloc(sizeof(*b));
+ memset(b, 0, sizeof(*b));
+
+ b->begin.filter = NULL;
+ b->end.filter = NULL;
+ b->begin.next = &b->end;
+ b->begin.prev = &b->end;
+ b->end.next = &b->begin;
+ b->end.prev = &b->begin;
+
+ return b;
+}
+
+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 = 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_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;
+}
+
+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 == NULL);
+ builder->name = name;
+}
+
+void grpc_channel_stack_builder_set_channel_arguments(
+ grpc_channel_stack_builder *builder, const grpc_channel_args *args) {
+ GPR_ASSERT(builder->args == NULL);
+ builder->args = args;
+}
+
+void grpc_channel_stack_builder_set_transport(
+ grpc_channel_stack_builder *builder, grpc_transport *transport) {
+ GPR_ASSERT(builder->transport == NULL);
+ builder->transport = transport;
+}
+
+grpc_transport *grpc_channel_stack_builder_get_transport(
+ grpc_channel_stack_builder *builder) {
+ return builder->transport;
+}
+
+const grpc_channel_args *grpc_channel_stack_builder_get_channel_arguments(
+ grpc_channel_stack_builder *builder) {
+ return builder->args;
+}
+
+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_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 = gpr_malloc(sizeof(*new));
+ new->next = before->next;
+ new->prev = before;
+ new->next->prev = new->prev->next = new;
+ new->filter = filter;
+ new->init = post_init_func;
+ new->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;
+ }
+ gpr_free(builder);
+}
+
+void *grpc_channel_stack_builder_finish(grpc_exec_ctx *exec_ctx,
+ grpc_channel_stack_builder *builder,
+ size_t prefix_bytes, int initial_refs,
+ grpc_iomgr_cb_func destroy,
+ void *destroy_arg) {
+ // 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 =
+ 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
+ char *result = gpr_malloc(prefix_bytes + channel_stack_size);
+ // fetch a pointer to the channel stack
+ grpc_channel_stack *channel_stack =
+ (grpc_channel_stack *)(result + prefix_bytes);
+ // and initialize it
+ grpc_channel_stack_init(exec_ctx, initial_refs, destroy,
+ destroy_arg == NULL ? result : destroy_arg, filters,
+ num_filters, builder->args, builder->name,
+ channel_stack);
+
+ // run post-initialization functions
+ i = 0;
+ for (filter_node *p = builder->begin.next; p != &builder->end; p = p->next) {
+ if (p->init != NULL) {
+ p->init(channel_stack, grpc_channel_stack_element(channel_stack, i),
+ p->init_arg);
+ }
+ i++;
+ }
+
+ grpc_channel_stack_builder_destroy(builder);
+ gpr_free((grpc_channel_filter **)filters);
+
+ return result;
+}
diff --git a/src/core/lib/channel/channel_stack_builder.h b/src/core/lib/channel/channel_stack_builder.h
new file mode 100644
index 0000000000..8532c4462a
--- /dev/null
+++ b/src/core/lib/channel/channel_stack_builder.h
@@ -0,0 +1,155 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H
+#define GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H
+
+#include <stdbool.h>
+
+#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);
+
+/// 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);
+
+/// Set channel arguments: \a args must continue to exist until after
+/// grpc_channel_stack_builder_finish returns
+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);
+
+/// 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);
+
+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;
+
+/// 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
+/// 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
+void *grpc_channel_stack_builder_finish(grpc_exec_ctx *exec_ctx,
+ grpc_channel_stack_builder *builder,
+ size_t prefix_bytes, int initial_refs,
+ grpc_iomgr_cb_func destroy,
+ void *destroy_arg);
+
+/// Destroy the builder without creating a channel stack
+void grpc_channel_stack_builder_destroy(grpc_channel_stack_builder *builder);
+
+extern int grpc_trace_channel_stack_builder;
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_STACK_BUILDER_H */
diff --git a/src/core/lib/channel/client_channel.c b/src/core/lib/channel/client_channel.c
new file mode 100644
index 0000000000..9fdf803ecf
--- /dev/null
+++ b/src/core/lib/channel/client_channel.c
@@ -0,0 +1,526 @@
+/*
+ *
+ * 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/lib/channel/client_channel.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/subchannel_call_holder.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/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 (owned) */
+ 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_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_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);
+ }
+ GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver");
+ grpc_resolver_next(exec_ctx, chand->resolver,
+ &chand->incoming_configuration,
+ &chand->on_config_changed);
+ gpr_mu_unlock(&chand->mu_config);
+ } else {
+ if (chand->resolver != NULL) {
+ grpc_resolver_shutdown(exec_ctx, chand->resolver);
+ GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
+ 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 (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_exec_ctx_enqueue(exec_ctx, op->on_consumed, true, NULL);
+
+ GPR_ASSERT(op->set_accept_stream == false);
+ 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");
+ grpc_resolver_shutdown(exec_ctx, chand->resolver);
+ GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel");
+ 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);
+}
+
+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");
+ chand->interested_parties = grpc_pollset_set_create();
+}
+
+/* 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/src/core/lib/channel/client_channel.h b/src/core/lib/channel/client_channel.h
new file mode 100644
index 0000000000..8777796fb6
--- /dev/null
+++ b/src/core/lib/channel/client_channel.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_CLIENT_CHANNEL_H
+#define GRPC_CORE_LIB_CHANNEL_CLIENT_CHANNEL_H
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/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_CORE_LIB_CHANNEL_CLIENT_CHANNEL_H */
diff --git a/src/core/lib/channel/compress_filter.c b/src/core/lib/channel/compress_filter.c
new file mode 100644
index 0000000000..04bb7cc76f
--- /dev/null
+++ b/src/core/lib/channel/compress_filter.c
@@ -0,0 +1,304 @@
+/*
+ *
+ * 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 <assert.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/compress_filter.h"
+#include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/compression/message_compress.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/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/src/core/lib/channel/compress_filter.h b/src/core/lib/channel/compress_filter.h
new file mode 100644
index 0000000000..9010074335
--- /dev/null
+++ b/src/core/lib/channel/compress_filter.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_COMPRESS_FILTER_H
+#define GRPC_CORE_LIB_CHANNEL_COMPRESS_FILTER_H
+
+#include "src/core/lib/channel/channel_stack.h"
+
+#define GRPC_COMPRESS_REQUEST_ALGORITHM_KEY "grpc-internal-encoding-request"
+
+/** Compression filter for outgoing data.
+ *
+ * See <grpc/compression.h> 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_CORE_LIB_CHANNEL_COMPRESS_FILTER_H */
diff --git a/src/core/lib/channel/connected_channel.c b/src/core/lib/channel/connected_channel.c
new file mode 100644
index 0000000000..5e3a8974ce
--- /dev/null
+++ b/src/core/lib/channel/connected_channel.c
@@ -0,0 +1,176 @@
+/*
+ *
+ * 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/lib/channel/connected_channel.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/string.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 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;
+ 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;
+
+ 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;
+ 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);
+ 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;
+ 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);
+}
+
+static const grpc_channel_filter 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",
+};
+
+static void bind_transport(grpc_channel_stack *channel_stack,
+ grpc_channel_element *elem, void *t) {
+ channel_data *cd = (channel_data *)elem->channel_data;
+ GPR_ASSERT(elem->filter == &connected_channel_filter);
+ GPR_ASSERT(cd->transport == NULL);
+ cd->transport = 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(t);
+}
+
+bool grpc_add_connected_filter(grpc_channel_stack_builder *builder,
+ void *arg_must_be_null) {
+ GPR_ASSERT(arg_must_be_null == NULL);
+ grpc_transport *t = grpc_channel_stack_builder_get_transport(builder);
+ GPR_ASSERT(t != NULL);
+ return grpc_channel_stack_builder_append_filter(
+ builder, &connected_channel_filter, bind_transport, t);
+}
+
+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/src/core/lib/channel/connected_channel.h b/src/core/lib/channel/connected_channel.h
new file mode 100644
index 0000000000..4f20b751cc
--- /dev/null
+++ b/src/core/lib/channel/connected_channel.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H
+#define GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H
+
+#include "src/core/lib/channel/channel_stack_builder.h"
+
+bool grpc_add_connected_filter(grpc_channel_stack_builder *builder,
+ void *arg_must_be_null);
+
+#endif /* GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H */
diff --git a/src/core/lib/channel/context.h b/src/core/lib/channel/context.h
new file mode 100644
index 0000000000..bca102da9a
--- /dev/null
+++ b/src/core/lib/channel/context.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_CONTEXT_H
+#define GRPC_CORE_LIB_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_CORE_LIB_CHANNEL_CONTEXT_H */
diff --git a/src/core/lib/channel/http_client_filter.c b/src/core/lib/channel/http_client_filter.c
new file mode 100644
index 0000000000..7dbac38414
--- /dev/null
+++ b/src/core/lib/channel/http_client_filter.c
@@ -0,0 +1,255 @@
+/*
+ * 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/lib/channel/http_client_filter.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <string.h>
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/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->recv_initial_metadata_ready;
+ op->recv_initial_metadata_ready = &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/src/core/lib/channel/http_client_filter.h b/src/core/lib/channel/http_client_filter.h
new file mode 100644
index 0000000000..418426e9cc
--- /dev/null
+++ b/src/core/lib/channel/http_client_filter.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_HTTP_CLIENT_FILTER_H
+#define GRPC_CORE_LIB_CHANNEL_HTTP_CLIENT_FILTER_H
+
+#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;
+
+#define GRPC_ARG_HTTP2_SCHEME "grpc.http2_scheme"
+
+#endif /* GRPC_CORE_LIB_CHANNEL_HTTP_CLIENT_FILTER_H */
diff --git a/src/core/lib/channel/http_server_filter.c b/src/core/lib/channel/http_server_filter.c
new file mode 100644
index 0000000000..df99b77ab3
--- /dev/null
+++ b/src/core/lib/channel/http_server_filter.c
@@ -0,0 +1,240 @@
+/*
+ *
+ * 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/lib/channel/http_server_filter.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <string.h>
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/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->recv_initial_metadata_ready;
+ op->recv_initial_metadata_ready = &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/src/core/lib/channel/http_server_filter.h b/src/core/lib/channel/http_server_filter.h
new file mode 100644
index 0000000000..c8cf920ded
--- /dev/null
+++ b/src/core/lib/channel/http_server_filter.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_HTTP_SERVER_FILTER_H
+#define GRPC_CORE_LIB_CHANNEL_HTTP_SERVER_FILTER_H
+
+#include "src/core/lib/channel/channel_stack.h"
+
+/* Processes metadata on the client side for HTTP2 transports */
+extern const grpc_channel_filter grpc_http_server_filter;
+
+#endif /* GRPC_CORE_LIB_CHANNEL_HTTP_SERVER_FILTER_H */
diff --git a/src/core/lib/channel/subchannel_call_holder.c b/src/core/lib/channel/subchannel_call_holder.c
new file mode 100644
index 0000000000..6c6d42dd73
--- /dev/null
+++ b/src/core/lib/channel/subchannel_call_holder.c
@@ -0,0 +1,259 @@
+/*
+ *
+ * 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/lib/channel/subchannel_call_holder.h"
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/lib/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;
+ gpr_mu_lock(&holder->mu);
+ GPR_ASSERT(holder->creation_phase ==
+ GRPC_SUBCHANNEL_CALL_HOLDER_PICKING_SUBCHANNEL);
+ holder->creation_phase = GRPC_SUBCHANNEL_CALL_HOLDER_NOT_CREATING;
+ if (holder->connected_subchannel == NULL) {
+ fail_locked(exec_ctx, holder);
+ } else if (1 == gpr_atm_acq_load(&holder->subchannel_call)) {
+ /* already cancelled before subchannel became ready */
+ 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_transport_stream_op_finish_with_failure(exec_ctx,
+ &holder->waiting_ops[i]);
+ }
+ 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/src/core/lib/channel/subchannel_call_holder.h b/src/core/lib/channel/subchannel_call_holder.h
new file mode 100644
index 0000000000..882f366792
--- /dev/null
+++ b/src/core/lib/channel/subchannel_call_holder.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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_CORE_LIB_CHANNEL_SUBCHANNEL_CALL_HOLDER_H
+#define GRPC_CORE_LIB_CHANNEL_SUBCHANNEL_CALL_HOLDER_H
+
+#include "src/core/lib/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.
+
+ The channel filter uses 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). */
+ 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 /* GRPC_CORE_LIB_CHANNEL_SUBCHANNEL_CALL_HOLDER_H */
diff --git a/src/core/lib/client_config/README.md b/src/core/lib/client_config/README.md
new file mode 100644
index 0000000000..fff7a5af5b
--- /dev/null
+++ b/src/core/lib/client_config/README.md
@@ -0,0 +1,66 @@
+Client Configuration Support for GRPC
+=====================================
+
+This library provides high level configuration machinery to construct client
+channels and load balance between them.
+
+Each grpc_channel is created with a grpc_resolver. It is the resolver's duty
+to resolve a name into configuration data for the channel. Such configuration
+data might include:
+
+- a list of (ip, port) addresses to connect to
+- a load balancing policy to decide which server to send a request to
+- a set of filters to mutate outgoing requests (say, by adding metadata)
+
+The resolver provides this data as a stream of grpc_client_config objects to
+the channel. We represent configuration as a stream so that it can be changed
+by the resolver during execution, by reacting to external events (such as a
+new configuration file being pushed to some store).
+
+
+Load Balancing
+--------------
+
+Load balancing configuration is provided by a grpc_lb_policy object, stored as
+part of grpc_client_config.
+
+The primary job of the load balancing policies is to pick a target server given only the
+initial metadata for a request. It does this by providing a grpc_subchannel
+object to the owning channel.
+
+
+Sub-Channels
+------------
+
+A sub-channel provides a connection to a server for a client channel. It has a
+connectivity state like a regular channel, and so can be connected or
+disconnected. This connectivity state can be used to inform load balancing
+decisions (for example, by avoiding disconnected backends).
+
+Configured sub-channels are fully setup to participate in the grpc data plane.
+Their behavior is specified by a set of grpc channel filters defined at their
+construction. To customize this behavior, resolvers build
+grpc_subchannel_factory objects, which use the decorator pattern to customize
+construction arguments for concrete grpc_subchannel instances.
+
+
+Naming for GRPC
+===============
+
+Names in GRPC are represented by a URI (as defined in
+[RFC 3986](https://tools.ietf.org/html/rfc3986)).
+
+The following schemes are currently supported:
+
+dns:///host:port - dns schemes are currently supported so long as authority is
+ empty (authority based dns resolution is expected in a future
+ release)
+
+unix:path - the unix scheme is used to create and connect to unix domain
+ sockets - the authority must be empty, and the path
+ represents the absolute or relative path to the desired
+ socket
+
+ipv4:host:port - a pre-resolved ipv4 dotted decimal address/port combination
+
+ipv6:[host]:port - a pre-resolved ipv6 address/port combination
diff --git a/src/core/lib/client_config/client_config.c b/src/core/lib/client_config/client_config.c
new file mode 100644
index 0000000000..82c8d68099
--- /dev/null
+++ b/src/core/lib/client_config/client_config.c
@@ -0,0 +1,74 @@
+/*
+ *
+ * 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/lib/client_config/client_config.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+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)) {
+ if (c->lb_policy != NULL) {
+ 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/src/core/lib/client_config/client_config.h b/src/core/lib/client_config/client_config.h
new file mode 100644
index 0000000000..404ec0d3a5
--- /dev/null
+++ b/src/core/lib/client_config/client_config.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_CLIENT_CONFIG_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_CLIENT_CONFIG_H
+
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_CLIENT_CONFIG_H */
diff --git a/src/core/lib/client_config/connector.c b/src/core/lib/client_config/connector.c
new file mode 100644
index 0000000000..f51d862c6d
--- /dev/null
+++ b/src/core/lib/client_config/connector.c
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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/lib/client_config/connector.h"
+
+grpc_connector* grpc_connector_ref(grpc_connector* connector) {
+ connector->vtable->ref(connector);
+ return 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/src/core/lib/client_config/connector.h b/src/core/lib/client_config/connector.h
new file mode 100644
index 0000000000..21b925aade
--- /dev/null
+++ b/src/core/lib/client_config/connector.h
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_CONNECTOR_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_CONNECTOR_H
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/iomgr/sockaddr.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;
+ /** 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;
+
+ /** 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);
+};
+
+grpc_connector *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 /* GRPC_CORE_LIB_CLIENT_CONFIG_CONNECTOR_H */
diff --git a/src/core/lib/client_config/default_initial_connect_string.c b/src/core/lib/client_config/default_initial_connect_string.c
new file mode 100644
index 0000000000..86eb37de77
--- /dev/null
+++ b/src/core/lib/client_config/default_initial_connect_string.c
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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 <grpc/support/slice.h>
+#include "src/core/lib/iomgr/sockaddr.h"
+
+void grpc_set_default_initial_connect_string(struct sockaddr **addr,
+ size_t *addr_len,
+ gpr_slice *initial_str) {}
diff --git a/src/core/lib/client_config/initial_connect_string.c b/src/core/lib/client_config/initial_connect_string.c
new file mode 100644
index 0000000000..95ae728316
--- /dev/null
+++ b/src/core/lib/client_config/initial_connect_string.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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/lib/client_config/initial_connect_string.h"
+
+#include <stddef.h>
+
+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/src/core/lib/client_config/initial_connect_string.h b/src/core/lib/client_config/initial_connect_string.h
new file mode 100644
index 0000000000..eec42fa240
--- /dev/null
+++ b/src/core/lib/client_config/initial_connect_string.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H
+
+#include <grpc/support/slice.h>
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_INITIAL_CONNECT_STRING_H */
diff --git a/src/core/lib/client_config/lb_policies/load_balancer_api.c b/src/core/lib/client_config/lb_policies/load_balancer_api.c
new file mode 100644
index 0000000000..4cbed200df
--- /dev/null
+++ b/src/core/lib/client_config/lb_policies/load_balancer_api.c
@@ -0,0 +1,163 @@
+/*
+ *
+ * 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 "src/core/lib/client_config/lb_policies/load_balancer_api.h"
+#include "third_party/nanopb/pb_decode.h"
+#include "third_party/nanopb/pb_encode.h"
+
+#include <grpc/support/alloc.h>
+
+typedef struct decode_serverlist_arg {
+ int first_pass;
+ int i;
+ size_t num_servers;
+ grpc_grpclb_server **servers;
+} 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 = *arg;
+ if (dec_arg->first_pass != 0) { /* first pass */
+ grpc_grpclb_server server;
+ if (!pb_decode(stream, grpc_lb_v0_Server_fields, &server)) {
+ return false;
+ }
+ dec_arg->num_servers++;
+ } else { /* second pass */
+ grpc_grpclb_server *server = gpr_malloc(sizeof(grpc_grpclb_server));
+ GPR_ASSERT(dec_arg->num_servers > 0);
+ if (dec_arg->i == 0) { /* first iteration of second pass */
+ dec_arg->servers =
+ gpr_malloc(sizeof(grpc_grpclb_server *) * dec_arg->num_servers);
+ }
+ if (!pb_decode(stream, grpc_lb_v0_Server_fields, server)) {
+ return false;
+ }
+ dec_arg->servers[dec_arg->i++] = server;
+ }
+
+ return true;
+}
+
+grpc_grpclb_request *grpc_grpclb_request_create(const char *lb_service_name) {
+ grpc_grpclb_request *req = gpr_malloc(sizeof(grpc_grpclb_request));
+
+ req->has_client_stats = 0; /* TODO(dgq): add support for stats once defined */
+ req->has_initial_request = 1;
+ req->initial_request.has_name = 1;
+ strncpy(req->initial_request.name, lb_service_name,
+ GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH);
+ return req;
+}
+
+gpr_slice grpc_grpclb_request_encode(const grpc_grpclb_request *request) {
+ size_t encoded_length;
+ pb_ostream_t sizestream;
+ pb_ostream_t outputstream;
+ gpr_slice slice;
+ memset(&sizestream, 0, sizeof(pb_ostream_t));
+ pb_encode(&sizestream, grpc_lb_v0_LoadBalanceRequest_fields, request);
+ encoded_length = sizestream.bytes_written;
+
+ slice = gpr_slice_malloc(encoded_length);
+ outputstream =
+ pb_ostream_from_buffer(GPR_SLICE_START_PTR(slice), encoded_length);
+ GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v0_LoadBalanceRequest_fields,
+ request) != 0);
+ return slice;
+}
+
+void grpc_grpclb_request_destroy(grpc_grpclb_request *request) {
+ gpr_free(request);
+}
+
+grpc_grpclb_response *grpc_grpclb_response_parse(gpr_slice encoded_response) {
+ bool status;
+ pb_istream_t stream =
+ pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_response),
+ GPR_SLICE_LENGTH(encoded_response));
+ grpc_grpclb_response *res = gpr_malloc(sizeof(grpc_grpclb_response));
+ memset(res, 0, sizeof(*res));
+ status = pb_decode(&stream, grpc_lb_v0_LoadBalanceResponse_fields, res);
+ GPR_ASSERT(status == true);
+ return res;
+}
+
+grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
+ gpr_slice encoded_response) {
+ grpc_grpclb_serverlist *sl = gpr_malloc(sizeof(grpc_grpclb_serverlist));
+ bool status;
+ decode_serverlist_arg arg;
+ pb_istream_t stream =
+ pb_istream_from_buffer(GPR_SLICE_START_PTR(encoded_response),
+ GPR_SLICE_LENGTH(encoded_response));
+ pb_istream_t stream_at_start = stream;
+ grpc_grpclb_response *res = gpr_malloc(sizeof(grpc_grpclb_response));
+ memset(res, 0, sizeof(*res));
+ memset(&arg, 0, sizeof(decode_serverlist_arg));
+
+ res->server_list.servers.funcs.decode = decode_serverlist;
+ res->server_list.servers.arg = &arg;
+ arg.first_pass = 1;
+ status = pb_decode(&stream, grpc_lb_v0_LoadBalanceResponse_fields, res);
+ GPR_ASSERT(status == true);
+ GPR_ASSERT(arg.num_servers > 0);
+
+ arg.first_pass = 0;
+ status =
+ pb_decode(&stream_at_start, grpc_lb_v0_LoadBalanceResponse_fields, res);
+ GPR_ASSERT(status == true);
+ GPR_ASSERT(arg.servers != NULL);
+
+ sl->num_servers = arg.num_servers;
+ sl->servers = arg.servers;
+ if (res->server_list.has_expiration_interval) {
+ sl->expiration_interval = res->server_list.expiration_interval;
+ }
+ grpc_grpclb_response_destroy(res);
+ return sl;
+}
+
+void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist) {
+ size_t i;
+ for (i = 0; i < serverlist->num_servers; i++) {
+ gpr_free(serverlist->servers[i]);
+ }
+ gpr_free(serverlist->servers);
+ gpr_free(serverlist);
+}
+
+void grpc_grpclb_response_destroy(grpc_grpclb_response *response) {
+ gpr_free(response);
+}
diff --git a/src/core/lib/client_config/lb_policies/load_balancer_api.h b/src/core/lib/client_config/lb_policies/load_balancer_api.h
new file mode 100644
index 0000000000..83299adfa9
--- /dev/null
+++ b/src/core/lib/client_config/lb_policies/load_balancer_api.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_LOAD_BALANCER_API_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_LOAD_BALANCER_API_H
+
+#include <grpc/support/slice_buffer.h>
+
+#include "src/core/lib/client_config/lb_policy_factory.h"
+#include "src/core/lib/proto/grpc/lb/v0/load_balancer.pb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH 128
+
+typedef grpc_lb_v0_LoadBalanceRequest grpc_grpclb_request;
+typedef grpc_lb_v0_LoadBalanceResponse grpc_grpclb_response;
+typedef grpc_lb_v0_Server grpc_grpclb_server;
+typedef grpc_lb_v0_Duration grpc_grpclb_duration;
+typedef struct grpc_grpclb_serverlist {
+ grpc_grpclb_server **servers;
+ size_t num_servers;
+ grpc_grpclb_duration expiration_interval;
+} 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);
+
+/** Protocol Buffers v3-encode \a request */
+gpr_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_response as a \a
+ * grpc_grpclb_response */
+grpc_grpclb_response *grpc_grpclb_response_parse(gpr_slice encoded_response);
+
+/** Destroy \a serverlist */
+void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist);
+
+/** Parse the list of servers from an encoded \a grpc_grpclb_response */
+grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist(
+ gpr_slice encoded_response);
+
+/** Destroy \a response */
+void grpc_grpclb_response_destroy(grpc_grpclb_response *response);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_LOAD_BALANCER_API_H */
diff --git a/src/core/lib/client_config/lb_policies/pick_first.c b/src/core/lib/client_config/lb_policies/pick_first.c
new file mode 100644
index 0000000000..2e399b73f9
--- /dev/null
+++ b/src/core/lib/client_config/lb_policies/pick_first.c
@@ -0,0 +1,421 @@
+/*
+ *
+ * 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/lib/client_config/lb_policies/pick_first.h"
+#include "src/core/lib/client_config/lb_policy_factory.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include "src/core/lib/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) {
+ if (args->num_subchannels == 0) return NULL;
+ pick_first_lb_policy *p = gpr_malloc(sizeof(*p));
+ 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/src/core/lib/client_config/lb_policies/pick_first.h b/src/core/lib/client_config/lb_policies/pick_first.h
new file mode 100644
index 0000000000..dba86ea7ad
--- /dev/null
+++ b/src/core/lib/client_config/lb_policies/pick_first.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_PICK_FIRST_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_PICK_FIRST_H
+
+#include "src/core/lib/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 /* GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_PICK_FIRST_H */
diff --git a/src/core/lib/client_config/lb_policies/round_robin.c b/src/core/lib/client_config/lb_policies/round_robin.c
new file mode 100644
index 0000000000..c904c5f921
--- /dev/null
+++ b/src/core/lib/client_config/lb_policies/round_robin.c
@@ -0,0 +1,542 @@
+/*
+ *
+ * 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/lib/client_config/lb_policies/round_robin.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include "src/core/lib/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/src/core/lib/client_config/lb_policies/round_robin.h b/src/core/lib/client_config/lb_policies/round_robin.h
new file mode 100644
index 0000000000..52db1caa0c
--- /dev/null
+++ b/src/core/lib/client_config/lb_policies/round_robin.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_ROUND_ROBIN_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_ROUND_ROBIN_H
+
+#include "src/core/lib/client_config/lb_policy.h"
+
+extern int grpc_lb_round_robin_trace;
+
+#include "src/core/lib/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 /* GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICIES_ROUND_ROBIN_H */
diff --git a/src/core/lib/client_config/lb_policy.c b/src/core/lib/client_config/lb_policy.c
new file mode 100644
index 0000000000..ee20ccd76a
--- /dev/null
+++ b/src/core/lib/client_config/lb_policy.c
@@ -0,0 +1,134 @@
+/*
+ *
+ * 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/lib/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);
+ policy->interested_parties = grpc_pollset_set_create();
+}
+
+#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/src/core/lib/client_config/lb_policy.h b/src/core/lib/client_config/lb_policy.h
new file mode 100644
index 0000000000..58a0a04d85
--- /dev/null
+++ b/src/core/lib/client_config/lb_policy.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_LB_POLICY_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICY_H
+
+#include "src/core/lib/client_config/subchannel.h"
+#include "src/core/lib/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;
+ /* owned pointer to interested parties in load balancing decisions */
+ 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_CORE_LIB_CLIENT_CONFIG_LB_POLICY_H */
diff --git a/src/core/lib/client_config/lb_policy_factory.c b/src/core/lib/client_config/lb_policy_factory.c
new file mode 100644
index 0000000000..2ca6f42f89
--- /dev/null
+++ b/src/core/lib/client_config/lb_policy_factory.c
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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/lib/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/src/core/lib/client_config/lb_policy_factory.h b/src/core/lib/client_config/lb_policy_factory.h
new file mode 100644
index 0000000000..36eaf178d9
--- /dev/null
+++ b/src/core/lib/client_config/lb_policy_factory.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_LB_POLICY_FACTORY_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICY_FACTORY_H
+
+#include "src/core/lib/client_config/lb_policy.h"
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_LB_POLICY_FACTORY_H */
diff --git a/src/core/lib/client_config/lb_policy_registry.c b/src/core/lib/client_config/lb_policy_registry.c
new file mode 100644
index 0000000000..13acfe78cd
--- /dev/null
+++ b/src/core/lib/client_config/lb_policy_registry.c
@@ -0,0 +1,88 @@
+/*
+ *
+ * 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/lib/client_config/lb_policy_registry.h"
+
+#include <string.h>
+
+#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/src/core/lib/client_config/lb_policy_registry.h b/src/core/lib/client_config/lb_policy_registry.h
new file mode 100644
index 0000000000..c251fd9f08
--- /dev/null
+++ b/src/core/lib/client_config/lb_policy_registry.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_LB_POLICY_REGISTRY_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_LB_POLICY_REGISTRY_H
+
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_LB_POLICY_REGISTRY_H */
diff --git a/src/core/lib/client_config/resolver.c b/src/core/lib/client_config/resolver.c
new file mode 100644
index 0000000000..32f0643adb
--- /dev/null
+++ b/src/core/lib/client_config/resolver.c
@@ -0,0 +1,82 @@
+/*
+ *
+ * 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/lib/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/src/core/lib/client_config/resolver.h b/src/core/lib/client_config/resolver.h
new file mode 100644
index 0000000000..1ee879293a
--- /dev/null
+++ b/src/core/lib/client_config/resolver.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_RESOLVER_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVER_H
+
+#include "src/core/lib/client_config/client_config.h"
+#include "src/core/lib/client_config/subchannel.h"
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_RESOLVER_H */
diff --git a/src/core/lib/client_config/resolver_factory.c b/src/core/lib/client_config/resolver_factory.c
new file mode 100644
index 0000000000..0f76c664fa
--- /dev/null
+++ b/src/core/lib/client_config/resolver_factory.c
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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/lib/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/src/core/lib/client_config/resolver_factory.h b/src/core/lib/client_config/resolver_factory.h
new file mode 100644
index 0000000000..7765c3c844
--- /dev/null
+++ b/src/core/lib/client_config/resolver_factory.h
@@ -0,0 +1,82 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_RESOLVER_FACTORY_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVER_FACTORY_H
+
+#include "src/core/lib/client_config/resolver.h"
+#include "src/core/lib/client_config/subchannel_factory.h"
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_RESOLVER_FACTORY_H */
diff --git a/src/core/lib/client_config/resolver_registry.c b/src/core/lib/client_config/resolver_registry.c
new file mode 100644
index 0000000000..29bd00c284
--- /dev/null
+++ b/src/core/lib/client_config/resolver_registry.c
@@ -0,0 +1,137 @@
+/*
+ *
+ * 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/lib/client_config/resolver_registry.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#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/src/core/lib/client_config/resolver_registry.h b/src/core/lib/client_config/resolver_registry.h
new file mode 100644
index 0000000000..22289ca6bd
--- /dev/null
+++ b/src/core/lib/client_config/resolver_registry.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_RESOLVER_REGISTRY_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVER_REGISTRY_H
+
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_RESOLVER_REGISTRY_H */
diff --git a/src/core/lib/client_config/resolvers/dns_resolver.c b/src/core/lib/client_config/resolvers/dns_resolver.c
new file mode 100644
index 0000000000..ab445730ad
--- /dev/null
+++ b/src/core/lib/client_config/resolvers/dns_resolver.c
@@ -0,0 +1,309 @@
+/*
+ *
+ * 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/lib/client_config/resolvers/dns_resolver.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/client_config/lb_policy_registry.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/support/backoff.h"
+#include "src/core/lib/support/string.h"
+
+#define BACKOFF_MULTIPLIER 1.6
+#define BACKOFF_JITTER 0.2
+#define BACKOFF_MIN_SECONDS 1
+#define BACKOFF_MAX_SECONDS 120
+
+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;
+ /** retry timer */
+ bool have_retry_timer;
+ grpc_timer retry_timer;
+ /** retry backoff state */
+ gpr_backoff backoff_state;
+} 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->have_retry_timer) {
+ grpc_timer_cancel(exec_ctx, &r->retry_timer);
+ }
+ 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) {
+ gpr_backoff_reset(&r->backoff_state);
+ 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) {
+ gpr_backoff_reset(&r->backoff_state);
+ dns_start_resolving_locked(r);
+ } else {
+ dns_maybe_finish_next_locked(exec_ctx, r);
+ }
+ gpr_mu_unlock(&r->mu);
+}
+
+static void dns_on_retry_timer(grpc_exec_ctx *exec_ctx, void *arg,
+ bool success) {
+ dns_resolver *r = arg;
+
+ gpr_mu_lock(&r->mu);
+ r->have_retry_timer = false;
+ if (success) {
+ if (!r->resolving) {
+ dns_start_resolving_locked(r);
+ }
+ }
+ gpr_mu_unlock(&r->mu);
+
+ GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer");
+}
+
+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;
+ gpr_mu_lock(&r->mu);
+ GPR_ASSERT(r->resolving);
+ r->resolving = 0;
+ if (addresses != NULL) {
+ grpc_lb_policy_args lb_policy_args;
+ config = grpc_client_config_create();
+ subchannels = gpr_malloc(sizeof(grpc_subchannel *) * addresses->naddrs);
+ size_t naddrs = 0;
+ 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;
+ grpc_subchannel *subchannel = grpc_subchannel_factory_create_subchannel(
+ exec_ctx, r->subchannel_factory, &args);
+ if (subchannel != NULL) {
+ subchannels[naddrs++] = subchannel;
+ }
+ }
+ memset(&lb_policy_args, 0, sizeof(lb_policy_args));
+ lb_policy_args.subchannels = subchannels;
+ lb_policy_args.num_subchannels = naddrs;
+ lb_policy = grpc_lb_policy_create(r->lb_policy_name, &lb_policy_args);
+ if (lb_policy != NULL) {
+ 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);
+ } else {
+ gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC);
+ gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now);
+ gpr_timespec timeout = gpr_time_sub(next_try, now);
+ gpr_log(GPR_DEBUG, "dns resolution failed: retrying in %d.%09d seconds",
+ timeout.tv_sec, timeout.tv_nsec);
+ GPR_ASSERT(!r->have_retry_timer);
+ r->have_retry_timer = true;
+ GRPC_RESOLVER_REF(&r->base, "retry-timer");
+ grpc_timer_init(exec_ctx, &r->retry_timer, next_try, dns_on_retry_timer, r,
+ now);
+ }
+ 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;
+ gpr_backoff_init(&r->backoff_state, BACKOFF_MULTIPLIER, BACKOFF_JITTER,
+ BACKOFF_MIN_SECONDS * 1000, BACKOFF_MAX_SECONDS * 1000);
+ 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/src/core/lib/client_config/resolvers/dns_resolver.h b/src/core/lib/client_config/resolvers/dns_resolver.h
new file mode 100644
index 0000000000..eb46e41c77
--- /dev/null
+++ b/src/core/lib/client_config/resolvers/dns_resolver.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H
+
+#include "src/core/lib/client_config/resolver_factory.h"
+
+/** Create a dns resolver factory */
+grpc_resolver_factory *grpc_dns_resolver_factory_create(void);
+
+#endif /* GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVERS_DNS_RESOLVER_H */
diff --git a/src/core/lib/client_config/resolvers/sockaddr_resolver.c b/src/core/lib/client_config/resolvers/sockaddr_resolver.c
new file mode 100644
index 0000000000..66cddc3ed9
--- /dev/null
+++ b/src/core/lib/client_config/resolvers/sockaddr_resolver.c
@@ -0,0 +1,372 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include "src/core/lib/client_config/resolvers/sockaddr_resolver.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/client_config/lb_policy_registry.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/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);
+}
+
+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, prefix) \
+ static grpc_resolver *name##_factory_create_resolver( \
+ grpc_resolver_factory *factory, grpc_resolver_args *args) { \
+ return sockaddr_create(args, "pick_first", prefix##parse_##name); \
+ } \
+ static const grpc_resolver_factory_vtable name##_factory_vtable = { \
+ sockaddr_factory_ref, sockaddr_factory_unref, \
+ name##_factory_create_resolver, prefix##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_HAVE_UNIX_SOCKET
+DECL_FACTORY(unix, grpc_)
+#endif
+DECL_FACTORY(ipv4, ) DECL_FACTORY(ipv6, )
diff --git a/src/core/lib/client_config/resolvers/sockaddr_resolver.h b/src/core/lib/client_config/resolvers/sockaddr_resolver.h
new file mode 100644
index 0000000000..45c55bd160
--- /dev/null
+++ b/src/core/lib/client_config/resolvers/sockaddr_resolver.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_RESOLVERS_SOCKADDR_RESOLVER_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVERS_SOCKADDR_RESOLVER_H
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_RESOLVERS_SOCKADDR_RESOLVER_H */
diff --git a/src/core/lib/client_config/resolvers/zookeeper_resolver.c b/src/core/lib/client_config/resolvers/zookeeper_resolver.c
new file mode 100644
index 0000000000..3bb0bbdf5c
--- /dev/null
+++ b/src/core/lib/client_config/resolvers/zookeeper_resolver.c
@@ -0,0 +1,520 @@
+/*
+ *
+ * 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/lib/client_config/resolvers/zookeeper_resolver.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+#include <grpc/grpc_zookeeper.h>
+#include <zookeeper/zookeeper.h>
+
+#include "src/core/lib/client_config/lb_policy_registry.h"
+#include "src/core/lib/client_config/resolver_registry.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/json/json.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/surface/api_trace.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/src/core/lib/client_config/resolvers/zookeeper_resolver.h b/src/core/lib/client_config/resolvers/zookeeper_resolver.h
new file mode 100644
index 0000000000..7ee7604360
--- /dev/null
+++ b/src/core/lib/client_config/resolvers/zookeeper_resolver.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H
+
+#include "src/core/lib/client_config/resolver_factory.h"
+
+/** Create a zookeeper resolver factory */
+grpc_resolver_factory *grpc_zookeeper_resolver_factory_create(void);
+
+#endif /* GRPC_CORE_LIB_CLIENT_CONFIG_RESOLVERS_ZOOKEEPER_RESOLVER_H */
diff --git a/src/core/lib/client_config/subchannel.c b/src/core/lib/client_config/subchannel.c
new file mode 100644
index 0000000000..41242f0dd7
--- /dev/null
+++ b/src/core/lib/client_config/subchannel.c
@@ -0,0 +1,678 @@
+/*
+ *
+ * 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/lib/client_config/subchannel.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/avl.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/client_channel.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/client_config/initial_connect_string.h"
+#include "src/core/lib/client_config/subchannel_index.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/backoff.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/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;
+
+ grpc_subchannel_key *key;
+
+ /** 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;
+ /** backoff state */
+ gpr_backoff backoff_state;
+ /** 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 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);
+ grpc_subchannel_key_destroy(exec_ctx, c->key);
+ 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;
+}
+
+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 NULL;
+ 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 NULL;
+ }
+ }
+}
+
+static void disconnect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) {
+ grpc_connected_subchannel *con;
+ grpc_subchannel_index_unregister(exec_ctx, c->key, c);
+ 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_exec_ctx *exec_ctx,
+ grpc_connector *connector,
+ grpc_subchannel_args *args) {
+ grpc_subchannel_key *key = grpc_subchannel_key_create(connector, args);
+ grpc_subchannel *c = grpc_subchannel_index_find(exec_ctx, key);
+ if (c) {
+ grpc_subchannel_key_destroy(exec_ctx, key);
+ return c;
+ }
+
+ c = gpr_malloc(sizeof(*c));
+ memset(c, 0, 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 = 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);
+ c->pollset_set = grpc_pollset_set_create();
+ 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_backoff_init(&c->backoff_state,
+ GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER,
+ GRPC_SUBCHANNEL_RECONNECT_JITTER,
+ GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000,
+ GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000);
+ if (c->args) {
+ for (size_t 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);
+ gpr_backoff_init(&c->backoff_state, 1.0, 0.0,
+ c->args->args[i].value.integer,
+ c->args->args[i].value.integer);
+ }
+ }
+ }
+ gpr_mu_init(&c->mu);
+
+ return grpc_subchannel_index_register(exec_ctx, key, 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 = c->next_attempt;
+ 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->next_attempt =
+ gpr_backoff_begin(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC));
+ 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) {
+ 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)) {
+ c->connecting = 1;
+ /* released by connection */
+ GRPC_SUBCHANNEL_WEAK_REF(c, "connecting");
+ start_connect(exec_ctx, c);
+ }
+ gpr_mu_unlock(&c->mu);
+ }
+}
+
+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_locked(grpc_exec_ctx *exec_ctx,
+ grpc_subchannel *c) {
+ grpc_connected_subchannel *con;
+ grpc_channel_stack *stk;
+ state_watcher *sw_subchannel;
+
+ /* construct channel stack */
+ con = grpc_channel_init_create_stack(
+ exec_ctx, GRPC_CLIENT_SUBCHANNEL, 0, c->connecting_result.channel_args, 1,
+ connection_destroy, NULL, c->connecting_result.transport);
+ stk = CHANNEL_STACK_FROM_CONNECTION(con);
+ 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);
+
+ if (c->disconnected) {
+ gpr_free(sw_subchannel);
+ 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");
+}
+
+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;
+ }
+ if (iomgr_success) {
+ c->next_attempt =
+ gpr_backoff_step(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC));
+ continue_connect(exec_ctx, c);
+ gpr_mu_unlock(&c->mu);
+ } else {
+ gpr_mu_unlock(&c->mu);
+ 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;
+
+ GRPC_SUBCHANNEL_WEAK_REF(c, "connected");
+ gpr_mu_lock(&c->mu);
+ if (c->connecting_result.transport != NULL) {
+ publish_transport_locked(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_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);
+ GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting");
+}
+
+/*
+ * 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/src/core/lib/client_config/subchannel.h b/src/core/lib/client_config/subchannel.h
new file mode 100644
index 0000000000..b4f545be52
--- /dev/null
+++ b/src/core/lib/client_config/subchannel.h
@@ -0,0 +1,174 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_H
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/client_config/connector.h"
+#include "src/core/lib/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_REF_FROM_WEAK_REF(p, r) \
+ grpc_subchannel_ref_from_weak_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_REF_FROM_WEAK_REF(p, r) \
+ grpc_subchannel_ref_from_weak_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
+
+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_exec_ctx *exec_ctx,
+ 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_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 {
+ /* 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;
+ /** Address to connect to */
+ struct sockaddr *addr;
+ size_t addr_len;
+};
+
+/** create a subchannel given a connector */
+grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx,
+ grpc_connector *connector,
+ grpc_subchannel_args *args);
+
+#endif /* GRPC_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_H */
diff --git a/src/core/lib/client_config/subchannel_factory.c b/src/core/lib/client_config/subchannel_factory.c
new file mode 100644
index 0000000000..727a48a6c8
--- /dev/null
+++ b/src/core/lib/client_config/subchannel_factory.c
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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/lib/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/src/core/lib/client_config/subchannel_factory.h b/src/core/lib/client_config/subchannel_factory.h
new file mode 100644
index 0000000000..3ba2f860fe
--- /dev/null
+++ b/src/core/lib/client_config/subchannel_factory.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/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_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_FACTORY_H */
diff --git a/src/core/lib/client_config/subchannel_index.c b/src/core/lib/client_config/subchannel_index.c
new file mode 100644
index 0000000000..2c545002a2
--- /dev/null
+++ b/src/core/lib/client_config/subchannel_index.c
@@ -0,0 +1,262 @@
+//
+//
+// 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 "src/core/lib/client_config/subchannel_index.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/avl.h>
+#include <grpc/support/tls.h>
+
+#include "src/core/lib/channel/channel_args.h"
+
+// a map of subchannel_key --> subchannel, used for detecting connections
+// to the same destination in order to share them
+static gpr_avl g_subchannel_index;
+
+static gpr_mu g_mu;
+
+struct grpc_subchannel_key {
+ grpc_connector *connector;
+ grpc_subchannel_args args;
+};
+
+GPR_TLS_DECL(subchannel_index_exec_ctx);
+
+static void enter_ctx(grpc_exec_ctx *exec_ctx) {
+ GPR_ASSERT(gpr_tls_get(&subchannel_index_exec_ctx) == 0);
+ gpr_tls_set(&subchannel_index_exec_ctx, (intptr_t)exec_ctx);
+}
+
+static void leave_ctx(grpc_exec_ctx *exec_ctx) {
+ GPR_ASSERT(gpr_tls_get(&subchannel_index_exec_ctx) == (intptr_t)exec_ctx);
+ gpr_tls_set(&subchannel_index_exec_ctx, 0);
+}
+
+static grpc_exec_ctx *current_ctx() {
+ grpc_exec_ctx *c = (grpc_exec_ctx *)gpr_tls_get(&subchannel_index_exec_ctx);
+ GPR_ASSERT(c != NULL);
+ return c;
+}
+
+static grpc_subchannel_key *create_key(
+ grpc_connector *connector, grpc_subchannel_args *args,
+ grpc_channel_args *(*copy_channel_args)(const grpc_channel_args *args)) {
+ grpc_subchannel_key *k = gpr_malloc(sizeof(*k));
+ k->connector = grpc_connector_ref(connector);
+ k->args.filter_count = args->filter_count;
+ k->args.filters = gpr_malloc(sizeof(*k->args.filters) * k->args.filter_count);
+ memcpy((grpc_channel_filter *)k->args.filters, args->filters,
+ sizeof(*k->args.filters) * k->args.filter_count);
+ k->args.addr_len = args->addr_len;
+ k->args.addr = gpr_malloc(args->addr_len);
+ memcpy(k->args.addr, args->addr, k->args.addr_len);
+ k->args.args = copy_channel_args(args->args);
+ return k;
+}
+
+grpc_subchannel_key *grpc_subchannel_key_create(grpc_connector *connector,
+ grpc_subchannel_args *args) {
+ return create_key(connector, args, grpc_channel_args_normalize);
+}
+
+static grpc_subchannel_key *subchannel_key_copy(grpc_subchannel_key *k) {
+ return create_key(k->connector, &k->args, grpc_channel_args_copy);
+}
+
+static int subchannel_key_compare(grpc_subchannel_key *a,
+ grpc_subchannel_key *b) {
+ int c = GPR_ICMP(a->connector, b->connector);
+ if (c != 0) return c;
+ c = GPR_ICMP(a->args.addr_len, b->args.addr_len);
+ if (c != 0) return c;
+ c = GPR_ICMP(a->args.filter_count, b->args.filter_count);
+ if (c != 0) return c;
+ c = memcmp(a->args.addr, b->args.addr, a->args.addr_len);
+ if (c != 0) return c;
+ 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_exec_ctx *exec_ctx,
+ grpc_subchannel_key *k) {
+ grpc_connector_unref(exec_ctx, k->connector);
+ gpr_free(k->args.addr);
+ gpr_free((grpc_channel_args *)k->args.filters);
+ grpc_channel_args_destroy((grpc_channel_args *)k->args.args);
+ gpr_free(k);
+}
+
+static void sck_avl_destroy(void *p) {
+ grpc_subchannel_key_destroy(current_ctx(), p);
+}
+
+static void *sck_avl_copy(void *p) { return subchannel_key_copy(p); }
+
+static long sck_avl_compare(void *a, void *b) {
+ return subchannel_key_compare(a, b);
+}
+
+static void scv_avl_destroy(void *p) {
+ GRPC_SUBCHANNEL_WEAK_UNREF(current_ctx(), p, "subchannel_index");
+}
+
+static void *scv_avl_copy(void *p) {
+ GRPC_SUBCHANNEL_WEAK_REF(p, "subchannel_index");
+ return p;
+}
+
+static const gpr_avl_vtable subchannel_avl_vtable = {
+ .destroy_key = sck_avl_destroy,
+ .copy_key = sck_avl_copy,
+ .compare_keys = sck_avl_compare,
+ .destroy_value = scv_avl_destroy,
+ .copy_value = scv_avl_copy};
+
+void grpc_subchannel_index_init(void) {
+ g_subchannel_index = gpr_avl_create(&subchannel_avl_vtable);
+ gpr_mu_init(&g_mu);
+ gpr_tls_init(&subchannel_index_exec_ctx);
+}
+
+void grpc_subchannel_index_shutdown(void) {
+ gpr_mu_destroy(&g_mu);
+ gpr_avl_unref(g_subchannel_index);
+ gpr_tls_destroy(&subchannel_index_exec_ctx);
+}
+
+grpc_subchannel *grpc_subchannel_index_find(grpc_exec_ctx *exec_ctx,
+ grpc_subchannel_key *key) {
+ enter_ctx(exec_ctx);
+
+ // 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);
+ gpr_avl index = gpr_avl_ref(g_subchannel_index);
+ gpr_mu_unlock(&g_mu);
+
+ grpc_subchannel *c =
+ GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(gpr_avl_get(index, key), "index_find");
+ gpr_avl_unref(index);
+
+ leave_ctx(exec_ctx);
+ return c;
+}
+
+grpc_subchannel *grpc_subchannel_index_register(grpc_exec_ctx *exec_ctx,
+ grpc_subchannel_key *key,
+ grpc_subchannel *constructed) {
+ enter_ctx(exec_ctx);
+
+ grpc_subchannel *c = NULL;
+
+ while (c == NULL) {
+ // Compare and swap loop:
+ // - take a reference to the current index
+ gpr_mu_lock(&g_mu);
+ gpr_avl index = gpr_avl_ref(g_subchannel_index);
+ gpr_mu_unlock(&g_mu);
+
+ // - Check to see if a subchannel already exists
+ c = gpr_avl_get(index, key);
+ if (c != NULL) {
+ // yes -> we're done
+ GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, constructed, "index_register");
+ } else {
+ // no -> update the avl and compare/swap
+ gpr_avl updated =
+ gpr_avl_add(gpr_avl_ref(index), subchannel_key_copy(key),
+ GRPC_SUBCHANNEL_WEAK_REF(constructed, "index_register"));
+
+ // 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(gpr_avl, updated, g_subchannel_index);
+ c = constructed;
+ }
+ gpr_mu_unlock(&g_mu);
+
+ gpr_avl_unref(updated);
+ }
+ gpr_avl_unref(index);
+ }
+
+ leave_ctx(exec_ctx);
+
+ return c;
+}
+
+void grpc_subchannel_index_unregister(grpc_exec_ctx *exec_ctx,
+ grpc_subchannel_key *key,
+ grpc_subchannel *constructed) {
+ enter_ctx(exec_ctx);
+
+ bool done = false;
+ while (!done) {
+ // Compare and swap loop:
+ // - take a reference to the current index
+ gpr_mu_lock(&g_mu);
+ gpr_avl index = gpr_avl_ref(g_subchannel_index);
+ gpr_mu_unlock(&g_mu);
+
+ // Check to see if this key still refers to the previously
+ // registered subchannel
+ grpc_subchannel *c = gpr_avl_get(index, key);
+ if (c != constructed) {
+ gpr_avl_unref(index);
+ break;
+ }
+
+ // compare and swap the update (some other thread may have
+ // mutated the index behind us)
+ gpr_avl updated = gpr_avl_remove(gpr_avl_ref(index), key);
+
+ gpr_mu_lock(&g_mu);
+ if (index.root == g_subchannel_index.root) {
+ GPR_SWAP(gpr_avl, updated, g_subchannel_index);
+ done = true;
+ }
+ gpr_mu_unlock(&g_mu);
+
+ gpr_avl_unref(updated);
+ gpr_avl_unref(index);
+ }
+
+ leave_ctx(exec_ctx);
+}
diff --git a/src/core/lib/client_config/subchannel_index.h b/src/core/lib/client_config/subchannel_index.h
new file mode 100644
index 0000000000..bc5f03beb4
--- /dev/null
+++ b/src/core/lib/client_config/subchannel_index.h
@@ -0,0 +1,77 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_INDEX_H
+#define GRPC_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_INDEX_H
+
+#include "src/core/lib/client_config/connector.h"
+#include "src/core/lib/client_config/subchannel.h"
+
+/** \file Provides an index of active subchannels so that they can be
+ shared amongst channels */
+
+typedef struct grpc_subchannel_key grpc_subchannel_key;
+
+/** Create a key that can be used to uniquely identify a subchannel */
+grpc_subchannel_key *grpc_subchannel_key_create(grpc_connector *con,
+ grpc_subchannel_args *args);
+
+/** Destroy a subchannel key */
+void grpc_subchannel_key_destroy(grpc_exec_ctx *exec_ctx,
+ 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_exec_ctx *exec_ctx,
+ 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_exec_ctx *exec_ctx,
+ grpc_subchannel_key *key,
+ grpc_subchannel *constructed);
+
+/** Remove \a constructed as the registered subchannel for \a key. */
+void grpc_subchannel_index_unregister(grpc_exec_ctx *exec_ctx,
+ grpc_subchannel_key *key,
+ grpc_subchannel *constructed);
+
+/** Initialize the subchannel index (global) */
+void grpc_subchannel_index_init(void);
+/** Shutdown the subchannel index (global) */
+void grpc_subchannel_index_shutdown(void);
+
+#endif /* GRPC_CORE_LIB_CLIENT_CONFIG_SUBCHANNEL_INDEX_H */
diff --git a/src/core/lib/client_config/uri_parser.c b/src/core/lib/client_config/uri_parser.c
new file mode 100644
index 0000000000..d3228dec5f
--- /dev/null
+++ b/src/core/lib/client_config/uri_parser.c
@@ -0,0 +1,242 @@
+/*
+ *
+ * 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/lib/client_config/uri_parser.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/string_util.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,
+ 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/src/core/lib/client_config/uri_parser.h b/src/core/lib/client_config/uri_parser.h
new file mode 100644
index 0000000000..d70d451e60
--- /dev/null
+++ b/src/core/lib/client_config/uri_parser.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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_CORE_LIB_CLIENT_CONFIG_URI_PARSER_H
+#define GRPC_CORE_LIB_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 /* GRPC_CORE_LIB_CLIENT_CONFIG_URI_PARSER_H */
diff --git a/src/core/lib/compression/algorithm_metadata.h b/src/core/lib/compression/algorithm_metadata.h
new file mode 100644
index 0000000000..47f33abdc7
--- /dev/null
+++ b/src/core/lib/compression/algorithm_metadata.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H
+#define GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H
+
+#include <grpc/compression.h>
+#include "src/core/lib/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_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H */
diff --git a/src/core/lib/compression/compression_algorithm.c b/src/core/lib/compression/compression_algorithm.c
new file mode 100644
index 0000000000..f781b45042
--- /dev/null
+++ b/src/core/lib/compression/compression_algorithm.c
@@ -0,0 +1,203 @@
+/*
+ *
+ * Copyright 2015-2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/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, uint32_t accepted_encodings) {
+ GRPC_API_TRACE("grpc_compression_algorithm_for_level(level=%d)", 1,
+ ((int)level));
+ if (level > GRPC_COMPRESS_LEVEL_HIGH) {
+ gpr_log(GPR_ERROR, "Unknown compression level %d.", (int)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_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_compression_algorithm algos_ranking[] = {GRPC_COMPRESS_GZIP,
+ GRPC_COMPRESS_DEFLATE};
+
+ /* intersect algos_ranking with the supported ones keeping the ranked order */
+ grpc_compression_algorithm
+ sorted_supported_algos[GRPC_COMPRESS_ALGORITHMS_COUNT];
+ size_t algos_supported_idx = 0;
+ for (size_t i = 0; i < GPR_ARRAY_SIZE(algos_ranking); i++) {
+ const grpc_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();
+ };
+}
+
+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/src/core/lib/compression/message_compress.c b/src/core/lib/compression/message_compress.c
new file mode 100644
index 0000000000..b4b6a2d75e
--- /dev/null
+++ b/src/core/lib/compression/message_compress.c
@@ -0,0 +1,198 @@
+/*
+ *
+ * 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/lib/compression/message_compress.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include <zlib.h>
+
+#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/src/core/lib/compression/message_compress.h b/src/core/lib/compression/message_compress.h
new file mode 100644
index 0000000000..b71608139e
--- /dev/null
+++ b/src/core/lib/compression/message_compress.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * 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_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H
+#define GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H
+
+#include <grpc/compression.h>
+#include <grpc/support/slice_buffer.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_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_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H */
diff --git a/src/core/lib/debug/trace.c b/src/core/lib/debug/trace.c
new file mode 100644
index 0000000000..786dd9324f
--- /dev/null
+++ b/src/core/lib/debug/trace.c
@@ -0,0 +1,136 @@
+/*
+ *
+ * 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/lib/debug/trace.h"
+
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/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/src/core/lib/debug/trace.h b/src/core/lib/debug/trace.h
new file mode 100644
index 0000000000..76ea5a7c64
--- /dev/null
+++ b/src/core/lib/debug/trace.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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_CORE_LIB_DEBUG_TRACE_H
+#define GRPC_CORE_LIB_DEBUG_TRACE_H
+
+#include <grpc/support/port_platform.h>
+
+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_CORE_LIB_DEBUG_TRACE_H */
diff --git a/src/core/lib/http/format_request.c b/src/core/lib/http/format_request.c
new file mode 100644
index 0000000000..95b3918646
--- /dev/null
+++ b/src/core/lib/http/format_request.c
@@ -0,0 +1,120 @@
+/*
+ *
+ * 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/lib/http/format_request.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/support/string.h"
+
+static void fill_common_header(const grpc_httpcli_request *request,
+ gpr_strvec *buf) {
+ 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"));
+ 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"));
+ }
+}
+
+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->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", (unsigned long)body_size);
+ gpr_strvec_add(&out, tmp);
+ }
+ gpr_strvec_add(&out, gpr_strdup("\r\n"));
+ tmp = gpr_strvec_flatten(&out, &out_len);
+ gpr_strvec_destroy(&out);
+
+ if (body_bytes) {
+ tmp = gpr_realloc(tmp, out_len + body_size);
+ memcpy(tmp + out_len, body_bytes, body_size);
+ out_len += body_size;
+ }
+
+ return gpr_slice_new(tmp, out_len, gpr_free);
+}
diff --git a/src/core/lib/http/format_request.h b/src/core/lib/http/format_request.h
new file mode 100644
index 0000000000..2e933d804b
--- /dev/null
+++ b/src/core/lib/http/format_request.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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_CORE_LIB_HTTP_FORMAT_REQUEST_H
+#define GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H
+
+#include <grpc/support/slice.h>
+#include "src/core/lib/http/httpcli.h"
+
+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_CORE_LIB_HTTP_FORMAT_REQUEST_H */
diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c
new file mode 100644
index 0000000000..aab28ad8b6
--- /dev/null
+++ b/src/core/lib/http/httpcli.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * 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/lib/http/httpcli.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.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/tcp_client.h"
+#include "src/core/lib/support/string.h"
+
+typedef struct {
+ gpr_slice request_text;
+ grpc_http_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) {
+ 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(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.http.response : NULL);
+ grpc_http_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_http_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 {
+ int parse_success = grpc_http_parser_eof(&req->parser);
+ if (parse_success && (req->parser.type != GRPC_HTTP_RESPONSE)) {
+ parse_success = 0;
+ }
+ finish(exec_ctx, req, parse_success);
+ }
+}
+
+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_http_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->http.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->http.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/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h
new file mode 100644
index 0000000000..b8d54a8586
--- /dev/null
+++ b/src/core/lib/http/httpcli.h
@@ -0,0 +1,144 @@
+/*
+ *
+ * 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_CORE_LIB_HTTP_HTTPCLI_H
+#define GRPC_CORE_LIB_HTTP_HTTPCLI_H
+
+#include <stddef.h>
+
+#include <grpc/support/time.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/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)(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 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;
+
+/* 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_http_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_CORE_LIB_HTTP_HTTPCLI_H */
diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c
new file mode 100644
index 0000000000..6f1630ac1f
--- /dev/null
+++ b/src/core/lib/http/httpcli_security_connector.c
@@ -0,0 +1,188 @@
+/*
+ *
+ * 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/lib/http/httpcli.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/security/handshake.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/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_channel_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->base, true,
+ 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_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.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;
+ }
+ c->base.do_handshake = httpcli_ssl_do_handshake;
+ *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_channel_security_connector_do_handshake(
+ exec_ctx, sc, tcp, on_secure_transport_setup_done, c);
+ GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli");
+}
+
+const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake};
diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c
new file mode 100644
index 0000000000..5d4e304615
--- /dev/null
+++ b/src/core/lib/http/parser.c
@@ -0,0 +1,313 @@
+/*
+ *
+ * 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/lib/http/parser.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+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 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') 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->http.response.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 int 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) goto error;
+ parser->http.request.method = buf2str(beg, (size_t)(cur - beg - 1));
+
+ beg = cur;
+ while (cur != end && *cur++ != ' ')
+ ;
+ if (cur == end) goto error;
+ parser->http.request.path = buf2str(beg, (size_t)(cur - beg - 1));
+
+ 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;
+ vers_major = (uint8_t)(*cur++ - '1' + 1);
+ ++cur;
+ if (cur == end) goto error;
+ vers_minor = (uint8_t)(*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 {
+ goto error;
+ }
+ } else if (vers_major == 2) {
+ if (vers_minor == 0) {
+ parser->http.request.version = GRPC_HTTP_HTTP20;
+ } else {
+ goto error;
+ }
+ } else {
+ goto error;
+ }
+
+ return 1;
+
+error:
+ gpr_log(GPR_ERROR, "Failed parsing request line");
+ return 0;
+}
+
+static int handle_first_line(grpc_http_parser *parser) {
+ if (parser->cur_line[0] == 'H') {
+ parser->type = GRPC_HTTP_RESPONSE;
+ return handle_response_line(parser);
+ } else {
+ parser->type = GRPC_HTTP_REQUEST;
+ return handle_request_line(parser);
+ }
+}
+
+static int 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 = NULL;
+ grpc_http_header **hdrs = NULL;
+ grpc_http_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->type == GRPC_HTTP_RESPONSE) {
+ hdr_count = &parser->http.response.hdr_count;
+ hdrs = &parser->http.response.hdrs;
+ } else if (parser->type == GRPC_HTTP_REQUEST) {
+ hdr_count = &parser->http.request.hdr_count;
+ hdrs = &parser->http.request.hdrs;
+ } else {
+ return 0;
+ }
+
+ if (*hdr_count == parser->hdr_capacity) {
+ parser->hdr_capacity =
+ GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2);
+ *hdrs = gpr_realloc(*hdrs, parser->hdr_capacity * sizeof(**hdrs));
+ }
+ (*hdrs)[(*hdr_count)++] = hdr;
+ return 1;
+
+error:
+ gpr_free(hdr.key);
+ gpr_free(hdr.value);
+ return 0;
+}
+
+static int finish_line(grpc_http_parser *parser) {
+ switch (parser->state) {
+ case GRPC_HTTP_FIRST_LINE:
+ if (!handle_first_line(parser)) {
+ return 0;
+ }
+ parser->state = GRPC_HTTP_HEADERS;
+ break;
+ case GRPC_HTTP_HEADERS:
+ if (parser->cur_line_length == 2) {
+ parser->state = GRPC_HTTP_BODY;
+ break;
+ }
+ if (!add_header(parser)) {
+ return 0;
+ }
+ break;
+ case GRPC_HTTP_BODY:
+ GPR_UNREACHABLE_CODE(return 0);
+ }
+
+ parser->cur_line_length = 0;
+ return 1;
+}
+
+static int addbyte_body(grpc_http_parser *parser, uint8_t byte) {
+ size_t *body_length = NULL;
+ char **body = NULL;
+
+ 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 {
+ return 0;
+ }
+
+ if (*body_length == parser->body_capacity) {
+ parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2);
+ *body = gpr_realloc((void *)*body, parser->body_capacity);
+ }
+ (*body)[*body_length] = (char)byte;
+ (*body_length)++;
+
+ return 1;
+}
+
+static int addbyte(grpc_http_parser *parser, uint8_t byte) {
+ switch (parser->state) {
+ case GRPC_HTTP_FIRST_LINE:
+ case GRPC_HTTP_HEADERS:
+ if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) {
+ gpr_log(GPR_ERROR, "HTTP client max line length (%d) exceeded",
+ GRPC_HTTP_PARSER_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_HTTP_BODY:
+ return addbyte_body(parser, byte);
+ }
+ GPR_UNREACHABLE_CODE(return 0);
+}
+
+void grpc_http_parser_init(grpc_http_parser *parser) {
+ memset(parser, 0, sizeof(*parser));
+ parser->state = GRPC_HTTP_FIRST_LINE;
+ parser->type = GRPC_HTTP_UNKNOWN;
+}
+
+void grpc_http_parser_destroy(grpc_http_parser *parser) {
+ size_t i;
+ if (parser->type == GRPC_HTTP_RESPONSE) {
+ gpr_free(parser->http.response.body);
+ for (i = 0; i < parser->http.response.hdr_count; i++) {
+ gpr_free(parser->http.response.hdrs[i].key);
+ gpr_free(parser->http.response.hdrs[i].value);
+ }
+ gpr_free(parser->http.response.hdrs);
+ } else if (parser->type == GRPC_HTTP_REQUEST) {
+ gpr_free(parser->http.request.body);
+ for (i = 0; i < parser->http.request.hdr_count; i++) {
+ gpr_free(parser->http.request.hdrs[i].key);
+ gpr_free(parser->http.request.hdrs[i].value);
+ }
+ gpr_free(parser->http.request.hdrs);
+ gpr_free(parser->http.request.method);
+ gpr_free(parser->http.request.path);
+ }
+}
+
+int grpc_http_parser_parse(grpc_http_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_http_parser_eof(grpc_http_parser *parser) {
+ return parser->state == GRPC_HTTP_BODY;
+}
diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h
new file mode 100644
index 0000000000..6a72174aa6
--- /dev/null
+++ b/src/core/lib/http/parser.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * 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_CORE_LIB_HTTP_PARSER_H
+#define GRPC_CORE_LIB_HTTP_PARSER_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.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_UNKNOWN
+} 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;
+ /* Headers: count and key/values */
+ 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_response;
+
+typedef struct {
+ grpc_http_parser_state state;
+ grpc_http_type type;
+
+ union {
+ grpc_http_response response;
+ grpc_http_request request;
+ } http;
+ size_t body_capacity;
+ size_t hdr_capacity;
+
+ uint8_t cur_line[GRPC_HTTP_PARSER_MAX_HEADER_LENGTH];
+ size_t cur_line_length;
+} grpc_http_parser;
+
+void grpc_http_parser_init(grpc_http_parser *parser);
+void grpc_http_parser_destroy(grpc_http_parser *parser);
+
+int grpc_http_parser_parse(grpc_http_parser *parser, gpr_slice slice);
+int grpc_http_parser_eof(grpc_http_parser *parser);
+
+#endif /* GRPC_CORE_LIB_HTTP_PARSER_H */
diff --git a/src/core/lib/iomgr/closure.c b/src/core/lib/iomgr/closure.c
new file mode 100644
index 0000000000..724ebc284a
--- /dev/null
+++ b/src/core/lib/iomgr/closure.c
@@ -0,0 +1,98 @@
+/*
+ *
+ * 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/lib/iomgr/closure.h"
+
+#include <grpc/support/alloc.h>
+
+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/src/core/lib/iomgr/closure.h b/src/core/lib/iomgr/closure.h
new file mode 100644
index 0000000000..2597cf1706
--- /dev/null
+++ b/src/core/lib/iomgr/closure.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_CLOSURE_H
+#define GRPC_CORE_LIB_IOMGR_CLOSURE_H
+
+#include <grpc/support/port_platform.h>
+#include <stdbool.h>
+
+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_CORE_LIB_IOMGR_CLOSURE_H */
diff --git a/src/core/lib/iomgr/endpoint.c b/src/core/lib/iomgr/endpoint.c
new file mode 100644
index 0000000000..576b5a6e5c
--- /dev/null
+++ b/src/core/lib/iomgr/endpoint.c
@@ -0,0 +1,67 @@
+/*
+ *
+ * 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/lib/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/src/core/lib/iomgr/endpoint.h b/src/core/lib/iomgr/endpoint.h
new file mode 100644
index 0000000000..918e705fbd
--- /dev/null
+++ b/src/core/lib/iomgr/endpoint.h
@@ -0,0 +1,102 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_ENDPOINT_H
+#define GRPC_CORE_LIB_IOMGR_ENDPOINT_H
+
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+/* An endpoint caps a streaming channel between two communicating processes.
+ Examples may be: a tcp socket, <stdin+stdout>, 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_CORE_LIB_IOMGR_ENDPOINT_H */
diff --git a/src/core/lib/iomgr/endpoint_pair.h b/src/core/lib/iomgr/endpoint_pair.h
new file mode 100644
index 0000000000..bef8bb3518
--- /dev/null
+++ b/src/core/lib/iomgr/endpoint_pair.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_ENDPOINT_PAIR_H
+#define GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H
+
+#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,
+ size_t read_slice_size);
+
+#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H */
diff --git a/src/core/lib/iomgr/endpoint_pair_posix.c b/src/core/lib/iomgr/endpoint_pair_posix.c
new file mode 100644
index 0000000000..e0ce47c773
--- /dev/null
+++ b/src/core/lib/iomgr/endpoint_pair_posix.c
@@ -0,0 +1,83 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_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 <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/support/string.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]));
+ 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/src/core/lib/iomgr/endpoint_pair_windows.c b/src/core/lib/iomgr/endpoint_pair_windows.c
new file mode 100644
index 0000000000..cba18db81f
--- /dev/null
+++ b/src/core/lib/iomgr/endpoint_pair_windows.c
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+#include "src/core/lib/iomgr/endpoint_pair.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+#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, (struct sockaddr *)&addr, sizeof(addr)) !=
+ SOCKET_ERROR);
+ GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR);
+ GPR_ASSERT(getsockname(lst_sock, (struct sockaddr *)&addr, &addr_len) !=
+ SOCKET_ERROR);
+
+ cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+ WSA_FLAG_OVERLAPPED);
+ GPR_ASSERT(cli_sock != INVALID_SOCKET);
+
+ GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr *)&addr, addr_len, NULL,
+ NULL, NULL, NULL) == 0);
+ svr_sock = accept(lst_sock, (struct sockaddr *)&addr, &addr_len);
+ GPR_ASSERT(svr_sock != INVALID_SOCKET);
+
+ closesocket(lst_sock);
+ grpc_tcp_prepare_socket(cli_sock);
+ grpc_tcp_prepare_socket(svr_sock);
+
+ sv[1] = cli_sock;
+ sv[0] = svr_sock;
+}
+
+grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name,
+ size_t read_slice_size) {
+ SOCKET sv[2];
+ grpc_endpoint_pair p;
+ create_sockets(sv);
+ p.client = grpc_tcp_create(grpc_winsocket_create(sv[1], "endpoint:client"),
+ "endpoint:server");
+ p.server = grpc_tcp_create(grpc_winsocket_create(sv[0], "endpoint:server"),
+ "endpoint:client");
+ return p;
+}
+
+#endif
diff --git a/src/core/lib/iomgr/exec_ctx.c b/src/core/lib/iomgr/exec_ctx.c
new file mode 100644
index 0000000000..1ed6da623a
--- /dev/null
+++ b/src/core/lib/iomgr/exec_ctx.c
@@ -0,0 +1,151 @@
+/*
+ *
+ * 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/lib/iomgr/exec_ctx.h"
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+
+#include "src/core/lib/profiling/timers.h"
+
+#ifndef GRPC_EXECUTION_CONTEXT_SANITIZER
+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);
+}
+
+void grpc_exec_ctx_global_init(void) {}
+void grpc_exec_ctx_global_shutdown(void) {}
+#else
+static gpr_mu g_mu;
+static gpr_cv g_cv;
+static int g_threads = 0;
+
+static void run_closure(void *arg) {
+ grpc_closure *closure = arg;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ closure->cb(&exec_ctx, closure->cb_arg, (closure->final_data & 1) != 0);
+ grpc_exec_ctx_finish(&exec_ctx);
+ gpr_mu_lock(&g_mu);
+ if (--g_threads == 0) {
+ gpr_cv_signal(&g_cv);
+ }
+ gpr_mu_unlock(&g_mu);
+}
+
+static void start_closure(grpc_closure *closure) {
+ gpr_thd_id id;
+ gpr_mu_lock(&g_mu);
+ g_threads++;
+ gpr_mu_unlock(&g_mu);
+ gpr_thd_new(&id, run_closure, closure, NULL);
+}
+
+bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) { return false; }
+
+void grpc_exec_ctx_finish(grpc_exec_ctx *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);
+ if (closure == NULL) return;
+ closure->final_data = success;
+ start_closure(closure);
+}
+
+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);
+ if (list == NULL) return;
+ grpc_closure *p = list->head;
+ while (p) {
+ grpc_closure *start = p;
+ p = grpc_closure_next(start);
+ start_closure(start);
+ }
+ grpc_closure_list r = GRPC_CLOSURE_LIST_INIT;
+ *list = r;
+}
+
+void grpc_exec_ctx_global_init(void) {
+ gpr_mu_init(&g_mu);
+ gpr_cv_init(&g_cv);
+}
+
+void grpc_exec_ctx_global_shutdown(void) {
+ gpr_mu_lock(&g_mu);
+ while (g_threads != 0) {
+ gpr_cv_wait(&g_cv, &g_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
+ }
+ gpr_mu_unlock(&g_mu);
+
+ gpr_mu_destroy(&g_mu);
+ gpr_cv_destroy(&g_cv);
+}
+#endif
diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h
new file mode 100644
index 0000000000..e62ea2dedf
--- /dev/null
+++ b/src/core/lib/iomgr/exec_ctx.h
@@ -0,0 +1,98 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_EXEC_CTX_H
+#define GRPC_CORE_LIB_IOMGR_EXEC_CTX_H
+
+#include "src/core/lib/iomgr/closure.h"
+
+/* #define GRPC_EXECUTION_CONTEXT_SANITIZER 1 */
+
+/** 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;
+
+#ifndef GRPC_EXECUTION_CONTEXT_SANITIZER
+/** 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;
+};
+
+#define GRPC_EXEC_CTX_INIT \
+ { GRPC_CLOSURE_LIST_INIT }
+#else
+struct grpc_exec_ctx {
+ int unused;
+};
+#define GRPC_EXEC_CTX_INIT \
+ { 0 }
+#endif
+
+/** 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);
+
+void grpc_exec_ctx_global_init(void);
+void grpc_exec_ctx_global_shutdown(void);
+
+#endif /* GRPC_CORE_LIB_IOMGR_EXEC_CTX_H */
diff --git a/src/core/lib/iomgr/executor.c b/src/core/lib/iomgr/executor.c
new file mode 100644
index 0000000000..42a9db3cbb
--- /dev/null
+++ b/src/core/lib/iomgr/executor.c
@@ -0,0 +1,143 @@
+/*
+ *
+ * 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/lib/iomgr/executor.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include "src/core/lib/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/src/core/lib/iomgr/executor.h b/src/core/lib/iomgr/executor.h
new file mode 100644
index 0000000000..f1871416a0
--- /dev/null
+++ b/src/core/lib/iomgr/executor.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_EXECUTOR_H
+#define GRPC_CORE_LIB_IOMGR_EXECUTOR_H
+
+#include "src/core/lib/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_CORE_LIB_IOMGR_EXECUTOR_H */
diff --git a/src/core/lib/iomgr/fd_posix.c b/src/core/lib/iomgr/fd_posix.c
new file mode 100644
index 0000000000..72c924bdcb
--- /dev/null
+++ b/src/core/lib/iomgr/fd_posix.c
@@ -0,0 +1,454 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/fd_posix.h"
+
+#include <assert.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/pollset_posix.h"
+
+#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_mu_lock(&r->mu);
+ 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;
+ gpr_atm_rel_store(&r->refst, 1);
+ gpr_mu_unlock(&r->mu);
+
+ 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(&watcher->pollset->mu);
+ GPR_ASSERT(watcher->worker);
+ grpc_pollset_kick_ext(watcher->pollset, watcher->worker,
+ GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP);
+ gpr_mu_unlock(&watcher->pollset->mu);
+}
+
+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/src/core/lib/iomgr/fd_posix.h b/src/core/lib/iomgr/fd_posix.h
new file mode 100644
index 0000000000..69d09ef5e3
--- /dev/null
+++ b/src/core/lib/iomgr/fd_posix.h
@@ -0,0 +1,192 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_FD_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_FD_POSIX_H
+
+#include <grpc/support/atm.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/pollset.h"
+
+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_CORE_LIB_IOMGR_FD_POSIX_H */
diff --git a/src/core/lib/iomgr/iocp_windows.c b/src/core/lib/iomgr/iocp_windows.c
new file mode 100644
index 0000000000..682a32c0da
--- /dev/null
+++ b/src/core/lib/iomgr/iocp_windows.c
@@ -0,0 +1,208 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include <winsock2.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
+#include <grpc/support/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(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)));
+}
+
+grpc_iocp_work_status 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 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 {
+ 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);
+ 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_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_iocp_work_status work_status;
+
+ do {
+ work_status = grpc_iocp_work(&exec_ctx, gpr_inf_past(GPR_CLOCK_MONOTONIC));
+ } while (work_status == GRPC_IOCP_WORK_KICK ||
+ 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/src/core/lib/iomgr/iocp_windows.h b/src/core/lib/iomgr/iocp_windows.h
new file mode 100644
index 0000000000..856c837fb4
--- /dev/null
+++ b/src/core/lib/iomgr/iocp_windows.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_IOCP_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H
+
+#include <grpc/support/sync.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_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_CORE_LIB_IOMGR_IOCP_WINDOWS_H */
diff --git a/src/core/lib/iomgr/iomgr.c b/src/core/lib/iomgr/iomgr.c
new file mode 100644
index 0000000000..bb544c8280
--- /dev/null
+++ b/src/core/lib/iomgr/iomgr.c
@@ -0,0 +1,175 @@
+/*
+ *
+ * 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/lib/iomgr/iomgr.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/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_exec_ctx_global_init();
+ 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");
+ if (grpc_iomgr_abort_on_leaks()) {
+ abort();
+ }
+ }
+ 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();
+ grpc_exec_ctx_global_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);
+}
+
+bool grpc_iomgr_abort_on_leaks(void) {
+ char *env = gpr_getenv("GRPC_ABORT_ON_LEAKS");
+ if (env == NULL) return false;
+ static const char *truthy[] = {"yes", "Yes", "YES", "true",
+ "True", "TRUE", "1"};
+ for (size_t i = 0; i < GPR_ARRAY_SIZE(truthy); i++) {
+ if (0 == strcmp(env, truthy[i])) return true;
+ }
+ return false;
+}
diff --git a/src/core/lib/iomgr/iomgr.h b/src/core/lib/iomgr/iomgr.h
new file mode 100644
index 0000000000..babf0a85b7
--- /dev/null
+++ b/src/core/lib/iomgr/iomgr.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_IOMGR_H
+#define GRPC_CORE_LIB_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_CORE_LIB_IOMGR_IOMGR_H */
diff --git a/src/core/lib/iomgr/iomgr_internal.h b/src/core/lib/iomgr/iomgr_internal.h
new file mode 100644
index 0000000000..0963630c61
--- /dev/null
+++ b/src/core/lib/iomgr/iomgr_internal.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_IOMGR_INTERNAL_H
+#define GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H
+
+#include <stdbool.h>
+
+#include <grpc/support/sync.h>
+#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;
+
+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);
+
+bool grpc_iomgr_abort_on_leaks(void);
+
+#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H */
diff --git a/src/core/lib/iomgr/iomgr_posix.c b/src/core/lib/iomgr/iomgr_posix.c
new file mode 100644
index 0000000000..e4990f7bce
--- /dev/null
+++ b/src/core/lib/iomgr/iomgr_posix.c
@@ -0,0 +1,52 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/fd_posix.h"
+#include "src/core/lib/iomgr/iomgr_posix.h"
+#include "src/core/lib/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/src/core/lib/iomgr/iomgr_posix.h b/src/core/lib/iomgr/iomgr_posix.h
new file mode 100644
index 0000000000..6a8996e403
--- /dev/null
+++ b/src/core/lib/iomgr/iomgr_posix.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_IOMGR_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H
+
+#include "src/core/lib/iomgr/iomgr_internal.h"
+
+#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_POSIX_H */
diff --git a/src/core/lib/iomgr/iomgr_windows.c b/src/core/lib/iomgr/iomgr_windows.c
new file mode 100644
index 0000000000..af7e616394
--- /dev/null
+++ b/src/core/lib/iomgr/iomgr_windows.c
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr_win32.h"
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/socket_windows.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/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h
new file mode 100644
index 0000000000..6156124862
--- /dev/null
+++ b/src/core/lib/iomgr/pollset.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_POLLSET_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+
+#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 */
+
+typedef struct grpc_pollset grpc_pollset;
+typedef struct grpc_pollset_worker grpc_pollset_worker;
+
+size_t grpc_pollset_size(void);
+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_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 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.
+
+ 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_CORE_LIB_IOMGR_POLLSET_H */
diff --git a/src/core/lib/iomgr/pollset_multipoller_with_epoll.c b/src/core/lib/iomgr/pollset_multipoller_with_epoll.c
new file mode 100644
index 0000000000..fa1b0d2d84
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_multipoller_with_epoll.c
@@ -0,0 +1,324 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX_MULTIPOLL_WITH_EPOLL
+
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/fd_posix.h"
+#include "src/core/lib/iomgr/pollset_posix.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/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/src/core/lib/iomgr/pollset_multipoller_with_poll_posix.c b/src/core/lib/iomgr/pollset_multipoller_with_poll_posix.c
new file mode 100644
index 0000000000..9b33f6dbb2
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_multipoller_with_poll_posix.c
@@ -0,0 +1,234 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/pollset_posix.h"
+
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/fd_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/pollset_posix.h"
+#include "src/core/lib/support/block_annotate.h"
+
+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];
+ GRPC_FD_REF(watchers[pfd_count].fd, "multipoller_start");
+ 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++) {
+ grpc_fd *fd = watchers[i].fd;
+ pfds[i].events = (short)grpc_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;
+ 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/src/core/lib/iomgr/pollset_posix.c b/src/core/lib/iomgr/pollset_posix.c
new file mode 100644
index 0000000000..259c7bc194
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_posix.c
@@ -0,0 +1,633 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/pollset_posix.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/tls.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/fd_posix.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/block_annotate.h"
+
+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;
+}
+
+size_t grpc_pollset_size(void) { return sizeof(grpc_pollset); }
+
+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 **mu) {
+ gpr_mu_init(&pollset->mu);
+ *mu = &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);
+ 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_hdl, gpr_timespec now,
+ gpr_timespec deadline) {
+ grpc_pollset_worker worker;
+ *worker_hdl = &worker;
+
+ /* 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;
+ }
+ /* 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);
+ }
+ }
+ *worker_hdl = NULL;
+ 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/src/core/lib/iomgr/pollset_posix.h b/src/core/lib/iomgr/pollset_posix.h
new file mode 100644
index 0000000000..7d8e9fc279
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_posix.h
@@ -0,0 +1,153 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_POLLSET_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_POSIX_H
+
+#include <poll.h>
+
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/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;
+
+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 {
+ /* 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;
+};
+
+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);
+};
+
+/* 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 */
+/* NOTE: Any changes to grpc_poll_function must take place when the gRPC
+ is certainly not doing any polling anywhere.
+ Otherwise, there might be a race between changing the variable and actually
+ doing a polling operation */
+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_CORE_LIB_IOMGR_POLLSET_POSIX_H */
diff --git a/src/core/lib/iomgr/pollset_set.h b/src/core/lib/iomgr/pollset_set.h
new file mode 100644
index 0000000000..fb29d692d7
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_set.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_POLLSET_SET_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_H
+
+#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;
+
+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_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_CORE_LIB_IOMGR_POLLSET_SET_H */
diff --git a/src/core/lib/iomgr/pollset_set_posix.c b/src/core/lib/iomgr/pollset_set_posix.c
new file mode 100644
index 0000000000..d6142f9b6b
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_set_posix.c
@@ -0,0 +1,202 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/pollset_posix.h"
+#include "src/core/lib/iomgr/pollset_set_posix.h"
+
+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 *grpc_pollset_set_create(void) {
+ grpc_pollset_set *pollset_set = gpr_malloc(sizeof(*pollset_set));
+ memset(pollset_set, 0, sizeof(*pollset_set));
+ gpr_mu_init(&pollset_set->mu);
+ return pollset_set;
+}
+
+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);
+ gpr_free(pollset_set);
+}
+
+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/src/core/lib/iomgr/pollset_set_posix.h b/src/core/lib/iomgr/pollset_set_posix.h
new file mode 100644
index 0000000000..4e6b063c6f
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_set_posix.h
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_POLLSET_SET_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_POSIX_H
+
+#include "src/core/lib/iomgr/fd_posix.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+
+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_CORE_LIB_IOMGR_POLLSET_SET_POSIX_H */
diff --git a/src/core/lib/iomgr/pollset_set_windows.c b/src/core/lib/iomgr/pollset_set_windows.c
new file mode 100644
index 0000000000..0b14e446ae
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_set_windows.c
@@ -0,0 +1,60 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/pollset_set_windows.h"
+
+grpc_pollset_set* grpc_pollset_set_create(pollset_set) { return NULL; }
+
+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/src/core/lib/iomgr/pollset_set_windows.h b/src/core/lib/iomgr/pollset_set_windows.h
new file mode 100644
index 0000000000..7c2cea23de
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_set_windows.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H
+
+#include "src/core/lib/iomgr/pollset_set.h"
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_WINDOWS_H */
diff --git a/src/core/lib/iomgr/pollset_windows.c b/src/core/lib/iomgr/pollset_windows.c
new file mode 100644
index 0000000000..6b339127a8
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_windows.c
@@ -0,0 +1,240 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include <grpc/support/log.h>
+#include <grpc/support/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"
+
+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;
+}
+
+size_t grpc_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. */
+
+void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) {
+ *mu = &grpc_polling_mu;
+ 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_hdl, gpr_timespec now,
+ gpr_timespec deadline) {
+ grpc_pollset_worker worker;
+ *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(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);
+ *worker_hdl = NULL;
+}
+
+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/src/core/lib/iomgr/pollset_windows.h b/src/core/lib/iomgr/pollset_windows.h
new file mode 100644
index 0000000000..fa9553ffea
--- /dev/null
+++ b/src/core/lib/iomgr/pollset_windows.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_POLLSET_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H
+
+#include <grpc/support/sync.h>
+
+#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;
+};
+
+#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H */
diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h
new file mode 100644
index 0000000000..f748288685
--- /dev/null
+++ b/src/core/lib/iomgr/resolve_address.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H
+#define GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H
+
+#include <stddef.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/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. */
+extern grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
+ const char *name, const char *default_port);
+
+#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */
diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c
new file mode 100644
index 0000000000..ebecb39c16
--- /dev/null
+++ b/src/core/lib/iomgr/resolve_address_posix.c
@@ -0,0 +1,178 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/support/block_annotate.h"
+#include "src/core/lib/support/string.h"
+
+typedef struct {
+ char *name;
+ char *default_port;
+ grpc_resolve_cb cb;
+ grpc_closure request_closure;
+ void *arg;
+} request;
+
+static grpc_resolved_addresses *blocking_resolve_address_impl(
+ 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;
+
+ 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);
+ }
+
+ /* 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;
+}
+
+grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
+ const char *name, const char *default_port) = blocking_resolve_address_impl;
+
+/* 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/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c
new file mode 100644
index 0000000000..bde1f1b7f7
--- /dev/null
+++ b/src/core/lib/iomgr/resolve_address_windows.c
@@ -0,0 +1,169 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+#ifdef GPR_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/support/block_annotate.h"
+#include "src/core/lib/support/string.h"
+
+typedef struct {
+ char *name;
+ char *default_port;
+ grpc_resolve_cb cb;
+ grpc_closure request_closure;
+ void *arg;
+} request;
+
+static grpc_resolved_addresses *blocking_resolve_address_impl(
+ 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;
+}
+
+grpc_resolved_addresses *(*grpc_blocking_resolve_address)(
+ const char *name, const char *default_port) = blocking_resolve_address_impl;
+
+/* 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/src/core/lib/iomgr/sockaddr.h b/src/core/lib/iomgr/sockaddr.h
new file mode 100644
index 0000000000..66a930ee6a
--- /dev/null
+++ b/src/core/lib/iomgr/sockaddr.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_SOCKADDR_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+#include "src/core/lib/iomgr/sockaddr_win32.h"
+#endif
+
+#ifdef GPR_POSIX_SOCKETADDR
+#include "src/core/lib/iomgr/sockaddr_posix.h"
+#endif
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_H */
diff --git a/src/core/lib/iomgr/sockaddr_posix.h b/src/core/lib/iomgr/sockaddr_posix.h
new file mode 100644
index 0000000000..79a7467c5d
--- /dev/null
+++ b/src/core/lib/iomgr/sockaddr_posix.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_SOCKADDR_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_POSIX_H */
diff --git a/src/core/lib/iomgr/sockaddr_utils.c b/src/core/lib/iomgr/sockaddr_utils.c
new file mode 100644
index 0000000000..127d95c618
--- /dev/null
+++ b/src/core/lib/iomgr/sockaddr_utils.c
@@ -0,0 +1,227 @@
+/*
+ *
+ * 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 "src/core/lib/iomgr/sockaddr_utils.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/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;
+ default:
+ return grpc_sockaddr_to_uri_unix_if_possible(addr);
+ }
+}
+
+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);
+ default:
+ if (grpc_is_unix_socket(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 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/src/core/lib/iomgr/sockaddr_utils.h b/src/core/lib/iomgr/sockaddr_utils.h
new file mode 100644
index 0000000000..20a3e3bec3
--- /dev/null
+++ b/src/core/lib/iomgr/sockaddr_utils.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_SOCKADDR_UTILS_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H
+
+#include "src/core/lib/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_CORE_LIB_IOMGR_SOCKADDR_UTILS_H */
diff --git a/src/core/lib/iomgr/sockaddr_win32.h b/src/core/lib/iomgr/sockaddr_win32.h
new file mode 100644
index 0000000000..2dd7111240
--- /dev/null
+++ b/src/core/lib/iomgr/sockaddr_win32.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_SOCKADDR_WIN32_H
+#define GRPC_CORE_LIB_IOMGR_SOCKADDR_WIN32_H
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+// must be included after the above
+#include <mswsock.h>
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_WIN32_H */
diff --git a/src/core/lib/iomgr/socket_utils_common_posix.c b/src/core/lib/iomgr/socket_utils_common_posix.c
new file mode 100644
index 0000000000..9dbc2784e4
--- /dev/null
+++ b/src/core/lib/iomgr/socket_utils_common_posix.c
@@ -0,0 +1,208 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/support/string.h"
+
+/* set a socket to non blocking mode */
+int grpc_set_socket_nonblocking(int fd, int non_blocking) {
+ int oldflags = fcntl(fd, F_GETFL, 0);
+ if (oldflags < 0) {
+ return 0;
+ }
+
+ if (non_blocking) {
+ oldflags |= O_NONBLOCK;
+ } else {
+ oldflags &= ~O_NONBLOCK;
+ }
+
+ if (fcntl(fd, F_SETFL, oldflags) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int grpc_set_socket_no_sigpipe_if_possible(int fd) {
+#ifdef GPR_HAVE_SO_NOSIGPIPE
+ int val = 1;
+ int newval;
+ socklen_t intlen = sizeof(newval);
+ return 0 == setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) &&
+ 0 == getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen) &&
+ (newval != 0) == val;
+#else
+ return 1;
+#endif
+}
+
+/* set a socket to close on exec */
+int grpc_set_socket_cloexec(int fd, int close_on_exec) {
+ int oldflags = fcntl(fd, F_GETFD, 0);
+ if (oldflags < 0) {
+ return 0;
+ }
+
+ if (close_on_exec) {
+ oldflags |= FD_CLOEXEC;
+ } else {
+ oldflags &= ~FD_CLOEXEC;
+ }
+
+ if (fcntl(fd, F_SETFD, oldflags) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* set a socket to reuse old addresses */
+int grpc_set_socket_reuse_addr(int fd, int reuse) {
+ int val = (reuse != 0);
+ int newval;
+ socklen_t intlen = sizeof(newval);
+ return 0 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) &&
+ 0 == getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen) &&
+ (newval != 0) == val;
+}
+
+/* disable nagle */
+int grpc_set_socket_low_latency(int fd, int low_latency) {
+ int val = (low_latency != 0);
+ int newval;
+ socklen_t intlen = sizeof(newval);
+ return 0 == setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) &&
+ 0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen) &&
+ (newval != 0) == val;
+}
+
+static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT;
+static int g_ipv6_loopback_available;
+
+static void probe_ipv6_once(void) {
+ int fd = socket(AF_INET6, SOCK_STREAM, 0);
+ g_ipv6_loopback_available = 0;
+ if (fd < 0) {
+ gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed.");
+ } else {
+ struct sockaddr_in6 addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
+ g_ipv6_loopback_available = 1;
+ } else {
+ gpr_log(GPR_INFO,
+ "Disabling AF_INET6 sockets because ::1 is not available.");
+ }
+ close(fd);
+ }
+}
+
+int grpc_ipv6_loopback_available(void) {
+ gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once);
+ return g_ipv6_loopback_available;
+}
+
+/* This should be 0 in production, but it may be enabled for testing or
+ debugging purposes, to simulate an environment where IPv6 sockets can't
+ also speak IPv4. */
+int grpc_forbid_dualstack_sockets_for_testing = 0;
+
+static int set_socket_dualstack(int fd) {
+ if (!grpc_forbid_dualstack_sockets_for_testing) {
+ const int off = 0;
+ return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
+ } else {
+ /* Force an IPv6-only socket, for testing purposes. */
+ const int on = 1;
+ setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ return 0;
+ }
+}
+
+int grpc_create_dualstack_socket(const struct sockaddr *addr, int type,
+ int protocol, grpc_dualstack_mode *dsmode) {
+ int family = addr->sa_family;
+ if (family == AF_INET6) {
+ int fd;
+ if (grpc_ipv6_loopback_available()) {
+ fd = socket(family, type, protocol);
+ } else {
+ fd = -1;
+ errno = EAFNOSUPPORT;
+ }
+ /* Check if we've got a valid dualstack socket. */
+ if (fd >= 0 && set_socket_dualstack(fd)) {
+ *dsmode = GRPC_DSMODE_DUALSTACK;
+ return fd;
+ }
+ /* If this isn't an IPv4 address, then return whatever we've got. */
+ if (!grpc_sockaddr_is_v4mapped(addr, NULL)) {
+ *dsmode = GRPC_DSMODE_IPV6;
+ return fd;
+ }
+ /* Fall back to AF_INET. */
+ if (fd >= 0) {
+ close(fd);
+ }
+ family = AF_INET;
+ }
+ *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE;
+ return socket(family, type, protocol);
+}
+
+#endif
diff --git a/src/core/lib/iomgr/socket_utils_linux.c b/src/core/lib/iomgr/socket_utils_linux.c
new file mode 100644
index 0000000000..e7dfe892ca
--- /dev/null
+++ b/src/core/lib/iomgr/socket_utils_linux.c
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX_SOCKETUTILS
+
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
+ int nonblock, int cloexec) {
+ int flags = 0;
+ flags |= nonblock ? SOCK_NONBLOCK : 0;
+ flags |= cloexec ? SOCK_CLOEXEC : 0;
+ return accept4(sockfd, addr, addrlen, flags);
+}
+
+#endif
diff --git a/src/core/lib/iomgr/socket_utils_posix.c b/src/core/lib/iomgr/socket_utils_posix.c
new file mode 100644
index 0000000000..b2fa00c5c1
--- /dev/null
+++ b/src/core/lib/iomgr/socket_utils_posix.c
@@ -0,0 +1,70 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKETUTILS
+
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+
+int grpc_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen,
+ int nonblock, int cloexec) {
+ int fd, flags;
+
+ fd = accept(sockfd, addr, addrlen);
+ if (fd >= 0) {
+ if (nonblock) {
+ flags = fcntl(fd, F_GETFL, 0);
+ if (flags < 0) goto close_and_error;
+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) goto close_and_error;
+ }
+ if (cloexec) {
+ flags = fcntl(fd, F_GETFD, 0);
+ if (flags < 0) goto close_and_error;
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != 0) goto close_and_error;
+ }
+ }
+ return fd;
+
+close_and_error:
+ close(fd);
+ return -1;
+}
+
+#endif /* GPR_POSIX_SOCKETUTILS */
diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h
new file mode 100644
index 0000000000..063f298d72
--- /dev/null
+++ b/src/core/lib/iomgr/socket_utils_posix.h
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H
+
+#include <sys/socket.h>
+#include <unistd.h>
+
+/* 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_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H */
diff --git a/src/core/lib/iomgr/socket_windows.c b/src/core/lib/iomgr/socket_windows.c
new file mode 100644
index 0000000000..1023a6d4f8
--- /dev/null
+++ b/src/core/lib/iomgr/socket_windows.c
@@ -0,0 +1,100 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include <winsock2.h>
+
+// must be included after winsock2.h
+#include <mswsock.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
+#include <grpc/support/string_util.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"
+#include "src/core/lib/iomgr/socket_windows.h"
+
+grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) {
+ char *final_name;
+ grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket));
+ memset(r, 0, sizeof(grpc_winsocket));
+ r->socket = socket;
+ gpr_mu_init(&r->state_mu);
+ gpr_asprintf(&final_name, "%s:socket=0x%p", name, r);
+ grpc_iomgr_register_object(&r->iomgr_object, final_name);
+ gpr_free(final_name);
+ grpc_iocp_add_socket(r);
+ return r;
+}
+
+/* Schedule a shutdown of the socket operations. Will call the pending
+ operations to abort them. We need to do that this way because of the
+ various callsites of that function, which happens to be in various
+ mutex hold states, and that'd be unsafe to call them directly. */
+void grpc_winsocket_shutdown(grpc_winsocket *winsocket) {
+ /* Grab the function pointer for DisconnectEx for that specific socket.
+ It may change depending on the interface. */
+ int status;
+ GUID guid = WSAID_DISCONNECTEX;
+ LPFN_DISCONNECTEX DisconnectEx;
+ DWORD ioctl_num_bytes;
+
+ status = WSAIoctl(winsocket->socket, SIO_GET_EXTENSION_FUNCTION_POINTER,
+ &guid, sizeof(guid), &DisconnectEx, sizeof(DisconnectEx),
+ &ioctl_num_bytes, NULL, NULL);
+
+ if (status == 0) {
+ DisconnectEx(winsocket->socket, NULL, 0, 0);
+ } else {
+ char *utf8_message = gpr_format_message(WSAGetLastError());
+ gpr_log(GPR_ERROR, "Unable to retrieve DisconnectEx pointer : %s",
+ utf8_message);
+ gpr_free(utf8_message);
+ }
+ closesocket(winsocket->socket);
+}
+
+void grpc_winsocket_destroy(grpc_winsocket *winsocket) {
+ grpc_iomgr_unregister_object(&winsocket->iomgr_object);
+ gpr_mu_destroy(&winsocket->state_mu);
+ gpr_free(winsocket);
+}
+
+#endif /* GPR_WINSOCK_SOCKET */
diff --git a/src/core/lib/iomgr/socket_windows.h b/src/core/lib/iomgr/socket_windows.h
new file mode 100644
index 0000000000..74447896c9
--- /dev/null
+++ b/src/core/lib/iomgr/socket_windows.h
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_SOCKET_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H
+
+#include <grpc/support/port_platform.h>
+#include <winsock2.h>
+
+#include <grpc/support/atm.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/exec_ctx.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
+ inside the winsocket wrapper. */
+typedef struct grpc_winsocket_callback_info {
+ /* This is supposed to be a WSAOVERLAPPED, but in order to get that
+ definition, we need to include ws2tcpip.h, which needs to be included
+ from the top, otherwise it'll clash with a previous inclusion of
+ windows.h that in turns includes winsock.h. If anyone knows a way
+ to do it properly, feel free to send a patch. */
+ OVERLAPPED overlapped;
+ /* The callback information for the pending operation. May be empty if the
+ caller hasn't registered a callback yet. */
+ 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
+ altogether by forcing the caller to always register its callback before
+ proceeding queue an operation, but it is frequent for an IO Completion
+ Port to trigger quickly. This way we avoid a context switch for calling
+ the callback. We also simplify the read / write operations to avoid having
+ to hold a mutex for a long amount of time. */
+ int has_pending_iocp;
+ /* The results of the overlapped operation. */
+ DWORD bytes_transfered;
+ int wsa_error;
+} grpc_winsocket_callback_info;
+
+/* This is a wrapper to a Windows socket. A socket can have one outstanding
+ read, and one outstanding write. Doing an asynchronous accept means waiting
+ for a read operation. Doing an asynchronous connect means waiting for a
+ write operation. These are completely arbitrary ties between the operation
+ and the kind of event, because we can have one overlapped per pending
+ operation, whichever its nature is. So we could have more dedicated pending
+ operation callbacks for connect and listen. But given the scope of listen
+ and accept, we don't need to go to that extent and waste memory. Also, this
+ is closer to what happens in posix world. */
+typedef struct grpc_winsocket {
+ SOCKET socket;
+
+ grpc_winsocket_callback_info write_info;
+ grpc_winsocket_callback_info read_info;
+
+ gpr_mu state_mu;
+
+ /* You can't add the same socket twice to the same IO Completion Port.
+ This prevents that. */
+ int added_to_iocp;
+
+ grpc_closure shutdown_closure;
+
+ /* A label for iomgr to track outstanding objects */
+ grpc_iomgr_object iomgr_object;
+} grpc_winsocket;
+
+/* Create a wrapped windows handle. This takes ownership of it, meaning that
+ it will be responsible for closing it. */
+grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name);
+
+/* Initiate an asynchronous shutdown of the socket. Will call off any pending
+ operation to cancel them. */
+void grpc_winsocket_shutdown(grpc_winsocket *socket);
+
+/* Destroy a socket. Should only be called if there's no pending operation. */
+void grpc_winsocket_destroy(grpc_winsocket *socket);
+
+#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H */
diff --git a/src/core/lib/iomgr/tcp_client.h b/src/core/lib/iomgr/tcp_client.h
new file mode 100644
index 0000000000..6bbe26445a
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_client.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_TCP_CLIENT_H
+#define GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H
+
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/pollset_set.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+/* 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_CORE_LIB_IOMGR_TCP_CLIENT_H */
diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c
new file mode 100644
index 0000000000..b8ef643298
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_client_posix.c
@@ -0,0 +1,306 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/tcp_client.h"
+
+#include <errno.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/iomgr_posix.h"
+#include "src/core/lib/iomgr/pollset_posix.h"
+#include "src/core/lib/iomgr/pollset_set_posix.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/timer.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/support/string.h"
+
+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) ||
+ (!grpc_is_unix_socket(addr) && !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/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c
new file mode 100644
index 0000000000..86b8d58975
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_client_windows.c
@@ -0,0 +1,221 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr_win32.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/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"
+
+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/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c
new file mode 100644
index 0000000000..1898d96901
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_posix.c
@@ -0,0 +1,493 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/tcp_posix.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/pollset_posix.h"
+#include "src/core/lib/iomgr/pollset_set_posix.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/string.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/src/core/lib/iomgr/tcp_posix.h b/src/core/lib/iomgr/tcp_posix.h
new file mode 100644
index 0000000000..09c4436f1f
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_posix.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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_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 "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/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_CORE_LIB_IOMGR_TCP_POSIX_H */
diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h
new file mode 100644
index 0000000000..81edb61997
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_server.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_TCP_SERVER_H
+#define GRPC_CORE_LIB_IOMGR_TCP_SERVER_H
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/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_CORE_LIB_IOMGR_TCP_SERVER_H */
diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c
new file mode 100644
index 0000000000..ef1bf9aa94
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_server_posix.c
@@ -0,0 +1,607 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/tcp_server.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/pollset_posix.h"
+#include "src/core/lib/iomgr/resolve_address.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/unix_sockets_posix.h"
+#include "src/core/lib/support/string.h"
+
+#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;
+ } 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;
+};
+
+/* 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) {
+ grpc_unlink_if_unix_domain_socket(&sp->addr.sockaddr);
+ 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) || !grpc_set_socket_cloexec(fd, 1) ||
+ (!grpc_is_unix_socket(addr) && (!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;
+ }
+
+ 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;
+ }
+ grpc_unlink_if_unix_domain_socket((struct sockaddr *)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);
+ }
+ 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));
+ } else {
+ if (dsmode == GRPC_DSMODE_IPV4 &&
+ grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
+ addr = (struct sockaddr *)&addr4_copy;
+ addr_len = sizeof(addr4_copy);
+ }
+ sp2 = sp;
+ 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/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c
new file mode 100644
index 0000000000..3d6a29b2e2
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_server_windows.c
@@ -0,0 +1,557 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include <io.h>
+
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/iocp_windows.h"
+#include "src/core/lib/iomgr/pollset_windows.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(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) {
+ 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/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c
new file mode 100644
index 0000000000..c1ce725f2c
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_windows.c
@@ -0,0 +1,402 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINSOCK_SOCKET
+
+#include "src/core/lib/iomgr/sockaddr_win32.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/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/timer.h"
+
+static int set_non_block(SOCKET sock) {
+ int status;
+ unsigned long param = 1;
+ DWORD ret;
+ status =
+ WSAIoctl(sock, FIONBIO, &param, 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 *)&param,
+ 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 (info->wsa_error != 0 && !tcp->shutting_down) {
+ if (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 (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/src/core/lib/iomgr/tcp_windows.h b/src/core/lib/iomgr/tcp_windows.h
new file mode 100644
index 0000000000..7a9ebd85eb
--- /dev/null
+++ b/src/core/lib/iomgr/tcp_windows.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * 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_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 "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, char *peer_string);
+
+int grpc_tcp_prepare_socket(SOCKET sock);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H */
diff --git a/src/core/lib/iomgr/time_averaged_stats.c b/src/core/lib/iomgr/time_averaged_stats.c
new file mode 100644
index 0000000000..f24d68087e
--- /dev/null
+++ b/src/core/lib/iomgr/time_averaged_stats.c
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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/lib/iomgr/time_averaged_stats.h"
+
+void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats,
+ double init_avg, double regress_weight,
+ double persistence_factor) {
+ stats->init_avg = init_avg;
+ stats->regress_weight = regress_weight;
+ stats->persistence_factor = persistence_factor;
+ stats->batch_total_value = 0;
+ stats->batch_num_samples = 0;
+ stats->aggregate_total_weight = 0;
+ stats->aggregate_weighted_avg = init_avg;
+}
+
+void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats,
+ double value) {
+ stats->batch_total_value += value;
+ ++stats->batch_num_samples;
+}
+
+double grpc_time_averaged_stats_update_average(
+ grpc_time_averaged_stats* stats) {
+ /* Start with the current batch: */
+ double weighted_sum = stats->batch_total_value;
+ double total_weight = stats->batch_num_samples;
+ if (stats->regress_weight > 0) {
+ /* Add in the regression towards init_avg_: */
+ weighted_sum += stats->regress_weight * stats->init_avg;
+ total_weight += stats->regress_weight;
+ }
+ if (stats->persistence_factor > 0) {
+ /* Add in the persistence: */
+ const double prev_sample_weight =
+ stats->persistence_factor * stats->aggregate_total_weight;
+ weighted_sum += prev_sample_weight * stats->aggregate_weighted_avg;
+ total_weight += prev_sample_weight;
+ }
+ stats->aggregate_weighted_avg =
+ (total_weight > 0) ? (weighted_sum / total_weight) : stats->init_avg;
+ stats->aggregate_total_weight = total_weight;
+ stats->batch_num_samples = 0;
+ stats->batch_total_value = 0;
+ return stats->aggregate_weighted_avg;
+}
diff --git a/src/core/lib/iomgr/time_averaged_stats.h b/src/core/lib/iomgr/time_averaged_stats.h
new file mode 100644
index 0000000000..4a662e17ec
--- /dev/null
+++ b/src/core/lib/iomgr/time_averaged_stats.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * 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_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
+ weighted mean. It is designed for batch operations where we do many adds
+ before updating the average. */
+
+typedef struct {
+ /* The initial average value. This is the reported average until the first
+ grpc_time_averaged_stats_update_average call. If a positive regress_weight
+ is used, we also regress towards this value on each update. */
+ double init_avg;
+ /* The sample weight of "init_avg" that is mixed in with each call to
+ grpc_time_averaged_stats_update_average. If the calls to
+ grpc_time_averaged_stats_add_sample stop, this will cause the average to
+ regress back to the mean. This should be non-negative. Set it to 0 to
+ disable the bias. A value of 1 has the effect of adding in 1 bonus sample
+ with value init_avg to each sample period. */
+ double regress_weight;
+ /* This determines the rate of decay of the time-averaging from one period
+ to the next by scaling the aggregate_total_weight of samples from prior
+ periods when combining with the latest period. It should be in the range
+ [0,1]. A higher value adapts more slowly. With a value of 0.5, if the
+ batches each have k samples, the samples_in_avg_ will grow to 2 k, so the
+ weighting of the time average will eventually be 1/3 new batch and 2/3
+ old average. */
+ double persistence_factor;
+
+ /* The total value of samples since the last UpdateAverage(). */
+ double batch_total_value;
+ /* The number of samples since the last UpdateAverage(). */
+ double batch_num_samples;
+ /* The time-decayed sum of batch_num_samples_ over previous batches. This is
+ the "weight" of the old aggregate_weighted_avg_ when updating the
+ average. */
+ double aggregate_total_weight;
+ /* A time-decayed average of the (batch_total_value_ / batch_num_samples_),
+ computed by decaying the samples_in_avg_ weight in the weighted average. */
+ double aggregate_weighted_avg;
+} grpc_time_averaged_stats;
+
+/* See the comments on the members above for an explanation of init_avg,
+ regress_weight, and persistence_factor. */
+void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats,
+ double init_avg, double regress_weight,
+ double persistence_factor);
+/* Add a sample to the current batch. */
+void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats,
+ double value);
+/* Complete a batch and compute the new estimate of the average sample
+ value. */
+double grpc_time_averaged_stats_update_average(grpc_time_averaged_stats* stats);
+
+#endif /* GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H */
diff --git a/src/core/lib/iomgr/timer.c b/src/core/lib/iomgr/timer.c
new file mode 100644
index 0000000000..4748f9b270
--- /dev/null
+++ b/src/core/lib/iomgr/timer.c
@@ -0,0 +1,356 @@
+/*
+ *
+ * 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/lib/iomgr/timer.h"
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/iomgr/time_averaged_stats.h"
+#include "src/core/lib/iomgr/timer_heap.h"
+
+#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);
+ } else if (next != NULL) {
+ /* TODO(ctiller): this forces calling code to do an short poll, and
+ then retry the timer check (because this time through the timer list was
+ contended).
+
+ We could reduce the cost here dramatically by keeping a count of how many
+ currently active pollers got through the uncontended case above
+ successfully, and waking up other pollers IFF that count drops to zero.
+
+ Once that count is in place, this entire else branch could disappear. */
+ *next = gpr_time_min(
+ *next, gpr_time_add(now, gpr_time_from_millis(1, GPR_TIMESPAN)));
+ }
+
+ return (int)n;
+}
+
+bool 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/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h
new file mode 100644
index 0000000000..54f301c5ed
--- /dev/null
+++ b/src/core/lib/iomgr/timer.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_TIMER_H
+#define GRPC_CORE_LIB_IOMGR_TIMER_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr.h"
+
+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);
+
+/* iomgr internal api for dealing with timers */
+
+/* Check for timers to be run, and run them.
+ Return true 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. */
+bool 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_CORE_LIB_IOMGR_TIMER_H */
diff --git a/src/core/lib/iomgr/timer_heap.c b/src/core/lib/iomgr/timer_heap.c
new file mode 100644
index 0000000000..d43b6ccf75
--- /dev/null
+++ b/src/core/lib/iomgr/timer_heap.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * 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/lib/iomgr/timer_heap.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/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 = (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;
+ if (left_child >= length) break;
+ uint32_t right_child = left_child + 1;
+ uint32_t 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/src/core/lib/iomgr/timer_heap.h b/src/core/lib/iomgr/timer_heap.h
new file mode 100644
index 0000000000..d5112cf0de
--- /dev/null
+++ b/src/core/lib/iomgr/timer_heap.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_TIMER_HEAP_H
+#define GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H
+
+#include "src/core/lib/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_CORE_LIB_IOMGR_TIMER_HEAP_H */
diff --git a/src/core/lib/iomgr/udp_server.c b/src/core/lib/iomgr/udp_server.c
new file mode 100644
index 0000000000..3d8bcc9c81
--- /dev/null
+++ b/src/core/lib/iomgr/udp_server.c
@@ -0,0 +1,418 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_NEED_UDP
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/udp_server.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/iomgr/fd_posix.h"
+#include "src/core/lib/iomgr/pollset_posix.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr_utils.h"
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+#include "src/core/lib/iomgr/unix_sockets_posix.h"
+#include "src/core/lib/support/string.h"
+
+#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;
+ } addr;
+ size_t addr_len;
+ grpc_closure read_closure;
+ grpc_closure destroyed_closure;
+ grpc_udp_server_read_cb read_cb;
+} server_port;
+
+/* 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, NULL);
+
+ 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,
+ bool 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];
+ grpc_unlink_if_unix_domain_socket(&sp->addr.sockaddr);
+ 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, bool success) {
+ server_port *sp = arg;
+
+ if (!success) {
+ 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;
+
+ grpc_unlink_if_unix_domain_socket((struct sockaddr *)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/src/core/lib/iomgr/udp_server.h b/src/core/lib/iomgr/udp_server.h
new file mode 100644
index 0000000000..316845ad66
--- /dev/null
+++ b/src/core/lib/iomgr/udp_server.h
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_UDP_SERVER_H
+#define GRPC_CORE_LIB_IOMGR_UDP_SERVER_H
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/fd_posix.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;
+
+/* 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,
+ struct 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,
+ struct 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_CORE_LIB_IOMGR_UDP_SERVER_H */
diff --git a/src/core/lib/iomgr/unix_sockets_posix.c b/src/core/lib/iomgr/unix_sockets_posix.c
new file mode 100644
index 0000000000..42e44989e0
--- /dev/null
+++ b/src/core/lib/iomgr/unix_sockets_posix.c
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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 "src/core/lib/iomgr/unix_sockets_posix.h"
+
+#ifdef GPR_HAVE_UNIX_SOCKET
+
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <grpc/support/alloc.h>
+
+void grpc_create_socketpair_if_unix(int sv[2]) {
+ GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
+}
+
+grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name) {
+ struct sockaddr_un *un;
+
+ grpc_resolved_addresses *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);
+ addrs->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1;
+ return addrs;
+}
+
+int grpc_is_unix_socket(const struct sockaddr *addr) {
+ return addr->sa_family == AF_UNIX;
+}
+
+void grpc_unlink_if_unix_domain_socket(const struct sockaddr *addr) {
+ if (addr->sa_family != AF_UNIX) {
+ return;
+ }
+ struct sockaddr_un *un = (struct sockaddr_un *)addr;
+ struct stat st;
+
+ if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
+ unlink(un->sun_path);
+ }
+}
+
+int grpc_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;
+}
+
+char *grpc_unix_get_default_authority(grpc_resolver_factory *factory,
+ grpc_uri *uri) {
+ return gpr_strdup("localhost");
+}
+
+char *grpc_sockaddr_to_uri_unix_if_possible(const struct sockaddr *addr) {
+ if (addr->sa_family != AF_UNIX) {
+ return NULL;
+ }
+
+ char *result;
+ gpr_asprintf(&result, "unix:%s", ((struct sockaddr_un *)addr)->sun_path);
+ return result;
+}
+
+#endif
diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h
new file mode 100644
index 0000000000..752cab85a5
--- /dev/null
+++ b/src/core/lib/iomgr/unix_sockets_posix.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/client_config/resolver_factory.h"
+#include "src/core/lib/client_config/uri_parser.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/sockaddr.h"
+
+void grpc_create_socketpair_if_unix(int sv[2]);
+
+grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name);
+
+int grpc_is_unix_socket(const struct sockaddr *addr);
+
+void grpc_unlink_if_unix_domain_socket(const struct sockaddr *addr);
+
+int grpc_parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len);
+
+char *grpc_unix_get_default_authority(grpc_resolver_factory *factory,
+ grpc_uri *uri);
+
+char *grpc_sockaddr_to_uri_unix_if_possible(const struct sockaddr *addr);
+
+#endif /* GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H */
diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.c b/src/core/lib/iomgr/unix_sockets_posix_noop.c
new file mode 100644
index 0000000000..06f6ee05e7
--- /dev/null
+++ b/src/core/lib/iomgr/unix_sockets_posix_noop.c
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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 "src/core/lib/iomgr/unix_sockets_posix.h"
+
+#ifndef GPR_HAVE_UNIX_SOCKET
+
+void grpc_create_socketpair_if_unix(int sv[2]) {}
+
+grpc_resolved_addresses *grpc_resolve_unix_domain_address(const char *name) {
+ return NULL;
+}
+
+int grpc_is_unix_socket(const struct sockaddr *addr) { return false; }
+
+void grpc_unlink_if_unix_domain_socket(const struct sockaddr *addr) {}
+
+int grpc_parse_unix(grpc_uri *uri, struct sockaddr_storage *addr, size_t *len) {
+ return 0;
+}
+
+char *grpc_unix_get_default_authority(grpc_resolver_factory *factory,
+ grpc_uri *uri) {
+ return NULL;
+}
+
+char *grpc_sockaddr_to_uri_unix_if_possible(const struct sockaddr *addr) {
+ return NULL;
+}
+
+#endif
diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.c b/src/core/lib/iomgr/wakeup_fd_eventfd.c
new file mode 100644
index 0000000000..41ded0ca4d
--- /dev/null
+++ b/src/core/lib/iomgr/wakeup_fd_eventfd.c
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX_EVENTFD
+
+#include <errno.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+#include "src/core/lib/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/src/core/lib/iomgr/wakeup_fd_nospecial.c b/src/core/lib/iomgr/wakeup_fd_nospecial.c
new file mode 100644
index 0000000000..39defa65c6
--- /dev/null
+++ b/src/core/lib/iomgr/wakeup_fd_nospecial.c
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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 is a dummy file to provide an invalid specialized_wakeup_fd_vtable on
+ * systems without anything better than pipe.
+ */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_NO_SPECIAL_WAKEUP_FD
+
+#include <stddef.h>
+#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 = {
+ NULL, NULL, NULL, NULL, check_availability_invalid};
+
+#endif /* GPR_POSIX_NO_SPECIAL_WAKEUP_FD */
diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.c b/src/core/lib/iomgr/wakeup_fd_pipe.c
new file mode 100644
index 0000000000..820919e4dd
--- /dev/null
+++ b/src/core/lib/iomgr/wakeup_fd_pipe.c
@@ -0,0 +1,102 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_WAKEUP_FD
+
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/iomgr/socket_utils_posix.h"
+
+static void pipe_init(grpc_wakeup_fd* fd_info) {
+ int pipefd[2];
+ /* TODO(klempner): Make this nonfatal */
+ int r = pipe(pipefd);
+ if (0 != r) {
+ gpr_log(GPR_ERROR, "pipe creation failed (%d): %s", errno, strerror(errno));
+ abort();
+ }
+ 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/src/core/lib/iomgr/wakeup_fd_pipe.h b/src/core/lib/iomgr/wakeup_fd_pipe.h
new file mode 100644
index 0000000000..bbdb1fc448
--- /dev/null
+++ b/src/core/lib/iomgr/wakeup_fd_pipe.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H
+#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H
+
+#include "src/core/lib/iomgr/wakeup_fd_posix.h"
+
+extern grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable;
+
+#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_PIPE_H */
diff --git a/src/core/lib/iomgr/wakeup_fd_posix.c b/src/core/lib/iomgr/wakeup_fd_posix.c
new file mode 100644
index 0000000000..c4d174fb34
--- /dev/null
+++ b/src/core/lib/iomgr/wakeup_fd_posix.c
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_WAKEUP_FD
+
+#include <stddef.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 = 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/src/core/lib/iomgr/wakeup_fd_posix.h b/src/core/lib/iomgr/wakeup_fd_posix.h
new file mode 100644
index 0000000000..20988d5fd3
--- /dev/null
+++ b/src/core/lib/iomgr/wakeup_fd_posix.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * wakeup_fd abstracts the concept of a file descriptor for the purpose of
+ * waking up a thread in select()/poll()/epoll_wait()/etc.
+
+ * The poll() family of system calls provide a way for a thread to block until
+ * there is activity on one (or more) of a set of file descriptors. An
+ * application may wish to wake up this thread to do non file related work. The
+ * typical way to do this is to add a pipe to the set of file descriptors, then
+ * write to the pipe to wake up the thread in poll().
+ *
+ * Linux has a lighter weight eventfd specifically designed for this purpose.
+ * wakeup_fd abstracts the difference between the two.
+ *
+ * Setup:
+ * 1. Before calling anything, call global_init() at least once.
+ * 1. Call grpc_wakeup_fd_create() to get a wakeup_fd.
+ * 2. Add the result of GRPC_WAKEUP_FD_FD to the set of monitored file
+ * descriptors for the poll() style API you are using. Monitor the file
+ * descriptor for readability.
+ * 3. To tear down, call grpc_wakeup_fd_destroy(). This closes the underlying
+ * file descriptor.
+ *
+ * Usage:
+ * 1. To wake up a polling thread, call grpc_wakeup_fd_wakeup() on a wakeup_fd
+ * it is monitoring.
+ * 2. If the polling thread was awakened by a wakeup_fd event, call
+ * grpc_wakeup_fd_consume_wakeup() on it.
+ */
+#ifndef GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H
+
+void grpc_wakeup_fd_global_init(void);
+void grpc_wakeup_fd_global_destroy(void);
+
+/* Force using the fallback implementation. This is intended for testing
+ * purposes only.*/
+void grpc_wakeup_fd_global_init_force_fallback(void);
+
+typedef struct grpc_wakeup_fd grpc_wakeup_fd;
+
+typedef struct grpc_wakeup_fd_vtable {
+ void (*init)(grpc_wakeup_fd* fd_info);
+ void (*consume)(grpc_wakeup_fd* fd_info);
+ void (*wakeup)(grpc_wakeup_fd* fd_info);
+ void (*destroy)(grpc_wakeup_fd* fd_info);
+ /* Must be called before calling any other functions */
+ int (*check_availability)(void);
+} grpc_wakeup_fd_vtable;
+
+struct grpc_wakeup_fd {
+ int read_fd;
+ int write_fd;
+};
+
+extern int grpc_allow_specialized_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);
+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_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H */
diff --git a/src/core/lib/iomgr/workqueue.h b/src/core/lib/iomgr/workqueue.h
new file mode 100644
index 0000000000..9c420c57de
--- /dev/null
+++ b/src/core/lib/iomgr/workqueue.h
@@ -0,0 +1,83 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_WORKQUEUE_H
+#define GRPC_CORE_LIB_IOMGR_WORKQUEUE_H
+
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/iomgr/pollset.h"
+
+#ifdef GPR_POSIX_SOCKET
+#include "src/core/lib/iomgr/workqueue_posix.h"
+#endif
+
+#ifdef GPR_WIN32
+#include "src/core/lib/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 /* GRPC_CORE_LIB_IOMGR_WORKQUEUE_H */
diff --git a/src/core/lib/iomgr/workqueue_posix.c b/src/core/lib/iomgr/workqueue_posix.c
new file mode 100644
index 0000000000..76830ef12d
--- /dev/null
+++ b/src/core/lib/iomgr/workqueue_posix.c
@@ -0,0 +1,144 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SOCKET
+
+#include "src/core/lib/iomgr/workqueue.h"
+
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/iomgr/fd_posix.h"
+#include "src/core/lib/iomgr/pollset_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_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
+ 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_exec_ctx_enqueue_list(exec_ctx, &workqueue->closure_list, NULL);
+ 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/src/core/lib/iomgr/workqueue_posix.h b/src/core/lib/iomgr/workqueue_posix.h
new file mode 100644
index 0000000000..956de8fb27
--- /dev/null
+++ b/src/core/lib/iomgr/workqueue_posix.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_WORKQUEUE_POSIX_H
+#define GRPC_CORE_LIB_IOMGR_WORKQUEUE_POSIX_H
+
+#include "src/core/lib/iomgr/wakeup_fd_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_CORE_LIB_IOMGR_WORKQUEUE_POSIX_H */
diff --git a/src/core/lib/iomgr/workqueue_windows.c b/src/core/lib/iomgr/workqueue_windows.c
new file mode 100644
index 0000000000..6697f93498
--- /dev/null
+++ b/src/core/lib/iomgr/workqueue_windows.c
@@ -0,0 +1,40 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include "src/core/lib/iomgr/workqueue.h"
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/lib/iomgr/workqueue_windows.h b/src/core/lib/iomgr/workqueue_windows.h
new file mode 100644
index 0000000000..8e6980b6d9
--- /dev/null
+++ b/src/core/lib/iomgr/workqueue_windows.h
@@ -0,0 +1,37 @@
+/*
+ *
+ * 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_CORE_LIB_IOMGR_WORKQUEUE_WINDOWS_H
+#define GRPC_CORE_LIB_IOMGR_WORKQUEUE_WINDOWS_H
+
+#endif /* GRPC_CORE_LIB_IOMGR_WORKQUEUE_WINDOWS_H */
diff --git a/src/core/lib/json/json.c b/src/core/lib/json/json.c
new file mode 100644
index 0000000000..9793045d91
--- /dev/null
+++ b/src/core/lib/json/json.c
@@ -0,0 +1,64 @@
+/*
+ *
+ * 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 <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/lib/json/json.h"
+
+grpc_json *grpc_json_create(grpc_json_type type) {
+ grpc_json *json = gpr_malloc(sizeof(*json));
+ memset(json, 0, sizeof(*json));
+ json->type = type;
+
+ return json;
+}
+
+void grpc_json_destroy(grpc_json *json) {
+ while (json->child) {
+ grpc_json_destroy(json->child);
+ }
+
+ if (json->next) {
+ json->next->prev = json->prev;
+ }
+
+ if (json->prev) {
+ json->prev->next = json->next;
+ } else if (json->parent) {
+ json->parent->child = json->next;
+ }
+
+ gpr_free(json);
+}
diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h
new file mode 100644
index 0000000000..41d87dd5ce
--- /dev/null
+++ b/src/core/lib/json/json.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * 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_CORE_LIB_JSON_JSON_H
+#define GRPC_CORE_LIB_JSON_JSON_H
+
+#include <stdlib.h>
+
+#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;
+} 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_CORE_LIB_JSON_JSON_H */
diff --git a/src/core/lib/json/json_common.h b/src/core/lib/json/json_common.h
new file mode 100644
index 0000000000..ce980040f8
--- /dev/null
+++ b/src/core/lib/json/json_common.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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_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/src/core/lib/json/json_reader.c b/src/core/lib/json/json_reader.c
new file mode 100644
index 0000000000..0807f029ce
--- /dev/null
+++ b/src/core/lib/json/json_reader.c
@@ -0,0 +1,659 @@
+/*
+ *
+ * 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 <string.h>
+
+#include <grpc/support/port_platform.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/json/json_reader.h"
+
+static void json_reader_string_clear(grpc_json_reader *reader) {
+ reader->vtable->string_clear(reader->userdata);
+}
+
+static void json_reader_string_add_char(grpc_json_reader *reader, uint32_t c) {
+ reader->vtable->string_add_char(reader->userdata, c);
+}
+
+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) {
+ return reader->vtable->read_char(reader->userdata);
+}
+
+static void json_reader_container_begins(grpc_json_reader *reader,
+ grpc_json_type type) {
+ reader->vtable->container_begins(reader->userdata, type);
+}
+
+static grpc_json_type grpc_json_reader_container_ends(
+ grpc_json_reader *reader) {
+ return reader->vtable->container_ends(reader->userdata);
+}
+
+static void json_reader_set_key(grpc_json_reader *reader) {
+ reader->vtable->set_key(reader->userdata);
+}
+
+static void json_reader_set_string(grpc_json_reader *reader) {
+ reader->vtable->set_string(reader->userdata);
+}
+
+static int json_reader_set_number(grpc_json_reader *reader) {
+ return reader->vtable->set_number(reader->userdata);
+}
+
+static void json_reader_set_true(grpc_json_reader *reader) {
+ reader->vtable->set_true(reader->userdata);
+}
+
+static void json_reader_set_false(grpc_json_reader *reader) {
+ reader->vtable->set_false(reader->userdata);
+}
+
+static void json_reader_set_null(grpc_json_reader *reader) {
+ reader->vtable->set_null(reader->userdata);
+}
+
+/* Call this function to initialize the reader structure. */
+void grpc_json_reader_init(grpc_json_reader *reader,
+ grpc_json_reader_vtable *vtable, void *userdata) {
+ memset(reader, 0, sizeof(*reader));
+ reader->vtable = vtable;
+ reader->userdata = userdata;
+ json_reader_string_clear(reader);
+ reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
+}
+
+int grpc_json_reader_is_complete(grpc_json_reader *reader) {
+ return ((reader->depth == 0) &&
+ ((reader->state == GRPC_JSON_STATE_END) ||
+ (reader->state == GRPC_JSON_STATE_VALUE_END)));
+}
+
+grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) {
+ uint32_t c, success;
+
+ /* This state-machine is a strict implementation of ECMA-404 */
+ for (;;) {
+ c = grpc_json_reader_read_char(reader);
+ switch (c) {
+ /* Let's process the error cases first. */
+ case GRPC_JSON_READ_CHAR_ERROR:
+ return GRPC_JSON_READ_ERROR;
+
+ case GRPC_JSON_READ_CHAR_EAGAIN:
+ return GRPC_JSON_EAGAIN;
+
+ case GRPC_JSON_READ_CHAR_EOF:
+ if (grpc_json_reader_is_complete(reader)) {
+ return GRPC_JSON_DONE;
+ } else {
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ /* Processing whitespaces. */
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ switch (reader->state) {
+ case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
+ case GRPC_JSON_STATE_OBJECT_KEY_END:
+ case GRPC_JSON_STATE_VALUE_BEGIN:
+ case GRPC_JSON_STATE_VALUE_END:
+ case GRPC_JSON_STATE_END:
+ break;
+
+ case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+ case GRPC_JSON_STATE_VALUE_STRING:
+ if (c != ' ') return GRPC_JSON_PARSE_ERROR;
+ if (reader->unicode_high_surrogate != 0)
+ return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_add_char(reader, c);
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER:
+ case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
+ case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
+ case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
+ success = (uint32_t)json_reader_set_number(reader);
+ if (!success) return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_clear(reader);
+ reader->state = GRPC_JSON_STATE_VALUE_END;
+ break;
+
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ /* Value, object or array terminations. */
+ case ',':
+ case '}':
+ case ']':
+ switch (reader->state) {
+ case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+ case GRPC_JSON_STATE_VALUE_STRING:
+ if (reader->unicode_high_surrogate != 0)
+ return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_add_char(reader, c);
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER:
+ case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
+ case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
+ case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
+ success = (uint32_t)json_reader_set_number(reader);
+ if (!success) return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_clear(reader);
+ reader->state = GRPC_JSON_STATE_VALUE_END;
+ /* The missing break here is intentional. */
+
+ case GRPC_JSON_STATE_VALUE_END:
+ case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
+ case GRPC_JSON_STATE_VALUE_BEGIN:
+ if (c == ',') {
+ if (reader->state != GRPC_JSON_STATE_VALUE_END) {
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ if (reader->in_object) {
+ reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
+ } else {
+ reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
+ }
+ } else {
+ if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR;
+ if ((c == '}') && !reader->in_object) {
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ if ((c == '}') &&
+ (reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) &&
+ !reader->container_just_begun) {
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR;
+ if ((c == ']') &&
+ (reader->state == GRPC_JSON_STATE_VALUE_BEGIN) &&
+ !reader->container_just_begun) {
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ reader->state = GRPC_JSON_STATE_VALUE_END;
+ switch (grpc_json_reader_container_ends(reader)) {
+ case GRPC_JSON_OBJECT:
+ reader->in_object = 1;
+ reader->in_array = 0;
+ break;
+ case GRPC_JSON_ARRAY:
+ reader->in_object = 0;
+ reader->in_array = 1;
+ break;
+ case GRPC_JSON_TOP_LEVEL:
+ GPR_ASSERT(reader->depth == 0);
+ reader->in_object = 0;
+ reader->in_array = 0;
+ reader->state = GRPC_JSON_STATE_END;
+ break;
+ default:
+ GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+ }
+ }
+ break;
+
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ /* In-string escaping. */
+ case '\\':
+ switch (reader->state) {
+ case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+ reader->escaped_string_was_key = 1;
+ reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_STRING:
+ reader->escaped_string_was_key = 0;
+ reader->state = GRPC_JSON_STATE_STRING_ESCAPE;
+ break;
+
+ /* This is the \\ case. */
+ case GRPC_JSON_STATE_STRING_ESCAPE:
+ if (reader->unicode_high_surrogate != 0)
+ return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_add_char(reader, '\\');
+ if (reader->escaped_string_was_key) {
+ reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+ } else {
+ reader->state = GRPC_JSON_STATE_VALUE_STRING;
+ }
+ break;
+
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ default:
+ reader->container_just_begun = 0;
+ switch (reader->state) {
+ case GRPC_JSON_STATE_OBJECT_KEY_BEGIN:
+ if (c != '"') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+ break;
+
+ case GRPC_JSON_STATE_OBJECT_KEY_STRING:
+ GPR_ASSERT(reader->unicode_high_surrogate == 0);
+ if (c == '"') {
+ reader->state = GRPC_JSON_STATE_OBJECT_KEY_END;
+ json_reader_set_key(reader);
+ json_reader_string_clear(reader);
+ } else {
+ if (c <= 0x001f) return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_add_char(reader, c);
+ }
+ break;
+
+ case GRPC_JSON_STATE_VALUE_STRING:
+ if (reader->unicode_high_surrogate != 0)
+ return GRPC_JSON_PARSE_ERROR;
+ if (c == '"') {
+ reader->state = GRPC_JSON_STATE_VALUE_END;
+ json_reader_set_string(reader);
+ json_reader_string_clear(reader);
+ } else {
+ if (c < 32) return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_add_char(reader, c);
+ }
+ break;
+
+ case GRPC_JSON_STATE_OBJECT_KEY_END:
+ if (c != ':') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_BEGIN;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_BEGIN:
+ switch (c) {
+ case 't':
+ reader->state = GRPC_JSON_STATE_VALUE_TRUE_R;
+ break;
+
+ case 'f':
+ reader->state = GRPC_JSON_STATE_VALUE_FALSE_A;
+ break;
+
+ case 'n':
+ reader->state = GRPC_JSON_STATE_VALUE_NULL_U;
+ break;
+
+ case '"':
+ reader->state = GRPC_JSON_STATE_VALUE_STRING;
+ break;
+
+ case '0':
+ json_reader_string_add_char(reader, c);
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO;
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ json_reader_string_add_char(reader, c);
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER;
+ break;
+
+ case '{':
+ reader->container_just_begun = 1;
+ json_reader_container_begins(reader, GRPC_JSON_OBJECT);
+ reader->depth++;
+ reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN;
+ reader->in_object = 1;
+ reader->in_array = 0;
+ break;
+
+ case '[':
+ reader->container_just_begun = 1;
+ json_reader_container_begins(reader, GRPC_JSON_ARRAY);
+ reader->depth++;
+ reader->in_object = 0;
+ reader->in_array = 1;
+ break;
+ }
+ break;
+
+ case GRPC_JSON_STATE_STRING_ESCAPE:
+ if (reader->escaped_string_was_key) {
+ reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+ } else {
+ reader->state = GRPC_JSON_STATE_VALUE_STRING;
+ }
+ if (reader->unicode_high_surrogate && c != 'u')
+ return GRPC_JSON_PARSE_ERROR;
+ switch (c) {
+ case '"':
+ case '/':
+ json_reader_string_add_char(reader, c);
+ break;
+ case 'b':
+ json_reader_string_add_char(reader, '\b');
+ break;
+ case 'f':
+ json_reader_string_add_char(reader, '\f');
+ break;
+ case 'n':
+ json_reader_string_add_char(reader, '\n');
+ break;
+ case 'r':
+ json_reader_string_add_char(reader, '\r');
+ break;
+ case 't':
+ json_reader_string_add_char(reader, '\t');
+ break;
+ case 'u':
+ reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1;
+ reader->unicode_char = 0;
+ break;
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ case GRPC_JSON_STATE_STRING_ESCAPE_U1:
+ case GRPC_JSON_STATE_STRING_ESCAPE_U2:
+ case GRPC_JSON_STATE_STRING_ESCAPE_U3:
+ case GRPC_JSON_STATE_STRING_ESCAPE_U4:
+ if ((c >= '0') && (c <= '9')) {
+ c -= '0';
+ } else if ((c >= 'A') && (c <= 'F')) {
+ c -= 'A' - 10;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ c -= 'a' - 10;
+ } else {
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ reader->unicode_char = (uint16_t)(reader->unicode_char << 4);
+ reader->unicode_char = (uint16_t)(reader->unicode_char | c);
+
+ switch (reader->state) {
+ case GRPC_JSON_STATE_STRING_ESCAPE_U1:
+ reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2;
+ break;
+ case GRPC_JSON_STATE_STRING_ESCAPE_U2:
+ reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3;
+ break;
+ case GRPC_JSON_STATE_STRING_ESCAPE_U3:
+ reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4;
+ break;
+ case GRPC_JSON_STATE_STRING_ESCAPE_U4:
+ /* See grpc_json_writer_escape_string to have a description
+ * of what's going on here.
+ */
+ if ((reader->unicode_char & 0xfc00) == 0xd800) {
+ /* high surrogate utf-16 */
+ if (reader->unicode_high_surrogate != 0)
+ return GRPC_JSON_PARSE_ERROR;
+ reader->unicode_high_surrogate = reader->unicode_char;
+ } else if ((reader->unicode_char & 0xfc00) == 0xdc00) {
+ /* low surrogate utf-16 */
+ uint32_t utf32;
+ if (reader->unicode_high_surrogate == 0)
+ return GRPC_JSON_PARSE_ERROR;
+ utf32 = 0x10000;
+ utf32 += (uint32_t)(
+ (reader->unicode_high_surrogate - 0xd800) * 0x400);
+ utf32 += (uint32_t)(reader->unicode_char - 0xdc00);
+ json_reader_string_add_utf32(reader, utf32);
+ reader->unicode_high_surrogate = 0;
+ } else {
+ /* anything else */
+ if (reader->unicode_high_surrogate != 0)
+ return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_add_utf32(reader, reader->unicode_char);
+ }
+ if (reader->escaped_string_was_key) {
+ reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING;
+ } else {
+ reader->state = GRPC_JSON_STATE_VALUE_STRING;
+ }
+ break;
+ default:
+ GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+ }
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER:
+ json_reader_string_add_char(reader, c);
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ case 'e':
+ case 'E':
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
+ break;
+ case '.':
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
+ break;
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL:
+ json_reader_string_add_char(reader, c);
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ case 'e':
+ case 'E':
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E;
+ break;
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER_ZERO:
+ if (c != '.') return GRPC_JSON_PARSE_ERROR;
+ json_reader_string_add_char(reader, c);
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER_DOT:
+ json_reader_string_add_char(reader, c);
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL;
+ break;
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER_E:
+ json_reader_string_add_char(reader, c);
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM;
+ break;
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NUMBER_EPM:
+ json_reader_string_add_char(reader, c);
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ case GRPC_JSON_STATE_VALUE_TRUE_R:
+ if (c != 'r') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_TRUE_U;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_TRUE_U:
+ if (c != 'u') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_TRUE_E;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_TRUE_E:
+ if (c != 'e') return GRPC_JSON_PARSE_ERROR;
+ json_reader_set_true(reader);
+ reader->state = GRPC_JSON_STATE_VALUE_END;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_FALSE_A:
+ if (c != 'a') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_FALSE_L;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_FALSE_L:
+ if (c != 'l') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_FALSE_S;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_FALSE_S:
+ if (c != 's') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_FALSE_E;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_FALSE_E:
+ if (c != 'e') return GRPC_JSON_PARSE_ERROR;
+ json_reader_set_false(reader);
+ reader->state = GRPC_JSON_STATE_VALUE_END;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NULL_U:
+ if (c != 'u') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_NULL_L1;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NULL_L1:
+ if (c != 'l') return GRPC_JSON_PARSE_ERROR;
+ reader->state = GRPC_JSON_STATE_VALUE_NULL_L2;
+ break;
+
+ case GRPC_JSON_STATE_VALUE_NULL_L2:
+ if (c != 'l') return GRPC_JSON_PARSE_ERROR;
+ json_reader_set_null(reader);
+ reader->state = GRPC_JSON_STATE_VALUE_END;
+ break;
+
+ /* All of the VALUE_END cases are handled in the specialized case
+ * above. */
+ case GRPC_JSON_STATE_VALUE_END:
+ switch (c) {
+ case ',':
+ case '}':
+ case ']':
+ GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+ break;
+
+ default:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ break;
+
+ case GRPC_JSON_STATE_END:
+ return GRPC_JSON_PARSE_ERROR;
+ }
+ }
+ }
+
+ GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR);
+}
diff --git a/src/core/lib/json/json_reader.h b/src/core/lib/json/json_reader.h
new file mode 100644
index 0000000000..37a838889d
--- /dev/null
+++ b/src/core/lib/json/json_reader.h
@@ -0,0 +1,160 @@
+/*
+ *
+ * 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_CORE_LIB_JSON_JSON_READER_H
+#define GRPC_CORE_LIB_JSON_JSON_READER_H
+
+#include <grpc/support/port_platform.h>
+#include "src/core/lib/json/json_common.h"
+
+typedef enum {
+ GRPC_JSON_STATE_OBJECT_KEY_BEGIN,
+ GRPC_JSON_STATE_OBJECT_KEY_STRING,
+ GRPC_JSON_STATE_OBJECT_KEY_END,
+ GRPC_JSON_STATE_VALUE_BEGIN,
+ GRPC_JSON_STATE_VALUE_STRING,
+ GRPC_JSON_STATE_STRING_ESCAPE,
+ GRPC_JSON_STATE_STRING_ESCAPE_U1,
+ GRPC_JSON_STATE_STRING_ESCAPE_U2,
+ GRPC_JSON_STATE_STRING_ESCAPE_U3,
+ GRPC_JSON_STATE_STRING_ESCAPE_U4,
+ GRPC_JSON_STATE_VALUE_NUMBER,
+ GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL,
+ GRPC_JSON_STATE_VALUE_NUMBER_ZERO,
+ GRPC_JSON_STATE_VALUE_NUMBER_DOT,
+ GRPC_JSON_STATE_VALUE_NUMBER_E,
+ GRPC_JSON_STATE_VALUE_NUMBER_EPM,
+ GRPC_JSON_STATE_VALUE_TRUE_R,
+ GRPC_JSON_STATE_VALUE_TRUE_U,
+ GRPC_JSON_STATE_VALUE_TRUE_E,
+ GRPC_JSON_STATE_VALUE_FALSE_A,
+ GRPC_JSON_STATE_VALUE_FALSE_L,
+ GRPC_JSON_STATE_VALUE_FALSE_S,
+ GRPC_JSON_STATE_VALUE_FALSE_E,
+ GRPC_JSON_STATE_VALUE_NULL_U,
+ GRPC_JSON_STATE_VALUE_NULL_L1,
+ GRPC_JSON_STATE_VALUE_NULL_L2,
+ GRPC_JSON_STATE_VALUE_END,
+ GRPC_JSON_STATE_END
+} grpc_json_reader_state;
+
+enum {
+ /* The first non-unicode value is 0x110000. But let's pick
+ * a value high enough to start our error codes from. These
+ * values are safe to return from the read_char function.
+ */
+ GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0,
+ GRPC_JSON_READ_CHAR_EAGAIN,
+ GRPC_JSON_READ_CHAR_ERROR
+};
+
+struct grpc_json_reader;
+
+typedef struct grpc_json_reader_vtable {
+ /* Clears your internal string scratchpad. */
+ void (*string_clear)(void *userdata);
+ /* Adds a char to the string scratchpad. */
+ void (*string_add_char)(void *userdata, uint32_t c);
+ /* Adds a utf32 char to the string scratchpad. */
+ 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);
+ /* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */
+ void (*container_begins)(void *userdata, grpc_json_type type);
+ /* Ends the current container. Must return the type of its parent. */
+ grpc_json_type (*container_ends)(void *userdata);
+ /* Your internal string scratchpad is an object's key. */
+ void (*set_key)(void *userdata);
+ /* Your internal string scratchpad is a string value. */
+ void (*set_string)(void *userdata);
+ /* Your internal string scratchpad is a numerical value. Return 1 if valid. */
+ int (*set_number)(void *userdata);
+ /* Sets the values true, false or null. */
+ void (*set_true)(void *userdata);
+ void (*set_false)(void *userdata);
+ void (*set_null)(void *userdata);
+} grpc_json_reader_vtable;
+
+typedef struct grpc_json_reader {
+ /* That structure is fully private, and initialized by grpc_json_reader_init.
+ * The definition is public so you can put it on your stack.
+ */
+
+ void *userdata;
+ grpc_json_reader_vtable *vtable;
+ int depth;
+ int in_object;
+ int in_array;
+ int escaped_string_was_key;
+ int container_just_begun;
+ uint16_t unicode_char, unicode_high_surrogate;
+ grpc_json_reader_state state;
+} grpc_json_reader;
+
+/* The return type of the parser. */
+typedef enum {
+ GRPC_JSON_DONE, /* The parser finished successfully. */
+ GRPC_JSON_EAGAIN, /* The parser yields to get more data. */
+ GRPC_JSON_READ_ERROR, /* The parser passes through a read error. */
+ GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */
+ GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */
+} grpc_json_reader_status;
+
+/* Call this function to start parsing the input. It will return the following:
+ * . GRPC_JSON_DONE if the input got eof, and the parsing finished
+ * successfully.
+ * . GRPC_JSON_EAGAIN if the read_char function returned again. Call the
+ * parser again as needed. It is okay to call the parser in polling mode,
+ * although a bit dull.
+ * . GRPC_JSON_READ_ERROR if the read_char function returned an error. The
+ * state isn't broken however, and the function can be called again if the
+ * error has been corrected. But please use the EAGAIN feature instead for
+ * consistency.
+ * . GRPC_JSON_PARSE_ERROR if the input was somehow invalid.
+ * . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid
+ * internal state.
+ */
+grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader);
+
+/* Call this function to initialize the reader structure. */
+void grpc_json_reader_init(grpc_json_reader *reader,
+ grpc_json_reader_vtable *vtable, void *userdata);
+
+/* You may call this from the read_char callback if you don't know where is the
+ * end of your input stream, and you'd like the json reader to hint you that it
+ * has completed reading its input, so you can return an EOF to it. Note that
+ * there might still be trailing whitespaces after that point.
+ */
+int grpc_json_reader_is_complete(grpc_json_reader *reader);
+
+#endif /* GRPC_CORE_LIB_JSON_JSON_READER_H */
diff --git a/src/core/lib/json/json_string.c b/src/core/lib/json/json_string.c
new file mode 100644
index 0000000000..8e6f1253dc
--- /dev/null
+++ b/src/core/lib/json/json_string.c
@@ -0,0 +1,379 @@
+/*
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.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
+ * the json data in memory.
+ *
+ * It also uses its own input as a scratchpad to store all of the decoded,
+ * unescaped strings. So we need to keep track of all these pointers in
+ * that opaque structure the reader will carry for us.
+ *
+ * Note that this works because the act of parsing json always reduces its
+ * input size, and never expands it.
+ */
+typedef struct {
+ grpc_json *top;
+ grpc_json *current_container;
+ grpc_json *current_value;
+ uint8_t *input;
+ uint8_t *key;
+ uint8_t *string;
+ uint8_t *string_ptr;
+ size_t remaining_input;
+} json_reader_userdata;
+
+/* This json writer will put everything in a big string.
+ * The point is that we allocate that string in chunks of 256 bytes.
+ */
+typedef struct {
+ char *output;
+ size_t free_space;
+ size_t string_len;
+ size_t allocated;
+} json_writer_userdata;
+
+/* This function checks if there's enough space left in the output buffer,
+ * and will enlarge it if necessary. We're only allocating chunks of 256
+ * bytes at a time (or multiples thereof).
+ */
+static void json_writer_output_check(void *userdata, size_t needed) {
+ json_writer_userdata *state = userdata;
+ if (state->free_space >= needed) return;
+ needed -= state->free_space;
+ /* Round up by 256 bytes. */
+ needed = (needed + 0xff) & ~0xffU;
+ state->output = gpr_realloc(state->output, state->allocated + needed);
+ state->free_space += needed;
+ state->allocated += needed;
+}
+
+/* These are needed by the writer's implementation. */
+static void json_writer_output_char(void *userdata, char c) {
+ json_writer_userdata *state = userdata;
+ json_writer_output_check(userdata, 1);
+ state->output[state->string_len++] = c;
+ state->free_space--;
+}
+
+static void json_writer_output_string_with_len(void *userdata, const char *str,
+ size_t len) {
+ json_writer_userdata *state = userdata;
+ json_writer_output_check(userdata, len);
+ memcpy(state->output + state->string_len, str, len);
+ state->string_len += len;
+ state->free_space -= len;
+}
+
+static void json_writer_output_string(void *userdata, const char *str) {
+ size_t len = strlen(str);
+ json_writer_output_string_with_len(userdata, str, len);
+}
+
+/* The reader asks us to clear our scratchpad. In our case, we'll simply mark
+ * the end of the current string, and advance our output pointer.
+ */
+static void json_reader_string_clear(void *userdata) {
+ json_reader_userdata *state = userdata;
+ if (state->string) {
+ GPR_ASSERT(state->string_ptr < state->input);
+ *state->string_ptr++ = 0;
+ }
+ state->string = state->string_ptr;
+}
+
+static void json_reader_string_add_char(void *userdata, uint32_t c) {
+ json_reader_userdata *state = userdata;
+ GPR_ASSERT(state->string_ptr < state->input);
+ GPR_ASSERT(c <= 0xff);
+ *state->string_ptr++ = (uint8_t)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) {
+ if (c <= 0x7f) {
+ json_reader_string_add_char(userdata, c);
+ } else if (c <= 0x7ff) {
+ uint32_t b1 = 0xc0 | ((c >> 6) & 0x1f);
+ uint32_t b2 = 0x80 | (c & 0x3f);
+ json_reader_string_add_char(userdata, b1);
+ json_reader_string_add_char(userdata, b2);
+ } else if (c <= 0xffff) {
+ uint32_t b1 = 0xe0 | ((c >> 12) & 0x0f);
+ uint32_t b2 = 0x80 | ((c >> 6) & 0x3f);
+ uint32_t b3 = 0x80 | (c & 0x3f);
+ json_reader_string_add_char(userdata, b1);
+ json_reader_string_add_char(userdata, b2);
+ json_reader_string_add_char(userdata, b3);
+ } else if (c <= 0x1fffff) {
+ uint32_t b1 = 0xf0 | ((c >> 18) & 0x07);
+ uint32_t b2 = 0x80 | ((c >> 12) & 0x3f);
+ uint32_t b3 = 0x80 | ((c >> 6) & 0x3f);
+ uint32_t b4 = 0x80 | (c & 0x3f);
+ json_reader_string_add_char(userdata, b1);
+ json_reader_string_add_char(userdata, b2);
+ json_reader_string_add_char(userdata, b3);
+ json_reader_string_add_char(userdata, b4);
+ }
+}
+
+/* We consider that the input may be a zero-terminated string. So we
+ * can end up hitting eof before the end of the alleged string length.
+ */
+static uint32_t json_reader_read_char(void *userdata) {
+ uint32_t r;
+ json_reader_userdata *state = userdata;
+
+ if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF;
+
+ r = *state->input++;
+ state->remaining_input--;
+
+ if (r == 0) {
+ state->remaining_input = 0;
+ return GRPC_JSON_READ_CHAR_EOF;
+ }
+
+ return r;
+}
+
+/* Helper function to create a new grpc_json object and link it into
+ * our tree-in-progress inside our opaque structure.
+ */
+static grpc_json *json_create_and_link(void *userdata, grpc_json_type type) {
+ json_reader_userdata *state = userdata;
+ grpc_json *json = grpc_json_create(type);
+
+ json->parent = state->current_container;
+ json->prev = state->current_value;
+ state->current_value = json;
+
+ if (json->prev) {
+ json->prev->next = json;
+ }
+ if (json->parent) {
+ if (!json->parent->child) {
+ json->parent->child = json;
+ }
+ if (json->parent->type == GRPC_JSON_OBJECT) {
+ json->key = (char *)state->key;
+ }
+ }
+ if (!state->top) {
+ state->top = json;
+ }
+
+ return json;
+}
+
+static void json_reader_container_begins(void *userdata, grpc_json_type type) {
+ json_reader_userdata *state = userdata;
+ grpc_json *container;
+
+ GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT);
+
+ container = json_create_and_link(userdata, type);
+ state->current_container = container;
+ state->current_value = NULL;
+}
+
+/* It's important to remember that the reader is mostly stateless, so it
+ * isn't trying to remember what the container was prior the one that just
+ * ends. Since we're keeping track of these for our own purpose, we are
+ * able to return that information back, which is useful for it to validate
+ * the input json stream.
+ *
+ * Also note that if we're at the top of the tree, and the last container
+ * ends, we have to return GRPC_JSON_TOP_LEVEL.
+ */
+static grpc_json_type json_reader_container_ends(void *userdata) {
+ grpc_json_type container_type = GRPC_JSON_TOP_LEVEL;
+ json_reader_userdata *state = userdata;
+
+ GPR_ASSERT(state->current_container);
+
+ state->current_value = state->current_container;
+ state->current_container = state->current_container->parent;
+
+ if (state->current_container) {
+ container_type = state->current_container->type;
+ }
+
+ return container_type;
+}
+
+/* The next 3 functions basically are the reader asking us to use our string
+ * scratchpad for one of these 3 purposes.
+ *
+ * Note that in the set_number case, we're not going to try interpreting it.
+ * We'll keep it as a string, and leave it to the caller to evaluate it.
+ */
+static void json_reader_set_key(void *userdata) {
+ json_reader_userdata *state = userdata;
+ state->key = state->string;
+}
+
+static void json_reader_set_string(void *userdata) {
+ json_reader_userdata *state = userdata;
+ grpc_json *json = json_create_and_link(userdata, GRPC_JSON_STRING);
+ json->value = (char *)state->string;
+}
+
+static int json_reader_set_number(void *userdata) {
+ json_reader_userdata *state = userdata;
+ grpc_json *json = json_create_and_link(userdata, GRPC_JSON_NUMBER);
+ json->value = (char *)state->string;
+ return 1;
+}
+
+/* The object types true, false and null are self-sufficient, and don't need
+ * any more information beside their type.
+ */
+static void json_reader_set_true(void *userdata) {
+ json_create_and_link(userdata, GRPC_JSON_TRUE);
+}
+
+static void json_reader_set_false(void *userdata) {
+ json_create_and_link(userdata, GRPC_JSON_FALSE);
+}
+
+static void json_reader_set_null(void *userdata) {
+ json_create_and_link(userdata, GRPC_JSON_NULL);
+}
+
+static grpc_json_reader_vtable reader_vtable = {
+ json_reader_string_clear, json_reader_string_add_char,
+ json_reader_string_add_utf32, json_reader_read_char,
+ json_reader_container_begins, json_reader_container_ends,
+ json_reader_set_key, json_reader_set_string,
+ json_reader_set_number, json_reader_set_true,
+ json_reader_set_false, json_reader_set_null};
+
+/* And finally, let's define our public API. */
+grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) {
+ grpc_json_reader reader;
+ json_reader_userdata state;
+ grpc_json *json = NULL;
+ grpc_json_reader_status status;
+
+ if (!input) return NULL;
+
+ state.top = state.current_container = state.current_value = NULL;
+ state.string = state.key = NULL;
+ state.string_ptr = state.input = (uint8_t *)input;
+ state.remaining_input = size;
+ grpc_json_reader_init(&reader, &reader_vtable, &state);
+
+ status = grpc_json_reader_run(&reader);
+ json = state.top;
+
+ if ((status != GRPC_JSON_DONE) && json) {
+ grpc_json_destroy(json);
+ json = NULL;
+ }
+
+ return json;
+}
+
+#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff
+
+grpc_json *grpc_json_parse_string(char *input) {
+ return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH);
+}
+
+static void json_dump_recursive(grpc_json_writer *writer, grpc_json *json,
+ int in_object) {
+ while (json) {
+ if (in_object) grpc_json_writer_object_key(writer, json->key);
+
+ switch (json->type) {
+ case GRPC_JSON_OBJECT:
+ case GRPC_JSON_ARRAY:
+ grpc_json_writer_container_begins(writer, json->type);
+ if (json->child)
+ json_dump_recursive(writer, json->child,
+ json->type == GRPC_JSON_OBJECT);
+ grpc_json_writer_container_ends(writer, json->type);
+ break;
+ case GRPC_JSON_STRING:
+ grpc_json_writer_value_string(writer, json->value);
+ break;
+ case GRPC_JSON_NUMBER:
+ grpc_json_writer_value_raw(writer, json->value);
+ break;
+ case GRPC_JSON_TRUE:
+ grpc_json_writer_value_raw_with_len(writer, "true", 4);
+ break;
+ case GRPC_JSON_FALSE:
+ grpc_json_writer_value_raw_with_len(writer, "false", 5);
+ break;
+ case GRPC_JSON_NULL:
+ grpc_json_writer_value_raw_with_len(writer, "null", 4);
+ break;
+ default:
+ GPR_UNREACHABLE_CODE(abort());
+ }
+ json = json->next;
+ }
+}
+
+static grpc_json_writer_vtable writer_vtable = {
+ json_writer_output_char, json_writer_output_string,
+ json_writer_output_string_with_len};
+
+char *grpc_json_dump_to_string(grpc_json *json, int indent) {
+ grpc_json_writer writer;
+ json_writer_userdata state;
+
+ state.output = NULL;
+ state.free_space = state.string_len = state.allocated = 0;
+ grpc_json_writer_init(&writer, indent, &writer_vtable, &state);
+
+ json_dump_recursive(&writer, json, 0);
+
+ json_writer_output_char(&state, 0);
+
+ return state.output;
+}
diff --git a/src/core/lib/json/json_writer.c b/src/core/lib/json/json_writer.c
new file mode 100644
index 0000000000..d614a72fc4
--- /dev/null
+++ b/src/core/lib/json/json_writer.c
@@ -0,0 +1,258 @@
+/*
+ *
+ * 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 <string.h>
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/lib/json/json_writer.h"
+
+static void json_writer_output_char(grpc_json_writer *writer, char c) {
+ writer->vtable->output_char(writer->userdata, c);
+}
+
+static void json_writer_output_string(grpc_json_writer *writer,
+ const char *str) {
+ writer->vtable->output_string(writer->userdata, str);
+}
+
+static void json_writer_output_string_with_len(grpc_json_writer *writer,
+ const char *str, size_t len) {
+ writer->vtable->output_string_with_len(writer->userdata, str, len);
+}
+
+void grpc_json_writer_init(grpc_json_writer *writer, int indent,
+ grpc_json_writer_vtable *vtable, void *userdata) {
+ memset(writer, 0, sizeof(*writer));
+ writer->container_empty = 1;
+ writer->indent = indent;
+ writer->vtable = vtable;
+ writer->userdata = userdata;
+}
+
+static void json_writer_output_indent(grpc_json_writer *writer) {
+ static const char spacesstr[] =
+ " "
+ " "
+ " "
+ " ";
+
+ unsigned spaces = (unsigned)(writer->depth * writer->indent);
+
+ if (writer->indent == 0) return;
+
+ if (writer->got_key) {
+ json_writer_output_char(writer, ' ');
+ return;
+ }
+
+ while (spaces >= (sizeof(spacesstr) - 1)) {
+ json_writer_output_string_with_len(writer, spacesstr,
+ sizeof(spacesstr) - 1);
+ spaces -= (unsigned)(sizeof(spacesstr) - 1);
+ }
+
+ if (spaces == 0) return;
+
+ json_writer_output_string_with_len(
+ writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces);
+}
+
+static void json_writer_value_end(grpc_json_writer *writer) {
+ if (writer->container_empty) {
+ writer->container_empty = 0;
+ if ((writer->indent == 0) || (writer->depth == 0)) return;
+ json_writer_output_char(writer, '\n');
+ } else {
+ json_writer_output_char(writer, ',');
+ if (writer->indent == 0) return;
+ json_writer_output_char(writer, '\n');
+ }
+}
+
+static void json_writer_escape_utf16(grpc_json_writer *writer, uint16_t utf16) {
+ static const char hex[] = "0123456789abcdef";
+
+ json_writer_output_string_with_len(writer, "\\u", 2);
+ json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]);
+ json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]);
+ json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]);
+ json_writer_output_char(writer, hex[(utf16)&0x0f]);
+}
+
+static void json_writer_escape_string(grpc_json_writer *writer,
+ const char *string) {
+ json_writer_output_char(writer, '"');
+
+ for (;;) {
+ uint8_t c = (uint8_t)*string++;
+ if (c == 0) {
+ break;
+ } else if ((c >= 32) && (c <= 126)) {
+ if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\');
+ json_writer_output_char(writer, (char)c);
+ } else if ((c < 32) || (c == 127)) {
+ switch (c) {
+ case '\b':
+ json_writer_output_string_with_len(writer, "\\b", 2);
+ break;
+ case '\f':
+ json_writer_output_string_with_len(writer, "\\f", 2);
+ break;
+ case '\n':
+ json_writer_output_string_with_len(writer, "\\n", 2);
+ break;
+ case '\r':
+ json_writer_output_string_with_len(writer, "\\r", 2);
+ break;
+ case '\t':
+ json_writer_output_string_with_len(writer, "\\t", 2);
+ break;
+ default:
+ json_writer_escape_utf16(writer, c);
+ break;
+ }
+ } else {
+ uint32_t utf32 = 0;
+ int extra = 0;
+ int i;
+ int valid = 1;
+ if ((c & 0xe0) == 0xc0) {
+ utf32 = c & 0x1f;
+ extra = 1;
+ } else if ((c & 0xf0) == 0xe0) {
+ utf32 = c & 0x0f;
+ extra = 2;
+ } else if ((c & 0xf8) == 0xf0) {
+ utf32 = c & 0x07;
+ extra = 3;
+ } else {
+ break;
+ }
+ for (i = 0; i < extra; i++) {
+ utf32 <<= 6;
+ c = (uint8_t)(*string++);
+ /* Breaks out and bail on any invalid UTF-8 sequence, including \0. */
+ if ((c & 0xc0) != 0x80) {
+ valid = 0;
+ break;
+ }
+ utf32 |= c & 0x3f;
+ }
+ if (!valid) break;
+ /* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam.
+ * Any other range is technically reserved for future usage, so if we
+ * don't want the software to break in the future, we have to allow
+ * anything else. The first non-unicode character is 0x110000. */
+ if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || (utf32 >= 0x110000))
+ break;
+ if (utf32 >= 0x10000) {
+ /* If utf32 contains a character that is above 0xffff, it needs to be
+ * broken down into a utf-16 surrogate pair. A surrogate pair is first
+ * a high surrogate, followed by a low surrogate. Each surrogate holds
+ * 10 bits of usable data, thus allowing a total of 20 bits of data.
+ * The high surrogate marker is 0xd800, while the low surrogate marker
+ * is 0xdc00. The low 10 bits of each will be the usable data.
+ *
+ * After re-combining the 20 bits of data, one has to add 0x10000 to
+ * the resulting value, in order to obtain the original character.
+ * This is obviously because the range 0x0000 - 0xffff can be written
+ * without any special trick.
+ *
+ * Since 0x10ffff is the highest allowed character, we're working in
+ * the range 0x00000 - 0xfffff after we decrement it by 0x10000.
+ * That range is exactly 20 bits.
+ */
+ utf32 -= 0x10000;
+ json_writer_escape_utf16(writer, (uint16_t)(0xd800 | (utf32 >> 10)));
+ json_writer_escape_utf16(writer, (uint16_t)(0xdc00 | (utf32 & 0x3ff)));
+ } else {
+ json_writer_escape_utf16(writer, (uint16_t)utf32);
+ }
+ }
+ }
+
+ json_writer_output_char(writer, '"');
+}
+
+void grpc_json_writer_container_begins(grpc_json_writer *writer,
+ grpc_json_type type) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '[');
+ writer->container_empty = 1;
+ writer->got_key = 0;
+ writer->depth++;
+}
+
+void grpc_json_writer_container_ends(grpc_json_writer *writer,
+ grpc_json_type type) {
+ if (writer->indent && !writer->container_empty)
+ json_writer_output_char(writer, '\n');
+ writer->depth--;
+ if (!writer->container_empty) json_writer_output_indent(writer);
+ json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']');
+ writer->container_empty = 0;
+ writer->got_key = 0;
+}
+
+void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string) {
+ json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_escape_string(writer, string);
+ json_writer_output_char(writer, ':');
+ writer->got_key = 1;
+}
+
+void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_output_string(writer, string);
+ writer->got_key = 0;
+}
+
+void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer,
+ const char *string, size_t len) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_output_string_with_len(writer, string, len);
+ writer->got_key = 0;
+}
+
+void grpc_json_writer_value_string(grpc_json_writer *writer,
+ const char *string) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_escape_string(writer, string);
+ writer->got_key = 0;
+}
diff --git a/src/core/lib/json/json_writer.h b/src/core/lib/json/json_writer.h
new file mode 100644
index 0000000000..f90e79cd74
--- /dev/null
+++ b/src/core/lib/json/json_writer.h
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* 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 <stdlib.h>
+
+#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/src/core/lib/profiling/basic_timers.c b/src/core/lib/profiling/basic_timers.c
new file mode 100644
index 0000000000..15a9584981
--- /dev/null
+++ b/src/core/lib/profiling/basic_timers.c
@@ -0,0 +1,274 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_BASIC_PROFILER
+
+#include "src/core/lib/profiling/timers.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <stdio.h>
+
+typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type;
+
+typedef struct gpr_timer_entry {
+ gpr_timespec tm;
+ const char *tagstr;
+ const char *file;
+ short line;
+ char type;
+ uint8_t important;
+ int thd;
+} gpr_timer_entry;
+
+#define MAX_COUNT 1000000
+
+typedef struct gpr_timer_log {
+ size_t num_entries;
+ 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;
+ /* valid iff head!=NULL */
+ gpr_timer_log *tail;
+} gpr_timer_log_list;
+
+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 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 __thread int g_thread_id;
+static int g_next_thread_id;
+
+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;
+ return 1;
+ } else {
+ log->prev = list->tail;
+ log->next = NULL;
+ list->tail->next = log;
+ list->tail = log;
+ return 0;
+ }
+}
+
+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) {
+ list->head->prev = NULL;
+ } else {
+ list->tail = NULL;
+ }
+ }
+ return out;
+}
+
+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) {
+ list->head->prev = NULL;
+ }
+ } else {
+ log->prev->next = log->next;
+ }
+ if (log->next == NULL) {
+ list->tail = log->prev;
+ if (list->tail != NULL) {
+ list->tail->next = NULL;
+ }
+ } else {
+ log->next->prev = log->prev;
+ }
+}
+
+static void write_log(gpr_timer_log *log) {
+ size_t i;
+ if (output_file == NULL) {
+ output_file = fopen(output_filename, "w");
+ }
+ for (i = 0; i < log->num_entries; 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\": "
+ "\"%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);
+ }
+}
+
+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) {
+ pthread_cond_wait(&g_cv, &g_mu);
+ }
+ if (log != NULL) {
+ pthread_mutex_unlock(&g_mu);
+ write_log(log);
+ free(log);
+ pthread_mutex_lock(&g_mu);
+ }
+ if (g_shutdown) {
+ pthread_mutex_unlock(&g_mu);
+ return;
+ }
+ }
+}
+
+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() {
+ pthread_mutex_lock(&g_mu);
+ g_shutdown = 1;
+ pthread_cond_signal(&g_cv);
+ pthread_mutex_unlock(&g_mu);
+ gpr_thd_join(g_writing_thread);
+
+ gpr_log(GPR_INFO, "flushing logs");
+
+ pthread_mutex_lock(&g_mu);
+ flush_logs(&g_done_logs);
+ flush_logs(&g_in_progress_logs);
+ pthread_mutex_unlock(&g_mu);
+
+ if (output_file) {
+ fclose(output_file);
+ }
+}
+
+void gpr_timers_set_log_filename(const char *filename) {
+ output_filename = 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);
+ atexit(finish_writing);
+}
+
+static void rotate_log() {
+ gpr_timer_log *new = malloc(sizeof(*new));
+ gpr_once_init(&g_once_init, init_output);
+ new->num_entries = 0;
+ pthread_mutex_lock(&g_mu);
+ if (g_thread_log != NULL) {
+ timer_log_remove(&g_in_progress_logs, g_thread_log);
+ if (timer_log_push_back(&g_done_logs, g_thread_log)) {
+ pthread_cond_signal(&g_cv);
+ }
+ } else {
+ g_thread_id = g_next_thread_id++;
+ }
+ timer_log_push_back(&g_in_progress_logs, new);
+ pthread_mutex_unlock(&g_mu);
+ g_thread_log = new;
+}
+
+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_thread_log == NULL || g_thread_log->num_entries == MAX_COUNT) {
+ rotate_log();
+ }
+
+ entry = &g_thread_log->log[g_thread_log->num_entries++];
+
+ entry->tm = gpr_now(GPR_CLOCK_PRECISE);
+ entry->tagstr = tagstr;
+ entry->type = type;
+ entry->file = file;
+ entry->line = (short)line;
+ entry->important = important != 0;
+ entry->thd = g_thread_id;
+}
+
+/* Latency profiler API implementation. */
+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,
+ int line) {
+ gpr_timers_log_add(tagstr, BEGIN, important, file, line);
+}
+
+void gpr_timer_end(const char *tagstr, int important, const char *file,
+ int line) {
+ gpr_timers_log_add(tagstr, END, important, file, line);
+}
+
+/* Basic profiler specific API functions. */
+void gpr_timers_global_init(void) {}
+
+void gpr_timers_global_destroy(void) {}
+
+#else /* !GRPC_BASIC_PROFILER */
+void gpr_timers_global_init(void) {}
+
+void gpr_timers_global_destroy(void) {}
+
+void gpr_timers_set_log_filename(const char *filename) {}
+#endif /* GRPC_BASIC_PROFILER */
diff --git a/src/core/lib/profiling/stap_probes.d b/src/core/lib/profiling/stap_probes.d
new file mode 100644
index 0000000000..153de91752
--- /dev/null
+++ b/src/core/lib/profiling/stap_probes.d
@@ -0,0 +1,7 @@
+provider _stap {
+ probe add_mark(int tag);
+ probe add_important_mark(int tag);
+ probe timing_ns_begin(int tag);
+ probe timing_ns_end(int tag);
+};
+
diff --git a/src/core/lib/profiling/stap_timers.c b/src/core/lib/profiling/stap_timers.c
new file mode 100644
index 0000000000..f55c1a569a
--- /dev/null
+++ b/src/core/lib/profiling/stap_timers.c
@@ -0,0 +1,65 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GRPC_STAP_PROFILER
+
+#include "src/core/lib/profiling/timers.h"
+
+#include <sys/sdt.h>
+/* 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/src/core/lib/profiling/timers.h b/src/core/lib/profiling/timers.h
new file mode 100644
index 0000000000..1303593ffb
--- /dev/null
+++ b/src/core/lib/profiling/timers.h
@@ -0,0 +1,119 @@
+/*
+ *
+ * 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_CORE_LIB_PROFILING_TIMERS_H
+#define GRPC_CORE_LIB_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_LIB_PROFILING_TIMERS_H */
diff --git a/src/core/lib/proto/grpc/lb/v0/load_balancer.pb.c b/src/core/lib/proto/grpc/lb/v0/load_balancer.pb.c
new file mode 100644
index 0000000000..8f82141f96
--- /dev/null
+++ b/src/core/lib/proto/grpc/lb/v0/load_balancer.pb.c
@@ -0,0 +1,119 @@
+/*
+ *
+ * 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.
+ *
+ */
+/* Automatically generated nanopb constant definitions */
+/* Generated by nanopb-0.3.5-dev */
+
+#include "src/core/lib/proto/grpc/lb/v0/load_balancer.pb.h"
+
+#if PB_PROTO_HEADER_VERSION != 30
+#error Regenerate this file with the current version of nanopb generator.
+#endif
+
+
+
+const pb_field_t grpc_lb_v0_Duration_fields[3] = {
+ PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v0_Duration, seconds, seconds, 0),
+ PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Duration, nanos, seconds, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_LoadBalanceRequest_fields[3] = {
+ PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v0_LoadBalanceRequest, initial_request, initial_request, &grpc_lb_v0_InitialLoadBalanceRequest_fields),
+ PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_LoadBalanceRequest, client_stats, initial_request, &grpc_lb_v0_ClientStats_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_InitialLoadBalanceRequest_fields[2] = {
+ PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v0_InitialLoadBalanceRequest, name, name, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_ClientStats_fields[4] = {
+ PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v0_ClientStats, total_requests, total_requests, 0),
+ PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_ClientStats, client_rpc_errors, total_requests, 0),
+ PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_ClientStats, dropped_requests, client_rpc_errors, 0),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_LoadBalanceResponse_fields[3] = {
+ PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v0_LoadBalanceResponse, initial_response, initial_response, &grpc_lb_v0_InitialLoadBalanceResponse_fields),
+ PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_LoadBalanceResponse, server_list, initial_response, &grpc_lb_v0_ServerList_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_InitialLoadBalanceResponse_fields[4] = {
+ PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v0_InitialLoadBalanceResponse, client_config, client_config, 0),
+ PB_FIELD( 2, STRING , OPTIONAL, STATIC , OTHER, grpc_lb_v0_InitialLoadBalanceResponse, load_balancer_delegate, client_config, 0),
+ PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_InitialLoadBalanceResponse, client_stats_report_interval, load_balancer_delegate, &grpc_lb_v0_Duration_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_ServerList_fields[3] = {
+ PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, grpc_lb_v0_ServerList, servers, servers, &grpc_lb_v0_Server_fields),
+ PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v0_ServerList, expiration_interval, servers, &grpc_lb_v0_Duration_fields),
+ PB_LAST_FIELD
+};
+
+const pb_field_t grpc_lb_v0_Server_fields[5] = {
+ PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v0_Server, ip_address, ip_address, 0),
+ PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Server, port, ip_address, 0),
+ PB_FIELD( 3, BYTES , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Server, load_balance_token, port, 0),
+ PB_FIELD( 4, BOOL , OPTIONAL, STATIC , OTHER, grpc_lb_v0_Server, drop_request, 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_v0_LoadBalanceRequest, initial_request) < 65536 && pb_membersize(grpc_lb_v0_LoadBalanceRequest, client_stats) < 65536 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, initial_response) < 65536 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, server_list) < 65536 && pb_membersize(grpc_lb_v0_InitialLoadBalanceResponse, client_stats_report_interval) < 65536 && pb_membersize(grpc_lb_v0_ServerList, servers) < 65536 && pb_membersize(grpc_lb_v0_ServerList, expiration_interval) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_lb_v0_Duration_grpc_lb_v0_LoadBalanceRequest_grpc_lb_v0_InitialLoadBalanceRequest_grpc_lb_v0_ClientStats_grpc_lb_v0_LoadBalanceResponse_grpc_lb_v0_InitialLoadBalanceResponse_grpc_lb_v0_ServerList_grpc_lb_v0_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_v0_LoadBalanceRequest, initial_request) < 256 && pb_membersize(grpc_lb_v0_LoadBalanceRequest, client_stats) < 256 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, initial_response) < 256 && pb_membersize(grpc_lb_v0_LoadBalanceResponse, server_list) < 256 && pb_membersize(grpc_lb_v0_InitialLoadBalanceResponse, client_stats_report_interval) < 256 && pb_membersize(grpc_lb_v0_ServerList, servers) < 256 && pb_membersize(grpc_lb_v0_ServerList, expiration_interval) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_lb_v0_Duration_grpc_lb_v0_LoadBalanceRequest_grpc_lb_v0_InitialLoadBalanceRequest_grpc_lb_v0_ClientStats_grpc_lb_v0_LoadBalanceResponse_grpc_lb_v0_InitialLoadBalanceResponse_grpc_lb_v0_ServerList_grpc_lb_v0_Server)
+#endif
+
+
diff --git a/src/core/lib/proto/grpc/lb/v0/load_balancer.pb.h b/src/core/lib/proto/grpc/lb/v0/load_balancer.pb.h
new file mode 100644
index 0000000000..3599f881bb
--- /dev/null
+++ b/src/core/lib/proto/grpc/lb/v0/load_balancer.pb.h
@@ -0,0 +1,182 @@
+/*
+ *
+ * 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.
+ *
+ */
+/* Automatically generated nanopb header */
+/* Generated by nanopb-0.3.5-dev */
+
+#ifndef PB_LOAD_BALANCER_PB_H_INCLUDED
+#define PB_LOAD_BALANCER_PB_H_INCLUDED
+#include "third_party/nanopb/pb.h"
+#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_v0_ClientStats {
+ bool has_total_requests;
+ int64_t total_requests;
+ bool has_client_rpc_errors;
+ int64_t client_rpc_errors;
+ bool has_dropped_requests;
+ int64_t dropped_requests;
+} grpc_lb_v0_ClientStats;
+
+typedef struct _grpc_lb_v0_Duration {
+ bool has_seconds;
+ int64_t seconds;
+ bool has_nanos;
+ int32_t nanos;
+} grpc_lb_v0_Duration;
+
+typedef struct _grpc_lb_v0_InitialLoadBalanceRequest {
+ bool has_name;
+ char name[128];
+} grpc_lb_v0_InitialLoadBalanceRequest;
+
+typedef PB_BYTES_ARRAY_T(64) grpc_lb_v0_Server_load_balance_token_t;
+typedef struct _grpc_lb_v0_Server {
+ bool has_ip_address;
+ char ip_address[46];
+ bool has_port;
+ int32_t port;
+ bool has_load_balance_token;
+ grpc_lb_v0_Server_load_balance_token_t load_balance_token;
+ bool has_drop_request;
+ bool drop_request;
+} grpc_lb_v0_Server;
+
+typedef struct _grpc_lb_v0_InitialLoadBalanceResponse {
+ bool has_client_config;
+ char client_config[64];
+ bool has_load_balancer_delegate;
+ char load_balancer_delegate[64];
+ bool has_client_stats_report_interval;
+ grpc_lb_v0_Duration client_stats_report_interval;
+} grpc_lb_v0_InitialLoadBalanceResponse;
+
+typedef struct _grpc_lb_v0_LoadBalanceRequest {
+ bool has_initial_request;
+ grpc_lb_v0_InitialLoadBalanceRequest initial_request;
+ bool has_client_stats;
+ grpc_lb_v0_ClientStats client_stats;
+} grpc_lb_v0_LoadBalanceRequest;
+
+typedef struct _grpc_lb_v0_ServerList {
+ pb_callback_t servers;
+ bool has_expiration_interval;
+ grpc_lb_v0_Duration expiration_interval;
+} grpc_lb_v0_ServerList;
+
+typedef struct _grpc_lb_v0_LoadBalanceResponse {
+ bool has_initial_response;
+ grpc_lb_v0_InitialLoadBalanceResponse initial_response;
+ bool has_server_list;
+ grpc_lb_v0_ServerList server_list;
+} grpc_lb_v0_LoadBalanceResponse;
+
+/* Default values for struct fields */
+
+/* Initializer values for message structs */
+#define grpc_lb_v0_Duration_init_default {false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceRequest_init_default {false, grpc_lb_v0_InitialLoadBalanceRequest_init_default, false, grpc_lb_v0_ClientStats_init_default}
+#define grpc_lb_v0_InitialLoadBalanceRequest_init_default {false, ""}
+#define grpc_lb_v0_ClientStats_init_default {false, 0, false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceResponse_init_default {false, grpc_lb_v0_InitialLoadBalanceResponse_init_default, false, grpc_lb_v0_ServerList_init_default}
+#define grpc_lb_v0_InitialLoadBalanceResponse_init_default {false, "", false, "", false, grpc_lb_v0_Duration_init_default}
+#define grpc_lb_v0_ServerList_init_default {{{NULL}, NULL}, false, grpc_lb_v0_Duration_init_default}
+#define grpc_lb_v0_Server_init_default {false, "", false, 0, false, {0, {0}}, false, 0}
+#define grpc_lb_v0_Duration_init_zero {false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceRequest_init_zero {false, grpc_lb_v0_InitialLoadBalanceRequest_init_zero, false, grpc_lb_v0_ClientStats_init_zero}
+#define grpc_lb_v0_InitialLoadBalanceRequest_init_zero {false, ""}
+#define grpc_lb_v0_ClientStats_init_zero {false, 0, false, 0, false, 0}
+#define grpc_lb_v0_LoadBalanceResponse_init_zero {false, grpc_lb_v0_InitialLoadBalanceResponse_init_zero, false, grpc_lb_v0_ServerList_init_zero}
+#define grpc_lb_v0_InitialLoadBalanceResponse_init_zero {false, "", false, "", false, grpc_lb_v0_Duration_init_zero}
+#define grpc_lb_v0_ServerList_init_zero {{{NULL}, NULL}, false, grpc_lb_v0_Duration_init_zero}
+#define grpc_lb_v0_Server_init_zero {false, "", false, 0, false, {0, {0}}, false, 0}
+
+/* Field tags (for use in manual encoding/decoding) */
+#define grpc_lb_v0_ClientStats_total_requests_tag 1
+#define grpc_lb_v0_ClientStats_client_rpc_errors_tag 2
+#define grpc_lb_v0_ClientStats_dropped_requests_tag 3
+#define grpc_lb_v0_Duration_seconds_tag 1
+#define grpc_lb_v0_Duration_nanos_tag 2
+#define grpc_lb_v0_InitialLoadBalanceRequest_name_tag 1
+#define grpc_lb_v0_Server_ip_address_tag 1
+#define grpc_lb_v0_Server_port_tag 2
+#define grpc_lb_v0_Server_load_balance_token_tag 3
+#define grpc_lb_v0_Server_drop_request_tag 4
+#define grpc_lb_v0_InitialLoadBalanceResponse_client_config_tag 1
+#define grpc_lb_v0_InitialLoadBalanceResponse_load_balancer_delegate_tag 2
+#define grpc_lb_v0_InitialLoadBalanceResponse_client_stats_report_interval_tag 3
+#define grpc_lb_v0_LoadBalanceRequest_initial_request_tag 1
+#define grpc_lb_v0_LoadBalanceRequest_client_stats_tag 2
+#define grpc_lb_v0_ServerList_servers_tag 1
+#define grpc_lb_v0_ServerList_expiration_interval_tag 3
+#define grpc_lb_v0_LoadBalanceResponse_initial_response_tag 1
+#define grpc_lb_v0_LoadBalanceResponse_server_list_tag 2
+
+/* Struct field encoding specification for nanopb */
+extern const pb_field_t grpc_lb_v0_Duration_fields[3];
+extern const pb_field_t grpc_lb_v0_LoadBalanceRequest_fields[3];
+extern const pb_field_t grpc_lb_v0_InitialLoadBalanceRequest_fields[2];
+extern const pb_field_t grpc_lb_v0_ClientStats_fields[4];
+extern const pb_field_t grpc_lb_v0_LoadBalanceResponse_fields[3];
+extern const pb_field_t grpc_lb_v0_InitialLoadBalanceResponse_fields[4];
+extern const pb_field_t grpc_lb_v0_ServerList_fields[3];
+extern const pb_field_t grpc_lb_v0_Server_fields[5];
+
+/* Maximum encoded size of messages (where known) */
+#define grpc_lb_v0_Duration_size 22
+#define grpc_lb_v0_LoadBalanceRequest_size 169
+#define grpc_lb_v0_InitialLoadBalanceRequest_size 131
+#define grpc_lb_v0_ClientStats_size 33
+#define grpc_lb_v0_LoadBalanceResponse_size (165 + grpc_lb_v0_ServerList_size)
+#define grpc_lb_v0_InitialLoadBalanceResponse_size 156
+#define grpc_lb_v0_Server_size 127
+
+/* Message IDs (where set with "msgid" option) */
+#ifdef PB_MSGID
+
+#define LOAD_BALANCER_MESSAGES \
+
+
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/src/core/lib/security/auth_filters.h b/src/core/lib/security/auth_filters.h
new file mode 100644
index 0000000000..162b60e2c8
--- /dev/null
+++ b/src/core/lib/security/auth_filters.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_AUTH_FILTERS_H
+#define GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H
+
+#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;
+
+#endif /* GRPC_CORE_LIB_SECURITY_AUTH_FILTERS_H */
diff --git a/src/core/lib/security/b64.c b/src/core/lib/security/b64.c
new file mode 100644
index 0000000000..1d3879534c
--- /dev/null
+++ b/src/core/lib/security/b64.c
@@ -0,0 +1,233 @@
+/*
+ *
+ * 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/lib/security/b64.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+/* --- Constants. --- */
+
+static const int8_t base64_bytes[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 0x3E, -1, -1, -1, 0x3F,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1, -1,
+ -1, 0x7F, -1, -1, -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12,
+ 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, -1, -1, -1, -1, -1,
+ -1, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
+ 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30,
+ 0x31, 0x32, 0x33, -1, -1, -1, -1, -1};
+
+static const char base64_url_unsafe_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char base64_url_safe_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+#define GRPC_BASE64_PAD_CHAR '='
+#define GRPC_BASE64_PAD_BYTE 0x7F
+#define GRPC_BASE64_MULTILINE_LINE_LEN 76
+#define GRPC_BASE64_MULTILINE_NUM_BLOCKS (GRPC_BASE64_MULTILINE_LINE_LEN / 4)
+
+/* --- base64 functions. --- */
+
+char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe,
+ int multiline) {
+ const unsigned char *data = vdata;
+ const char *base64_chars =
+ url_safe ? base64_url_safe_chars : base64_url_unsafe_chars;
+ size_t result_projected_size =
+ 4 * ((data_size + 3) / 3) +
+ 2 * (multiline ? (data_size / (3 * GRPC_BASE64_MULTILINE_NUM_BLOCKS))
+ : 0) +
+ 1;
+ char *result = gpr_malloc(result_projected_size);
+ char *current = result;
+ size_t num_blocks = 0;
+ size_t i = 0;
+
+ /* Encode each block. */
+ while (data_size >= 3) {
+ *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+ *current++ =
+ base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)];
+ *current++ =
+ base64_chars[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)];
+ *current++ = base64_chars[data[i + 2] & 0x3F];
+
+ data_size -= 3;
+ i += 3;
+ if (multiline && (++num_blocks == GRPC_BASE64_MULTILINE_NUM_BLOCKS)) {
+ *current++ = '\r';
+ *current++ = '\n';
+ num_blocks = 0;
+ }
+ }
+
+ /* Take care of the tail. */
+ if (data_size == 2) {
+ *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+ *current++ =
+ base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)];
+ *current++ = base64_chars[(data[i + 1] & 0x0F) << 2];
+ *current++ = GRPC_BASE64_PAD_CHAR;
+ } else if (data_size == 1) {
+ *current++ = base64_chars[(data[i] >> 2) & 0x3F];
+ *current++ = base64_chars[(data[i] & 0x03) << 4];
+ *current++ = GRPC_BASE64_PAD_CHAR;
+ *current++ = GRPC_BASE64_PAD_CHAR;
+ }
+
+ GPR_ASSERT(current >= result);
+ GPR_ASSERT((uintptr_t)(current - result) < result_projected_size);
+ result[current - result] = '\0';
+ return result;
+}
+
+gpr_slice grpc_base64_decode(const char *b64, int url_safe) {
+ return grpc_base64_decode_with_len(b64, strlen(b64), url_safe);
+}
+
+static void decode_one_char(const unsigned char *codes, unsigned char *result,
+ size_t *result_offset) {
+ uint32_t packed = ((uint32_t)codes[0] << 2) | ((uint32_t)codes[1] >> 4);
+ result[(*result_offset)++] = (unsigned char)packed;
+}
+
+static void decode_two_chars(const unsigned char *codes, unsigned char *result,
+ size_t *result_offset) {
+ 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 int decode_group(const unsigned char *codes, size_t num_codes,
+ unsigned char *result, size_t *result_offset) {
+ GPR_ASSERT(num_codes <= 4);
+
+ /* Short end groups that may not have padding. */
+ if (num_codes == 1) {
+ gpr_log(GPR_ERROR, "Invalid group. Must be at least 2 bytes.");
+ return 0;
+ }
+ if (num_codes == 2) {
+ decode_one_char(codes, result, result_offset);
+ return 1;
+ }
+ if (num_codes == 3) {
+ decode_two_chars(codes, result, result_offset);
+ return 1;
+ }
+
+ /* Regular 4 byte groups with padding or not. */
+ GPR_ASSERT(num_codes == 4);
+ if (codes[0] == GRPC_BASE64_PAD_BYTE || codes[1] == GRPC_BASE64_PAD_BYTE) {
+ gpr_log(GPR_ERROR, "Invalid padding detected.");
+ return 0;
+ }
+ if (codes[2] == GRPC_BASE64_PAD_BYTE) {
+ if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+ decode_one_char(codes, result, result_offset);
+ } else {
+ gpr_log(GPR_ERROR, "Invalid padding detected.");
+ return 0;
+ }
+ } else if (codes[3] == GRPC_BASE64_PAD_BYTE) {
+ decode_two_chars(codes, result, result_offset);
+ } else {
+ /* No padding. */
+ 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);
+ }
+ return 1;
+}
+
+gpr_slice grpc_base64_decode_with_len(const char *b64, size_t b64_len,
+ int url_safe) {
+ gpr_slice result = gpr_slice_malloc(b64_len);
+ unsigned char *current = GPR_SLICE_START_PTR(result);
+ size_t result_size = 0;
+ unsigned char codes[4];
+ size_t num_codes = 0;
+
+ while (b64_len--) {
+ unsigned char c = (unsigned char)(*b64++);
+ signed char code;
+ if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue;
+ if (url_safe) {
+ if (c == '+' || c == '/') {
+ gpr_log(GPR_ERROR, "Invalid character for url safe base64 %c", c);
+ goto fail;
+ }
+ if (c == '-') {
+ c = '+';
+ } else if (c == '_') {
+ c = '/';
+ }
+ }
+ code = base64_bytes[c];
+ if (code == -1) {
+ if (c != '\r' && c != '\n') {
+ gpr_log(GPR_ERROR, "Invalid character %c", c);
+ goto fail;
+ }
+ } else {
+ codes[num_codes++] = (unsigned char)code;
+ if (num_codes == 4) {
+ if (!decode_group(codes, num_codes, current, &result_size)) goto fail;
+ num_codes = 0;
+ }
+ }
+ }
+
+ if (num_codes != 0 &&
+ !decode_group(codes, num_codes, current, &result_size)) {
+ goto fail;
+ }
+ GPR_SLICE_SET_LENGTH(result, result_size);
+ return result;
+
+fail:
+ gpr_slice_unref(result);
+ return gpr_empty_slice();
+}
diff --git a/src/core/lib/security/b64.h b/src/core/lib/security/b64.h
new file mode 100644
index 0000000000..0bf372a1e7
--- /dev/null
+++ b/src/core/lib/security/b64.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_B64_H
+#define GRPC_CORE_LIB_SECURITY_B64_H
+
+#include <grpc/support/slice.h>
+
+/* 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_CORE_LIB_SECURITY_B64_H */
diff --git a/src/core/lib/security/client_auth_filter.c b/src/core/lib/security/client_auth_filter.c
new file mode 100644
index 0000000000..b9e5bf0339
--- /dev/null
+++ b/src/core/lib/security/client_auth_filter.c
@@ -0,0 +1,336 @@
+/*
+ *
+ * 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/lib/security/auth_filters.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/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 */
+ 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/src/core/lib/security/credentials.c b/src/core/lib/security/credentials.c
new file mode 100644
index 0000000000..99a07e5c13
--- /dev/null
+++ b/src/core/lib/security/credentials.c
@@ -0,0 +1,1281 @@
+/*
+ *
+ * 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/lib/security/credentials.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/http_client_filter.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/support/string.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+/* -- 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_server_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);
+}
+
+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) {
+ 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.vtable = &cred_ptr_vtable;
+ 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_server_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_http_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_http_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_http_header header = {"Metadata-Flavor", "Google"};
+ grpc_httpcli_request request;
+ memset(&request, 0, sizeof(grpc_httpcli_request));
+ request.host = GRPC_COMPUTE_ENGINE_METADATA_HOST;
+ request.http.path = GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH;
+ request.http.hdr_count = 1;
+ request.http.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_http_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.http.path = GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH;
+ request.http.hdr_count = 1;
+ request.http.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_server_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/src/core/lib/security/credentials.h b/src/core/lib/security/credentials.h
new file mode 100644
index 0000000000..7168b98942
--- /dev/null
+++ b/src/core/lib/security/credentials.h
@@ -0,0 +1,377 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_CREDENTIALS_H
+#define GRPC_CORE_LIB_SECURITY_CREDENTIALS_H
+
+#include <grpc/grpc.h>
+#include <grpc/grpc_security.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/transport/metadata_batch.h"
+
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/security/json_token.h"
+#include "src/core/lib/security/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_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_http_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_server_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_server_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_CORE_LIB_SECURITY_CREDENTIALS_H */
diff --git a/src/core/lib/security/credentials_metadata.c b/src/core/lib/security/credentials_metadata.c
new file mode 100644
index 0000000000..c3bfcb11b5
--- /dev/null
+++ b/src/core/lib/security/credentials_metadata.c
@@ -0,0 +1,101 @@
+/*
+ *
+ * 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/lib/security/credentials.h"
+
+#include <grpc/support/alloc.h>
+
+#include <string.h>
+
+static void store_ensure_capacity(grpc_credentials_md_store *store) {
+ if (store->num_entries == store->allocated) {
+ store->allocated = (store->allocated == 0) ? 1 : store->allocated * 2;
+ store->entries = gpr_realloc(
+ store->entries, store->allocated * sizeof(grpc_credentials_md));
+ }
+}
+
+grpc_credentials_md_store *grpc_credentials_md_store_create(
+ size_t initial_capacity) {
+ grpc_credentials_md_store *store =
+ gpr_malloc(sizeof(grpc_credentials_md_store));
+ memset(store, 0, sizeof(grpc_credentials_md_store));
+ if (initial_capacity > 0) {
+ store->entries = gpr_malloc(initial_capacity * sizeof(grpc_credentials_md));
+ store->allocated = initial_capacity;
+ }
+ gpr_ref_init(&store->refcount, 1);
+ return store;
+}
+
+void grpc_credentials_md_store_add(grpc_credentials_md_store *store,
+ gpr_slice key, gpr_slice value) {
+ if (store == NULL) return;
+ store_ensure_capacity(store);
+ store->entries[store->num_entries].key = gpr_slice_ref(key);
+ store->entries[store->num_entries].value = gpr_slice_ref(value);
+ store->num_entries++;
+}
+
+void grpc_credentials_md_store_add_cstrings(grpc_credentials_md_store *store,
+ const char *key,
+ const char *value) {
+ if (store == NULL) return;
+ store_ensure_capacity(store);
+ store->entries[store->num_entries].key = gpr_slice_from_copied_string(key);
+ store->entries[store->num_entries].value =
+ gpr_slice_from_copied_string(value);
+ store->num_entries++;
+}
+
+grpc_credentials_md_store *grpc_credentials_md_store_ref(
+ grpc_credentials_md_store *store) {
+ if (store == NULL) return NULL;
+ gpr_ref(&store->refcount);
+ return store;
+}
+
+void grpc_credentials_md_store_unref(grpc_credentials_md_store *store) {
+ if (store == NULL) return;
+ if (gpr_unref(&store->refcount)) {
+ if (store->entries != NULL) {
+ size_t i;
+ for (i = 0; i < store->num_entries; i++) {
+ gpr_slice_unref(store->entries[i].key);
+ gpr_slice_unref(store->entries[i].value);
+ }
+ gpr_free(store->entries);
+ }
+ gpr_free(store);
+ }
+}
diff --git a/src/core/lib/security/credentials_posix.c b/src/core/lib/security/credentials_posix.c
new file mode 100644
index 0000000000..b758cd0a1a
--- /dev/null
+++ b/src/core/lib/security/credentials_posix.c
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_FILE
+
+#include "src/core/lib/security/credentials.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/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/src/core/lib/security/credentials_win32.c b/src/core/lib/security/credentials_win32.c
new file mode 100644
index 0000000000..a225ab0d7d
--- /dev/null
+++ b/src/core/lib/security/credentials_win32.c
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include "src/core/lib/security/credentials.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/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/src/core/lib/security/google_default_credentials.c b/src/core/lib/security/google_default_credentials.c
new file mode 100644
index 0000000000..5c342288cc
--- /dev/null
+++ b/src/core/lib/security/google_default_credentials.c
@@ -0,0 +1,266 @@
+/*
+ *
+ * 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/lib/security/credentials.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/http/parser.h"
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/support/load_file.h"
+#include "src/core/lib/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_state_mu;
+static gpr_mu *g_polling_mu;
+static gpr_once g_once = GPR_ONCE_INIT;
+
+static void init_default_credentials(void) { gpr_mu_init(&g_state_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_http_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_http_header *header = &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_pollset_kick(detector->pollset, NULL);
+ gpr_mu_unlock(g_polling_mu);
+}
+
+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);
+
+ detector.pollset = gpr_malloc(grpc_pollset_size());
+ grpc_pollset_init(detector.pollset, &g_polling_mu);
+ detector.is_done = 0;
+ detector.success = 0;
+
+ memset(&request, 0, sizeof(grpc_httpcli_request));
+ request.host = GRPC_COMPUTE_ENGINE_DETECTION_HOST;
+ request.http.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(g_polling_mu);
+ while (!detector.is_done) {
+ grpc_pollset_worker *worker = NULL;
+ grpc_pollset_work(&exec_ctx, detector.pollset, &worker,
+ gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_inf_future(GPR_CLOCK_MONOTONIC));
+ }
+ gpr_mu_unlock(g_polling_mu);
+
+ 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);
+ g_polling_mu = NULL;
+
+ gpr_free(detector.pollset);
+
+ 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_state_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_state_mu);
+ return result;
+}
+
+void grpc_flush_cached_google_default_credentials(void) {
+ gpr_once_init(&g_once, init_default_credentials);
+ gpr_mu_lock(&g_state_mu);
+ if (default_credentials != NULL) {
+ grpc_channel_credentials_unref(default_credentials);
+ default_credentials = NULL;
+ }
+ compute_engine_detection_done = 0;
+ gpr_mu_unlock(&g_state_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/src/core/lib/security/handshake.c b/src/core/lib/security/handshake.c
new file mode 100644
index 0000000000..adb6d7fe4e
--- /dev/null
+++ b/src/core/lib/security/handshake.c
@@ -0,0 +1,336 @@
+/*
+ *
+ * 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/lib/security/handshake.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include "src/core/lib/security/secure_endpoint.h"
+#include "src/core/lib/security/security_context.h"
+
+#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256
+
+typedef struct {
+ grpc_security_connector *connector;
+ tsi_handshaker *handshaker;
+ bool is_client_side;
+ 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) {
+ GPR_ASSERT(!h->is_client_side);
+ grpc_security_connector_handshake_list *node;
+ grpc_security_connector_handshake_list *tmp;
+ grpc_server_security_connector *sc =
+ (grpc_server_security_connector *)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->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,
+ bool is_client_side,
+ 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->is_client_side = is_client_side;
+ 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 (!is_client_side) {
+ grpc_server_security_connector *server_connector =
+ (grpc_server_security_connector *)connector;
+ handshake_node = gpr_malloc(sizeof(grpc_security_connector_handshake_list));
+ handshake_node->handshake = h;
+ gpr_mu_lock(&server_connector->mu);
+ handshake_node->next = server_connector->handshaking_handshakes;
+ server_connector->handshaking_handshakes = handshake_node;
+ gpr_mu_unlock(&server_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/src/core/lib/security/handshake.h b/src/core/lib/security/handshake.h
new file mode 100644
index 0000000000..b5d7bb3282
--- /dev/null
+++ b/src/core/lib/security/handshake.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_HANDSHAKE_H
+#define GRPC_CORE_LIB_SECURITY_HANDSHAKE_H
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/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,
+ bool is_client_side,
+ 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_CORE_LIB_SECURITY_HANDSHAKE_H */
diff --git a/src/core/lib/security/json_token.c b/src/core/lib/security/json_token.c
new file mode 100644
index 0000000000..97054286d9
--- /dev/null
+++ b/src/core/lib/security/json_token.c
@@ -0,0 +1,411 @@
+/*
+ *
+ * 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/lib/security/json_token.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/b64.h"
+#include "src/core/lib/support/string.h"
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+
+/* --- 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/src/core/lib/security/json_token.h b/src/core/lib/security/json_token.h
new file mode 100644
index 0000000000..376fb03875
--- /dev/null
+++ b/src/core/lib/security/json_token.h
@@ -0,0 +1,118 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_JSON_TOKEN_H
+#define GRPC_CORE_LIB_SECURITY_JSON_TOKEN_H
+
+#include <grpc/support/slice.h>
+#include <openssl/rsa.h>
+
+#include "src/core/lib/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_CORE_LIB_SECURITY_JSON_TOKEN_H */
diff --git a/src/core/lib/security/jwt_verifier.c b/src/core/lib/security/jwt_verifier.c
new file mode 100644
index 0000000000..460b92f9a0
--- /dev/null
+++ b/src/core/lib/security/jwt_verifier.c
@@ -0,0 +1,843 @@
+/*
+ *
+ * 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/lib/security/jwt_verifier.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "src/core/lib/http/httpcli.h"
+#include "src/core/lib/security/b64.h"
+#include "src/core/lib/tsi/ssl_types.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/sync.h>
+#include <openssl/pem.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 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:
+ { <kid1>: <x5091>, <kid2>: <x5092>, ... } */
+ 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.http.path = strchr(jwks_uri, '/');
+ if (req.http.path == NULL) {
+ req.http.path = "";
+ } else {
+ *(req.host + (req.http.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.http.path, "/%s", iss);
+ } else {
+ *(path_prefix++) = '\0';
+ gpr_asprintf(&req.http.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.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 = 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.http.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/src/core/lib/security/jwt_verifier.h b/src/core/lib/security/jwt_verifier.h
new file mode 100644
index 0000000000..28a9eff048
--- /dev/null
+++ b/src/core/lib/security/jwt_verifier.h
@@ -0,0 +1,136 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_JWT_VERIFIER_H
+#define GRPC_CORE_LIB_SECURITY_JWT_VERIFIER_H
+
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/json/json.h"
+
+#include <grpc/support/slice.h>
+#include <grpc/support/time.h>
+
+/* --- 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://<key_url_prefix>/<issuer_email>
+ 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_CORE_LIB_SECURITY_JWT_VERIFIER_H */
diff --git a/src/core/lib/security/secure_endpoint.c b/src/core/lib/security/secure_endpoint.c
new file mode 100644
index 0000000000..e233b081ef
--- /dev/null
+++ b/src/core/lib/security/secure_endpoint.c
@@ -0,0 +1,384 @@
+/*
+ *
+ * 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/lib/security/secure_endpoint.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/tsi/transport_security_interface.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/src/core/lib/security/secure_endpoint.h b/src/core/lib/security/secure_endpoint.h
new file mode 100644
index 0000000000..57bd160a52
--- /dev/null
+++ b/src/core/lib/security/secure_endpoint.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
+#define GRPC_CORE_LIB_SECURITY_SECURE_ENDPOINT_H
+
+#include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/endpoint.h"
+
+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_CORE_LIB_SECURITY_SECURE_ENDPOINT_H */
diff --git a/src/core/lib/security/security_connector.c b/src/core/lib/security/security_connector.c
new file mode 100644
index 0000000000..5474bc3a9e
--- /dev/null
+++ b/src/core/lib/security/security_connector.c
@@ -0,0 +1,812 @@
+/*
+ *
+ * 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/lib/security/security_connector.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/host_port.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/handshake.h"
+#include "src/core/lib/security/secure_endpoint.h"
+#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/support/load_file.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/chttp2/alpn.h"
+#include "src/core/lib/tsi/fake_transport_security.h"
+#include "src/core/lib/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_server_security_connector_shutdown(
+ grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector) {
+ grpc_security_connector_handshake_list *tmp;
+ 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_channel_security_connector_do_handshake(
+ grpc_exec_ctx *exec_ctx, grpc_channel_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->do_handshake(exec_ctx, sc, nonsecure_endpoint, cb, user_data);
+ }
+}
+
+void grpc_server_security_connector_do_handshake(
+ grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
+ grpc_tcp_server_acceptor *acceptor, 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->do_handshake(exec_ctx, sc, acceptor, 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");
+}
+
+static int connector_pointer_cmp(void *a, void *b) { return GPR_ICMP(a, b); }
+
+static const grpc_arg_pointer_vtable connector_pointer_vtable = {
+ connector_pointer_arg_copy, connector_pointer_arg_destroy,
+ connector_pointer_cmp};
+
+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.vtable = &connector_pointer_vtable;
+ 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) {
+ grpc_server_security_connector *c = (grpc_server_security_connector *)sc;
+ gpr_mu_destroy(&c->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 ? "<EMPTY>" : 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_channel_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->base,
+ true, nonsecure_endpoint, cb, user_data);
+}
+
+static void fake_server_do_handshake(grpc_exec_ctx *exec_ctx,
+ grpc_server_security_connector *sc,
+ grpc_tcp_server_acceptor *acceptor,
+ 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->base,
+ false, nonsecure_endpoint, cb, user_data);
+}
+
+static grpc_security_connector_vtable fake_channel_vtable = {
+ fake_channel_destroy, fake_check_peer};
+
+static grpc_security_connector_vtable fake_server_vtable = {fake_server_destroy,
+ 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.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;
+ c->do_handshake = fake_channel_do_handshake;
+ return c;
+}
+
+grpc_server_security_connector *grpc_fake_server_security_connector_create(
+ void) {
+ grpc_server_security_connector *c =
+ gpr_malloc(sizeof(grpc_server_security_connector));
+ memset(c, 0, sizeof(*c));
+ gpr_ref_init(&c->base.refcount, 1);
+ c->base.vtable = &fake_server_vtable;
+ c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME;
+ c->do_handshake = fake_server_do_handshake;
+ 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_server_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(&c->base.mu);
+ gpr_free(sc);
+}
+
+static grpc_security_status ssl_create_handshaker(
+ tsi_ssl_handshaker_factory *handshaker_factory, bool 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_channel_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, true,
+ 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->base, true,
+ nonsecure_endpoint, cb, user_data);
+ }
+}
+
+static void ssl_server_do_handshake(grpc_exec_ctx *exec_ctx,
+ grpc_server_security_connector *sc,
+ grpc_tcp_server_acceptor *acceptor,
+ 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, false, NULL, &handshaker);
+ if (status != GRPC_SECURITY_OK) {
+ cb(exec_ctx, user_data, status, NULL, NULL);
+ } else {
+ grpc_do_security_handshake(exec_ctx, handshaker, &sc->base, false,
+ 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);
+ } else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) {
+ grpc_auth_context_add_property(ctx, GRPC_X509_PEM_CERT_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_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 = (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_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 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_check_peer};
+
+static grpc_security_connector_vtable ssl_server_vtable = {
+ ssl_server_destroy, 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.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;
+ c->base.do_handshake = ssl_channel_do_handshake;
+ 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_server_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.base.refcount, 1);
+ c->base.base.url_scheme = GRPC_SSL_URL_SCHEME;
+ c->base.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.base);
+ *sc = NULL;
+ goto error;
+ }
+ gpr_mu_init(&c->base.mu);
+ c->base.do_handshake = ssl_server_do_handshake;
+ *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/src/core/lib/security/security_connector.h b/src/core/lib/security/security_connector.h
new file mode 100644
index 0000000000..d50091c628
--- /dev/null
+++ b/src/core/lib/security/security_connector.h
@@ -0,0 +1,266 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_SECURITY_CONNECTOR_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONNECTOR_H
+
+#include <grpc/grpc_security.h>
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/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 (*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;
+ const char *url_scheme;
+};
+
+/* 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
+
+/* 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);
+
+/* 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;
+ 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);
+ void (*do_handshake)(grpc_exec_ctx *exec_ctx,
+ grpc_channel_security_connector *sc,
+ grpc_endpoint *nonsecure_endpoint,
+ grpc_security_handshake_done_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);
+
+/* Handshake. */
+void grpc_channel_security_connector_do_handshake(
+ grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector,
+ grpc_endpoint *nonsecure_endpoint, grpc_security_handshake_done_cb cb,
+ void *user_data);
+
+/* --- server_security_connector object. ---
+
+ A server security connector object represents away to configure the
+ underlying transport security mechanism on the server side. */
+
+typedef struct grpc_server_security_connector grpc_server_security_connector;
+
+struct grpc_server_security_connector {
+ grpc_security_connector base;
+ gpr_mu mu;
+ grpc_security_connector_handshake_list *handshaking_handshakes;
+ const grpc_channel_args *channel_args;
+ void (*do_handshake)(grpc_exec_ctx *exec_ctx,
+ grpc_server_security_connector *sc,
+ grpc_tcp_server_acceptor *acceptor,
+ grpc_endpoint *nonsecure_endpoint,
+ grpc_security_handshake_done_cb cb, void *user_data);
+};
+
+void grpc_server_security_connector_do_handshake(
+ grpc_exec_ctx *exec_ctx, grpc_server_security_connector *sc,
+ grpc_tcp_server_acceptor *acceptor, grpc_endpoint *nonsecure_endpoint,
+ grpc_security_handshake_done_cb cb, void *user_data);
+
+void grpc_server_security_connector_shutdown(
+ grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector);
+
+/* --- 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_server_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_server_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_CORE_LIB_SECURITY_SECURITY_CONNECTOR_H */
diff --git a/src/core/lib/security/security_context.c b/src/core/lib/security/security_context.c
new file mode 100644
index 0000000000..0e66373bd8
--- /dev/null
+++ b/src/core/lib/security/security_context.c
@@ -0,0 +1,347 @@
+/*
+ *
+ * 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 <string.h>
+
+#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+
+#include <grpc/grpc_security.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+/* --- 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");
+}
+
+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) {
+ 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.vtable = &auth_context_pointer_vtable;
+ 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/src/core/lib/security/security_context.h b/src/core/lib/security/security_context.h
new file mode 100644
index 0000000000..e9e4e503bc
--- /dev/null
+++ b/src/core/lib/security/security_context.h
@@ -0,0 +1,114 @@
+/*
+ *
+ * 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_CORE_LIB_SECURITY_SECURITY_CONTEXT_H
+#define GRPC_CORE_LIB_SECURITY_SECURITY_CONTEXT_H
+
+#include "src/core/lib/iomgr/pollset.h"
+#include "src/core/lib/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_CORE_LIB_SECURITY_SECURITY_CONTEXT_H */
diff --git a/src/core/lib/security/server_auth_filter.c b/src/core/lib/security/server_auth_filter.c
new file mode 100644
index 0000000000..158cde0e2c
--- /dev/null
+++ b/src/core/lib/security/server_auth_filter.c
@@ -0,0 +1,264 @@
+/*
+ *
+ * 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 <string.h>
+
+#include "src/core/lib/security/auth_filters.h"
+#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/security_context.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+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->recv_initial_metadata_ready;
+ op->recv_initial_metadata_ready = &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/src/core/lib/security/server_secure_chttp2.c b/src/core/lib/security/server_secure_chttp2.c
new file mode 100644
index 0000000000..7c9dd221ed
--- /dev/null
+++ b/src/core/lib/security/server_secure_chttp2.c
@@ -0,0 +1,264 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/security/auth_filters.h"
+#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/server.h"
+#include "src/core/lib/transport/chttp2_transport.h"
+
+typedef struct grpc_server_secure_state {
+ grpc_server *server;
+ grpc_tcp_server *tcp;
+ grpc_server_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->base, "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) {
+ 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, 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_server_security_connector_do_handshake(
+ exec_ctx, state->sc, acceptor, 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_server_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_server_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->base, "server");
+ }
+ if (state) {
+ gpr_free(state);
+ }
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+ return 0;
+}
diff --git a/src/core/lib/statistics/census_init.c b/src/core/lib/statistics/census_init.c
new file mode 100644
index 0000000000..bbecd62764
--- /dev/null
+++ b/src/core/lib/statistics/census_init.c
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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/lib/statistics/census_interface.h"
+
+#include <grpc/support/log.h>
+#include "src/core/lib/statistics/census_rpc_stats.h"
+#include "src/core/lib/statistics/census_tracing.h"
+
+void census_init(void) {
+ census_tracing_init();
+ census_stats_store_init();
+}
+
+void census_shutdown(void) {
+ census_stats_store_shutdown();
+ census_tracing_shutdown();
+}
diff --git a/src/core/lib/statistics/census_interface.h b/src/core/lib/statistics/census_interface.h
new file mode 100644
index 0000000000..b3b3439072
--- /dev/null
+++ b/src/core/lib/statistics/census_interface.h
@@ -0,0 +1,76 @@
+/*
+ *
+ * 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_CORE_LIB_STATISTICS_CENSUS_INTERFACE_H
+#define GRPC_CORE_LIB_STATISTICS_CENSUS_INTERFACE_H
+
+#include <grpc/support/port_platform.h>
+
+/* 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 <full quantified rpc service name>/<rpc function name>. 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_CORE_LIB_STATISTICS_CENSUS_INTERFACE_H */
diff --git a/src/core/lib/statistics/census_log.c b/src/core/lib/statistics/census_log.c
new file mode 100644
index 0000000000..1fb942a78a
--- /dev/null
+++ b/src/core/lib/statistics/census_log.c
@@ -0,0 +1,603 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* 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/lib/statistics/census_log.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/useful.h>
+#include <string.h>
+
+/* 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/src/core/lib/statistics/census_log.h b/src/core/lib/statistics/census_log.h
new file mode 100644
index 0000000000..c3fbd555ba
--- /dev/null
+++ b/src/core/lib/statistics/census_log.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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_CORE_LIB_STATISTICS_CENSUS_LOG_H
+#define GRPC_CORE_LIB_STATISTICS_CENSUS_LOG_H
+
+#include <stddef.h>
+
+/* 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_CORE_LIB_STATISTICS_CENSUS_LOG_H */
diff --git a/src/core/lib/statistics/census_rpc_stats.c b/src/core/lib/statistics/census_rpc_stats.c
new file mode 100644
index 0000000000..2182561668
--- /dev/null
+++ b/src/core/lib/statistics/census_rpc_stats.c
@@ -0,0 +1,253 @@
+/*
+ *
+ * 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 <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/statistics/census_interface.h"
+#include "src/core/lib/statistics/census_rpc_stats.h"
+#include "src/core/lib/statistics/census_tracing.h"
+#include "src/core/lib/statistics/hash_table.h"
+#include "src/core/lib/statistics/window_stats.h"
+#include "src/core/lib/support/murmur_hash.h"
+#include "src/core/lib/support/string.h"
+
+#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/src/core/lib/statistics/census_rpc_stats.h b/src/core/lib/statistics/census_rpc_stats.h
new file mode 100644
index 0000000000..00bb48205e
--- /dev/null
+++ b/src/core/lib/statistics/census_rpc_stats.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * 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_CORE_LIB_STATISTICS_CENSUS_RPC_STATS_H
+#define GRPC_CORE_LIB_STATISTICS_CENSUS_RPC_STATS_H
+
+#include <grpc/support/port_platform.h>
+#include "src/core/lib/statistics/census_interface.h"
+
+#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_CORE_LIB_STATISTICS_CENSUS_RPC_STATS_H */
diff --git a/src/core/lib/statistics/census_tracing.c b/src/core/lib/statistics/census_tracing.c
new file mode 100644
index 0000000000..b58ae733fc
--- /dev/null
+++ b/src/core/lib/statistics/census_tracing.c
@@ -0,0 +1,241 @@
+/*
+ *
+ * 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/lib/statistics/census_tracing.h"
+#include "src/core/lib/statistics/census_interface.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/sync.h>
+#include "src/core/lib/statistics/hash_table.h"
+#include "src/core/lib/support/string.h"
+
+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/src/core/lib/statistics/census_tracing.h b/src/core/lib/statistics/census_tracing.h
new file mode 100644
index 0000000000..a101abf3cb
--- /dev/null
+++ b/src/core/lib/statistics/census_tracing.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * 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_CORE_LIB_STATISTICS_CENSUS_TRACING_H
+#define GRPC_CORE_LIB_STATISTICS_CENSUS_TRACING_H
+
+#include <grpc/support/time.h>
+#include "src/core/lib/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_CORE_LIB_STATISTICS_CENSUS_TRACING_H */
diff --git a/src/core/lib/statistics/hash_table.c b/src/core/lib/statistics/hash_table.c
new file mode 100644
index 0000000000..18b7442a0c
--- /dev/null
+++ b/src/core/lib/statistics/hash_table.c
@@ -0,0 +1,303 @@
+/*
+ *
+ * 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/lib/statistics/hash_table.h"
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+
+#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/src/core/lib/statistics/hash_table.h b/src/core/lib/statistics/hash_table.h
new file mode 100644
index 0000000000..8f74ec82aa
--- /dev/null
+++ b/src/core/lib/statistics/hash_table.h
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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_CORE_LIB_STATISTICS_HASH_TABLE_H
+#define GRPC_CORE_LIB_STATISTICS_HASH_TABLE_H
+
+#include <stddef.h>
+
+#include <grpc/support/port_platform.h>
+
+/* 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_CORE_LIB_STATISTICS_HASH_TABLE_H */
diff --git a/src/core/lib/statistics/window_stats.c b/src/core/lib/statistics/window_stats.c
new file mode 100644
index 0000000000..53427a24bc
--- /dev/null
+++ b/src/core/lib/statistics/window_stats.c
@@ -0,0 +1,316 @@
+/*
+ *
+ * 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/lib/statistics/window_stats.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <grpc/support/useful.h>
+#include <math.h>
+#include <stddef.h>
+#include <string.h>
+
+/* 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/src/core/lib/statistics/window_stats.h b/src/core/lib/statistics/window_stats.h
new file mode 100644
index 0000000000..8dec50d620
--- /dev/null
+++ b/src/core/lib/statistics/window_stats.h
@@ -0,0 +1,173 @@
+/*
+ *
+ * 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_CORE_LIB_STATISTICS_WINDOW_STATS_H
+#define GRPC_CORE_LIB_STATISTICS_WINDOW_STATS_H
+
+#include <grpc/support/time.h>
+
+/* 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_CORE_LIB_STATISTICS_WINDOW_STATS_H */
diff --git a/src/core/lib/support/alloc.c b/src/core/lib/support/alloc.c
new file mode 100644
index 0000000000..27fa6a95ed
--- /dev/null
+++ b/src/core/lib/support/alloc.c
@@ -0,0 +1,90 @@
+/*
+ *
+ * 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 <grpc/support/alloc.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <stdlib.h>
+#include "src/core/lib/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) { gpr_free(((void **)ptr)[-1]); }
diff --git a/src/core/lib/support/avl.c b/src/core/lib/support/avl.c
new file mode 100644
index 0000000000..f378b3ee17
--- /dev/null
+++ b/src/core/lib/support/avl.c
@@ -0,0 +1,288 @@
+/*
+ *
+ * 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 <grpc/support/avl.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+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_value(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/src/core/lib/support/backoff.c b/src/core/lib/support/backoff.c
new file mode 100644
index 0000000000..e89ef47220
--- /dev/null
+++ b/src/core/lib/support/backoff.c
@@ -0,0 +1,76 @@
+/*
+ *
+ * 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 "src/core/lib/support/backoff.h"
+
+#include <grpc/support/useful.h>
+
+void gpr_backoff_init(gpr_backoff *backoff, double multiplier, double jitter,
+ int64_t min_timeout_millis, int64_t max_timeout_millis) {
+ backoff->multiplier = multiplier;
+ backoff->jitter = jitter;
+ backoff->min_timeout_millis = min_timeout_millis;
+ backoff->max_timeout_millis = max_timeout_millis;
+ backoff->rng_state = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec;
+}
+
+gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now) {
+ backoff->current_timeout_millis = backoff->min_timeout_millis;
+ return gpr_time_add(
+ now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN));
+}
+
+/* Generate a random number between 0 and 1. */
+static double generate_uniform_random_number(uint32_t *rng_state) {
+ *rng_state = (1103515245 * *rng_state + 12345) % ((uint32_t)1 << 31);
+ return *rng_state / (double)((uint32_t)1 << 31);
+}
+
+gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now) {
+ double new_timeout_millis =
+ backoff->multiplier * (double)backoff->current_timeout_millis;
+ double jitter_range = backoff->jitter * new_timeout_millis;
+ double jitter =
+ (2 * generate_uniform_random_number(&backoff->rng_state) - 1) *
+ jitter_range;
+ backoff->current_timeout_millis =
+ GPR_CLAMP((int64_t)(new_timeout_millis + jitter),
+ backoff->min_timeout_millis, backoff->max_timeout_millis);
+ return gpr_time_add(
+ now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN));
+}
+
+void gpr_backoff_reset(gpr_backoff *backoff) {
+ // forces step() to return a timeout of min_timeout_millis
+ backoff->current_timeout_millis = 0;
+}
diff --git a/src/core/lib/support/backoff.h b/src/core/lib/support/backoff.h
new file mode 100644
index 0000000000..6d40c15546
--- /dev/null
+++ b/src/core/lib/support/backoff.h
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SUPPORT_BACKOFF_H
+#define GRPC_CORE_LIB_SUPPORT_BACKOFF_H
+
+#include <grpc/support/time.h>
+
+typedef struct {
+ /// const: multiplier between retry attempts
+ double multiplier;
+ /// const: amount to randomize backoffs
+ double jitter;
+ /// const: minimum time between retries in milliseconds
+ int64_t min_timeout_millis;
+ /// const: maximum time between retries in milliseconds
+ int64_t max_timeout_millis;
+
+ /// random number generator
+ uint32_t rng_state;
+
+ /// current retry timeout in milliseconds
+ int64_t current_timeout_millis;
+} gpr_backoff;
+
+/// Initialize backoff machinery - does not need to be destroyed
+void gpr_backoff_init(gpr_backoff *backoff, double multiplier, double jitter,
+ int64_t min_timeout_millis, int64_t max_timeout_millis);
+
+/// Begin retry loop: returns a timespec for the NEXT retry
+gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now);
+/// Step a retry loop: returns a timespec for the NEXT retry
+gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now);
+/// Reset the backoff, so the next gpr_backoff_step will be a gpr_backoff_begin
+/// instead
+void gpr_backoff_reset(gpr_backoff *backoff);
+
+#endif /* GRPC_CORE_LIB_SUPPORT_BACKOFF_H */
diff --git a/src/core/lib/support/block_annotate.h b/src/core/lib/support/block_annotate.h
new file mode 100644
index 0000000000..bd3071655e
--- /dev/null
+++ b/src/core/lib/support/block_annotate.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_BLOCK_ANNOTATE_H
+#define GRPC_CORE_LIB_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_CORE_LIB_SUPPORT_BLOCK_ANNOTATE_H */
diff --git a/src/core/lib/support/cmdline.c b/src/core/lib/support/cmdline.c
new file mode 100644
index 0000000000..35c4990b22
--- /dev/null
+++ b/src/core/lib/support/cmdline.c
@@ -0,0 +1,347 @@
+/*
+ *
+ * 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 <grpc/support/cmdline.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/support/string.h"
+
+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/src/core/lib/support/cpu_iphone.c b/src/core/lib/support/cpu_iphone.c
new file mode 100644
index 0000000000..e83191bada
--- /dev/null
+++ b/src/core/lib/support/cpu_iphone.c
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_CPU_IPHONE
+
+/* Probably 2 instead of 1, but see comment on gpr_cpu_current_cpu. */
+unsigned gpr_cpu_num_cores(void) { return 1; }
+
+/* Most code that's using this is using it to shard across work queues. So
+ unless profiling shows it's a problem or there appears a way to detect the
+ currently running CPU core, let's have it shard the default way.
+ Note that the interface in cpu.h lets gpr_cpu_num_cores return 0, but doing
+ it makes it impossible for gpr_cpu_current_cpu to satisfy its stated range,
+ and some code might be relying on it. */
+unsigned gpr_cpu_current_cpu(void) { return 0; }
+
+#endif /* GPR_CPU_IPHONE */
diff --git a/src/core/lib/support/cpu_linux.c b/src/core/lib/support/cpu_linux.c
new file mode 100644
index 0000000000..5597df2d03
--- /dev/null
+++ b/src/core/lib/support/cpu_linux.c
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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 _GNU_SOURCE
+#define _GNU_SOURCE
+#endif /* _GNU_SOURCE */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_CPU_LINUX
+
+#include <errno.h>
+#include <sched.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/cpu.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+static int ncpus = 0;
+
+static void init_num_cpus() {
+ /* This must be signed. sysconf returns -1 when the number cannot be
+ determined */
+ ncpus = (int)sysconf(_SC_NPROCESSORS_ONLN);
+ if (ncpus < 1) {
+ gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1");
+ ncpus = 1;
+ }
+}
+
+unsigned gpr_cpu_num_cores(void) {
+ static gpr_once once = GPR_ONCE_INIT;
+ gpr_once_init(&once, init_num_cpus);
+ return (unsigned)ncpus;
+}
+
+unsigned gpr_cpu_current_cpu(void) {
+ int cpu = sched_getcpu();
+ if (cpu < 0) {
+ gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno));
+ return 0;
+ }
+ return (unsigned)cpu;
+}
+
+#endif /* GPR_CPU_LINUX */
diff --git a/src/core/lib/support/cpu_posix.c b/src/core/lib/support/cpu_posix.c
new file mode 100644
index 0000000000..e508ddd8ca
--- /dev/null
+++ b/src/core/lib/support/cpu_posix.c
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_CPU_POSIX
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+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/src/core/lib/support/cpu_windows.c b/src/core/lib/support/cpu_windows.c
new file mode 100644
index 0000000000..0f84a9e5ea
--- /dev/null
+++ b/src/core/lib/support/cpu_windows.c
@@ -0,0 +1,47 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+#include <grpc/support/log.h>
+
+unsigned gpr_cpu_num_cores(void) {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwNumberOfProcessors;
+}
+
+unsigned gpr_cpu_current_cpu(void) { return GetCurrentProcessorNumber(); }
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/lib/support/env.h b/src/core/lib/support/env.h
new file mode 100644
index 0000000000..ddc4ee3c6d
--- /dev/null
+++ b/src/core/lib/support/env.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_ENV_H
+#define GRPC_CORE_LIB_SUPPORT_ENV_H
+
+#include <stdio.h>
+
+#include <grpc/support/slice.h>
+
+#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_CORE_LIB_SUPPORT_ENV_H */
diff --git a/src/core/lib/support/env_linux.c b/src/core/lib/support/env_linux.c
new file mode 100644
index 0000000000..a86133e6c3
--- /dev/null
+++ b/src/core/lib/support/env_linux.c
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX_ENV
+
+#include "src/core/lib/support/env.h"
+
+#include <dlfcn.h>
+#include <features.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/support/string.h"
+
+char *gpr_getenv(const char *name) {
+#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) {
+ gpr_log(GPR_DEBUG,
+ "Warning: insecure environment read function '%s' used",
+ names[i]);
+ }
+ }
+ char *result = getenv_func(name);
+ return result == NULL ? result : gpr_strdup(result);
+#elif __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17)
+ char *result = secure_getenv(name);
+ return result == NULL ? result : gpr_strdup(result);
+#else
+ gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used",
+ "getenv");
+ char *result = getenv(name);
+ return result == NULL ? result : gpr_strdup(result);
+#endif
+}
+
+void gpr_setenv(const char *name, const char *value) {
+ int res = setenv(name, value, 1);
+ GPR_ASSERT(res == 0);
+}
+
+#endif /* GPR_LINUX_ENV */
diff --git a/src/core/lib/support/env_posix.c b/src/core/lib/support/env_posix.c
new file mode 100644
index 0000000000..1b57b094a9
--- /dev/null
+++ b/src/core/lib/support/env_posix.c
@@ -0,0 +1,57 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_ENV
+
+#include "src/core/lib/support/env.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+
+#include <grpc/support/string_util.h>
+#include "src/core/lib/support/string.h"
+
+char *gpr_getenv(const char *name) {
+ char *result = getenv(name);
+ return result == NULL ? result : gpr_strdup(result);
+}
+
+void gpr_setenv(const char *name, const char *value) {
+ int res = setenv(name, value, 1);
+ GPR_ASSERT(res == 0);
+}
+
+#endif /* GPR_POSIX_ENV */
diff --git a/src/core/lib/support/env_win32.c b/src/core/lib/support/env_win32.c
new file mode 100644
index 0000000000..566feee49e
--- /dev/null
+++ b/src/core/lib/support/env_win32.c
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include "src/core/lib/support/env.h"
+#include "src/core/lib/support/string.h"
+
+#ifdef __MINGW32__
+errno_t getenv_s(size_t *size_needed, char *buffer, size_t size,
+ const char *varname);
+#else
+#include <stdlib.h>
+#endif
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+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/src/core/lib/support/histogram.c b/src/core/lib/support/histogram.c
new file mode 100644
index 0000000000..62227be1a6
--- /dev/null
+++ b/src/core/lib/support/histogram.c
@@ -0,0 +1,244 @@
+/*
+ *
+ * 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 <grpc/support/histogram.h>
+
+#include <math.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/useful.h>
+
+/* 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/src/core/lib/support/host_port.c b/src/core/lib/support/host_port.c
new file mode 100644
index 0000000000..e03f6241ff
--- /dev/null
+++ b/src/core/lib/support/host_port.c
@@ -0,0 +1,110 @@
+/*
+ *
+ * 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 <grpc/support/host_port.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/support/string.h"
+
+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') {
+ /* ]<end> */
+ port_start = NULL;
+ } else if (rbracket[1] == ':') {
+ /* ]:<port?> */
+ port_start = rbracket + 2;
+ } else {
+ /* ]<invalid> */
+ return 0;
+ }
+ host_start = name + 1;
+ host_len = (size_t)(rbracket - host_start);
+ if (memchr(host_start, ':', host_len) == NULL) {
+ /* Require all bracketed hosts to contain a colon, because a hostname or
+ IPv4 address should never use brackets. */
+ return 0;
+ }
+ } else {
+ const char *colon = strchr(name, ':');
+ if (colon != NULL && strchr(colon + 1, ':') == NULL) {
+ /* Exactly 1 colon. Split into host:port. */
+ host_start = name;
+ host_len = (size_t)(colon - name);
+ port_start = colon + 1;
+ } else {
+ /* 0 or 2+ colons. Bare hostname or IPv6 litearal. */
+ host_start = name;
+ host_len = strlen(name);
+ port_start = NULL;
+ }
+ }
+
+ /* Allocate return values. */
+ *host = gpr_malloc(host_len + 1);
+ memcpy(*host, host_start, host_len);
+ (*host)[host_len] = '\0';
+
+ if (port_start != NULL) {
+ *port = gpr_strdup(port_start);
+ }
+
+ return 1;
+}
diff --git a/src/core/lib/support/load_file.c b/src/core/lib/support/load_file.c
new file mode 100644
index 0000000000..0cecd5edd5
--- /dev/null
+++ b/src/core/lib/support/load_file.c
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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/lib/support/load_file.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/support/block_annotate.h"
+#include "src/core/lib/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/src/core/lib/support/load_file.h b/src/core/lib/support/load_file.h
new file mode 100644
index 0000000000..fe030c967e
--- /dev/null
+++ b/src/core/lib/support/load_file.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_LOAD_FILE_H
+#define GRPC_CORE_LIB_SUPPORT_LOAD_FILE_H
+
+#include <stdio.h>
+
+#include <grpc/support/slice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_SUPPORT_LOAD_FILE_H */
diff --git a/src/core/lib/support/log.c b/src/core/lib/support/log.c
new file mode 100644
index 0000000000..cd6a0726cf
--- /dev/null
+++ b/src/core/lib/support/log.c
@@ -0,0 +1,66 @@
+/*
+ *
+ * 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 <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+
+#include <stdio.h>
+#include <string.h>
+
+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/src/core/lib/support/log_android.c b/src/core/lib/support/log_android.c
new file mode 100644
index 0000000000..640c9d7099
--- /dev/null
+++ b/src/core/lib/support/log_android.c
@@ -0,0 +1,87 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_ANDROID
+
+#include <android/log.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+static android_LogPriority severity_to_log_priority(gpr_log_severity severity) {
+ switch (severity) {
+ case GPR_LOG_SEVERITY_DEBUG:
+ return ANDROID_LOG_DEBUG;
+ case GPR_LOG_SEVERITY_INFO:
+ return ANDROID_LOG_INFO;
+ case GPR_LOG_SEVERITY_ERROR:
+ return ANDROID_LOG_ERROR;
+ }
+ return ANDROID_LOG_DEFAULT;
+}
+
+void gpr_log(const char *file, int line, gpr_log_severity severity,
+ const char *format, ...) {
+ char *message = NULL;
+ va_list args;
+ va_start(args, format);
+ vasprintf(&message, format, args);
+ va_end(args);
+ gpr_log_message(file, line, severity, message);
+ free(message);
+}
+
+void gpr_default_log(gpr_log_func_args *args) {
+ char *final_slash;
+ const char *display_file;
+ char *output = NULL;
+
+ final_slash = strrchr(args->file, '/');
+ if (final_slash == NULL)
+ display_file = args->file;
+ else
+ display_file = final_slash + 1;
+
+ asprintf(&output, "%s:%d] %s", display_file, args->line, args->message);
+
+ __android_log_write(severity_to_log_priority(args->severity), "GRPC", output);
+
+ /* allocated by asprintf => use free, not gpr_free */
+ free(output);
+}
+
+#endif /* GPR_ANDROID */
diff --git a/src/core/lib/support/log_linux.c b/src/core/lib/support/log_linux.c
new file mode 100644
index 0000000000..e60512c526
--- /dev/null
+++ b/src/core/lib/support/log_linux.c
@@ -0,0 +1,105 @@
+/*
+ *
+ * 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 _POSIX_SOURCE
+#define _POSIX_SOURCE
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_LINUX
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+#include <linux/unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <time.h>
+#include <unistd.h>
+
+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/src/core/lib/support/log_posix.c b/src/core/lib/support/log_posix.c
new file mode 100644
index 0000000000..7429dd0a2c
--- /dev/null
+++ b/src/core/lib/support/log_posix.c
@@ -0,0 +1,102 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#if defined(GPR_POSIX_LOG)
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+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/src/core/lib/support/log_win32.c b/src/core/lib/support/log_win32.c
new file mode 100644
index 0000000000..cec99440a5
--- /dev/null
+++ b/src/core/lib/support/log_win32.c
@@ -0,0 +1,126 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/log_win32.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/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/src/core/lib/support/murmur_hash.c b/src/core/lib/support/murmur_hash.c
new file mode 100644
index 0000000000..97832f1510
--- /dev/null
+++ b/src/core/lib/support/murmur_hash.c
@@ -0,0 +1,96 @@
+/*
+ *
+ * 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/lib/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/src/core/lib/support/murmur_hash.h b/src/core/lib/support/murmur_hash.h
new file mode 100644
index 0000000000..e54cdf2592
--- /dev/null
+++ b/src/core/lib/support/murmur_hash.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_MURMUR_HASH_H
+#define GRPC_CORE_LIB_SUPPORT_MURMUR_HASH_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+/* 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_SUPPORT_MURMUR_HASH_H */
diff --git a/src/core/lib/support/slice.c b/src/core/lib/support/slice.c
new file mode 100644
index 0000000000..cf3953ce4e
--- /dev/null
+++ b/src/core/lib/support/slice.c
@@ -0,0 +1,343 @@
+/*
+ *
+ * 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 <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+
+#include <string.h>
+
+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/src/core/lib/support/slice_buffer.c b/src/core/lib/support/slice_buffer.c
new file mode 100644
index 0000000000..563e659dd7
--- /dev/null
+++ b/src/core/lib/support/slice_buffer.c
@@ -0,0 +1,282 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+#include <grpc/support/slice_buffer.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+/* 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/src/core/lib/support/stack_lockfree.c b/src/core/lib/support/stack_lockfree.c
new file mode 100644
index 0000000000..de80486132
--- /dev/null
+++ b/src/core/lib/support/stack_lockfree.c
@@ -0,0 +1,185 @@
+/*
+ *
+ * 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/lib/support/stack_lockfree.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+
+/* 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;
+
+/* make sure that entries aligned to 8-bytes */
+#define ENTRY_ALIGNMENT_BITS 3
+/* reserve this entry as invalid */
+#define INVALID_ENTRY_INDEX ((1 << 16) - 1)
+
+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/src/core/lib/support/stack_lockfree.h b/src/core/lib/support/stack_lockfree.h
new file mode 100644
index 0000000000..a030a01d1f
--- /dev/null
+++ b/src/core/lib/support/stack_lockfree.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_STACK_LOCKFREE_H
+#define GRPC_CORE_LIB_SUPPORT_STACK_LOCKFREE_H
+
+#include <stddef.h>
+
+typedef struct gpr_stack_lockfree gpr_stack_lockfree;
+
+/* This stack must specify the maximum number of entries to track.
+ The current implementation only allows up to 65534 entries */
+gpr_stack_lockfree *gpr_stack_lockfree_create(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_CORE_LIB_SUPPORT_STACK_LOCKFREE_H */
diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c
new file mode 100644
index 0000000000..365d861de3
--- /dev/null
+++ b/src/core/lib/support/string.c
@@ -0,0 +1,296 @@
+/*
+ *
+ * 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/lib/support/string.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/useful.h>
+
+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/src/core/lib/support/string.h b/src/core/lib/support/string.h
new file mode 100644
index 0000000000..68c02878e0
--- /dev/null
+++ b/src/core/lib/support/string.h
@@ -0,0 +1,121 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_STRING_H
+#define GRPC_CORE_LIB_SUPPORT_STRING_H
+
+#include <stddef.h>
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+
+#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_CORE_LIB_SUPPORT_STRING_H */
diff --git a/src/core/lib/support/string_posix.c b/src/core/lib/support/string_posix.c
new file mode 100644
index 0000000000..a73b3106a5
--- /dev/null
+++ b/src/core/lib/support/string_posix.c
@@ -0,0 +1,86 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_STRING
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+int gpr_asprintf(char **strp, const char *format, ...) {
+ va_list args;
+ int ret;
+ char buf[64];
+ size_t strp_buflen;
+
+ /* Use a constant-sized buffer to determine the length. */
+ va_start(args, format);
+ ret = vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ if (ret < 0) {
+ *strp = NULL;
+ return -1;
+ }
+
+ /* Allocate a new buffer, with space for the NUL terminator. */
+ strp_buflen = (size_t)ret + 1;
+ if ((*strp = gpr_malloc(strp_buflen)) == NULL) {
+ /* This shouldn't happen, because gpr_malloc() calls abort(). */
+ return -1;
+ }
+
+ /* Return early if we have all the bytes. */
+ if (strp_buflen <= sizeof(buf)) {
+ memcpy(*strp, buf, strp_buflen);
+ return ret;
+ }
+
+ /* Try again using the larger buffer. */
+ va_start(args, format);
+ ret = vsnprintf(*strp, strp_buflen, format, args);
+ va_end(args);
+ if ((size_t)ret == strp_buflen - 1) {
+ return ret;
+ }
+
+ /* This should never happen. */
+ gpr_free(*strp);
+ *strp = NULL;
+ return -1;
+}
+
+#endif /* GPR_POSIX_STRING */
diff --git a/src/core/lib/support/string_win32.c b/src/core/lib/support/string_win32.c
new file mode 100644
index 0000000000..16b7e37f2a
--- /dev/null
+++ b/src/core/lib/support/string_win32.c
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+
+#include "src/core/lib/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/src/core/lib/support/string_win32.h b/src/core/lib/support/string_win32.h
new file mode 100644
index 0000000000..f47d567715
--- /dev/null
+++ b/src/core/lib/support/string_win32.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_STRING_WIN32_H
+#define GRPC_CORE_LIB_SUPPORT_STRING_WIN32_H
+
+#include <grpc/support/port_platform.h>
+
+#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_CORE_LIB_SUPPORT_STRING_WIN32_H */
diff --git a/src/core/lib/support/subprocess_posix.c b/src/core/lib/support/subprocess_posix.c
new file mode 100644
index 0000000000..662e7dd999
--- /dev/null
+++ b/src/core/lib/support/subprocess_posix.c
@@ -0,0 +1,112 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SUBPROCESS
+
+#include <grpc/support/subprocess.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+struct gpr_subprocess {
+ int pid;
+ int joined;
+};
+
+const char *gpr_subprocess_binary_extension() { return ""; }
+
+gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) {
+ gpr_subprocess *r;
+ int pid;
+ char **exec_args;
+
+ pid = fork();
+ if (pid == -1) {
+ return NULL;
+ } else if (pid == 0) {
+ exec_args = gpr_malloc(((size_t)argc + 1) * sizeof(char *));
+ memcpy(exec_args, argv, (size_t)argc * sizeof(char *));
+ exec_args[argc] = NULL;
+ execv(exec_args[0], exec_args);
+ /* if we reach here, an error has occurred */
+ gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0], strerror(errno));
+ _exit(1);
+ return NULL;
+ } else {
+ r = gpr_malloc(sizeof(gpr_subprocess));
+ memset(r, 0, sizeof(*r));
+ r->pid = pid;
+ return r;
+ }
+}
+
+void gpr_subprocess_destroy(gpr_subprocess *p) {
+ if (!p->joined) {
+ kill(p->pid, SIGKILL);
+ gpr_subprocess_join(p);
+ }
+ gpr_free(p);
+}
+
+int gpr_subprocess_join(gpr_subprocess *p) {
+ int status;
+retry:
+ if (waitpid(p->pid, &status, 0) == -1) {
+ if (errno == EINTR) {
+ goto retry;
+ }
+ gpr_log(GPR_ERROR, "waitpid failed: %s", strerror(errno));
+ return -1;
+ }
+ return status;
+}
+
+void gpr_subprocess_interrupt(gpr_subprocess *p) {
+ if (!p->joined) {
+ kill(p->pid, SIGINT);
+ }
+}
+
+#endif /* GPR_POSIX_SUBPROCESS */
diff --git a/src/core/lib/support/subprocess_windows.c b/src/core/lib/support/subprocess_windows.c
new file mode 100644
index 0000000000..264306f1bd
--- /dev/null
+++ b/src/core/lib/support/subprocess_windows.c
@@ -0,0 +1,141 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WINDOWS_SUBPROCESS
+
+#include <string.h>
+#include <tchar.h>
+#include <windows.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/subprocess.h>
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/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/src/core/lib/support/sync.c b/src/core/lib/support/sync.c
new file mode 100644
index 0000000000..800cf20287
--- /dev/null
+++ b/src/core/lib/support/sync.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* Generic implementation of synchronization primitives. */
+
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+
+/* 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_ref_non_zero(gpr_refcount *r) {
+ gpr_atm prior = gpr_atm_no_barrier_fetch_add(&r->count, 1);
+ GPR_ASSERT(prior > 0);
+}
+
+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/src/core/lib/support/sync_posix.c b/src/core/lib/support/sync_posix.c
new file mode 100644
index 0000000000..a5e59db8c7
--- /dev/null
+++ b/src/core/lib/support/sync_posix.c
@@ -0,0 +1,104 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SYNC
+
+#include <errno.h>
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+#include <time.h>
+#include "src/core/lib/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/src/core/lib/support/sync_win32.c b/src/core/lib/support/sync_win32.c
new file mode 100644
index 0000000000..41998ebcb6
--- /dev/null
+++ b/src/core/lib/support/sync_win32.c
@@ -0,0 +1,133 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/time.h>
+
+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/src/core/lib/support/thd.c b/src/core/lib/support/thd.c
new file mode 100644
index 0000000000..d59aace38d
--- /dev/null
+++ b/src/core/lib/support/thd.c
@@ -0,0 +1,64 @@
+/*
+ *
+ * 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 implementation for gpr threads. */
+
+#include <memory.h>
+
+#include <grpc/support/thd.h>
+
+enum { GPR_THD_JOINABLE = 1 };
+
+gpr_thd_options gpr_thd_options_default(void) {
+ gpr_thd_options options;
+ memset(&options, 0, sizeof(options));
+ return options;
+}
+
+void gpr_thd_options_set_detached(gpr_thd_options* options) {
+ options->flags &= ~GPR_THD_JOINABLE;
+}
+
+void gpr_thd_options_set_joinable(gpr_thd_options* options) {
+ options->flags |= GPR_THD_JOINABLE;
+}
+
+int gpr_thd_options_is_detached(const gpr_thd_options* options) {
+ if (!options) return 1;
+ return (options->flags & GPR_THD_JOINABLE) == 0;
+}
+
+int gpr_thd_options_is_joinable(const gpr_thd_options* options) {
+ if (!options) return 0;
+ return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE;
+}
diff --git a/src/core/lib/support/thd_internal.h b/src/core/lib/support/thd_internal.h
new file mode 100644
index 0000000000..f269a3249e
--- /dev/null
+++ b/src/core/lib/support/thd_internal.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_THD_INTERNAL_H
+#define GRPC_CORE_LIB_SUPPORT_THD_INTERNAL_H
+
+/* Internal interfaces between modules within the gpr support library. */
+
+#endif /* GRPC_CORE_LIB_SUPPORT_THD_INTERNAL_H */
diff --git a/src/core/lib/support/thd_posix.c b/src/core/lib/support/thd_posix.c
new file mode 100644
index 0000000000..4d874d3656
--- /dev/null
+++ b/src/core/lib/support/thd_posix.c
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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 implementation for gpr threads. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_SYNC
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+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/src/core/lib/support/thd_win32.c b/src/core/lib/support/thd_win32.c
new file mode 100644
index 0000000000..630eb7f625
--- /dev/null
+++ b/src/core/lib/support/thd_win32.c
@@ -0,0 +1,117 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* Windows implementation for gpr threads. */
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <string.h>
+
+#if defined(_MSC_VER)
+#define thread_local __declspec(thread)
+#elif defined(__GNUC__)
+#define thread_local __thread
+#else
+#error "Unknown compiler - please file a bug report"
+#endif
+
+struct thd_info {
+ void (*body)(void *arg); /* body of a thread */
+ void *arg; /* argument to a thread */
+ HANDLE join_event; /* if joinable, the join event */
+ int joinable; /* true if not detached */
+};
+
+static thread_local struct thd_info *g_thd_info;
+
+/* Destroys a thread info */
+static void destroy_thread(struct thd_info *t) {
+ if (t->joinable) CloseHandle(t->join_event);
+ gpr_free(t);
+}
+
+/* Body of every thread started via gpr_thd_new. */
+static DWORD WINAPI thread_body(void *v) {
+ g_thd_info = (struct thd_info *)v;
+ g_thd_info->body(g_thd_info->arg);
+ if (g_thd_info->joinable) {
+ BOOL ret = SetEvent(g_thd_info->join_event);
+ GPR_ASSERT(ret);
+ } else {
+ destroy_thread(g_thd_info);
+ }
+ return 0;
+}
+
+int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg,
+ const gpr_thd_options *options) {
+ HANDLE handle;
+ struct thd_info *info = gpr_malloc(sizeof(*info));
+ info->body = thd_body;
+ info->arg = arg;
+ *t = 0;
+ if (gpr_thd_options_is_joinable(options)) {
+ info->joinable = 1;
+ info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (info->join_event == NULL) {
+ gpr_free(info);
+ return 0;
+ }
+ } else {
+ info->joinable = 0;
+ }
+ handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL);
+ if (handle == NULL) {
+ destroy_thread(info);
+ } else {
+ *t = (gpr_thd_id)info;
+ CloseHandle(handle);
+ }
+ return handle != NULL;
+}
+
+gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; }
+
+void gpr_thd_join(gpr_thd_id t) {
+ struct thd_info *info = (struct thd_info *)t;
+ DWORD ret = WaitForSingleObject(info->join_event, INFINITE);
+ GPR_ASSERT(ret == WAIT_OBJECT_0);
+ destroy_thread(info);
+}
+
+#endif /* GPR_WIN32 */
diff --git a/src/core/lib/support/time.c b/src/core/lib/support/time.c
new file mode 100644
index 0000000000..0e2c8fcf1a
--- /dev/null
+++ b/src/core/lib/support/time.c
@@ -0,0 +1,304 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* Generic implementation of time calls. */
+
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+int gpr_time_cmp(gpr_timespec a, gpr_timespec b) {
+ int cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec);
+ GPR_ASSERT(a.clock_type == b.clock_type);
+ if (cmp == 0) {
+ cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec);
+ }
+ return cmp;
+}
+
+gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b) {
+ return gpr_time_cmp(a, b) < 0 ? a : b;
+}
+
+gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) {
+ return gpr_time_cmp(a, b) > 0 ? a : b;
+}
+
+gpr_timespec gpr_time_0(gpr_clock_type type) {
+ gpr_timespec out;
+ out.tv_sec = 0;
+ out.tv_nsec = 0;
+ out.clock_type = type;
+ return out;
+}
+
+gpr_timespec gpr_inf_future(gpr_clock_type type) {
+ gpr_timespec out;
+ out.tv_sec = INT64_MAX;
+ out.tv_nsec = 0;
+ out.clock_type = type;
+ return out;
+}
+
+gpr_timespec gpr_inf_past(gpr_clock_type type) {
+ gpr_timespec out;
+ out.tv_sec = INT64_MIN;
+ out.tv_nsec = 0;
+ out.clock_type = type;
+ return out;
+}
+
+/* TODO(ctiller): consider merging _nanos, _micros, _millis into a single
+ function for maintainability. Similarly for _seconds, _minutes, and _hours */
+
+gpr_timespec gpr_time_from_nanos(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);
+ } 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);
+ }
+ return result;
+}
+
+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);
+ } 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);
+ }
+ return result;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) {
+ gpr_timespec sum;
+ int64_t inc = 0;
+ GPR_ASSERT(b.clock_type == GPR_TIMESPAN);
+ sum.clock_type = a.clock_type;
+ sum.tv_nsec = a.tv_nsec + b.tv_nsec;
+ if (sum.tv_nsec >= GPR_NS_PER_SEC) {
+ sum.tv_nsec -= GPR_NS_PER_SEC;
+ inc++;
+ }
+ if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) {
+ sum = a;
+ } else if (b.tv_sec == INT64_MAX ||
+ (b.tv_sec >= 0 && a.tv_sec >= INT64_MAX - b.tv_sec)) {
+ sum = gpr_inf_future(sum.clock_type);
+ } else if (b.tv_sec == INT64_MIN ||
+ (b.tv_sec <= 0 && a.tv_sec <= INT64_MIN - b.tv_sec)) {
+ sum = gpr_inf_past(sum.clock_type);
+ } else {
+ sum.tv_sec = a.tv_sec + b.tv_sec;
+ if (inc != 0 && sum.tv_sec == INT64_MAX - 1) {
+ sum = gpr_inf_future(sum.clock_type);
+ } else {
+ sum.tv_sec += inc;
+ }
+ }
+ return sum;
+}
+
+gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) {
+ gpr_timespec diff;
+ int64_t dec = 0;
+ if (b.clock_type == GPR_TIMESPAN) {
+ diff.clock_type = a.clock_type;
+ } else {
+ GPR_ASSERT(a.clock_type == b.clock_type);
+ diff.clock_type = GPR_TIMESPAN;
+ }
+ diff.tv_nsec = a.tv_nsec - b.tv_nsec;
+ if (diff.tv_nsec < 0) {
+ diff.tv_nsec += GPR_NS_PER_SEC;
+ dec++;
+ }
+ if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) {
+ diff = a;
+ } else if (b.tv_sec == INT64_MIN ||
+ (b.tv_sec <= 0 && a.tv_sec >= INT64_MAX + b.tv_sec)) {
+ diff = gpr_inf_future(GPR_CLOCK_REALTIME);
+ } else if (b.tv_sec == INT64_MAX ||
+ (b.tv_sec >= 0 && a.tv_sec <= INT64_MIN + b.tv_sec)) {
+ diff = gpr_inf_past(GPR_CLOCK_REALTIME);
+ } else {
+ diff.tv_sec = a.tv_sec - b.tv_sec;
+ if (dec != 0 && diff.tv_sec == INT64_MIN + 1) {
+ diff = gpr_inf_past(GPR_CLOCK_REALTIME);
+ } else {
+ diff.tv_sec -= dec;
+ }
+ }
+ return diff;
+}
+
+int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) {
+ int cmp_ab;
+
+ GPR_ASSERT(a.clock_type == b.clock_type);
+ GPR_ASSERT(threshold.clock_type == GPR_TIMESPAN);
+
+ cmp_ab = gpr_time_cmp(a, b);
+ if (cmp_ab == 0) return 1;
+ if (cmp_ab < 0) {
+ return gpr_time_cmp(gpr_time_sub(b, a), threshold) <= 0;
+ } else {
+ return gpr_time_cmp(gpr_time_sub(a, b), threshold) <= 0;
+ }
+}
+
+int32_t gpr_time_to_millis(gpr_timespec t) {
+ if (t.tv_sec >= 2147483) {
+ if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) {
+ return 2147483 * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS;
+ }
+ return 2147483647;
+ } else if (t.tv_sec <= -2147483) {
+ /* TODO(ctiller): correct handling here (it's so far in the past do we
+ care?) */
+ return -2147483647;
+ } else {
+ return (int32_t)(t.tv_sec * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS);
+ }
+}
+
+double gpr_timespec_to_micros(gpr_timespec t) {
+ return (double)t.tv_sec * GPR_US_PER_SEC + t.tv_nsec * 1e-3;
+}
+
+gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) {
+ if (t.clock_type == clock_type) {
+ return t;
+ }
+
+ if (t.tv_nsec == 0) {
+ if (t.tv_sec == INT64_MAX) {
+ t.clock_type = clock_type;
+ return t;
+ }
+ if (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);
+ }
+
+ return gpr_time_add(gpr_now(clock_type),
+ gpr_time_sub(t, gpr_now(t.clock_type)));
+}
diff --git a/src/core/lib/support/time_posix.c b/src/core/lib/support/time_posix.c
new file mode 100644
index 0000000000..fcfab2f2fa
--- /dev/null
+++ b/src/core/lib/support/time_posix.c
@@ -0,0 +1,170 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+#include "src/core/lib/support/time_precise.h"
+
+#ifdef GPR_POSIX_TIME
+
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __linux__
+#include <sys/syscall.h>
+#endif
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/support/block_annotate.h"
+
+static struct timespec timespec_from_gpr(gpr_timespec gts) {
+ struct timespec rv;
+ if (sizeof(time_t) < sizeof(int64_t)) {
+ /* 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_nsec = gts.tv_nsec;
+ return rv;
+}
+
+#if _POSIX_TIMERS > 0
+static gpr_timespec gpr_from_timespec(struct timespec ts,
+ gpr_clock_type clock_type) {
+ /*
+ * timespec.tv_sec can have smaller size than gpr_timespec.tv_sec,
+ * but we are only using this function to implement gpr_now
+ * so there's no need to handle "infinity" values.
+ */
+ gpr_timespec rv;
+ rv.tv_sec = ts.tv_sec;
+ rv.tv_nsec = (int32_t)ts.tv_nsec;
+ rv.clock_type = clock_type;
+ return rv;
+}
+
+/** maps gpr_clock_type --> clockid_t for clock_gettime */
+static const clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC,
+ CLOCK_REALTIME};
+
+void gpr_time_init(void) { gpr_precise_clock_init(); }
+
+gpr_timespec gpr_now(gpr_clock_type clock_type) {
+ struct timespec now;
+ GPR_ASSERT(clock_type != GPR_TIMESPAN);
+ if (clock_type == GPR_CLOCK_PRECISE) {
+ gpr_timespec ret;
+ gpr_precise_clock_now(&ret);
+ return ret;
+ } else {
+#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
+ clock_gettime(clockid_for_gpr_clock[clock_type], &now);
+#endif
+ return gpr_from_timespec(now, clock_type);
+ }
+}
+#else
+/* For some reason Apple's OSes haven't implemented clock_gettime. */
+
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+
+static double g_time_scale;
+static uint64_t g_time_start;
+
+void gpr_time_init(void) {
+ mach_timebase_info_data_t tb = {0, 1};
+ gpr_precise_clock_init();
+ mach_timebase_info(&tb);
+ g_time_scale = tb.numer;
+ g_time_scale /= tb.denom;
+ g_time_start = mach_absolute_time();
+}
+
+gpr_timespec gpr_now(gpr_clock_type clock) {
+ gpr_timespec now;
+ struct timeval now_tv;
+ double now_dbl;
+
+ now.clock_type = clock;
+ switch (clock) {
+ case GPR_CLOCK_REALTIME:
+ gettimeofday(&now_tv, NULL);
+ now.tv_sec = now_tv.tv_sec;
+ now.tv_nsec = now_tv.tv_usec * 1000;
+ break;
+ case GPR_CLOCK_MONOTONIC:
+ now_dbl = (mach_absolute_time() - g_time_start) * g_time_scale;
+ now.tv_sec = (int64_t)(now_dbl * 1e-9);
+ now.tv_nsec = (int32_t)(now_dbl - ((double)now.tv_sec) * 1e9);
+ break;
+ case GPR_CLOCK_PRECISE:
+ gpr_precise_clock_now(&now);
+ break;
+ case GPR_TIMESPAN:
+ abort();
+ }
+
+ return now;
+}
+#endif
+
+void gpr_sleep_until(gpr_timespec until) {
+ gpr_timespec now;
+ gpr_timespec delta;
+ struct timespec delta_ts;
+ int ns_result;
+
+ for (;;) {
+ /* We could simplify by using clock_nanosleep instead, but it might be
+ * slightly less portable. */
+ now = gpr_now(until.clock_type);
+ if (gpr_time_cmp(until, now) <= 0) {
+ return;
+ }
+
+ delta = gpr_time_sub(until, now);
+ delta_ts = timespec_from_gpr(delta);
+ GRPC_SCHEDULING_START_BLOCKING_REGION;
+ ns_result = nanosleep(&delta_ts, NULL);
+ GRPC_SCHEDULING_END_BLOCKING_REGION;
+ if (ns_result == 0) {
+ break;
+ }
+ }
+}
+
+#endif /* GPR_POSIX_TIME */
diff --git a/src/core/lib/support/time_precise.c b/src/core/lib/support/time_precise.c
new file mode 100644
index 0000000000..31ac47e0f8
--- /dev/null
+++ b/src/core/lib/support/time_precise.c
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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 <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <stdio.h>
+
+#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/src/core/lib/support/time_precise.h b/src/core/lib/support/time_precise.h
new file mode 100644
index 0000000000..e1faee1f9f
--- /dev/null
+++ b/src/core/lib/support/time_precise.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_TIME_PRECISE_H
+#define GRPC_CORE_LIB_SUPPORT_TIME_PRECISE_H
+
+#include <grpc/support/time.h>
+
+void gpr_precise_clock_init(void);
+void gpr_precise_clock_now(gpr_timespec *clk);
+
+#endif /* GRPC_CORE_LIB_SUPPORT_TIME_PRECISE_H */
diff --git a/src/core/lib/support/time_win32.c b/src/core/lib/support/time_win32.c
new file mode 100644
index 0000000000..a6ac003fb8
--- /dev/null
+++ b/src/core/lib/support/time_win32.c
@@ -0,0 +1,110 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <grpc/support/log.h>
+#include <grpc/support/time.h>
+#include <limits.h>
+#include <process.h>
+#include <sys/timeb.h>
+
+#include "src/core/lib/support/block_annotate.h"
+#include "src/core/lib/support/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;
+}
+
+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(&timestamp);
+ 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/src/core/lib/support/tls_pthread.c b/src/core/lib/support/tls_pthread.c
new file mode 100644
index 0000000000..bdc7ed14ae
--- /dev/null
+++ b/src/core/lib/support/tls_pthread.c
@@ -0,0 +1,45 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_PTHREAD_TLS
+
+#include <grpc/support/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/src/core/lib/support/tmpfile.h b/src/core/lib/support/tmpfile.h
new file mode 100644
index 0000000000..4fec2076e3
--- /dev/null
+++ b/src/core/lib/support/tmpfile.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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_CORE_LIB_SUPPORT_TMPFILE_H
+#define GRPC_CORE_LIB_SUPPORT_TMPFILE_H
+
+#include <stdio.h>
+
+#include <grpc/support/slice.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 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_CORE_LIB_SUPPORT_TMPFILE_H */
diff --git a/src/core/lib/support/tmpfile_posix.c b/src/core/lib/support/tmpfile_posix.c
new file mode 100644
index 0000000000..743f45e1bc
--- /dev/null
+++ b/src/core/lib/support/tmpfile_posix.c
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_POSIX_FILE
+
+#include "src/core/lib/support/tmpfile.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/support/string.h"
+
+FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) {
+ FILE *result = NULL;
+ char *template;
+ int fd;
+
+ if (tmp_filename != NULL) *tmp_filename = NULL;
+
+ gpr_asprintf(&template, "/tmp/%s_XXXXXX", prefix);
+ GPR_ASSERT(template != NULL);
+
+ fd = mkstemp(template);
+ if (fd == -1) {
+ gpr_log(GPR_ERROR, "mkstemp failed for template %s with error %s.",
+ template, strerror(errno));
+ goto end;
+ }
+ result = fdopen(fd, "w+");
+ if (result == NULL) {
+ gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).",
+ template, fd, strerror(errno));
+ unlink(template);
+ close(fd);
+ goto end;
+ }
+
+end:
+ if (result != NULL && tmp_filename != NULL) {
+ *tmp_filename = template;
+ } else {
+ gpr_free(template);
+ }
+ return result;
+}
+
+#endif /* GPR_POSIX_FILE */
diff --git a/src/core/lib/support/tmpfile_win32.c b/src/core/lib/support/tmpfile_win32.c
new file mode 100644
index 0000000000..05d92b6036
--- /dev/null
+++ b/src/core/lib/support/tmpfile_win32.c
@@ -0,0 +1,84 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#ifdef GPR_WIN32
+
+#include <io.h>
+#include <stdio.h>
+#include <string.h>
+#include <tchar.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/support/string_win32.h"
+#include "src/core/lib/support/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_WIN32 */
diff --git a/src/core/lib/support/wrap_memcpy.c b/src/core/lib/support/wrap_memcpy.c
new file mode 100644
index 0000000000..15c289f7b8
--- /dev/null
+++ b/src/core/lib/support/wrap_memcpy.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <string.h>
+
+/* 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/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c
new file mode 100644
index 0000000000..368683378e
--- /dev/null
+++ b/src/core/lib/surface/alarm.c
@@ -0,0 +1,84 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/surface/completion_queue.h"
+
+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_cq_begin_op(cq, tag);
+ grpc_timer_init(&exec_ctx, &alarm->alarm,
+ gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC),
+ alarm_cb, alarm, gpr_now(GPR_CLOCK_MONOTONIC));
+ 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/src/core/lib/surface/api_trace.c b/src/core/lib/surface/api_trace.c
new file mode 100644
index 0000000000..3702c024db
--- /dev/null
+++ b/src/core/lib/surface/api_trace.c
@@ -0,0 +1,36 @@
+/*
+ *
+ * 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/lib/surface/api_trace.h"
+
+int grpc_api_trace = 0;
diff --git a/src/core/lib/surface/api_trace.h b/src/core/lib/surface/api_trace.h
new file mode 100644
index 0000000000..b50011c9e5
--- /dev/null
+++ b/src/core/lib/surface/api_trace.h
@@ -0,0 +1,65 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_API_TRACE_H
+#define GRPC_CORE_LIB_SURFACE_API_TRACE_H
+
+#include <grpc/support/log.h>
+#include "src/core/lib/debug/trace.h"
+
+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_CORE_LIB_SURFACE_API_TRACE_H */
diff --git a/src/core/lib/surface/byte_buffer.c b/src/core/lib/surface/byte_buffer.c
new file mode 100644
index 0000000000..03071ef92c
--- /dev/null
+++ b/src/core/lib/surface/byte_buffer.c
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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 <grpc/byte_buffer.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+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/src/core/lib/surface/byte_buffer_reader.c b/src/core/lib/surface/byte_buffer_reader.c
new file mode 100644
index 0000000000..7248f5fe71
--- /dev/null
+++ b/src/core/lib/surface/byte_buffer_reader.c
@@ -0,0 +1,123 @@
+/*
+ *
+ * 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 <grpc/byte_buffer_reader.h>
+#include <string.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+
+#include "src/core/lib/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/src/core/lib/surface/call.c b/src/core/lib/surface/call.c
new file mode 100644
index 0000000000..d63a4a7401
--- /dev/null
+++ b/src/core/lib/surface/call.c
@@ -0,0 +1,1491 @@
+/*
+ *
+ * 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 <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/compression/algorithm_metadata.h"
+#include "src/core/lib/iomgr/timer.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/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/completion_queue.h"
+#include "src/core/lib/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;
+
+ /* have we received initial metadata */
+ bool has_initial_md_been_received;
+
+ 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;
+ grpc_closure receiving_initial_metadata_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;
+
+ struct {
+ void *bctlp;
+ bool success;
+ } saved_receiving_stream_ready_ctx;
+};
+
+#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 process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl,
+ bool success) {
+ 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 void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp,
+ bool success) {
+ batch_control *bctl = bctlp;
+ grpc_call *call = bctl->call;
+
+ gpr_mu_lock(&bctl->call->mu);
+ if (bctl->call->has_initial_md_been_received) {
+ gpr_mu_unlock(&bctl->call->mu);
+ process_data_after_md(exec_ctx, bctlp, success);
+ } else {
+ call->saved_receiving_stream_ready_ctx.bctlp = bctlp;
+ call->saved_receiving_stream_ready_ctx.success = success;
+ gpr_mu_unlock(&bctl->call->mu);
+ }
+}
+
+static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx,
+ void *bctlp, bool success) {
+ batch_control *bctl = bctlp;
+ grpc_call *call = bctl->call;
+
+ gpr_mu_lock(&call->mu);
+
+ grpc_metadata_batch *md =
+ &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
+ grpc_metadata_batch_filter(md, recv_initial_filter, call);
+ call->has_initial_md_been_received = true;
+
+ 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 (call->saved_receiving_stream_ready_ctx.bctlp != NULL) {
+ grpc_closure *saved_rsr_closure = grpc_closure_create(
+ receiving_stream_ready, call->saved_receiving_stream_ready_ctx.bctlp);
+ grpc_exec_ctx_enqueue(exec_ctx, saved_rsr_closure,
+ call->saved_receiving_stream_ready_ctx.success, NULL);
+ call->saved_receiving_stream_ready_ctx.bctlp = NULL;
+ }
+
+ gpr_mu_unlock(&call->mu);
+
+ 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_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 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;
+ grpc_closure_init(&call->receiving_initial_metadata_ready,
+ receiving_initial_metadata_ready, bctl);
+ bctl->recv_initial_metadata = 1;
+ stream_op.recv_initial_metadata =
+ &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */];
+ stream_op.recv_initial_metadata_ready =
+ &call->receiving_initial_metadata_ready;
+ num_completion_callbacks_needed++;
+ 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; }
+
+grpc_compression_algorithm grpc_call_compression_for_level(
+ grpc_call *call, grpc_compression_level level) {
+ gpr_mu_lock(&call->mu);
+ const uint32_t accepted_encodings = call->encodings_accepted_by_peer;
+ gpr_mu_unlock(&call->mu);
+ return grpc_compression_algorithm_for_level(level, accepted_encodings);
+}
diff --git a/src/core/lib/surface/call.h b/src/core/lib/surface/call.h
new file mode 100644
index 0000000000..e2e75865be
--- /dev/null
+++ b/src/core/lib/surface/call.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_CALL_H
+#define GRPC_CORE_LIB_SURFACE_CALL_H
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/context.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/surface_trace.h"
+
+#include <grpc/grpc.h>
+#include <grpc/impl/codegen/compression_types.h>
+
+#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);
+
+/* 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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_SURFACE_CALL_H */
diff --git a/src/core/lib/surface/call_details.c b/src/core/lib/surface/call_details.c
new file mode 100644
index 0000000000..08f606d84a
--- /dev/null
+++ b/src/core/lib/surface/call_details.c
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+
+#include <string.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));
+}
+
+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/src/core/lib/surface/call_log_batch.c b/src/core/lib/surface/call_log_batch.c
new file mode 100644
index 0000000000..bc5a2ffb65
--- /dev/null
+++ b/src/core/lib/surface/call_log_batch.c
@@ -0,0 +1,118 @@
+/*
+ *
+ * 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/lib/surface/call.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/support/string.h"
+
+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/src/core/lib/surface/call_test_only.h b/src/core/lib/surface/call_test_only.h
new file mode 100644
index 0000000000..400214189e
--- /dev/null
+++ b/src/core/lib/surface/call_test_only.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_CALL_TEST_ONLY_H
+#define GRPC_CORE_LIB_SURFACE_CALL_TEST_ONLY_H
+
+#include <grpc/grpc.h>
+
+#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_CORE_LIB_SURFACE_CALL_TEST_ONLY_H */
diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c
new file mode 100644
index 0000000000..d815daa70c
--- /dev/null
+++ b/src/core/lib/surface/channel.c
@@ -0,0 +1,324 @@
+/*
+ *
+ * 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/lib/surface/channel.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/client_config/resolver_registry.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/support/string.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/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;
+ 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(grpc_exec_ctx *exec_ctx, const char *target,
+ const grpc_channel_args *args,
+ grpc_channel_stack_type channel_stack_type,
+ grpc_transport *optional_transport) {
+ bool is_client = grpc_channel_stack_type_is_client(channel_stack_type);
+
+ grpc_channel *channel = grpc_channel_init_create_stack(
+ exec_ctx, channel_stack_type, sizeof(grpc_channel), args, 1,
+ destroy_channel, NULL, optional_transport);
+
+ memset(channel, 0, sizeof(*channel));
+ channel->target = gpr_strdup(target);
+ 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 (size_t 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);
+ }
+
+ 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/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h
new file mode 100644
index 0000000000..09de0fccc9
--- /dev/null
+++ b/src/core/lib/surface/channel.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_CHANNEL_H
+#define GRPC_CORE_LIB_SURFACE_CHANNEL_H
+
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/client_config/subchannel_factory.h"
+#include "src/core/lib/surface/channel_stack_type.h"
+
+grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target,
+ const grpc_channel_args *args,
+ grpc_channel_stack_type channel_stack_type,
+ grpc_transport *optional_transport);
+
+/** 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_CORE_LIB_SURFACE_CHANNEL_H */
diff --git a/src/core/lib/surface/channel_connectivity.c b/src/core/lib/surface/channel_connectivity.c
new file mode 100644
index 0000000000..2f5d763e70
--- /dev/null
+++ b/src/core/lib/surface/channel_connectivity.c
@@ -0,0 +1,207 @@
+/*
+ *
+ * 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/lib/surface/channel.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/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_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;
+ }
+ 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 {
+ 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 {
+ abort();
+ }
+
+ grpc_exec_ctx_finish(&exec_ctx);
+}
diff --git a/src/core/lib/surface/channel_create.c b/src/core/lib/surface/channel_create.c
new file mode 100644
index 0000000000..e8777ce816
--- /dev/null
+++ b/src/core/lib/surface/channel_create.c
@@ -0,0 +1,223 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+
+#include "src/core/lib/census/grpc_filter.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/client_channel.h"
+#include "src/core/lib/channel/compress_filter.h"
+#include "src/core/lib/channel/http_client_filter.h"
+#include "src/core/lib/client_config/resolver_registry.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/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;
+ } 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(exec_ctx, &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;
+ grpc_resolver *resolver;
+ subchannel_factory *f;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ GRPC_API_TRACE(
+ "grpc_insecure_channel_create(target=%p, args=%p, reserved=%p)", 3,
+ (target, args, reserved));
+ GPR_ASSERT(!reserved);
+
+ channel =
+ grpc_channel_create(&exec_ctx, target, args, GRPC_CLIENT_CHANNEL, NULL);
+
+ 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/src/core/lib/surface/channel_init.c b/src/core/lib/surface/channel_init.c
new file mode 100644
index 0000000000..fc69f61f77
--- /dev/null
+++ b/src/core/lib/surface/channel_init.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * 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 "src/core/lib/surface/channel_init.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/useful.h>
+
+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 = NULL;
+ 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 =
+ 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 = a;
+ const stage_slot *sb = 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 = (void *)(uintptr_t)0xdeadbeef;
+ }
+}
+
+static const char *name_for_type(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");
+}
+
+void *grpc_channel_init_create_stack(
+ grpc_exec_ctx *exec_ctx, grpc_channel_stack_type type, size_t prefix_bytes,
+ const grpc_channel_args *args, int initial_refs, grpc_iomgr_cb_func destroy,
+ void *destroy_arg, grpc_transport *transport) {
+ GPR_ASSERT(g_finalized);
+
+ grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create();
+ grpc_channel_stack_builder_set_name(builder, name_for_type(type));
+ grpc_channel_stack_builder_set_channel_arguments(builder, args);
+ grpc_channel_stack_builder_set_transport(builder, transport);
+
+ 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)) {
+ grpc_channel_stack_builder_destroy(builder);
+ return NULL;
+ }
+ }
+
+ return grpc_channel_stack_builder_finish(exec_ctx, builder, prefix_bytes,
+ initial_refs, destroy, destroy_arg);
+}
diff --git a/src/core/lib/surface/channel_init.h b/src/core/lib/surface/channel_init.h
new file mode 100644
index 0000000000..a4d8271ca6
--- /dev/null
+++ b/src/core/lib/surface/channel_init.h
@@ -0,0 +1,86 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H
+#define GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H
+
+#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"
+
+/// 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)
+void *grpc_channel_init_create_stack(
+ grpc_exec_ctx *exec_ctx, grpc_channel_stack_type type, size_t prefix_bytes,
+ const grpc_channel_args *args, int initial_refs, grpc_iomgr_cb_func destroy,
+ void *destroy_arg, grpc_transport *optional_transport);
+
+#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_INIT_H */
diff --git a/src/core/lib/surface/channel_ping.c b/src/core/lib/surface/channel_ping.c
new file mode 100644
index 0000000000..dd862cdadd
--- /dev/null
+++ b/src/core/lib/surface/channel_ping.c
@@ -0,0 +1,79 @@
+/*
+ *
+ * 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/lib/surface/channel.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#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(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/src/core/lib/surface/channel_stack_type.c b/src/core/lib/surface/channel_stack_type.c
new file mode 100644
index 0000000000..c35d603ca3
--- /dev/null
+++ b/src/core/lib/surface/channel_stack_type.c
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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 "src/core/lib/surface/channel_stack_type.h"
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.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;);
+}
diff --git a/src/core/lib/surface/channel_stack_type.h b/src/core/lib/surface/channel_stack_type.h
new file mode 100644
index 0000000000..16608fa386
--- /dev/null
+++ b/src/core/lib/surface/channel_stack_type.h
@@ -0,0 +1,58 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H
+#define GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H
+
+#include <stdbool.h>
+
+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);
+
+#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H */
diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c
new file mode 100644
index 0000000000..a0d7002053
--- /dev/null
+++ b/src/core/lib/surface/completion_queue.c
@@ -0,0 +1,512 @@
+/*
+ *
+ * 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/lib/surface/completion_queue.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/time.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/support/string.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/event_string.h"
+#include "src/core/lib/surface/surface_trace.h"
+
+typedef struct {
+ grpc_pollset_worker **worker;
+ void *tag;
+} plucker;
+
+/* Completion queue structure */
+struct grpc_completion_queue {
+ /** owned by pollset */
+ gpr_mu *mu;
+ /** 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;
+ /** 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;
+};
+
+#define POLLSET_FROM_CQ(cq) ((grpc_pollset *)(cq + 1))
+
+static gpr_mu g_freelist_mu;
+static 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(POLLSET_FROM_CQ(g_freelist));
+#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_size());
+ grpc_pollset_init(POLLSET_FROM_CQ(cc), &cc->mu);
+#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(POLLSET_FROM_CQ(cc));
+ 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(cc->mu);
+ 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(cc->mu);
+#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(cc->mu);
+#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(POLLSET_FROM_CQ(cc), pluck_worker);
+ gpr_mu_unlock(cc->mu);
+ } 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, POLLSET_FROM_CQ(cc),
+ &cc->pollset_shutdown_done);
+ gpr_mu_unlock(cc->mu);
+ }
+
+ 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 = NULL;
+ 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(cc->mu);
+ 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(cc->mu);
+ 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(cc->mu);
+ 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(cc->mu);
+ memset(&ret, 0, sizeof(ret));
+ ret.type = GRPC_QUEUE_TIMEOUT;
+ break;
+ }
+ first_loop = 0;
+ /* 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? */
+ gpr_timespec iteration_deadline = deadline;
+ if (grpc_timer_check(&exec_ctx, now, &iteration_deadline)) {
+ GPR_TIMER_MARK("alarm_triggered", 0);
+ gpr_mu_unlock(cc->mu);
+ grpc_exec_ctx_flush(&exec_ctx);
+ gpr_mu_lock(cc->mu);
+ continue;
+ } else {
+ grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc), &worker, now,
+ iteration_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 = NULL;
+ 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(cc->mu);
+ 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(cc->mu);
+ 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(cc->mu);
+ 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(cc->mu);
+ 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(cc->mu);
+ memset(&ret, 0, sizeof(ret));
+ ret.type = GRPC_QUEUE_TIMEOUT;
+ break;
+ }
+ first_loop = 0;
+ /* 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? */
+ gpr_timespec iteration_deadline = deadline;
+ if (grpc_timer_check(&exec_ctx, now, &iteration_deadline)) {
+ GPR_TIMER_MARK("alarm_triggered", 0);
+ gpr_mu_unlock(cc->mu);
+ grpc_exec_ctx_flush(&exec_ctx);
+ gpr_mu_lock(cc->mu);
+ } else {
+ grpc_pollset_work(&exec_ctx, POLLSET_FROM_CQ(cc), &worker, now,
+ iteration_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(cc->mu);
+ if (cc->shutdown_called) {
+ gpr_mu_unlock(cc->mu);
+ 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, POLLSET_FROM_CQ(cc),
+ &cc->pollset_shutdown_done);
+ }
+ gpr_mu_unlock(cc->mu);
+ 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 POLLSET_FROM_CQ(cc);
+}
+
+void grpc_cq_mark_server_cq(grpc_completion_queue *cc) { cc->is_server_cq = 1; }
+
+int grpc_cq_is_server_cq(grpc_completion_queue *cc) { return cc->is_server_cq; }
diff --git a/src/core/lib/surface/completion_queue.h b/src/core/lib/surface/completion_queue.h
new file mode 100644
index 0000000000..35591cb6f4
--- /dev/null
+++ b/src/core/lib/surface/completion_queue.h
@@ -0,0 +1,91 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_COMPLETION_QUEUE_H
+#define GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_H
+
+/* Internal API for completion queues */
+
+#include <grpc/grpc.h>
+#include "src/core/lib/iomgr/pollset.h"
+
+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_CORE_LIB_SURFACE_COMPLETION_QUEUE_H */
diff --git a/src/core/lib/surface/event_string.c b/src/core/lib/surface/event_string.c
new file mode 100644
index 0000000000..360c718a17
--- /dev/null
+++ b/src/core/lib/surface/event_string.c
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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/lib/surface/event_string.h"
+
+#include <stdio.h>
+
+#include <grpc/byte_buffer.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/support/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 == NULL) return gpr_strdup("null");
+
+ gpr_strvec_init(&buf);
+
+ switch (ev->type) {
+ case GRPC_QUEUE_TIMEOUT:
+ gpr_strvec_add(&buf, gpr_strdup("QUEUE_TIMEOUT"));
+ break;
+ case GRPC_QUEUE_SHUTDOWN:
+ gpr_strvec_add(&buf, gpr_strdup("QUEUE_SHUTDOWN"));
+ break;
+ case GRPC_OP_COMPLETE:
+ gpr_strvec_add(&buf, gpr_strdup("OP_COMPLETE: "));
+ addhdr(&buf, ev);
+ adderr(&buf, ev->success);
+ break;
+ }
+
+ out = gpr_strvec_flatten(&buf, NULL);
+ gpr_strvec_destroy(&buf);
+ return out;
+}
diff --git a/src/core/lib/surface/event_string.h b/src/core/lib/surface/event_string.h
new file mode 100644
index 0000000000..577e9c718f
--- /dev/null
+++ b/src/core/lib/surface/event_string.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_EVENT_STRING_H
+#define GRPC_CORE_LIB_SURFACE_EVENT_STRING_H
+
+#include <grpc/grpc.h>
+
+/* 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/src/core/lib/surface/init.c b/src/core/lib/surface/init.c
new file mode 100644
index 0000000000..dcb9c62d36
--- /dev/null
+++ b/src/core/lib/surface/init.c
@@ -0,0 +1,239 @@
+/*
+ *
+ * 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 <grpc/support/port_platform.h>
+
+#include <limits.h>
+#include <memory.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/time.h>
+/* TODO(ctiller): find another way? - better not to include census here */
+#include "src/core/lib/census/grpc_plugin.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/client_channel.h"
+#include "src/core/lib/channel/compress_filter.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/http_client_filter.h"
+#include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/client_config/lb_policies/pick_first.h"
+#include "src/core/lib/client_config/lb_policies/round_robin.h"
+#include "src/core/lib/client_config/lb_policy_registry.h"
+#include "src/core/lib/client_config/resolver_registry.h"
+#include "src/core/lib/client_config/resolvers/dns_resolver.h"
+#include "src/core/lib/client_config/resolvers/sockaddr_resolver.h"
+#include "src/core/lib/client_config/subchannel.h"
+#include "src/core/lib/client_config/subchannel_index.h"
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/iomgr/executor.h"
+#include "src/core/lib/iomgr/iomgr.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/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/surface/surface_trace.h"
+#include "src/core/lib/transport/chttp2_transport.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/transport/transport_impl.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);
+ /* TODO(ctiller): ideally remove this strict linkage */
+ grpc_register_plugin(census_grpc_plugin_init, census_grpc_plugin_destroy);
+ g_initializations = 0;
+}
+
+static bool append_filter(grpc_channel_stack_builder *builder, void *arg) {
+ return grpc_channel_stack_builder_append_filter(
+ builder, (const grpc_channel_filter *)arg, NULL, NULL);
+}
+
+static bool prepend_filter(grpc_channel_stack_builder *builder, void *arg) {
+ return grpc_channel_stack_builder_prepend_filter(
+ builder, (const grpc_channel_filter *)arg, NULL, NULL);
+}
+
+static bool maybe_add_http_filter(grpc_channel_stack_builder *builder,
+ void *arg) {
+ grpc_transport *t = grpc_channel_stack_builder_get_transport(builder);
+ if (t && strstr(t->vtable->name, "http")) {
+ return grpc_channel_stack_builder_prepend_filter(
+ builder, (const grpc_channel_filter *)arg, NULL, NULL);
+ }
+ return true;
+}
+
+static void register_builtin_channel_init() {
+ grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX, prepend_filter,
+ (void *)&grpc_compress_filter);
+ grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX,
+ prepend_filter,
+ (void *)&grpc_compress_filter);
+ grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, prepend_filter,
+ (void *)&grpc_compress_filter);
+ grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX,
+ maybe_add_http_filter,
+ (void *)&grpc_http_client_filter);
+ grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX,
+ grpc_add_connected_filter, NULL);
+ grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX,
+ maybe_add_http_filter,
+ (void *)&grpc_http_client_filter);
+ grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX,
+ grpc_add_connected_filter, NULL);
+ grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
+ maybe_add_http_filter,
+ (void *)&grpc_http_server_filter);
+ grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
+ grpc_add_connected_filter, NULL);
+ grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX, append_filter,
+ (void *)&grpc_client_channel_filter);
+ grpc_channel_init_register_stage(GRPC_CLIENT_LAME_CHANNEL, INT_MAX,
+ 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) {
+ gpr_time_init();
+ grpc_mdctx_global_init();
+ grpc_channel_init_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_register_tracer("channel_stack_builder",
+ &grpc_trace_channel_stack_builder);
+ grpc_security_pre_init();
+ grpc_iomgr_init();
+ grpc_executor_init();
+ grpc_tracer_init("GRPC_TRACE");
+ gpr_timers_global_init();
+ grpc_cq_global_init();
+ grpc_subchannel_index_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();
+ }
+ }
+ /* register channel finalization AFTER all plugins, to ensure that it's run
+ * at the appropriate time */
+ grpc_register_security_filters();
+ register_builtin_channel_init();
+ /* no more changes to channel init pipelines */
+ grpc_channel_init_finalize();
+ }
+ 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();
+ grpc_subchannel_index_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_channel_init_shutdown();
+ 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/src/core/lib/surface/init.h b/src/core/lib/surface/init.h
new file mode 100644
index 0000000000..10e2a5896e
--- /dev/null
+++ b/src/core/lib/surface/init.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_INIT_H
+#define GRPC_CORE_LIB_SURFACE_INIT_H
+
+void grpc_register_security_filters(void);
+void grpc_security_pre_init(void);
+int grpc_is_initialized(void);
+
+#endif /* GRPC_CORE_LIB_SURFACE_INIT_H */
diff --git a/src/core/lib/surface/init_secure.c b/src/core/lib/surface/init_secure.c
new file mode 100644
index 0000000000..d3c2f645a7
--- /dev/null
+++ b/src/core/lib/surface/init_secure.c
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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/lib/surface/init.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/security/auth_filters.h"
+#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/secure_endpoint.h"
+#include "src/core/lib/security/security_connector.h"
+#include "src/core/lib/surface/channel_init.h"
+#include "src/core/lib/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);
+}
+
+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_SECURITY_CONNECTOR_ARG, args->args[i].key)) {
+ return grpc_channel_stack_builder_prepend_filter(
+ builder, &grpc_client_auth_filter, NULL, NULL);
+ }
+ }
+ }
+ 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, NULL, NULL);
+ }
+ }
+ }
+ return true;
+}
+
+void grpc_register_security_filters(void) {
+ grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX,
+ maybe_prepend_client_auth_filter, NULL);
+ grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX,
+ maybe_prepend_client_auth_filter, NULL);
+ grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX,
+ maybe_prepend_server_auth_filter, NULL);
+}
diff --git a/src/core/lib/surface/init_unsecure.c b/src/core/lib/surface/init_unsecure.c
new file mode 100644
index 0000000000..243c005d86
--- /dev/null
+++ b/src/core/lib/surface/init_unsecure.c
@@ -0,0 +1,38 @@
+/*
+ *
+ * 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/lib/surface/init.h"
+
+void grpc_security_pre_init(void) {}
+
+void grpc_register_security_filters(void) {}
diff --git a/src/core/lib/surface/lame_client.c b/src/core/lib/surface/lame_client.c
new file mode 100644
index 0000000000..95ec4b06c3
--- /dev/null
+++ b/src/core/lib/surface/lame_client.c
@@ -0,0 +1,155 @@
+/*
+ *
+ * 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/lib/surface/lame_client.h"
+
+#include <grpc/grpc.h>
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/call.h"
+#include "src/core/lib/surface/channel.h"
+
+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_transport_stream_op_finish_with_failure(exec_ctx, op);
+}
+
+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) {}
+
+const grpc_channel_filter grpc_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_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_channel_element *elem;
+ channel_data *chand;
+ grpc_channel *channel = grpc_channel_create(&exec_ctx, target, NULL,
+ GRPC_CLIENT_LAME_CHANNEL, NULL);
+ 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);
+ 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/src/core/lib/surface/lame_client.h b/src/core/lib/surface/lame_client.h
new file mode 100644
index 0000000000..5f6ea34d4b
--- /dev/null
+++ b/src/core/lib/surface/lame_client.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H
+#define GRPC_CORE_LIB_SURFACE_LAME_CLIENT_H
+
+#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/src/core/lib/surface/metadata_array.c b/src/core/lib/surface/metadata_array.c
new file mode 100644
index 0000000000..4436f2da87
--- /dev/null
+++ b/src/core/lib/surface/metadata_array.c
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+
+#include <string.h>
+
+#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/src/core/lib/surface/secure_channel_create.c b/src/core/lib/surface/secure_channel_create.c
new file mode 100644
index 0000000000..dcb367023e
--- /dev/null
+++ b/src/core/lib/surface/secure_channel_create.c
@@ -0,0 +1,319 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/client_channel.h"
+#include "src/core/lib/client_config/resolver_registry.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "src/core/lib/security/auth_filters.h"
+#include "src/core/lib/security/credentials.h"
+#include "src/core/lib/security/security_context.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/transport/chttp2_transport.h"
+#include "src/core/lib/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;
+ }
+ 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_channel_security_connector_do_handshake(exec_ctx, c->security_connector,
+ 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_channel_security_connector_do_handshake(
+ exec_ctx, c->security_connector, 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(exec_ctx, &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;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+ 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);
+
+ channel = grpc_channel_create(&exec_ctx, target, args_copy,
+ GRPC_CLIENT_CHANNEL, NULL);
+
+ 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/src/core/lib/surface/server.c b/src/core/lib/surface/server.c
new file mode 100644
index 0000000000..080734e9d5
--- /dev/null
+++ b/src/core/lib/surface/server.c
@@ -0,0 +1,1319 @@
+/*
+ *
+ * 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/lib/surface/server.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/iomgr/iomgr.h"
+#include "src/core/lib/support/stack_lockfree.h"
+#include "src/core/lib/support/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/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"
+
+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 {
+ 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);
+ 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_transport_op op;
+ memset(&op, 0, sizeof(op));
+ op.set_accept_stream = true;
+ op.on_consumed = &chand->finish_destroy_channel_closure;
+ grpc_channel_next_op(exec_ctx,
+ grpc_channel_stack_element(
+ grpc_channel_get_channel_stack(chand->channel), 0),
+ &op);
+}
+
+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->recv_initial_metadata_ready;
+ op->recv_initial_metadata_ready = &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);
+ }
+}
+
+const grpc_channel_filter grpc_server_top_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(const grpc_channel_args *args, void *reserved) {
+ size_t i;
+
+ GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved));
+
+ 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->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,
+ const grpc_channel_args *args) {
+ 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->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(exec_ctx, NULL, args, GRPC_SERVER_CHANNEL, transport);
+ 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;
+ }
+
+ 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");
+ memset(&op, 0, sizeof(op));
+ 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;
+ 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/src/core/lib/surface/server.h b/src/core/lib/surface/server.h
new file mode 100644
index 0000000000..3845eb2981
--- /dev/null
+++ b/src/core/lib/surface/server.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_SERVER_H
+#define GRPC_CORE_LIB_SURFACE_SERVER_H
+
+#include <grpc/grpc.h>
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/transport/transport.h"
+
+extern const grpc_channel_filter grpc_server_top_filter;
+
+/* 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,
+ 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_CORE_LIB_SURFACE_SERVER_H */
diff --git a/src/core/lib/surface/server_chttp2.c b/src/core/lib/surface/server_chttp2.c
new file mode 100644
index 0000000000..f0c2ee5153
--- /dev/null
+++ b/src/core/lib/surface/server_chttp2.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_server.h"
+#include "src/core/lib/surface/api_trace.h"
+#include "src/core/lib/surface/server.h"
+#include "src/core/lib/transport/chttp2_transport.h"
+
+static void setup_transport(grpc_exec_ctx *exec_ctx, void *server,
+ grpc_transport *transport) {
+ grpc_server_setup_transport(exec_ctx, server, transport,
+ 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/src/core/lib/surface/surface_trace.h b/src/core/lib/surface/surface_trace.h
new file mode 100644
index 0000000000..6b3f673924
--- /dev/null
+++ b/src/core/lib/surface/surface_trace.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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_CORE_LIB_SURFACE_SURFACE_TRACE_H
+#define GRPC_CORE_LIB_SURFACE_SURFACE_TRACE_H
+
+#include <grpc/support/log.h>
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/surface/api_trace.h"
+
+#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_CORE_LIB_SURFACE_SURFACE_TRACE_H */
diff --git a/src/core/lib/surface/validate_metadata.c b/src/core/lib/surface/validate_metadata.c
new file mode 100644
index 0000000000..bf4126867f
--- /dev/null
+++ b/src/core/lib/surface/validate_metadata.c
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/port_platform.h>
+
+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, 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 (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/src/core/lib/surface/version.c b/src/core/lib/surface/version.c
new file mode 100644
index 0000000000..7723f39401
--- /dev/null
+++ b/src/core/lib/surface/version.c
@@ -0,0 +1,39 @@
+/*
+ *
+ * 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 <grpc/grpc.h>
+
+const char *grpc_version_string(void) { return "0.14.0-dev"; }
diff --git a/src/core/lib/transport/byte_stream.c b/src/core/lib/transport/byte_stream.c
new file mode 100644
index 0000000000..79981aa154
--- /dev/null
+++ b/src/core/lib/transport/byte_stream.c
@@ -0,0 +1,78 @@
+/*
+ *
+ * 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/lib/transport/byte_stream.h"
+
+#include <stdlib.h>
+
+#include <grpc/support/log.h>
+
+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/src/core/lib/transport/byte_stream.h b/src/core/lib/transport/byte_stream.h
new file mode 100644
index 0000000000..e7346dafc3
--- /dev/null
+++ b/src/core/lib/transport/byte_stream.h
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_BYTE_STREAM_H
+#define GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H
+
+#include <grpc/support/slice_buffer.h>
+#include "src/core/lib/iomgr/exec_ctx.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)
+
+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_CORE_LIB_TRANSPORT_BYTE_STREAM_H */
diff --git a/src/core/lib/transport/chttp2/alpn.c b/src/core/lib/transport/chttp2/alpn.c
new file mode 100644
index 0000000000..befe319180
--- /dev/null
+++ b/src/core/lib/transport/chttp2/alpn.c
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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/lib/transport/chttp2/alpn.h"
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+/* in order of preference */
+static const char *const supported_versions[] = {"h2"};
+
+int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size) {
+ size_t i;
+ for (i = 0; i < GPR_ARRAY_SIZE(supported_versions); i++) {
+ if (!strncmp(version, supported_versions[i], size)) return 1;
+ }
+ return 0;
+}
+
+size_t grpc_chttp2_num_alpn_versions(void) {
+ return GPR_ARRAY_SIZE(supported_versions);
+}
+
+const char *grpc_chttp2_get_alpn_version_index(size_t i) {
+ GPR_ASSERT(i < GPR_ARRAY_SIZE(supported_versions));
+ return supported_versions[i];
+}
diff --git a/src/core/lib/transport/chttp2/alpn.h b/src/core/lib/transport/chttp2/alpn.h
new file mode 100644
index 0000000000..a9184e63a4
--- /dev/null
+++ b/src/core/lib/transport/chttp2/alpn.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_ALPN_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_ALPN_H
+
+#include <string.h>
+
+/* 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_LIB_TRANSPORT_CHTTP2_ALPN_H */
diff --git a/src/core/lib/transport/chttp2/bin_encoder.c b/src/core/lib/transport/chttp2/bin_encoder.c
new file mode 100644
index 0000000000..79d0aa3d6f
--- /dev/null
+++ b/src/core/lib/transport/chttp2/bin_encoder.c
@@ -0,0 +1,233 @@
+/*
+ *
+ * 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/lib/transport/chttp2/bin_encoder.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include "src/core/lib/transport/chttp2/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};
+
+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/src/core/lib/transport/chttp2/bin_encoder.h b/src/core/lib/transport/chttp2/bin_encoder.h
new file mode 100644
index 0000000000..1c5cd1e1c6
--- /dev/null
+++ b/src/core/lib/transport/chttp2/bin_encoder.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_BIN_ENCODER_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_BIN_ENCODER_H
+
+#include <grpc/support/slice.h>
+
+/* 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_CORE_LIB_TRANSPORT_CHTTP2_BIN_ENCODER_H */
diff --git a/src/core/lib/transport/chttp2/frame.h b/src/core/lib/transport/chttp2/frame.h
new file mode 100644
index 0000000000..4674bc9703
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame.h
@@ -0,0 +1,69 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_FRAME_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+
+/* 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_H */
diff --git a/src/core/lib/transport/chttp2/frame_data.c b/src/core/lib/transport/chttp2/frame_data.c
new file mode 100644
index 0000000000..cf25c3ccc1
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_data.c
@@ -0,0 +1,248 @@
+/*
+ *
+ * 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/lib/transport/chttp2/frame_data.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+#include "src/core/lib/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/src/core/lib/transport/chttp2/frame_data.h b/src/core/lib/transport/chttp2/frame_data.h
new file mode 100644
index 0000000000..da404a42c6
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_data.h
@@ -0,0 +1,101 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_DATA_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_FRAME_DATA_H
+
+/* Parser for GRPC streams embedded in DATA frames */
+
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/byte_stream.h"
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_DATA_H */
diff --git a/src/core/lib/transport/chttp2/frame_goaway.c b/src/core/lib/transport/chttp2/frame_goaway.c
new file mode 100644
index 0000000000..bb8c28df90
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_goaway.c
@@ -0,0 +1,193 @@
+/*
+ *
+ * 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/lib/transport/chttp2/frame_goaway.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+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/src/core/lib/transport/chttp2/frame_goaway.h b/src/core/lib/transport/chttp2/frame_goaway.h
new file mode 100644
index 0000000000..f64c44f3d9
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_goaway.h
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_GOAWAY_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_FRAME_GOAWAY_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/chttp2/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_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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_GOAWAY_H */
diff --git a/src/core/lib/transport/chttp2/frame_ping.c b/src/core/lib/transport/chttp2/frame_ping.c
new file mode 100644
index 0000000000..14ca394264
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_ping.c
@@ -0,0 +1,97 @@
+/*
+ *
+ * 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/lib/transport/chttp2/frame_ping.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+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/src/core/lib/transport/chttp2/frame_ping.h b/src/core/lib/transport/chttp2/frame_ping.h
new file mode 100644
index 0000000000..7640fc4773
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_ping.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_PING_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_FRAME_PING_H
+
+#include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_PING_H */
diff --git a/src/core/lib/transport/chttp2/frame_rst_stream.c b/src/core/lib/transport/chttp2/frame_rst_stream.c
new file mode 100644
index 0000000000..060912afc4
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_rst_stream.c
@@ -0,0 +1,99 @@
+/*
+ *
+ * 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/lib/transport/chttp2/frame_rst_stream.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/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/src/core/lib/transport/chttp2/frame_rst_stream.h b/src/core/lib/transport/chttp2/frame_rst_stream.h
new file mode 100644
index 0000000000..93155fde9d
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_rst_stream.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H
+
+#include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/chttp2/frame.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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_RST_STREAM_H */
diff --git a/src/core/lib/transport/chttp2/frame_settings.c b/src/core/lib/transport/chttp2/frame_settings.c
new file mode 100644
index 0000000000..48429c2a78
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_settings.c
@@ -0,0 +1,259 @@
+/*
+ *
+ * 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/lib/transport/chttp2/frame_settings.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/debug/trace.h"
+#include "src/core/lib/transport/chttp2/frame.h"
+#include "src/core/lib/transport/chttp2/http2_errors.h"
+#include "src/core/lib/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/src/core/lib/transport/chttp2/frame_settings.h b/src/core/lib/transport/chttp2/frame_settings.h
new file mode 100644
index 0000000000..8b294de021
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_settings.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_SETTINGS_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_FRAME_SETTINGS_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/chttp2/frame.h"
+
+typedef enum {
+ GRPC_CHTTP2_SPS_ID0,
+ GRPC_CHTTP2_SPS_ID1,
+ GRPC_CHTTP2_SPS_VAL0,
+ GRPC_CHTTP2_SPS_VAL1,
+ GRPC_CHTTP2_SPS_VAL2,
+ GRPC_CHTTP2_SPS_VAL3
+} grpc_chttp2_settings_parse_state;
+
+/* The things HTTP/2 defines as connection level settings */
+typedef enum {
+ GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE = 1,
+ GRPC_CHTTP2_SETTINGS_ENABLE_PUSH = 2,
+ GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 3,
+ GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 4,
+ GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE = 5,
+ GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 6,
+ GRPC_CHTTP2_NUM_SETTINGS
+} grpc_chttp2_setting_id;
+
+typedef struct {
+ grpc_chttp2_settings_parse_state state;
+ 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_SETTINGS_H */
diff --git a/src/core/lib/transport/chttp2/frame_window_update.c b/src/core/lib/transport/chttp2/frame_window_update.c
new file mode 100644
index 0000000000..2ab5003316
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_window_update.c
@@ -0,0 +1,113 @@
+/*
+ *
+ * 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/lib/transport/chttp2/frame_window_update.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+
+#include <grpc/support/log.h>
+
+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/src/core/lib/transport/chttp2/frame_window_update.h b/src/core/lib/transport/chttp2/frame_window_update.h
new file mode 100644
index 0000000000..4b1aea294d
--- /dev/null
+++ b/src/core/lib/transport/chttp2/frame_window_update.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H
+
+#include <grpc/support/slice.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CHTTP2_FRAME_WINDOW_UPDATE_H */
diff --git a/src/core/lib/transport/chttp2/hpack_encoder.c b/src/core/lib/transport/chttp2/hpack_encoder.c
new file mode 100644
index 0000000000..6b45929b04
--- /dev/null
+++ b/src/core/lib/transport/chttp2/hpack_encoder.c
@@ -0,0 +1,568 @@
+/*
+ *
+ * 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/lib/transport/chttp2/hpack_encoder.h"
+
+#include <assert.h>
+#include <string.h>
+
+/* This is here for grpc_is_binary_header
+ * TODO(murgatroid99): Remove this
+ */
+#include <grpc/grpc.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/transport/chttp2/bin_encoder.h"
+#include "src/core/lib/transport/chttp2/hpack_table.h"
+#include "src/core/lib/transport/chttp2/timeout_encoding.h"
+#include "src/core/lib/transport/chttp2/varint.h"
+#include "src/core/lib/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/src/core/lib/transport/chttp2/hpack_encoder.h b/src/core/lib/transport/chttp2/hpack_encoder.h
new file mode 100644
index 0000000000..de46a8f146
--- /dev/null
+++ b/src/core/lib/transport/chttp2/hpack_encoder.h
@@ -0,0 +1,95 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_HPACK_ENCODER_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_HPACK_ENCODER_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/slice_buffer.h>
+#include "src/core/lib/transport/chttp2/frame.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/metadata_batch.h"
+
+#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_CORE_LIB_TRANSPORT_CHTTP2_HPACK_ENCODER_H */
diff --git a/src/core/lib/transport/chttp2/hpack_parser.c b/src/core/lib/transport/chttp2/hpack_parser.c
new file mode 100644
index 0000000000..d41ebab147
--- /dev/null
+++ b/src/core/lib/transport/chttp2/hpack_parser.c
@@ -0,0 +1,1449 @@
+/*
+ *
+ * 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/lib/transport/chttp2/hpack_parser.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+/* This is here for grpc_is_binary_header
+ * TODO(murgatroid99): Remove this
+ */
+#include <grpc/grpc.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/chttp2/bin_encoder.h"
+
+typedef enum {
+ NOT_BINARY,
+ B64_BYTE0,
+ B64_BYTE1,
+ B64_BYTE2,
+ B64_BYTE3
+} binary_state;
+
+/* How parsing works:
+
+ The parser object keeps track of a function pointer which represents the
+ current parse state.
+
+ Each time new bytes are presented, we call into the current state, which
+ recursively parses until all bytes in the given chunk are exhausted.
+
+ The parse state that terminates then saves its function pointer to be the
+ current state so that it can resume when more bytes are available.
+
+ It's expected that most optimizing compilers will turn this code into
+ a set of indirect jumps, and so not waste stack space. */
+
+/* forward declarations for parsing states */
+static int parse_begin(grpc_chttp2_hpack_parser *p, const 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);
+
+/* we translate the first byte of a hpack field into one of these decoding
+ cases, then use a lookup table to jump directly to the appropriate parser.
+
+ _X => the integer index is all ones, meaning we need to do varint decoding
+ _V => the integer index is all zeros, meaning we need to decode an additional
+ string value */
+typedef enum {
+ INDEXED_FIELD,
+ INDEXED_FIELD_X,
+ LITHDR_INCIDX,
+ LITHDR_INCIDX_X,
+ LITHDR_INCIDX_V,
+ LITHDR_NOTIDX,
+ LITHDR_NOTIDX_X,
+ LITHDR_NOTIDX_V,
+ LITHDR_NVRIDX,
+ LITHDR_NVRIDX_X,
+ LITHDR_NVRIDX_V,
+ MAX_TBL_SIZE,
+ MAX_TBL_SIZE_X,
+ ILLEGAL
+} first_byte_type;
+
+/* jump table of parse state functions -- order must match first_byte_type
+ above */
+static const grpc_chttp2_hpack_parser_state first_byte_action[] = {
+ parse_indexed_field, parse_indexed_field_x, parse_lithdr_incidx,
+ parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx,
+ parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx,
+ parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size,
+ parse_max_tbl_size_x, parse_illegal_op};
+
+/* indexes the first byte to a parse state function - generated by
+ gen_hpack_tables.c */
+static const uint8_t first_byte_lut[256] = {
+ LITHDR_NOTIDX_V, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX,
+ LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX,
+ LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX,
+ LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX_X,
+ LITHDR_NVRIDX_V, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
+ LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
+ LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX,
+ LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE,
+ MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE_X,
+ LITHDR_INCIDX_V, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX,
+ LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX_X,
+ ILLEGAL, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD,
+ INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD_X,
+};
+
+/* state table for huffman decoding: given a state, gives an index/16 into
+ next_sub_tbl. Taking that index and adding the value of the nibble being
+ considered returns the next state.
+
+ generated by gen_hpack_tables.c */
+static const uint8_t next_tbl[256] = {
+ 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8, 1, 3, 3, 9, 10, 11, 1, 1,
+ 1, 12, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 14, 1, 15, 16, 1, 17, 1, 15, 2, 7, 3, 18, 19, 1, 1, 1, 1, 20, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 7, 21, 1, 22, 1, 1, 1, 1, 1,
+ 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, 23, 24, 25, 1, 1, 1, 1, 2, 2, 2,
+ 26, 3, 3, 27, 10, 28, 1, 1, 1, 1, 1, 1, 2, 3, 29, 10, 30, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 2,
+ 2, 2, 2, 2, 2, 2, 2, 32, 1, 1, 15, 33, 1, 34, 35, 9, 36, 1, 1, 1,
+ 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 26, 9,
+ 38, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 2, 2, 26, 3, 3, 39, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 7, 3, 3, 3, 40, 2,
+ 41, 1, 1, 1, 42, 43, 1, 1, 44, 1, 1, 1, 1, 15, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 45, 46, 1, 1, 2, 2, 2, 35, 3, 3, 18, 47, 2,
+};
+
+/* next state, based upon current state and the current nibble: see above.
+ generated by gen_hpack_tables.c */
+static const int16_t next_sub_tbl[48 * 16] = {
+ 1, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217,
+ 218, 2, 6, 10, 13, 14, 15, 16, 17, 2, 6, 10, 13, 14, 15,
+ 16, 17, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, 24, 3,
+ 7, 11, 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8,
+ 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,
+ 199, 200, 201, 202, 203, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 9, 133, 134, 135, 136, 137, 138, 139, 140,
+ 141, 142, 143, 144, 145, 146, 147, 3, 7, 11, 24, 3, 7, 11, 24,
+ 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 12, 132, 4, 8, 4, 8, 4, 8,
+ 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 21, 4, 8, 4,
+ 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 22, 23, 91, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 3,
+ 7, 11, 24, 3, 7, 11, 24, 0, 0, 0, 0, 0, 41, 42, 43,
+ 2, 6, 10, 13, 14, 15, 16, 17, 3, 7, 11, 24, 3, 7, 11,
+ 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0,
+ 44, 45, 2, 6, 10, 13, 14, 15, 16, 17, 46, 47, 48, 49, 50,
+ 51, 52, 57, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 73, 75, 76, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 3, 7, 11, 24, 3, 7, 11,
+ 24, 3, 7, 11, 24, 0, 0, 0, 0, 3, 7, 11, 24, 3, 7,
+ 11, 24, 4, 8, 4, 8, 0, 0, 0, 92, 0, 0, 0, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 7, 11, 24,
+ 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4,
+ 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 4,
+ 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0,
+ 0, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 2, 6, 10, 13, 14, 15, 16, 17, 4, 8, 4, 8, 4, 8,
+ 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 148,
+ 149, 150, 151, 3, 7, 11, 24, 4, 8, 4, 8, 0, 0, 0, 0,
+ 0, 0, 152, 153, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11,
+ 24, 154, 155, 156, 164, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7,
+ 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 157, 158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 4, 8, 4, 8, 4, 8,
+ 4, 8, 4, 8, 4, 8, 4, 8, 197, 198, 4, 8, 4, 8, 4,
+ 8, 4, 8, 0, 0, 0, 0, 0, 0, 219, 220, 3, 7, 11, 24,
+ 4, 8, 4, 8, 4, 8, 0, 0, 221, 222, 223, 224, 3, 7, 11,
+ 24, 3, 7, 11, 24, 4, 8, 4, 8, 4, 8, 225, 228, 4, 8,
+ 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, 229,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244,
+ 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 245, 246, 247, 248, 249, 250, 251, 252,
+ 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 255,
+};
+
+/* emission table: indexed like next_tbl, ultimately gives the byte to be
+ emitted, or -1 for no byte, or 256 for end of stream
+
+ generated by gen_hpack_tables.c */
+static const uint16_t emit_tbl[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 0, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 0,
+ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
+ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
+ 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
+ 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 0,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
+ 0, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+ 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
+ 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
+ 219, 220, 221, 0, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232,
+ 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248,
+};
+
+/* generated by gen_hpack_tables.c */
+static const int16_t emit_sub_tbl[249 * 16] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49,
+ 49, 49, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 97,
+ 97, 97, 97, 48, 48, 49, 49, 50, 50, 97, 97, 99, 99, 101, 101,
+ 105, 105, 111, 111, 48, 49, 50, 97, 99, 101, 105, 111, 115, 116, -1,
+ -1, -1, -1, -1, -1, 32, 32, 32, 32, 32, 32, 32, 32, 37, 37,
+ 37, 37, 37, 37, 37, 37, 99, 99, 99, 99, 101, 101, 101, 101, 105,
+ 105, 105, 105, 111, 111, 111, 111, 115, 115, 116, 116, 32, 37, 45, 46,
+ 47, 51, 52, 53, 54, 55, 56, 57, 61, 61, 61, 61, 61, 61, 61,
+ 61, 65, 65, 65, 65, 65, 65, 65, 65, 115, 115, 115, 115, 116, 116,
+ 116, 116, 32, 32, 37, 37, 45, 45, 46, 46, 61, 65, 95, 98, 100,
+ 102, 103, 104, 108, 109, 110, 112, 114, 117, -1, -1, 58, 58, 58, 58,
+ 58, 58, 58, 58, 66, 66, 66, 66, 66, 66, 66, 66, 47, 47, 51,
+ 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 61, 61,
+ 65, 65, 95, 95, 98, 98, 100, 100, 102, 102, 103, 103, 104, 104, 108,
+ 108, 109, 109, 110, 110, 112, 112, 114, 114, 117, 117, 58, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 89, 106, 107, 113, 118, 119, 120, 121, 122, -1, -1,
+ -1, -1, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 42, 42, 42,
+ 42, 42, 42, 44, 44, 44, 44, 44, 44, 44, 44, 59, 59, 59, 59,
+ 59, 59, 59, 59, 88, 88, 88, 88, 88, 88, 88, 88, 90, 90, 90,
+ 90, 90, 90, 90, 90, 33, 33, 34, 34, 40, 40, 41, 41, 63, 63,
+ 39, 43, 124, -1, -1, -1, 35, 35, 35, 35, 35, 35, 35, 35, 62,
+ 62, 62, 62, 62, 62, 62, 62, 0, 0, 0, 0, 36, 36, 36, 36,
+ 64, 64, 64, 64, 91, 91, 91, 91, 69, 69, 69, 69, 69, 69, 69,
+ 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71,
+ 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73,
+ 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75,
+ 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77,
+ 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79,
+ 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 81,
+ 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82,
+ 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84,
+ 84, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86,
+ 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 89, 89, 89, 89, 89,
+ 89, 89, 89, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107,
+ 107, 107, 107, 107, 113, 113, 113, 113, 113, 113, 113, 113, 118, 118, 118,
+ 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120,
+ 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122,
+ 122, 122, 122, 122, 122, 122, 122, 38, 38, 38, 38, 42, 42, 42, 42,
+ 44, 44, 44, 44, 59, 59, 59, 59, 88, 88, 88, 88, 90, 90, 90,
+ 90, 33, 34, 40, 41, 63, -1, -1, -1, 39, 39, 39, 39, 39, 39,
+ 39, 39, 43, 43, 43, 43, 43, 43, 43, 43, 124, 124, 124, 124, 124,
+ 124, 124, 124, 35, 35, 35, 35, 62, 62, 62, 62, 0, 0, 36, 36,
+ 64, 64, 91, 91, 93, 93, 126, 126, 94, 125, -1, -1, 60, 60, 60,
+ 60, 60, 60, 60, 60, 96, 96, 96, 96, 96, 96, 96, 96, 123, 123,
+ 123, 123, 123, 123, 123, 123, -1, -1, -1, -1, -1, -1, -1, -1, 92,
+ 92, 92, 92, 92, 92, 92, 92, 195, 195, 195, 195, 195, 195, 195, 195,
+ 208, 208, 208, 208, 208, 208, 208, 208, 128, 128, 128, 128, 130, 130, 130,
+ 130, 131, 131, 131, 131, 162, 162, 162, 162, 184, 184, 184, 184, 194, 194,
+ 194, 194, 224, 224, 224, 224, 226, 226, 226, 226, 153, 153, 161, 161, 167,
+ 167, 172, 172, 176, 176, 177, 177, 179, 179, 209, 209, 216, 216, 217, 217,
+ 227, 227, 229, 229, 230, 230, 129, 132, 133, 134, 136, 146, 154, 156, 160,
+ 163, 164, 169, 170, 173, 178, 181, 185, 186, 187, 189, 190, 196, 198, 228,
+ 232, 233, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 135,
+ 135, 135, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 137, 137, 137,
+ 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139,
+ 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141,
+ 141, 141, 143, 143, 143, 143, 143, 143, 143, 143, 147, 147, 147, 147, 147,
+ 147, 147, 147, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150,
+ 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152,
+ 152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 155, 157, 157,
+ 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 165,
+ 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166,
+ 168, 168, 168, 168, 168, 168, 168, 168, 174, 174, 174, 174, 174, 174, 174,
+ 174, 175, 175, 175, 175, 175, 175, 175, 175, 180, 180, 180, 180, 180, 180,
+ 180, 180, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183,
+ 183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191,
+ 191, 191, 191, 191, 197, 197, 197, 197, 197, 197, 197, 197, 231, 231, 231,
+ 231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 9, 9,
+ 9, 9, 142, 142, 142, 142, 144, 144, 144, 144, 145, 145, 145, 145, 148,
+ 148, 148, 148, 159, 159, 159, 159, 171, 171, 171, 171, 206, 206, 206, 206,
+ 215, 215, 215, 215, 225, 225, 225, 225, 236, 236, 236, 236, 237, 237, 237,
+ 237, 199, 199, 207, 207, 234, 234, 235, 235, 192, 193, 200, 201, 202, 205,
+ 210, 213, 218, 219, 238, 240, 242, 243, 255, -1, 203, 203, 203, 203, 203,
+ 203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 204, 211, 211, 211, 211,
+ 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 214, 214,
+ 214, 214, 214, 214, 214, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222,
+ 222, 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 241,
+ 241, 241, 241, 241, 241, 241, 241, 244, 244, 244, 244, 244, 244, 244, 244,
+ 245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246,
+ 246, 247, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248,
+ 248, 248, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251,
+ 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253,
+ 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 2, 2, 2,
+ 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6,
+ 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 11, 12,
+ 12, 12, 12, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16,
+ 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20,
+ 20, 21, 21, 21, 21, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25,
+ 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29,
+ 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 127, 127, 127, 127,
+ 220, 220, 220, 220, 249, 249, 249, 249, 10, 13, 22, 256, 93, 93, 93,
+ 93, 126, 126, 126, 126, 94, 94, 125, 125, 60, 96, 123, -1, 92, 195,
+ 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 128,
+ 128, 128, 128, 128, 128, 128, 128, 130, 130, 130, 130, 130, 130, 130, 130,
+ 131, 131, 131, 131, 131, 131, 131, 131, 162, 162, 162, 162, 162, 162, 162,
+ 162, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 194, 194, 194, 194,
+ 194, 194, 224, 224, 224, 224, 224, 224, 224, 224, 226, 226, 226, 226, 226,
+ 226, 226, 226, 153, 153, 153, 153, 161, 161, 161, 161, 167, 167, 167, 167,
+ 172, 172, 172, 172, 176, 176, 176, 176, 177, 177, 177, 177, 179, 179, 179,
+ 179, 209, 209, 209, 209, 216, 216, 216, 216, 217, 217, 217, 217, 227, 227,
+ 227, 227, 229, 229, 229, 229, 230, 230, 230, 230, 129, 129, 132, 132, 133,
+ 133, 134, 134, 136, 136, 146, 146, 154, 154, 156, 156, 160, 160, 163, 163,
+ 164, 164, 169, 169, 170, 170, 173, 173, 178, 178, 181, 181, 185, 185, 186,
+ 186, 187, 187, 189, 189, 190, 190, 196, 196, 198, 198, 228, 228, 232, 232,
+ 233, 233, 1, 135, 137, 138, 139, 140, 141, 143, 147, 149, 150, 151, 152,
+ 155, 157, 158, 165, 166, 168, 174, 175, 180, 182, 183, 188, 191, 197, 231,
+ 239, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, 9,
+ 9, 9, 9, 9, 9, 142, 142, 142, 142, 142, 142, 142, 142, 144, 144,
+ 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 148,
+ 148, 148, 148, 148, 148, 148, 148, 159, 159, 159, 159, 159, 159, 159, 159,
+ 171, 171, 171, 171, 171, 171, 171, 171, 206, 206, 206, 206, 206, 206, 206,
+ 206, 215, 215, 215, 215, 215, 215, 215, 215, 225, 225, 225, 225, 225, 225,
+ 225, 225, 236, 236, 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237,
+ 237, 237, 237, 199, 199, 199, 199, 207, 207, 207, 207, 234, 234, 234, 234,
+ 235, 235, 235, 235, 192, 192, 193, 193, 200, 200, 201, 201, 202, 202, 205,
+ 205, 210, 210, 213, 213, 218, 218, 219, 219, 238, 238, 240, 240, 242, 242,
+ 243, 243, 255, 255, 203, 204, 211, 212, 214, 221, 222, 223, 241, 244, 245,
+ 246, 247, 248, 250, 251, 252, 253, 254, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2,
+ 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
+ 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6,
+ 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12,
+ 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15,
+ 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17,
+ 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18,
+ 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20,
+ 20, 21, 21, 21, 21, 21, 21, 21, 21, 23, 23, 23, 23, 23, 23,
+ 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25,
+ 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27,
+ 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29,
+ 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31,
+ 31, 31, 31, 31, 31, 31, 127, 127, 127, 127, 127, 127, 127, 127, 220,
+ 220, 220, 220, 220, 220, 220, 220, 249, 249, 249, 249, 249, 249, 249, 249,
+ 10, 10, 13, 13, 22, 22, 256, 256, 67, 67, 67, 67, 67, 67, 67,
+ 67, 68, 68, 68, 68, 68, 68, 68, 68, 95, 95, 95, 95, 95, 95,
+ 95, 95, 98, 98, 98, 98, 98, 98, 98, 98, 100, 100, 100, 100, 100,
+ 100, 100, 100, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103,
+ 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 108, 108, 108,
+ 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110,
+ 110, 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 112, 112, 114,
+ 114, 114, 114, 114, 114, 114, 114, 117, 117, 117, 117, 117, 117, 117, 117,
+ 58, 58, 58, 58, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68,
+ 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72,
+ 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76,
+ 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79,
+ 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83,
+ 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87,
+ 87, 87, 89, 89, 89, 89, 106, 106, 106, 106, 107, 107, 107, 107, 113,
+ 113, 113, 113, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120,
+ 121, 121, 121, 121, 122, 122, 122, 122, 38, 38, 42, 42, 44, 44, 59,
+ 59, 88, 88, 90, 90, -1, -1, -1, -1, 33, 33, 33, 33, 33, 33,
+ 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40,
+ 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 63, 63, 63, 63,
+ 63, 63, 63, 63, 39, 39, 39, 39, 43, 43, 43, 43, 124, 124, 124,
+ 124, 35, 35, 62, 62, 0, 36, 64, 91, 93, 126, -1, -1, 94, 94,
+ 94, 94, 94, 94, 94, 94, 125, 125, 125, 125, 125, 125, 125, 125, 60,
+ 60, 60, 60, 96, 96, 96, 96, 123, 123, 123, 123, -1, -1, -1, -1,
+ 92, 92, 92, 92, 195, 195, 195, 195, 208, 208, 208, 208, 128, 128, 130,
+ 130, 131, 131, 162, 162, 184, 184, 194, 194, 224, 224, 226, 226, 153, 161,
+ 167, 172, 176, 177, 179, 209, 216, 217, 227, 229, 230, -1, -1, -1, -1,
+ -1, -1, -1, 129, 129, 129, 129, 129, 129, 129, 129, 132, 132, 132, 132,
+ 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134,
+ 134, 134, 134, 134, 134, 136, 136, 136, 136, 136, 136, 136, 136, 146, 146,
+ 146, 146, 146, 146, 146, 146, 154, 154, 154, 154, 154, 154, 154, 154, 156,
+ 156, 156, 156, 156, 156, 156, 156, 160, 160, 160, 160, 160, 160, 160, 160,
+ 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164,
+ 164, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170,
+ 170, 170, 173, 173, 173, 173, 173, 173, 173, 173, 178, 178, 178, 178, 178,
+ 178, 178, 178, 181, 181, 181, 181, 181, 181, 181, 181, 185, 185, 185, 185,
+ 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187,
+ 187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190,
+ 190, 190, 190, 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 198,
+ 198, 198, 198, 198, 198, 198, 198, 228, 228, 228, 228, 228, 228, 228, 228,
+ 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233,
+ 233, 1, 1, 1, 1, 135, 135, 135, 135, 137, 137, 137, 137, 138, 138,
+ 138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 143,
+ 143, 143, 143, 147, 147, 147, 147, 149, 149, 149, 149, 150, 150, 150, 150,
+ 151, 151, 151, 151, 152, 152, 152, 152, 155, 155, 155, 155, 157, 157, 157,
+ 157, 158, 158, 158, 158, 165, 165, 165, 165, 166, 166, 166, 166, 168, 168,
+ 168, 168, 174, 174, 174, 174, 175, 175, 175, 175, 180, 180, 180, 180, 182,
+ 182, 182, 182, 183, 183, 183, 183, 188, 188, 188, 188, 191, 191, 191, 191,
+ 197, 197, 197, 197, 231, 231, 231, 231, 239, 239, 239, 239, 9, 9, 142,
+ 142, 144, 144, 145, 145, 148, 148, 159, 159, 171, 171, 206, 206, 215, 215,
+ 225, 225, 236, 236, 237, 237, 199, 207, 234, 235, 192, 192, 192, 192, 192,
+ 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 200, 200, 200, 200,
+ 200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202,
+ 202, 202, 202, 202, 202, 205, 205, 205, 205, 205, 205, 205, 205, 210, 210,
+ 210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 213, 218,
+ 218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219,
+ 238, 238, 238, 238, 238, 238, 238, 238, 240, 240, 240, 240, 240, 240, 240,
+ 240, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243,
+ 243, 243, 255, 255, 255, 255, 255, 255, 255, 255, 203, 203, 203, 203, 204,
+ 204, 204, 204, 211, 211, 211, 211, 212, 212, 212, 212, 214, 214, 214, 214,
+ 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 241, 241, 241,
+ 241, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247,
+ 247, 247, 248, 248, 248, 248, 250, 250, 250, 250, 251, 251, 251, 251, 252,
+ 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 2, 2, 3, 3,
+ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 11, 11, 12, 12, 14,
+ 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30,
+ 30, 31, 31, 127, 127, 220, 220, 249, 249, -1, -1, 10, 10, 10, 10,
+ 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 22, 22, 22,
+ 22, 22, 22, 22, 22, 256, 256, 256, 256, 256, 256, 256, 256, 45, 45,
+ 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47,
+ 47, 47, 47, 47, 47, 47, 47, 51, 51, 51, 51, 51, 51, 51, 51,
+ 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53,
+ 53, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55,
+ 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57,
+ 57, 57, 57, 50, 50, 50, 50, 50, 50, 50, 50, 97, 97, 97, 97,
+ 97, 97, 97, 97, 99, 99, 99, 99, 99, 99, 99, 99, 101, 101, 101,
+ 101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 111, 111,
+ 111, 111, 111, 111, 111, 111, 115, 115, 115, 115, 115, 115, 115, 115, 116,
+ 116, 116, 116, 116, 116, 116, 116, 32, 32, 32, 32, 37, 37, 37, 37,
+ 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 51, 51, 51,
+ 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55,
+ 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 61, 61, 61, 61, 65,
+ 65, 65, 65, 95, 95, 95, 95, 98, 98, 98, 98, 100, 100, 100, 100,
+ 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 108, 108, 108,
+ 108, 109, 109, 109, 109, 110, 110, 110, 110, 112, 112, 112, 112, 114, 114,
+ 114, 114, 117, 117, 117, 117, 58, 58, 66, 66, 67, 67, 68, 68, 69,
+ 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76,
+ 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84,
+ 84, 85, 85, 86, 86, 87, 87, 89, 89, 106, 106, 107, 107, 113, 113,
+ 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 38, 42, 44, 59, 88,
+ 90, -1, -1, 33, 33, 33, 33, 34, 34, 34, 34, 40, 40, 40, 40,
+ 41, 41, 41, 41, 63, 63, 63, 63, 39, 39, 43, 43, 124, 124, 35,
+ 62, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36,
+ 36, 36, 36, 36, 36, 36, 64, 64, 64, 64, 64, 64, 64, 64, 91,
+ 91, 91, 91, 91, 91, 91, 91, 93, 93, 93, 93, 93, 93, 93, 93,
+ 126, 126, 126, 126, 126, 126, 126, 126, 94, 94, 94, 94, 125, 125, 125,
+ 125, 60, 60, 96, 96, 123, 123, -1, -1, 92, 92, 195, 195, 208, 208,
+ 128, 130, 131, 162, 184, 194, 224, 226, -1, -1, 153, 153, 153, 153, 153,
+ 153, 153, 153, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167,
+ 167, 167, 167, 167, 172, 172, 172, 172, 172, 172, 172, 172, 176, 176, 176,
+ 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 179, 179,
+ 179, 179, 179, 179, 179, 179, 209, 209, 209, 209, 209, 209, 209, 209, 216,
+ 216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 217,
+ 227, 227, 227, 227, 227, 227, 227, 227, 229, 229, 229, 229, 229, 229, 229,
+ 229, 230, 230, 230, 230, 230, 230, 230, 230, 129, 129, 129, 129, 132, 132,
+ 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 136, 136, 136, 136, 146,
+ 146, 146, 146, 154, 154, 154, 154, 156, 156, 156, 156, 160, 160, 160, 160,
+ 163, 163, 163, 163, 164, 164, 164, 164, 169, 169, 169, 169, 170, 170, 170,
+ 170, 173, 173, 173, 173, 178, 178, 178, 178, 181, 181, 181, 181, 185, 185,
+ 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 189, 189, 189, 189, 190,
+ 190, 190, 190, 196, 196, 196, 196, 198, 198, 198, 198, 228, 228, 228, 228,
+ 232, 232, 232, 232, 233, 233, 233, 233, 1, 1, 135, 135, 137, 137, 138,
+ 138, 139, 139, 140, 140, 141, 141, 143, 143, 147, 147, 149, 149, 150, 150,
+ 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 165, 165, 166, 166, 168,
+ 168, 174, 174, 175, 175, 180, 180, 182, 182, 183, 183, 188, 188, 191, 191,
+ 197, 197, 231, 231, 239, 239, 9, 142, 144, 145, 148, 159, 171, 206, 215,
+ 225, 236, 237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199, 199,
+ 199, 199, 199, 199, 199, 199, 207, 207, 207, 207, 207, 207, 207, 207, 234,
+ 234, 234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235,
+ 192, 192, 192, 192, 193, 193, 193, 193, 200, 200, 200, 200, 201, 201, 201,
+ 201, 202, 202, 202, 202, 205, 205, 205, 205, 210, 210, 210, 210, 213, 213,
+ 213, 213, 218, 218, 218, 218, 219, 219, 219, 219, 238, 238, 238, 238, 240,
+ 240, 240, 240, 242, 242, 242, 242, 243, 243, 243, 243, 255, 255, 255, 255,
+ 203, 203, 204, 204, 211, 211, 212, 212, 214, 214, 221, 221, 222, 222, 223,
+ 223, 241, 241, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 250, 250,
+ 251, 251, 252, 252, 253, 253, 254, 254, 2, 3, 4, 5, 6, 7, 8,
+ 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 127, 220, 249, -1, 10, 10, 10, 10, 13, 13, 13,
+ 13, 22, 22, 22, 22, 256, 256, 256, 256,
+};
+
+static const uint8_t inverse_base64[256] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255,
+ 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 64, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255,
+};
+
+/* emission helpers */
+static 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;
+ }
+ }
+ p->on_header(p->on_header_user_data, md);
+ return 1;
+}
+
+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;
+ return s;
+}
+
+/* jump to the next state */
+static int 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) {
+ if (cur == end) {
+ p->state = parse_begin;
+ return 1;
+ }
+
+ return first_byte_action[first_byte_lut[*cur]](p, cur, end);
+}
+
+/* stream dependency and prioritization data: we just skip it */
+static int parse_stream_weight(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_stream_weight;
+ return 1;
+ }
+
+ return p->after_prioritization(p, cur + 1, end);
+}
+
+static int parse_stream_dep3(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_stream_dep3;
+ return 1;
+ }
+
+ return parse_stream_weight(p, cur + 1, end);
+}
+
+static int parse_stream_dep2(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_stream_dep2;
+ return 1;
+ }
+
+ return parse_stream_dep3(p, cur + 1, end);
+}
+
+static int parse_stream_dep1(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_stream_dep1;
+ return 1;
+ }
+
+ return parse_stream_dep2(p, cur + 1, end);
+}
+
+static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_stream_dep0;
+ return 1;
+ }
+
+ return parse_stream_dep1(p, cur + 1, end);
+}
+
+/* emit an indexed field; for now just logs it to console; jumps to
+ begin the next field on completion */
+static int finish_indexed_field(grpc_chttp2_hpack_parser *p, const 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;
+ }
+ GRPC_MDELEM_REF(md);
+ return on_hdr(p, md, 0) && 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) {
+ 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 const grpc_chttp2_hpack_parser_state and_then[] = {
+ finish_indexed_field};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ p->index = 0x7f;
+ p->parsing.value = &p->index;
+ return parse_value0(p, cur + 1, end);
+}
+
+/* finish a literal header with incremental indexing: just log, and jump to '
+ begin */
+static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p, const 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 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);
+}
+
+/* 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 const grpc_chttp2_hpack_parser_state and_then[] = {
+ parse_value_string_with_indexed_key, finish_lithdr_incidx};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ p->index = (*cur) & 0x3f;
+ return parse_string_prefix(p, cur + 1, end);
+}
+
+/* parse a literal header with incremental indexing; index >= 63 */
+static int parse_lithdr_incidx_x(grpc_chttp2_hpack_parser *p,
+ const 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};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ p->index = 0x3f;
+ p->parsing.value = &p->index;
+ return parse_value0(p, cur + 1, end);
+}
+
+/* parse a literal header with incremental indexing; index = 0 */
+static int parse_lithdr_incidx_v(grpc_chttp2_hpack_parser *p,
+ const 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};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ return parse_string_prefix(p, cur + 1, end);
+}
+
+/* finish a literal header without incremental indexing */
+static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p, const 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);
+}
+
+/* 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);
+}
+
+/* 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 const grpc_chttp2_hpack_parser_state and_then[] = {
+ parse_value_string_with_indexed_key, finish_lithdr_notidx};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ p->index = (*cur) & 0xf;
+ return parse_string_prefix(p, cur + 1, end);
+}
+
+/* parse a literal header without incremental indexing; index >= 15 */
+static int parse_lithdr_notidx_x(grpc_chttp2_hpack_parser *p,
+ const 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};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ p->index = 0xf;
+ p->parsing.value = &p->index;
+ return parse_value0(p, cur + 1, end);
+}
+
+/* parse a literal header without incremental indexing; index == 0 */
+static int parse_lithdr_notidx_v(grpc_chttp2_hpack_parser *p,
+ const 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};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ return parse_string_prefix(p, cur + 1, end);
+}
+
+/* finish a literal header that is never indexed */
+static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p, const 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);
+}
+
+/* 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);
+}
+
+/* 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 const grpc_chttp2_hpack_parser_state and_then[] = {
+ parse_value_string_with_indexed_key, finish_lithdr_nvridx};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ p->index = (*cur) & 0xf;
+ return parse_string_prefix(p, cur + 1, end);
+}
+
+/* parse a literal header that is never indexed; index >= 15 */
+static int parse_lithdr_nvridx_x(grpc_chttp2_hpack_parser *p,
+ const 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};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ p->index = 0xf;
+ p->parsing.value = &p->index;
+ return parse_value0(p, cur + 1, end);
+}
+
+/* parse a literal header that is never indexed; index == 0 */
+static int parse_lithdr_nvridx_v(grpc_chttp2_hpack_parser *p,
+ const 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};
+ p->dynamic_table_update_allowed = 0;
+ p->next_state = and_then;
+ return parse_string_prefix(p, cur + 1, end);
+}
+
+/* finish parsing a max table size change */
+static int finish_max_tbl_size(grpc_chttp2_hpack_parser *p, const 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);
+}
+
+/* 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) {
+ if (p->dynamic_table_update_allowed == 0) {
+ return 0;
+ }
+ p->dynamic_table_update_allowed--;
+ p->index = (*cur) & 0x1f;
+ return finish_max_tbl_size(p, cur + 1, end);
+}
+
+/* parse a max table size change, max size >= 15 */
+static int parse_max_tbl_size_x(grpc_chttp2_hpack_parser *p, const 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;
+ }
+ p->dynamic_table_update_allowed--;
+ p->next_state = and_then;
+ p->index = 0x1f;
+ p->parsing.value = &p->index;
+ return parse_value0(p, cur + 1, end);
+}
+
+/* a parse error: jam the parse state into parse_error, and return error */
+static int parse_error(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ p->state = parse_error;
+ return 0;
+}
+
+static int 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);
+}
+
+/* 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) {
+ if (cur == end) {
+ p->state = parse_value0;
+ return 1;
+ }
+
+ *p->parsing.value += (*cur) & 0x7f;
+
+ if ((*cur) & 0x80) {
+ return parse_value1(p, cur + 1, end);
+ } else {
+ return parse_next(p, cur + 1, end);
+ }
+}
+
+/* parse the 2nd byte of a varint into p->parsing.value
+ no overflow is possible */
+static int parse_value1(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_value1;
+ return 1;
+ }
+
+ *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 7;
+
+ if ((*cur) & 0x80) {
+ return parse_value2(p, cur + 1, end);
+ } else {
+ return parse_next(p, cur + 1, end);
+ }
+}
+
+/* parse the 3rd byte of a varint into p->parsing.value
+ no overflow is possible */
+static int parse_value2(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_value2;
+ return 1;
+ }
+
+ *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 14;
+
+ if ((*cur) & 0x80) {
+ return parse_value3(p, cur + 1, end);
+ } else {
+ return parse_next(p, cur + 1, end);
+ }
+}
+
+/* parse the 4th byte of a varint into p->parsing.value
+ no overflow is possible */
+static int parse_value3(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (cur == end) {
+ p->state = parse_value3;
+ return 1;
+ }
+
+ *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 21;
+
+ if ((*cur) & 0x80) {
+ return parse_value4(p, cur + 1, end);
+ } else {
+ return parse_next(p, cur + 1, end);
+ }
+}
+
+/* parse the 5th byte of a varint into p->parsing.value
+ depending on the byte, we may overflow, and care must be taken */
+static int parse_value4(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ uint8_t c;
+ uint32_t cur_value;
+ uint32_t add_value;
+
+ if (cur == end) {
+ p->state = parse_value4;
+ return 1;
+ }
+
+ c = (*cur) & 0x7f;
+ if (c > 0xf) {
+ goto error;
+ }
+
+ cur_value = *p->parsing.value;
+ add_value = ((uint32_t)c) << 28;
+ if (add_value > 0xffffffffu - cur_value) {
+ goto error;
+ }
+
+ *p->parsing.value = cur_value + add_value;
+
+ if ((*cur) & 0x80) {
+ return parse_value5up(p, cur + 1, end);
+ } else {
+ return parse_next(p, cur + 1, end);
+ }
+
+error:
+ gpr_log(GPR_ERROR,
+ "integer overflow in hpack integer decoding: have 0x%08x, "
+ "got byte 0x%02x on byte 5",
+ *p->parsing.value, *cur);
+ return parse_error(p, cur, end);
+}
+
+/* parse any trailing bytes in a varint: it's possible to append an arbitrary
+ number of 0x80's and not affect the value - a zero will terminate - and
+ anything else will overflow */
+static int parse_value5up(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ while (cur != end && *cur == 0x80) {
+ ++cur;
+ }
+
+ if (cur == end) {
+ p->state = parse_value5up;
+ return 1;
+ }
+
+ if (*cur == 0) {
+ return parse_next(p, cur + 1, end);
+ }
+
+ gpr_log(GPR_ERROR,
+ "integer overflow in hpack integer decoding: have 0x%08x, "
+ "got byte 0x%02x sometime after byte 5",
+ *p->parsing.value, *cur);
+ return parse_error(p, cur, end);
+}
+
+/* parse a string prefix */
+static int 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;
+ }
+
+ p->strlen = (*cur) & 0x7f;
+ p->huff = (*cur) >> 7;
+ if (p->strlen == 0x7f) {
+ p->parsing.value = &p->strlen;
+ return parse_value0(p, cur + 1, end);
+ } else {
+ return parse_next(p, cur + 1, end);
+ }
+}
+
+/* append some bytes to a string */
+static void append_bytes(grpc_chttp2_hpack_parser_string *str,
+ const 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);
+ }
+ memcpy(str->str + str->length, data, length);
+ GPR_ASSERT(length <= UINT32_MAX - str->length);
+ str->length += (uint32_t)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;
+ uint32_t bits;
+ uint8_t decoded[3];
+ switch ((binary_state)p->binary) {
+ case NOT_BINARY:
+ append_bytes(str, cur, (size_t)(end - cur));
+ return 1;
+ b64_byte0:
+ case B64_BYTE0:
+ if (cur == end) {
+ p->binary = B64_BYTE0;
+ return 1;
+ }
+ bits = inverse_base64[*cur];
+ ++cur;
+ if (bits == 255)
+ return 0;
+ else if (bits == 64)
+ goto b64_byte0;
+ p->base64_buffer = bits << 18;
+ /* fallthrough */
+ b64_byte1:
+ case B64_BYTE1:
+ if (cur == end) {
+ p->binary = B64_BYTE1;
+ return 1;
+ }
+ bits = inverse_base64[*cur];
+ ++cur;
+ if (bits == 255)
+ return 0;
+ else if (bits == 64)
+ goto b64_byte1;
+ p->base64_buffer |= bits << 12;
+ /* fallthrough */
+ b64_byte2:
+ case B64_BYTE2:
+ if (cur == end) {
+ p->binary = B64_BYTE2;
+ return 1;
+ }
+ bits = inverse_base64[*cur];
+ ++cur;
+ if (bits == 255)
+ return 0;
+ else if (bits == 64)
+ goto b64_byte2;
+ p->base64_buffer |= bits << 6;
+ /* fallthrough */
+ b64_byte3:
+ case B64_BYTE3:
+ if (cur == end) {
+ p->binary = B64_BYTE3;
+ return 1;
+ }
+ bits = inverse_base64[*cur];
+ ++cur;
+ if (bits == 255)
+ return 0;
+ else if (bits == 64)
+ goto b64_byte3;
+ p->base64_buffer |= bits;
+ bits = p->base64_buffer;
+ decoded[0] = (uint8_t)(bits >> 16);
+ decoded[1] = (uint8_t)(bits >> 8);
+ decoded[2] = (uint8_t)(bits);
+ append_bytes(str, decoded, 3);
+ goto b64_byte0;
+ }
+ GPR_UNREACHABLE_CODE(return 1);
+}
+
+/* append a null terminator to a string */
+static int finish_str(grpc_chttp2_hpack_parser *p) {
+ uint8_t terminator = 0;
+ uint8_t decoded[2];
+ uint32_t bits;
+ grpc_chttp2_hpack_parser_string *str = p->parsing.str;
+ switch ((binary_state)p->binary) {
+ case NOT_BINARY:
+ break;
+ case B64_BYTE0:
+ break;
+ case B64_BYTE1:
+ gpr_log(GPR_ERROR, "illegal base64 encoding");
+ return 0; /* illegal encoding */
+ case B64_BYTE2:
+ bits = p->base64_buffer;
+ if (bits & 0xffff) {
+ gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%04x",
+ bits & 0xffff);
+ return 0;
+ }
+ decoded[0] = (uint8_t)(bits >> 16);
+ append_bytes(str, decoded, 1);
+ break;
+ case B64_BYTE3:
+ bits = p->base64_buffer;
+ if (bits & 0xff) {
+ gpr_log(GPR_ERROR, "trailing bits in base64 encoding: 0x%02x",
+ bits & 0xff);
+ return 0;
+ }
+ decoded[0] = (uint8_t)(bits >> 16);
+ decoded[1] = (uint8_t)(bits >> 8);
+ append_bytes(str, decoded, 2);
+ break;
+ }
+ append_bytes(str, &terminator, 1);
+ p->parsing.str->length--; /* don't actually count the null terminator */
+ return 1;
+}
+
+/* decode a nibble from a huffman encoded stream */
+static int huff_nibble(grpc_chttp2_hpack_parser *p, 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;
+ } else {
+ assert(emit == 256);
+ }
+ }
+ p->huff_state = next;
+ return 1;
+}
+
+/* decode full bytes from a huffman encoded stream */
+static int add_huff_bytes(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ for (; cur != end; ++cur) {
+ if (!huff_nibble(p, *cur >> 4) || !huff_nibble(p, *cur & 0xf)) return 0;
+ }
+ return 1;
+}
+
+/* decode some string bytes based on the current decoding mode
+ (huffman or not) */
+static int add_str_bytes(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ if (p->huff) {
+ return add_huff_bytes(p, cur, end);
+ } else {
+ return append_string(p, cur, end);
+ }
+}
+
+/* parse a string - tries to do large chunks at a time */
+static int parse_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end) {
+ size_t remaining = p->strlen - p->strgot;
+ size_t given = (size_t)(end - cur);
+ if (remaining <= given) {
+ return add_str_bytes(p, cur, cur + remaining) && finish_str(p) &&
+ parse_next(p, cur + remaining, end);
+ } else {
+ if (!add_str_bytes(p, cur, cur + given)) return 0;
+ GPR_ASSERT(given <= UINT32_MAX - p->strgot);
+ p->strgot += (uint32_t)given;
+ p->state = parse_string;
+ return 1;
+ }
+}
+
+/* begin parsing a string - performs setup, calls parse_string */
+static int begin_parse_string(grpc_chttp2_hpack_parser *p, const uint8_t *cur,
+ const uint8_t *end, uint8_t binary,
+ grpc_chttp2_hpack_parser_string *str) {
+ p->strgot = 0;
+ str->length = 0;
+ p->parsing.str = str;
+ p->huff_state = 0;
+ p->binary = binary;
+ return parse_string(p, cur, end);
+}
+
+/* parse the key string */
+static int parse_key_string(grpc_chttp2_hpack_parser *p, const 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 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;
+ }
+ return grpc_is_binary_header(
+ (const char *)GPR_SLICE_START_PTR(elem->key->slice),
+ GPR_SLICE_LENGTH(elem->key->slice))
+ ? BINARY_HEADER
+ : PLAINTEXT_HEADER;
+}
+
+/* parse the value string */
+static int parse_value_string(grpc_chttp2_hpack_parser *p, const 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 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 int 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;
+ 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->dynamic_table_update_allowed = 2;
+ grpc_chttp2_hptbl_init(&p->table);
+}
+
+void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) {
+ p->after_prioritization = p->state;
+ p->state = parse_stream_dep0;
+}
+
+void grpc_chttp2_hpack_parser_destroy(grpc_chttp2_hpack_parser *p) {
+ grpc_chttp2_hptbl_destroy(&p->table);
+ gpr_free(p->key.str);
+ gpr_free(p->value.str);
+}
+
+int grpc_chttp2_hpack_parser_parse(grpc_chttp2_hpack_parser *p,
+ const 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_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;
+ }
+ 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;
+ }
+ /* need to check for null stream: this can occur if we receive an invalid
+ stream id on a header */
+ if (stream_parsing != NULL) {
+ 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 (parser->is_eof) {
+ stream_parsing->received_close = 1;
+ }
+ }
+ parser->on_header = on_header_not_set;
+ parser->on_header_user_data = NULL;
+ parser->is_boundary = 0xde;
+ parser->is_eof = 0xde;
+ parser->dynamic_table_update_allowed = 2;
+ }
+ GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0);
+ return GRPC_CHTTP2_PARSE_OK;
+}
diff --git a/src/core/lib/transport/chttp2/hpack_parser.h b/src/core/lib/transport/chttp2/hpack_parser.h
new file mode 100644
index 0000000000..a534fd5cf4
--- /dev/null
+++ b/src/core/lib/transport/chttp2/hpack_parser.h
@@ -0,0 +1,116 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_HPACK_PARSER_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_HPACK_PARSER_H
+
+#include <stddef.h>
+
+#include <grpc/support/port_platform.h>
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/transport/chttp2/frame.h"
+#include "src/core/lib/transport/chttp2/hpack_table.h"
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CHTTP2_HPACK_PARSER_H */
diff --git a/src/core/lib/transport/chttp2/hpack_table.c b/src/core/lib/transport/chttp2/hpack_table.c
new file mode 100644
index 0000000000..f92bc26585
--- /dev/null
+++ b/src/core/lib/transport/chttp2/hpack_table.c
@@ -0,0 +1,361 @@
+/*
+ *
+ * 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/lib/transport/chttp2/hpack_table.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/support/murmur_hash.h"
+
+static struct {
+ const char *key;
+ const char *value;
+} static_table[] = {
+ /* 0: */
+ {NULL, NULL},
+ /* 1: */
+ {":authority", ""},
+ /* 2: */
+ {":method", "GET"},
+ /* 3: */
+ {":method", "POST"},
+ /* 4: */
+ {":path", "/"},
+ /* 5: */
+ {":path", "/index.html"},
+ /* 6: */
+ {":scheme", "http"},
+ /* 7: */
+ {":scheme", "https"},
+ /* 8: */
+ {":status", "200"},
+ /* 9: */
+ {":status", "204"},
+ /* 10: */
+ {":status", "206"},
+ /* 11: */
+ {":status", "304"},
+ /* 12: */
+ {":status", "400"},
+ /* 13: */
+ {":status", "404"},
+ /* 14: */
+ {":status", "500"},
+ /* 15: */
+ {"accept-charset", ""},
+ /* 16: */
+ {"accept-encoding", "gzip, deflate"},
+ /* 17: */
+ {"accept-language", ""},
+ /* 18: */
+ {"accept-ranges", ""},
+ /* 19: */
+ {"accept", ""},
+ /* 20: */
+ {"access-control-allow-origin", ""},
+ /* 21: */
+ {"age", ""},
+ /* 22: */
+ {"allow", ""},
+ /* 23: */
+ {"authorization", ""},
+ /* 24: */
+ {"cache-control", ""},
+ /* 25: */
+ {"content-disposition", ""},
+ /* 26: */
+ {"content-encoding", ""},
+ /* 27: */
+ {"content-language", ""},
+ /* 28: */
+ {"content-length", ""},
+ /* 29: */
+ {"content-location", ""},
+ /* 30: */
+ {"content-range", ""},
+ /* 31: */
+ {"content-type", ""},
+ /* 32: */
+ {"cookie", ""},
+ /* 33: */
+ {"date", ""},
+ /* 34: */
+ {"etag", ""},
+ /* 35: */
+ {"expect", ""},
+ /* 36: */
+ {"expires", ""},
+ /* 37: */
+ {"from", ""},
+ /* 38: */
+ {"host", ""},
+ /* 39: */
+ {"if-match", ""},
+ /* 40: */
+ {"if-modified-since", ""},
+ /* 41: */
+ {"if-none-match", ""},
+ /* 42: */
+ {"if-range", ""},
+ /* 43: */
+ {"if-unmodified-since", ""},
+ /* 44: */
+ {"last-modified", ""},
+ /* 45: */
+ {"link", ""},
+ /* 46: */
+ {"location", ""},
+ /* 47: */
+ {"max-forwards", ""},
+ /* 48: */
+ {"proxy-authenticate", ""},
+ /* 49: */
+ {"proxy-authorization", ""},
+ /* 50: */
+ {"range", ""},
+ /* 51: */
+ {"referer", ""},
+ /* 52: */
+ {"refresh", ""},
+ /* 53: */
+ {"retry-after", ""},
+ /* 54: */
+ {"server", ""},
+ /* 55: */
+ {"set-cookie", ""},
+ /* 56: */
+ {"strict-transport-security", ""},
+ /* 57: */
+ {"transfer-encoding", ""},
+ /* 58: */
+ {"user-agent", ""},
+ /* 59: */
+ {"vary", ""},
+ /* 60: */
+ {"via", ""},
+ /* 61: */
+ {"www-authenticate", ""},
+};
+
+static uint32_t entries_for_bytes(uint32_t bytes) {
+ return (bytes + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) /
+ GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
+}
+
+void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl) {
+ size_t i;
+
+ memset(tbl, 0, sizeof(*tbl));
+ tbl->current_table_bytes = tbl->max_bytes =
+ 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);
+ 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);
+ }
+}
+
+void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) {
+ size_t i;
+ for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
+ GRPC_MDELEM_UNREF(tbl->static_ents[i]);
+ }
+ for (i = 0; i < tbl->num_ents; i++) {
+ GRPC_MDELEM_UNREF(tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]);
+ }
+ gpr_free(tbl->ents);
+}
+
+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];
+ }
+ /* Otherwise, find the value in the list of valid entries */
+ tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1);
+ if (tbl_index < tbl->num_ents) {
+ uint32_t offset =
+ (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries;
+ return tbl->ents[offset];
+ }
+ /* Invalid entry: return error */
+ return NULL;
+}
+
+/* Evict one element from the table */
+static void evict1(grpc_chttp2_hptbl *tbl) {
+ grpc_mdelem *first_ent = tbl->ents[tbl->first_ent];
+ size_t elem_bytes = GPR_SLICE_LENGTH(first_ent->key->slice) +
+ GPR_SLICE_LENGTH(first_ent->value->slice) +
+ GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
+ GPR_ASSERT(elem_bytes <= tbl->mem_used);
+ tbl->mem_used -= (uint32_t)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);
+ uint32_t i;
+
+ for (i = 0; i < tbl->num_ents; i++) {
+ ents[i] = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries];
+ }
+ gpr_free(tbl->ents);
+ tbl->ents = ents;
+ tbl->cap_entries = new_cap;
+ tbl->first_ent = 0;
+}
+
+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);
+ 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) {
+ if (tbl->current_table_bytes == bytes) {
+ return 1;
+ }
+ 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;
+ }
+ gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes);
+ while (tbl->mem_used > bytes) {
+ evict1(tbl);
+ }
+ tbl->current_table_bytes = bytes;
+ tbl->max_entries = entries_for_bytes(bytes);
+ if (tbl->max_entries > tbl->cap_entries) {
+ rebuild_ents(tbl, GPR_MAX(tbl->max_entries, 2 * tbl->cap_entries));
+ } else if (tbl->max_entries < tbl->cap_entries / 3) {
+ uint32_t new_cap = GPR_MAX(tbl->max_entries, 16u);
+ if (new_cap != tbl->cap_entries) {
+ rebuild_ents(tbl, new_cap);
+ }
+ }
+ return 1;
+}
+
+int 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) +
+ 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;
+ }
+
+ /* we can't add elements bigger than the max table size */
+ if (elem_bytes > tbl->current_table_bytes) {
+ /* HPACK draft 10 section 4.4 states:
+ * If the size of the new entry is less than or equal to the maximum
+ * size, that entry is added to the table. It is not an error to
+ * attempt to add an entry that is larger than the maximum size; an
+ * attempt to add an entry larger than the entire table causes
+ * the table
+ * to be emptied of all existing entries, and results in an
+ * empty table.
+ */
+ while (tbl->num_ents) {
+ evict1(tbl);
+ }
+ return 1;
+ }
+
+ /* evict entries to ensure no overflow */
+ while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) {
+ evict1(tbl);
+ }
+
+ /* copy the finalized entry in */
+ tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] =
+ GRPC_MDELEM_REF(md);
+
+ /* update accounting values */
+ tbl->num_ents++;
+ tbl->mem_used += (uint32_t)elem_bytes;
+ return 1;
+}
+
+grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find(
+ const grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {
+ grpc_chttp2_hptbl_find_result r = {0, 0};
+ 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;
+ r.index = i + 1u;
+ r.has_value = md->value == ent->value;
+ if (r.has_value) return r;
+ }
+
+ /* Scan the dynamic table */
+ for (i = 0; i < tbl->num_ents; i++) {
+ 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;
+ r.index = idx;
+ r.has_value = md->value == ent->value;
+ if (r.has_value) return r;
+ }
+
+ return r;
+}
diff --git a/src/core/lib/transport/chttp2/hpack_table.h b/src/core/lib/transport/chttp2/hpack_table.h
new file mode 100644
index 0000000000..2cbc02dd9c
--- /dev/null
+++ b/src/core/lib/transport/chttp2/hpack_table.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_HPACK_TABLE_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_HPACK_TABLE_H
+
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.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);
+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_CORE_LIB_TRANSPORT_CHTTP2_HPACK_TABLE_H */
diff --git a/src/core/lib/transport/chttp2/hpack_tables.txt b/src/core/lib/transport/chttp2/hpack_tables.txt
new file mode 100644
index 0000000000..08842a0267
--- /dev/null
+++ b/src/core/lib/transport/chttp2/hpack_tables.txt
@@ -0,0 +1,66 @@
+Static table, from the spec:
+ +-------+-----------------------------+---------------+
+ | Index | Header Name | Header Value |
+ +-------+-----------------------------+---------------+
+ | 1 | :authority | |
+ | 2 | :method | GET |
+ | 3 | :method | POST |
+ | 4 | :path | / |
+ | 5 | :path | /index.html |
+ | 6 | :scheme | http |
+ | 7 | :scheme | https |
+ | 8 | :status | 200 |
+ | 9 | :status | 204 |
+ | 10 | :status | 206 |
+ | 11 | :status | 304 |
+ | 12 | :status | 400 |
+ | 13 | :status | 404 |
+ | 14 | :status | 500 |
+ | 15 | accept-charset | |
+ | 16 | accept-encoding | gzip, deflate |
+ | 17 | accept-language | |
+ | 18 | accept-ranges | |
+ | 19 | accept | |
+ | 20 | access-control-allow-origin | |
+ | 21 | age | |
+ | 22 | allow | |
+ | 23 | authorization | |
+ | 24 | cache-control | |
+ | 25 | content-disposition | |
+ | 26 | content-encoding | |
+ | 27 | content-language | |
+ | 28 | content-length | |
+ | 29 | content-location | |
+ | 30 | content-range | |
+ | 31 | content-type | |
+ | 32 | cookie | |
+ | 33 | date | |
+ | 34 | etag | |
+ | 35 | expect | |
+ | 36 | expires | |
+ | 37 | from | |
+ | 38 | host | |
+ | 39 | if-match | |
+ | 40 | if-modified-since | |
+ | 41 | if-none-match | |
+ | 42 | if-range | |
+ | 43 | if-unmodified-since | |
+ | 44 | last-modified | |
+ | 45 | link | |
+ | 46 | location | |
+ | 47 | max-forwards | |
+ | 48 | proxy-authenticate | |
+ | 49 | proxy-authorization | |
+ | 50 | range | |
+ | 51 | referer | |
+ | 52 | refresh | |
+ | 53 | retry-after | |
+ | 54 | server | |
+ | 55 | set-cookie | |
+ | 56 | strict-transport-security | |
+ | 57 | transfer-encoding | |
+ | 58 | user-agent | |
+ | 59 | vary | |
+ | 60 | via | |
+ | 61 | www-authenticate | |
+ +-------+-----------------------------+---------------+
diff --git a/src/core/lib/transport/chttp2/http2_errors.h b/src/core/lib/transport/chttp2/http2_errors.h
new file mode 100644
index 0000000000..0238f9d80b
--- /dev/null
+++ b/src/core/lib/transport/chttp2/http2_errors.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_HTTP2_ERRORS_H
+#define GRPC_CORE_LIB_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_CORE_LIB_TRANSPORT_CHTTP2_HTTP2_ERRORS_H */
diff --git a/src/core/lib/transport/chttp2/huffsyms.c b/src/core/lib/transport/chttp2/huffsyms.c
new file mode 100644
index 0000000000..27497e6ae0
--- /dev/null
+++ b/src/core/lib/transport/chttp2/huffsyms.c
@@ -0,0 +1,105 @@
+/*
+ *
+ * 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/lib/transport/chttp2/huffsyms.h"
+
+/* Constants pulled from the HPACK spec, and converted to C using the vim
+ command:
+ :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */
+const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = {
+ {0x1ff8, 13}, {0x7fffd8, 23}, {0xfffffe2, 28}, {0xfffffe3, 28},
+ {0xfffffe4, 28}, {0xfffffe5, 28}, {0xfffffe6, 28}, {0xfffffe7, 28},
+ {0xfffffe8, 28}, {0xffffea, 24}, {0x3ffffffc, 30}, {0xfffffe9, 28},
+ {0xfffffea, 28}, {0x3ffffffd, 30}, {0xfffffeb, 28}, {0xfffffec, 28},
+ {0xfffffed, 28}, {0xfffffee, 28}, {0xfffffef, 28}, {0xffffff0, 28},
+ {0xffffff1, 28}, {0xffffff2, 28}, {0x3ffffffe, 30}, {0xffffff3, 28},
+ {0xffffff4, 28}, {0xffffff5, 28}, {0xffffff6, 28}, {0xffffff7, 28},
+ {0xffffff8, 28}, {0xffffff9, 28}, {0xffffffa, 28}, {0xffffffb, 28},
+ {0x14, 6}, {0x3f8, 10}, {0x3f9, 10}, {0xffa, 12},
+ {0x1ff9, 13}, {0x15, 6}, {0xf8, 8}, {0x7fa, 11},
+ {0x3fa, 10}, {0x3fb, 10}, {0xf9, 8}, {0x7fb, 11},
+ {0xfa, 8}, {0x16, 6}, {0x17, 6}, {0x18, 6},
+ {0x0, 5}, {0x1, 5}, {0x2, 5}, {0x19, 6},
+ {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6},
+ {0x1e, 6}, {0x1f, 6}, {0x5c, 7}, {0xfb, 8},
+ {0x7ffc, 15}, {0x20, 6}, {0xffb, 12}, {0x3fc, 10},
+ {0x1ffa, 13}, {0x21, 6}, {0x5d, 7}, {0x5e, 7},
+ {0x5f, 7}, {0x60, 7}, {0x61, 7}, {0x62, 7},
+ {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7},
+ {0x67, 7}, {0x68, 7}, {0x69, 7}, {0x6a, 7},
+ {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, {0x6e, 7},
+ {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7},
+ {0xfc, 8}, {0x73, 7}, {0xfd, 8}, {0x1ffb, 13},
+ {0x7fff0, 19}, {0x1ffc, 13}, {0x3ffc, 14}, {0x22, 6},
+ {0x7ffd, 15}, {0x3, 5}, {0x23, 6}, {0x4, 5},
+ {0x24, 6}, {0x5, 5}, {0x25, 6}, {0x26, 6},
+ {0x27, 6}, {0x6, 5}, {0x74, 7}, {0x75, 7},
+ {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5},
+ {0x2b, 6}, {0x76, 7}, {0x2c, 6}, {0x8, 5},
+ {0x9, 5}, {0x2d, 6}, {0x77, 7}, {0x78, 7},
+ {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x7ffe, 15},
+ {0x7fc, 11}, {0x3ffd, 14}, {0x1ffd, 13}, {0xffffffc, 28},
+ {0xfffe6, 20}, {0x3fffd2, 22}, {0xfffe7, 20}, {0xfffe8, 20},
+ {0x3fffd3, 22}, {0x3fffd4, 22}, {0x3fffd5, 22}, {0x7fffd9, 23},
+ {0x3fffd6, 22}, {0x7fffda, 23}, {0x7fffdb, 23}, {0x7fffdc, 23},
+ {0x7fffdd, 23}, {0x7fffde, 23}, {0xffffeb, 24}, {0x7fffdf, 23},
+ {0xffffec, 24}, {0xffffed, 24}, {0x3fffd7, 22}, {0x7fffe0, 23},
+ {0xffffee, 24}, {0x7fffe1, 23}, {0x7fffe2, 23}, {0x7fffe3, 23},
+ {0x7fffe4, 23}, {0x1fffdc, 21}, {0x3fffd8, 22}, {0x7fffe5, 23},
+ {0x3fffd9, 22}, {0x7fffe6, 23}, {0x7fffe7, 23}, {0xffffef, 24},
+ {0x3fffda, 22}, {0x1fffdd, 21}, {0xfffe9, 20}, {0x3fffdb, 22},
+ {0x3fffdc, 22}, {0x7fffe8, 23}, {0x7fffe9, 23}, {0x1fffde, 21},
+ {0x7fffea, 23}, {0x3fffdd, 22}, {0x3fffde, 22}, {0xfffff0, 24},
+ {0x1fffdf, 21}, {0x3fffdf, 22}, {0x7fffeb, 23}, {0x7fffec, 23},
+ {0x1fffe0, 21}, {0x1fffe1, 21}, {0x3fffe0, 22}, {0x1fffe2, 21},
+ {0x7fffed, 23}, {0x3fffe1, 22}, {0x7fffee, 23}, {0x7fffef, 23},
+ {0xfffea, 20}, {0x3fffe2, 22}, {0x3fffe3, 22}, {0x3fffe4, 22},
+ {0x7ffff0, 23}, {0x3fffe5, 22}, {0x3fffe6, 22}, {0x7ffff1, 23},
+ {0x3ffffe0, 26}, {0x3ffffe1, 26}, {0xfffeb, 20}, {0x7fff1, 19},
+ {0x3fffe7, 22}, {0x7ffff2, 23}, {0x3fffe8, 22}, {0x1ffffec, 25},
+ {0x3ffffe2, 26}, {0x3ffffe3, 26}, {0x3ffffe4, 26}, {0x7ffffde, 27},
+ {0x7ffffdf, 27}, {0x3ffffe5, 26}, {0xfffff1, 24}, {0x1ffffed, 25},
+ {0x7fff2, 19}, {0x1fffe3, 21}, {0x3ffffe6, 26}, {0x7ffffe0, 27},
+ {0x7ffffe1, 27}, {0x3ffffe7, 26}, {0x7ffffe2, 27}, {0xfffff2, 24},
+ {0x1fffe4, 21}, {0x1fffe5, 21}, {0x3ffffe8, 26}, {0x3ffffe9, 26},
+ {0xffffffd, 28}, {0x7ffffe3, 27}, {0x7ffffe4, 27}, {0x7ffffe5, 27},
+ {0xfffec, 20}, {0xfffff3, 24}, {0xfffed, 20}, {0x1fffe6, 21},
+ {0x3fffe9, 22}, {0x1fffe7, 21}, {0x1fffe8, 21}, {0x7ffff3, 23},
+ {0x3fffea, 22}, {0x3fffeb, 22}, {0x1ffffee, 25}, {0x1ffffef, 25},
+ {0xfffff4, 24}, {0xfffff5, 24}, {0x3ffffea, 26}, {0x7ffff4, 23},
+ {0x3ffffeb, 26}, {0x7ffffe6, 27}, {0x3ffffec, 26}, {0x3ffffed, 26},
+ {0x7ffffe7, 27}, {0x7ffffe8, 27}, {0x7ffffe9, 27}, {0x7ffffea, 27},
+ {0x7ffffeb, 27}, {0xffffffe, 28}, {0x7ffffec, 27}, {0x7ffffed, 27},
+ {0x7ffffee, 27}, {0x7ffffef, 27}, {0x7fffff0, 27}, {0x3ffffee, 26},
+ {0x3fffffff, 30},
+};
diff --git a/src/core/lib/transport/chttp2/huffsyms.h b/src/core/lib/transport/chttp2/huffsyms.h
new file mode 100644
index 0000000000..1ca77b9207
--- /dev/null
+++ b/src/core/lib/transport/chttp2/huffsyms.h
@@ -0,0 +1,48 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_HUFFSYMS_H
+#define GRPC_CORE_LIB_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_CORE_LIB_TRANSPORT_CHTTP2_HUFFSYMS_H */
diff --git a/src/core/lib/transport/chttp2/incoming_metadata.c b/src/core/lib/transport/chttp2/incoming_metadata.c
new file mode 100644
index 0000000000..a1a8d37562
--- /dev/null
+++ b/src/core/lib/transport/chttp2/incoming_metadata.c
@@ -0,0 +1,96 @@
+/*
+ *
+ * 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/lib/transport/chttp2/incoming_metadata.h"
+
+#include <string.h>
+
+#include "src/core/lib/transport/chttp2/internal.h"
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+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/src/core/lib/transport/chttp2/incoming_metadata.h b/src/core/lib/transport/chttp2/incoming_metadata.h
new file mode 100644
index 0000000000..edfa0adf9d
--- /dev/null
+++ b/src/core/lib/transport/chttp2/incoming_metadata.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_INCOMING_METADATA_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_INCOMING_METADATA_H
+
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CHTTP2_INCOMING_METADATA_H */
diff --git a/src/core/lib/transport/chttp2/internal.h b/src/core/lib/transport/chttp2/internal.h
new file mode 100644
index 0000000000..346e404204
--- /dev/null
+++ b/src/core/lib/transport/chttp2/internal.h
@@ -0,0 +1,780 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_INTERNAL_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_INTERNAL_H
+
+#include <assert.h>
+#include <stdbool.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/transport/chttp2/frame.h"
+#include "src/core/lib/transport/chttp2/frame_data.h"
+#include "src/core/lib/transport/chttp2/frame_goaway.h"
+#include "src/core/lib/transport/chttp2/frame_ping.h"
+#include "src/core/lib/transport/chttp2/frame_rst_stream.h"
+#include "src/core/lib/transport/chttp2/frame_settings.h"
+#include "src/core/lib/transport/chttp2/frame_window_update.h"
+#include "src/core/lib/transport/chttp2/hpack_encoder.h"
+#include "src/core/lib/transport/chttp2/hpack_parser.h"
+#include "src/core/lib/transport/chttp2/incoming_metadata.h"
+#include "src/core/lib/transport/chttp2/stream_map.h"
+#include "src/core/lib/transport/connectivity_state.h"
+#include "src/core/lib/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;
+
+ /** Transport op to be applied post-parsing */
+ grpc_transport_op *post_parsing_op;
+};
+
+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_ready;
+ 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;
+ bool 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_exec_ctx *exec_ctx,
+ 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);
+
+bool 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);
+bool grpc_chttp2_list_remove_writable_stream(
+ grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_stream_global *stream_global) GRPC_MUST_USE_RESULT;
+
+void grpc_chttp2_list_add_writing_stream(
+ grpc_chttp2_transport_writing *transport_writing,
+ grpc_chttp2_stream_writing *stream_writing);
+int grpc_chttp2_list_have_writing_streams(
+ grpc_chttp2_transport_writing *transport_writing);
+int grpc_chttp2_list_pop_writing_stream(
+ grpc_chttp2_transport_writing *transport_writing,
+ grpc_chttp2_stream_writing **stream_writing);
+
+void grpc_chttp2_list_add_written_stream(
+ grpc_chttp2_transport_writing *transport_writing,
+ grpc_chttp2_stream_writing *stream_writing);
+int grpc_chttp2_list_pop_written_stream(
+ grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_transport_writing *transport_writing,
+ grpc_chttp2_stream_global **stream_global,
+ grpc_chttp2_stream_writing **stream_writing);
+
+void grpc_chttp2_list_add_parsing_seen_stream(
+ grpc_chttp2_transport_parsing *transport_parsing,
+ grpc_chttp2_stream_parsing *stream_parsing);
+int grpc_chttp2_list_pop_parsing_seen_stream(
+ grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_transport_parsing *transport_parsing,
+ grpc_chttp2_stream_global **stream_global,
+ grpc_chttp2_stream_parsing **stream_parsing);
+
+void grpc_chttp2_list_add_waiting_for_concurrency(
+ grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_stream_global *stream_global);
+int grpc_chttp2_list_pop_waiting_for_concurrency(
+ grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_stream_global **stream_global);
+
+void grpc_chttp2_list_add_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_exec_ctx *exec_ctx, grpc_chttp2_transport_writing *transport_writing,
+ bool is_window_available);
+
+void grpc_chttp2_list_add_stalled_by_transport(
+ grpc_chttp2_transport_writing *transport_writing,
+ grpc_chttp2_stream_writing *stream_writing);
+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);
+
+/** add a ref to the stream and add it to the writable list;
+ ref will be dropped in writing.c */
+void grpc_chttp2_become_writable(grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_stream_global *stream_global);
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_CHTTP2_INTERNAL_H */
diff --git a/src/core/lib/transport/chttp2/parsing.c b/src/core/lib/transport/chttp2/parsing.c
new file mode 100644
index 0000000000..9ee52f63f2
--- /dev/null
+++ b/src/core/lib/transport/chttp2/parsing.c
@@ -0,0 +1,866 @@
+/*
+ *
+ * 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/lib/transport/chttp2/internal.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/transport/chttp2/http2_errors.h"
+#include "src/core/lib/transport/chttp2/status_conversion.h"
+#include "src/core/lib/transport/chttp2/timeout_encoding.h"
+#include "src/core/lib/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_become_writable(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_become_writable(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/src/core/lib/transport/chttp2/status_conversion.c b/src/core/lib/transport/chttp2/status_conversion.c
new file mode 100644
index 0000000000..73dd63e720
--- /dev/null
+++ b/src/core/lib/transport/chttp2/status_conversion.c
@@ -0,0 +1,109 @@
+/*
+ *
+ * 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/lib/transport/chttp2/status_conversion.h"
+
+int grpc_chttp2_grpc_status_to_http2_error(grpc_status_code status) {
+ switch (status) {
+ case GRPC_STATUS_OK:
+ return GRPC_CHTTP2_NO_ERROR;
+ case GRPC_STATUS_CANCELLED:
+ return GRPC_CHTTP2_CANCEL;
+ case GRPC_STATUS_RESOURCE_EXHAUSTED:
+ return GRPC_CHTTP2_ENHANCE_YOUR_CALM;
+ case GRPC_STATUS_PERMISSION_DENIED:
+ return GRPC_CHTTP2_INADEQUATE_SECURITY;
+ case GRPC_STATUS_UNAVAILABLE:
+ return GRPC_CHTTP2_REFUSED_STREAM;
+ default:
+ return GRPC_CHTTP2_INTERNAL_ERROR;
+ }
+}
+
+grpc_status_code grpc_chttp2_http2_error_to_grpc_status(
+ grpc_chttp2_error_code error) {
+ switch (error) {
+ case GRPC_CHTTP2_NO_ERROR:
+ /* should never be received */
+ return GRPC_STATUS_INTERNAL;
+ case GRPC_CHTTP2_CANCEL:
+ return GRPC_STATUS_CANCELLED;
+ case GRPC_CHTTP2_ENHANCE_YOUR_CALM:
+ return GRPC_STATUS_RESOURCE_EXHAUSTED;
+ case GRPC_CHTTP2_INADEQUATE_SECURITY:
+ return GRPC_STATUS_PERMISSION_DENIED;
+ case GRPC_CHTTP2_REFUSED_STREAM:
+ return GRPC_STATUS_UNAVAILABLE;
+ default:
+ return GRPC_STATUS_INTERNAL;
+ }
+}
+
+grpc_status_code grpc_chttp2_http2_status_to_grpc_status(int status) {
+ switch (status) {
+ /* these HTTP2 status codes are called out explicitly in status.proto */
+ case 200:
+ return GRPC_STATUS_OK;
+ case 400:
+ return GRPC_STATUS_INVALID_ARGUMENT;
+ case 401:
+ return GRPC_STATUS_UNAUTHENTICATED;
+ case 403:
+ return GRPC_STATUS_PERMISSION_DENIED;
+ case 404:
+ return GRPC_STATUS_NOT_FOUND;
+ case 409:
+ return GRPC_STATUS_ABORTED;
+ case 412:
+ return GRPC_STATUS_FAILED_PRECONDITION;
+ case 429:
+ return GRPC_STATUS_RESOURCE_EXHAUSTED;
+ case 499:
+ return GRPC_STATUS_CANCELLED;
+ case 500:
+ return GRPC_STATUS_UNKNOWN;
+ case 501:
+ return GRPC_STATUS_UNIMPLEMENTED;
+ case 503:
+ return GRPC_STATUS_UNAVAILABLE;
+ case 504:
+ return GRPC_STATUS_DEADLINE_EXCEEDED;
+ /* everything else is unknown */
+ default:
+ return GRPC_STATUS_UNKNOWN;
+ }
+}
+
+int grpc_chttp2_grpc_status_to_http2_status(grpc_status_code status) {
+ return 200;
+}
diff --git a/src/core/lib/transport/chttp2/status_conversion.h b/src/core/lib/transport/chttp2/status_conversion.h
new file mode 100644
index 0000000000..241417d32e
--- /dev/null
+++ b/src/core/lib/transport/chttp2/status_conversion.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_STATUS_CONVERSION_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_STATUS_CONVERSION_H
+
+#include <grpc/grpc.h>
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CHTTP2_STATUS_CONVERSION_H */
diff --git a/src/core/lib/transport/chttp2/stream_lists.c b/src/core/lib/transport/chttp2/stream_lists.c
new file mode 100644
index 0000000000..b51a041dc7
--- /dev/null
+++ b/src/core/lib/transport/chttp2/stream_lists.c
@@ -0,0 +1,442 @@
+/*
+ *
+ * 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/lib/transport/chttp2/internal.h"
+
+#include <grpc/support/log.h>
+
+#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 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 = 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 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_global *transport_global,
+ grpc_chttp2_stream_global *stream_global) {
+ GPR_ASSERT(stream_global->id != 0);
+ return 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;
+}
+
+bool grpc_chttp2_list_remove_writable_stream(
+ grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_stream_global *stream_global) {
+ return stream_list_maybe_remove(TRANSPORT_FROM_GLOBAL(transport_global),
+ STREAM_FROM_GLOBAL(stream_global),
+ GRPC_CHTTP2_LIST_WRITABLE);
+}
+
+void grpc_chttp2_list_add_writing_stream(
+ grpc_chttp2_transport_writing *transport_writing,
+ grpc_chttp2_stream_writing *stream_writing) {
+ GPR_ASSERT(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) {
+ grpc_chttp2_stream *stream = STREAM_FROM_WRITING(stream_writing);
+ if (!stream->included[GRPC_CHTTP2_LIST_WRITING_STALLED_BY_TRANSPORT]) {
+ GRPC_CHTTP2_STREAM_REF(&stream->global, "chttp2_writing_stalled");
+ }
+ stream_list_add(TRANSPORT_FROM_WRITING(transport_writing), stream,
+ GRPC_CHTTP2_LIST_WRITING_STALLED_BY_TRANSPORT);
+}
+
+void grpc_chttp2_list_flush_writing_stalled_by_transport(
+ grpc_exec_ctx *exec_ctx, 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_become_writable(&transport->global, &stream->global);
+ } else {
+ grpc_chttp2_list_add_stalled_by_transport(transport_writing,
+ &stream->writing);
+ }
+ GRPC_CHTTP2_STREAM_UNREF(exec_ctx, &stream->global,
+ "chttp2_writing_stalled");
+ }
+}
+
+void grpc_chttp2_list_add_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_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/src/core/lib/transport/chttp2/stream_map.c b/src/core/lib/transport/chttp2/stream_map.c
new file mode 100644
index 0000000000..dbbbe783bf
--- /dev/null
+++ b/src/core/lib/transport/chttp2/stream_map.c
@@ -0,0 +1,197 @@
+/*
+ *
+ * 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/lib/transport/chttp2/stream_map.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+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/src/core/lib/transport/chttp2/stream_map.h b/src/core/lib/transport/chttp2/stream_map.h
new file mode 100644
index 0000000000..1c56b18e54
--- /dev/null
+++ b/src/core/lib/transport/chttp2/stream_map.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_STREAM_MAP_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_STREAM_MAP_H
+
+#include <grpc/support/port_platform.h>
+
+#include <stddef.h>
+
+/* 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_CORE_LIB_TRANSPORT_CHTTP2_STREAM_MAP_H */
diff --git a/src/core/lib/transport/chttp2/timeout_encoding.c b/src/core/lib/transport/chttp2/timeout_encoding.c
new file mode 100644
index 0000000000..0edacaafd3
--- /dev/null
+++ b/src/core/lib/transport/chttp2/timeout_encoding.c
@@ -0,0 +1,188 @@
+/*
+ *
+ * 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/lib/transport/chttp2/timeout_encoding.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/port_platform.h>
+#include "src/core/lib/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_TIMESPAN);
+ 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/src/core/lib/transport/chttp2/timeout_encoding.h b/src/core/lib/transport/chttp2/timeout_encoding.h
new file mode 100644
index 0000000000..731beb5a37
--- /dev/null
+++ b/src/core/lib/transport/chttp2/timeout_encoding.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H
+
+#include <grpc/support/time.h>
+#include "src/core/lib/support/string.h"
+
+#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_CORE_LIB_TRANSPORT_CHTTP2_TIMEOUT_ENCODING_H */
diff --git a/src/core/lib/transport/chttp2/varint.c b/src/core/lib/transport/chttp2/varint.c
new file mode 100644
index 0000000000..6dfef45362
--- /dev/null
+++ b/src/core/lib/transport/chttp2/varint.c
@@ -0,0 +1,65 @@
+/*
+ *
+ * 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/lib/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/src/core/lib/transport/chttp2/varint.h b/src/core/lib/transport/chttp2/varint.h
new file mode 100644
index 0000000000..e4a0ae3c22
--- /dev/null
+++ b/src/core/lib/transport/chttp2/varint.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_VARINT_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_VARINT_H
+
+#include <grpc/support/port_platform.h>
+
+/* Helpers for hpack varint encoding */
+
+/* length of a value that needs varint tail encoding (it's bigger than can be
+ bitpacked into the opcode byte) - returned value includes the length of the
+ opcode byte */
+uint32_t grpc_chttp2_hpack_varint_length(uint32_t tail_value);
+
+void grpc_chttp2_hpack_write_varint_tail(uint32_t tail_value, uint8_t* target,
+ uint32_t tail_length);
+
+/* maximum value that can be bitpacked with the opcode if the opcode has a
+ prefix
+ of length prefix_bits */
+#define GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) \
+ ((uint32_t)((1 << (8 - (prefix_bits))) - 1))
+
+/* length required to bitpack a value */
+#define GRPC_CHTTP2_VARINT_LENGTH(n, prefix_bits) \
+ ((n) < GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits) \
+ ? 1u \
+ : grpc_chttp2_hpack_varint_length( \
+ (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits)))
+
+#define GRPC_CHTTP2_WRITE_VARINT(n, prefix_bits, prefix_or, target, length) \
+ do { \
+ uint8_t* tgt = target; \
+ if ((length) == 1u) { \
+ (tgt)[0] = (uint8_t)((prefix_or) | (n)); \
+ } else { \
+ (tgt)[0] = \
+ (prefix_or) | (uint8_t)GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits); \
+ grpc_chttp2_hpack_write_varint_tail( \
+ (n)-GRPC_CHTTP2_MAX_IN_PREFIX(prefix_bits), (tgt) + 1, (length)-1); \
+ } \
+ } while (0)
+
+#endif /* GRPC_CORE_LIB_TRANSPORT_CHTTP2_VARINT_H */
diff --git a/src/core/lib/transport/chttp2/writing.c b/src/core/lib/transport/chttp2/writing.c
new file mode 100644
index 0000000000..daea331d31
--- /dev/null
+++ b/src/core/lib/transport/chttp2/writing.c
@@ -0,0 +1,350 @@
+/*
+ *
+ * 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/lib/transport/chttp2/internal.h"
+
+#include <limits.h>
+
+#include <grpc/support/log.h>
+
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/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_exec_ctx *exec_ctx, 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(
+ exec_ctx, 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)) {
+ bool sent_initial_metadata = stream_writing->sent_initial_metadata;
+ bool become_writable = false;
+
+ 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);
+
+ 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;
+ become_writable = true;
+ sent_initial_metadata = true;
+ }
+ 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) {
+ become_writable = true;
+ } else {
+ grpc_chttp2_list_add_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;
+ become_writable = true;
+ }
+ }
+
+ 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);
+ become_writable = true;
+ }
+
+ if (become_writable) {
+ grpc_chttp2_list_add_writing_stream(transport_writing, stream_writing);
+ } else {
+ GRPC_CHTTP2_STREAM_UNREF(exec_ctx, 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) {
+ grpc_chttp2_list_add_writing_stream(transport_writing, stream_writing);
+ } 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/src/core/lib/transport/chttp2_transport.c b/src/core/lib/transport/chttp2_transport.c
new file mode 100644
index 0000000000..7fed3d8b47
--- /dev/null
+++ b/src/core/lib/transport/chttp2_transport.c
@@ -0,0 +1,1785 @@
+/*
+ *
+ * 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/lib/transport/chttp2_transport.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice_buffer.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/chttp2/http2_errors.h"
+#include "src/core/lib/transport/chttp2/internal.h"
+#include "src/core/lib/transport/chttp2/status_conversion.h"
+#include "src/core/lib/transport/chttp2/timeout_encoding.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/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);
+ }
+
+ /* flush writable stream list to avoid dangling references */
+ grpc_chttp2_stream_global *stream_global;
+ grpc_chttp2_stream_writing *stream_writing;
+ while (grpc_chttp2_list_pop_writable_stream(
+ &t->global, &t->writing, &stream_global, &stream_writing)) {
+ GRPC_CHTTP2_STREAM_UNREF(exec_ctx, stream_global, "chttp2_writing");
+ }
+ }
+}
+
+#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_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_ready == 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(exec_ctx, &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
+ */
+
+void grpc_chttp2_become_writable(grpc_chttp2_transport_global *transport_global,
+ grpc_chttp2_stream_global *stream_global) {
+ if (!TRANSPORT_FROM_GLOBAL(transport_global)->closed &&
+ grpc_chttp2_list_add_writable_stream(transport_global, stream_global)) {
+ GRPC_CHTTP2_STREAM_REF(stream_global, "chttp2_writing");
+ }
+}
+
+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_become_writable(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_become_writable(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 {
+ stream_global->send_message = op->send_message;
+ if (stream_global->id != 0) {
+ grpc_chttp2_become_writable(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_become_writable(transport_global, stream_global);
+ }
+ }
+
+ if (op->recv_initial_metadata != NULL) {
+ GPR_ASSERT(stream_global->recv_initial_metadata_ready == NULL);
+ stream_global->recv_initial_metadata_ready =
+ op->recv_initial_metadata_ready;
+ 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_locked(grpc_exec_ctx *exec_ctx,
+ grpc_chttp2_transport *t,
+ grpc_transport_op *op) {
+ bool close_transport = false;
+
+ 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) {
+ 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) {
+ 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);
+ }
+
+ if (close_transport) {
+ close_transport_locked(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;
+
+ lock(t);
+
+ /* If there's a set_accept_stream ensure that we're not parsing
+ to avoid changing things out from underneath */
+ if (t->parsing_active && op->set_accept_stream) {
+ GPR_ASSERT(t->post_parsing_op == NULL);
+ t->post_parsing_op = gpr_malloc(sizeof(*op));
+ memcpy(t->post_parsing_op, op, sizeof(*op));
+ } else {
+ perform_transport_op_locked(exec_ctx, t, op);
+ }
+
+ 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_ready != NULL &&
+ stream_global->published_initial_metadata) {
+ grpc_chttp2_incoming_metadata_buffer_publish(
+ &stream_global->received_initial_metadata,
+ stream_global->recv_initial_metadata);
+ grpc_exec_ctx_enqueue(
+ exec_ctx, stream_global->recv_initial_metadata_ready, true, NULL);
+ stream_global->recv_initial_metadata_ready = NULL;
+ }
+ if (stream_global->recv_message_ready != NULL) {
+ 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) {
+ *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);
+ }
+ 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);
+ }
+ if (grpc_chttp2_list_remove_writable_stream(&t->global, &s->global)) {
+ GRPC_CHTTP2_STREAM_UNREF(exec_ctx, &s->global, "chttp2_writing");
+ }
+
+ 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_become_writable(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;
+ /* handle delayed transport ops (if there is one) */
+ if (t->post_parsing_op) {
+ grpc_transport_op *op = t->post_parsing_op;
+ t->post_parsing_op = NULL;
+ perform_transport_op_locked(exec_ctx, t, op);
+ gpr_free(op);
+ }
+ /* 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_become_writable(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),
+ "chttp2",
+ 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/src/core/lib/transport/chttp2_transport.h b/src/core/lib/transport/chttp2_transport.h
new file mode 100644
index 0000000000..5008cab7f8
--- /dev/null
+++ b/src/core/lib/transport/chttp2_transport.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CHTTP2_TRANSPORT_H
+#define GRPC_CORE_LIB_TRANSPORT_CHTTP2_TRANSPORT_H
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CHTTP2_TRANSPORT_H */
diff --git a/src/core/lib/transport/connectivity_state.c b/src/core/lib/transport/connectivity_state.c
new file mode 100644
index 0000000000..123eab8b36
--- /dev/null
+++ b/src/core/lib/transport/connectivity_state.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * 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/lib/transport/connectivity_state.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+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/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h
new file mode 100644
index 0000000000..6f92132438
--- /dev/null
+++ b/src/core/lib/transport/connectivity_state.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H
+#define GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H
+
+#include <grpc/grpc.h>
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H */
diff --git a/src/core/lib/transport/metadata.c b/src/core/lib/transport/metadata.c
new file mode 100644
index 0000000000..7605f09991
--- /dev/null
+++ b/src/core/lib/transport/metadata.c
@@ -0,0 +1,698 @@
+/*
+ *
+ * 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/lib/transport/metadata.h"
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include <grpc/compression.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
+
+#include "src/core/lib/iomgr/iomgr_internal.h"
+#include "src/core/lib/profiling/timers.h"
+#include "src/core/lib/support/murmur_hash.h"
+#include "src/core/lib/support/string.h"
+#include "src/core/lib/transport/chttp2/bin_encoder.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.
+ */
+
+#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);
+ if (grpc_iomgr_abort_on_leaks()) {
+ abort();
+ }
+ }
+ 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);
+ if (grpc_iomgr_abort_on_leaks()) {
+ abort();
+ }
+ }
+ 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/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h
new file mode 100644
index 0000000000..d72ec9accc
--- /dev/null
+++ b/src/core/lib/transport/metadata.h
@@ -0,0 +1,156 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_METADATA_H
+#define GRPC_CORE_LIB_TRANSPORT_METADATA_H
+
+#include <grpc/support/slice.h>
+#include <grpc/support/useful.h>
+
+/* 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_CORE_LIB_TRANSPORT_METADATA_H */
diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c
new file mode 100644
index 0000000000..bb79b8fa96
--- /dev/null
+++ b/src/core/lib/transport/metadata_batch.c
@@ -0,0 +1,194 @@
+/*
+ *
+ * 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/lib/transport/metadata_batch.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+#include "src/core/lib/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/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
new file mode 100644
index 0000000000..f1d4726989
--- /dev/null
+++ b/src/core/lib/transport/metadata_batch.h
@@ -0,0 +1,125 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_METADATA_BATCH_H
+#define GRPC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
+
+#include <grpc/grpc.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/slice.h>
+#include <grpc/support/time.h>
+#include "src/core/lib/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_CORE_LIB_TRANSPORT_METADATA_BATCH_H */
diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c
new file mode 100644
index 0000000000..eda277b3dc
--- /dev/null
+++ b/src/core/lib/transport/static_metadata.c
@@ -0,0 +1,160 @@
+/*
+ *
+ * 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/lib/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, 4, 8, 6, 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-bin",
+ "census-binary-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/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h
new file mode 100644
index 0000000000..aff136a6d2
--- /dev/null
+++ b/src/core/lib/transport/static_metadata.h
@@ -0,0 +1,408 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_STATIC_METADATA_H
+#define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H
+
+#include "src/core/lib/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-bin" */
+#define GRPC_MDSTR_CENSUS_BIN (&grpc_static_mdstr_table[22])
+/* "census-binary-bin" */
+#define GRPC_MDSTR_CENSUS_BINARY_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_CORE_LIB_TRANSPORT_STATIC_METADATA_H */
diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c
new file mode 100644
index 0000000000..18256aae5e
--- /dev/null
+++ b/src/core/lib/transport/transport.c
@@ -0,0 +1,184 @@
+/*
+ *
+ * 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/lib/transport/transport.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/atm.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/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_non_zero(&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->recv_initial_metadata_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/src/core/lib/transport/transport.h b/src/core/lib/transport/transport.h
new file mode 100644
index 0000000000..e98cfe9515
--- /dev/null
+++ b/src/core/lib/transport/transport.h
@@ -0,0 +1,242 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_TRANSPORT_H
+#define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_H
+
+#include <stddef.h>
+
+#include "src/core/lib/channel/context.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"
+
+/* 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;
+ /** Should be enqueued when initial metadata is ready to be processed. */
+ grpc_closure *recv_initial_metadata_ready;
+
+ /** 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
+ and recv_initial_metadata which have their own closures) 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.
+ 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)(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_CORE_LIB_TRANSPORT_TRANSPORT_H */
diff --git a/src/core/lib/transport/transport_impl.h b/src/core/lib/transport/transport_impl.h
new file mode 100644
index 0000000000..92fa5d519d
--- /dev/null
+++ b/src/core/lib/transport/transport_impl.h
@@ -0,0 +1,81 @@
+/*
+ *
+ * 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_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H
+#define GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H
+
+#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_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_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H */
diff --git a/src/core/lib/transport/transport_op_string.c b/src/core/lib/transport/transport_op_string.c
new file mode 100644
index 0000000000..1fa8fa5d4f
--- /dev/null
+++ b/src/core/lib/transport/transport_op_string.c
@@ -0,0 +1,140 @@
+/*
+ *
+ * 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/lib/channel/channel_stack.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/support/string.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,
+ 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/src/core/lib/tsi/fake_transport_security.c b/src/core/lib/tsi/fake_transport_security.c
new file mode 100644
index 0000000000..4b812f4803
--- /dev/null
+++ b/src/core/lib/tsi/fake_transport_security.c
@@ -0,0 +1,527 @@
+/*
+ *
+ * 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/lib/tsi/fake_transport_security.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+#include <grpc/support/useful.h>
+#include "src/core/lib/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/src/core/lib/tsi/fake_transport_security.h b/src/core/lib/tsi/fake_transport_security.h
new file mode 100644
index 0000000000..b887dfcb09
--- /dev/null
+++ b/src/core/lib/tsi/fake_transport_security.h
@@ -0,0 +1,61 @@
+/*
+ *
+ * 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_CORE_LIB_TSI_FAKE_TRANSPORT_SECURITY_H
+#define GRPC_CORE_LIB_TSI_FAKE_TRANSPORT_SECURITY_H
+
+#include "src/core/lib/tsi/transport_security_interface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for FAKE certs. */
+#define TSI_FAKE_CERTIFICATE_TYPE "FAKE"
+
+/* Creates a fake handshaker that will create a fake frame protector.
+
+ No cryptography is performed in these objects. They just simulate handshake
+ messages going back and forth for the handshaker and do some framing on
+ cleartext data for the protector. */
+tsi_handshaker *tsi_create_fake_handshaker(int is_client);
+
+/* Creates a protector directly without going through the handshake phase. */
+tsi_frame_protector *tsi_create_fake_protector(
+ size_t *max_protected_frame_size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_TSI_FAKE_TRANSPORT_SECURITY_H */
diff --git a/src/core/lib/tsi/ssl_transport_security.c b/src/core/lib/tsi/ssl_transport_security.c
new file mode 100644
index 0000000000..d03201eec6
--- /dev/null
+++ b/src/core/lib/tsi/ssl_transport_security.c
@@ -0,0 +1,1536 @@
+/*
+ *
+ * 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/lib/tsi/ssl_transport_security.h"
+
+#include <grpc/support/port_platform.h>
+
+#include <limits.h>
+#include <string.h>
+
+/* TODO(jboeuf): refactor inet_ntop into a portability header. */
+#ifdef GPR_WINSOCK_SOCKET
+#include <ws2tcpip.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+#include <grpc/support/log.h>
+#include <grpc/support/sync.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/useful.h>
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h> /* For OPENSSL_free */
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "src/core/lib/tsi/ssl_types.h"
+#include "src/core/lib/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, 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 = 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 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, (size_t)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 = NULL;
+ 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, (const char *)name,
+ (size_t)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 == NULL) {
+ 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 =
+ 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) +
+ 2 /* common name, certificate */ +
+ (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;
+
+ 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, (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;
+ 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 == NULL) 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 != NULL && !like_ip) {
+ if (does_entry_match_name(cn_property->value.data,
+ cn_property->value.length, name)) {
+ return 1;
+ }
+ }
+
+ return 0; /* Not found. */
+}
diff --git a/src/core/lib/tsi/ssl_transport_security.h b/src/core/lib/tsi/ssl_transport_security.h
new file mode 100644
index 0000000000..c9b9e8f54b
--- /dev/null
+++ b/src/core/lib/tsi/ssl_transport_security.h
@@ -0,0 +1,174 @@
+/*
+ *
+ * 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_CORE_LIB_TSI_SSL_TRANSPORT_SECURITY_H
+#define GRPC_CORE_LIB_TSI_SSL_TRANSPORT_SECURITY_H
+
+#include "src/core/lib/tsi/transport_security_interface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Value for the TSI_CERTIFICATE_TYPE_PEER_PROPERTY property for X509 certs. */
+#define TSI_X509_CERTIFICATE_TYPE "X509"
+
+/* This property is of type TSI_PEER_PROPERTY_STRING. */
+#define TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY "x509_subject_common_name"
+#define TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY \
+ "x509_subject_alternative_name"
+
+#define TSI_X509_PEM_CERT_PROPERTY "x509_pem_cert"
+
+#define TSI_SSL_ALPN_SELECTED_PROTOCOL "ssl_alpn_selected_protocol"
+
+/* --- tsi_ssl_handshaker_factory object ---
+
+ This object creates tsi_handshaker objects implemented in terms of the
+ TLS 1.2 specificiation. */
+
+typedef struct tsi_ssl_handshaker_factory tsi_ssl_handshaker_factory;
+
+/* Creates a client handshaker factory.
+ - pem_private_key is the buffer containing the PEM encoding of the client's
+ private key. This parameter can be NULL if the client does not have a
+ private key.
+ - pem_private_key_size is the size of the associated buffer.
+ - pem_cert_chain is the buffer containing the PEM encoding of the client's
+ certificate chain. This parameter can be NULL if the client does not have
+ a certificate chain.
+ - pem_cert_chain_size is the size of the associated buffer.
+ - pem_roots_cert is the buffer containing the PEM encoding of the server
+ root certificates. This parameter cannot be NULL.
+ - pem_roots_cert_size is the size of the associated buffer.
+ - cipher_suites contains an optional list of the ciphers that the client
+ supports. The format of this string is described in:
+ https://www.openssl.org/docs/apps/ciphers.html.
+ This parameter can be set to NULL to use the default set of ciphers.
+ TODO(jboeuf): Revisit the format of this parameter.
+ - alpn_protocols is an array containing the protocol names that the
+ handshakers created with this factory support. This parameter can be NULL.
+ - alpn_protocols_lengths is an array containing the lengths of the alpn
+ protocols specified in alpn_protocols. This parameter can be NULL.
+ - num_alpn_protocols is the number of alpn protocols and associated lengths
+ specified. If this parameter is 0, the other alpn parameters must be NULL.
+ - factory is the address of the factory pointer to be created.
+
+ - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+ where a parameter is invalid. */
+tsi_result tsi_create_ssl_client_handshaker_factory(
+ const unsigned char *pem_private_key, size_t pem_private_key_size,
+ const unsigned char *pem_cert_chain, size_t pem_cert_chain_size,
+ const unsigned char *pem_root_certs, size_t pem_root_certs_size,
+ const char *cipher_suites, const unsigned char **alpn_protocols,
+ const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
+ tsi_ssl_handshaker_factory **factory);
+
+/* Creates a server handshaker factory.
+ - version indicates which version of the specification to use.
+ - pem_private_keys is an array containing the PEM encoding of the server's
+ private keys. This parameter cannot be NULL. The size of the array is
+ given by the key_cert_pair_count parameter.
+ - pem_private_keys_sizes is the array containing the sizes of the associated
+ buffers.
+ - pem_cert_chains is an array containing the PEM encoding of the server's
+ cert chains. This parameter cannot be NULL. The size of the array is
+ given by the key_cert_pair_count parameter.
+ - pem_cert_chains_sizes is the array containing the sizes of the associated
+ buffers.
+ - key_cert_pair_count indicates the number of items in the private_key_files
+ and cert_chain_files parameters.
+ - pem_client_roots is the buffer containing the PEM encoding of the client
+ root certificates. This parameter may be NULL in which case the server will
+ not authenticate the client. If not NULL, the force_client_auth parameter
+ specifies if the server will accept only authenticated clients or both
+ authenticated and non-authenticated clients.
+ - pem_client_root_certs_size is the size of the associated buffer.
+ - force_client_auth, if set to non-zero will force the client to authenticate
+ with an SSL cert. Note that this option is ignored if pem_client_root_certs
+ is NULL or pem_client_roots_certs_size is 0
+ - cipher_suites contains an optional list of the ciphers that the server
+ supports. The format of this string is described in:
+ https://www.openssl.org/docs/apps/ciphers.html.
+ This parameter can be set to NULL to use the default set of ciphers.
+ TODO(jboeuf): Revisit the format of this parameter.
+ - alpn_protocols is an array containing the protocol names that the
+ handshakers created with this factory support. This parameter can be NULL.
+ - alpn_protocols_lengths is an array containing the lengths of the alpn
+ protocols specified in alpn_protocols. This parameter can be NULL.
+ - num_alpn_protocols is the number of alpn protocols and associated lengths
+ specified. If this parameter is 0, the other alpn parameters must be NULL.
+ - factory is the address of the factory pointer to be created.
+
+ - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+ where a parameter is invalid. */
+tsi_result tsi_create_ssl_server_handshaker_factory(
+ const unsigned char **pem_private_keys,
+ const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains,
+ const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count,
+ const unsigned char *pem_client_root_certs,
+ size_t pem_client_root_certs_size, int force_client_auth,
+ const char *cipher_suites, const unsigned char **alpn_protocols,
+ const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
+ tsi_ssl_handshaker_factory **factory);
+
+/* Creates a handshaker.
+ - self is the factory from which the handshaker will be created.
+ - server_name_indication indicates the name of the server the client is
+ trying to connect to which will be relayed to the server using the SNI
+ extension.
+ This parameter must be NULL for a server handshaker factory.
+ - handhshaker is the address of the handshaker pointer to be created.
+
+ - This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
+ where a parameter is invalid. */
+tsi_result tsi_ssl_handshaker_factory_create_handshaker(
+ tsi_ssl_handshaker_factory *self, const char *server_name_indication,
+ tsi_handshaker **handshaker);
+
+/* Destroys the handshaker factory. WARNING: it is unsafe to destroy a factory
+ while handshakers created with this factory are still in use. */
+void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory *self);
+
+/* Util that checks that an ssl peer matches a specific name.
+ Still TODO(jboeuf):
+ - handle mixed case.
+ - handle %encoded chars.
+ - handle public suffix wildchar more strictly (e.g. *.co.uk) */
+int tsi_ssl_peer_matches_name(const tsi_peer *peer, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_TSI_SSL_TRANSPORT_SECURITY_H */
diff --git a/src/core/lib/tsi/ssl_types.h b/src/core/lib/tsi/ssl_types.h
new file mode 100644
index 0000000000..c6e68c01ad
--- /dev/null
+++ b/src/core/lib/tsi/ssl_types.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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_CORE_LIB_TSI_SSL_TYPES_H
+#define GRPC_CORE_LIB_TSI_SSL_TYPES_H
+
+/* A collection of macros to cast between various integer types that are
+ * used differently between BoringSSL and OpenSSL:
+ * TSI_INT_AS_SIZE(x): convert 'int x' to a length parameter for an OpenSSL
+ * function
+ * TSI_SIZE_AS_SIZE(x): convert 'size_t x' to a length parameter for an OpenSSL
+ * function
+ */
+
+#include <openssl/ssl.h>
+
+#ifdef OPENSSL_IS_BORINGSSL
+#define TSI_INT_AS_SIZE(x) ((size_t)(x))
+#define TSI_SIZE_AS_SIZE(x) (x)
+#else
+#define TSI_INT_AS_SIZE(x) (x)
+#define TSI_SIZE_AS_SIZE(x) ((int)(x))
+#endif
+
+#endif /* GRPC_CORE_LIB_TSI_SSL_TYPES_H */
diff --git a/src/core/lib/tsi/test_creds/README b/src/core/lib/tsi/test_creds/README
new file mode 100644
index 0000000000..eb8482d648
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/README
@@ -0,0 +1,62 @@
+The test credentials (CONFIRMEDTESTKEY) have been generated with the following
+commands:
+
+Bad credentials (badclient.* / badserver.*):
+============================================
+
+These are self-signed certificates:
+
+$ openssl req -x509 -newkey rsa:1024 -keyout badserver.key -out badserver.pem \
+ -days 3650 -nodes
+
+When prompted for certificate information, everything is default except the
+common name which is set to badserver.test.google.com.
+
+
+Valid test credentials:
+=======================
+
+The ca is self-signed:
+----------------------
+
+$ openssl req -x509 -new -newkey rsa:1024 -nodes -out ca.pem -config ca-openssl.cnf -days 3650 -extensions v3_req
+When prompted for certificate information, everything is default.
+
+client is issued by CA:
+-----------------------
+
+$ openssl genrsa -out client.key.rsa 1024
+$ openssl pkcs8 -topk8 -in client.key.rsa -out client.key -nocrypt
+$ rm client.key.rsa
+$ openssl req -new -key client.key -out client.csr
+
+When prompted for certificate information, everything is default except the
+common name which is set to testclient.
+
+$ openssl ca -in client.csr -out client.pem
+
+server0 is issued by CA:
+------------------------
+
+$ openssl genrsa -out server0.key.rsa 1024
+$ openssl pkcs8 -topk8 -in server0.key.rsa -out server0.key -nocrypt
+$ rm server0.key.rsa
+$ openssl req -new -key server0.key -out server0.csr
+
+When prompted for certificate information, everything is default except the
+common name which is set to *.test.google.com.au.
+
+$ openssl ca -in server0.csr -out server0.pem
+
+server1 is issued by CA with a special config for subject alternative names:
+----------------------------------------------------------------------------
+
+$ openssl genrsa -out server1.key.rsa 1024
+$ openssl pkcs8 -topk8 -in server1.key.rsa -out server1.key -nocrypt
+$ rm server1.key.rsa
+$ openssl req -new -key server1.key -out server1.csr -config server1-openssl.cnf
+
+When prompted for certificate information, everything is default except the
+common name which is set to *.test.google.com.
+
+$ openssl ca -in server1.csr -out server1.pem
diff --git a/src/core/lib/tsi/test_creds/badclient.key b/src/core/lib/tsi/test_creds/badclient.key
new file mode 100644
index 0000000000..5832685122
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/badclient.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALJfYnFn4nkj52WF
+E5W2qUxCfjsEFyuXYYKS/07UPWsv3gpZhtjXgdeGL+dpwEBC0IRDBfGnkMp6YY5S
+O7rnEz0X3r/fvgYy+dEl2jnaA6zgc7RzMGl9U11d56gP9FiDC2190mvP/hpq2xLZ
+CTbIximpmaoQyxuuH1bbYunesIG/AgMBAAECgYAdqJCEzMIyZE7oaW0tOpcB0BiP
+FYoIvH4BKRH8eHvR476mt+YdDhBP1scGUmYeCT4Ej+RgHv2LPTgVYwT9eciP2+E/
+CBCNRel0Sw9JepwW0r+jWJtDY1pp6YXAgNRGX2UflvUsT+o9lZvagf9moLTMyGvU
+uLFnsyfLim1B4vXvWQJBANouZllXGZoSrZLtR3VgV4tzRQvJxu84kLeIk64Ov47X
+pHVBMTRBfzPEhbBodjr1m5OLaVLqkFcXftzRCrbWoKsCQQDRSoLLXOiLrtJ3DLJC
+rX7Y8wrHZrqk5bMdZLGa/UX8RanhVw3+Xp+urd1711umeNJfzu/MCk4a1KkG/CU0
+rqs9AkA4cSx1DD1JSG+yxMNpsAS1xJomFIrsM9vsPt7FdndDwrF+y+CovhDkGYDk
+RAHh+svGfZg/pQK2JRPimAmHhzqFAkEAu6Ya70s2FUeB3Mu9aJs2CD6hg3dQEVkB
+53DI7TX48d9kGW58VX1xnqS02LyWqAPcW5qm1kLHFLdndaPNmBaj4QJBAJugl367
+9d9t/QLTSuULLaoYv2vJT3s1y9HN89EoaDDEkPVfQu6GVEXgIBtim1sI/VPSzI8H
+aXvaTUwblFWSM70=
+-----END PRIVATE KEY-----
diff --git a/src/core/lib/tsi/test_creds/badclient.pem b/src/core/lib/tsi/test_creds/badclient.pem
new file mode 100644
index 0000000000..1785970221
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/badclient.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICoDCCAgmgAwIBAgIJANIz2/zoRiapMA0GCSqGSIb3DQEBBQUAMGkxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxIjAgBgNVBAMMGWJhZGNsaWVudC50ZXN0Lmdvb2dsZS5j
+b20wHhcNMTQwNzI4MjAwODI1WhcNMjQwNzI1MjAwODI1WjBpMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMSIwIAYDVQQDDBliYWRjbGllbnQudGVzdC5nb29nbGUuY29tMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyX2JxZ+J5I+dlhROVtqlMQn47BBcr
+l2GCkv9O1D1rL94KWYbY14HXhi/nacBAQtCEQwXxp5DKemGOUju65xM9F96/374G
+MvnRJdo52gOs4HO0czBpfVNdXeeoD/RYgwttfdJrz/4aatsS2Qk2yMYpqZmqEMsb
+rh9W22Lp3rCBvwIDAQABo1AwTjAdBgNVHQ4EFgQU523AJMR8Ds9V8fhf7gu1i0MM
+UqAwHwYDVR0jBBgwFoAU523AJMR8Ds9V8fhf7gu1i0MMUqAwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQUFAAOBgQCI/tvSBYH1iyfLaCTBKwpdj36+MkR9EeJJmImx
+X+bjhKWXwsBX4PDMWvdusr++QGUYtyoya+hfYMXRhXua39mD54xgloQNuu9REDwX
+Ffto+aOw3BcYducz6ofxicFK/Y2VeXDurSMpRv5TfGf2Qr6eOOdaRhj6ed7BibHk
+X1VGZA==
+-----END CERTIFICATE-----
diff --git a/src/core/lib/tsi/test_creds/badserver.key b/src/core/lib/tsi/test_creds/badserver.key
new file mode 100644
index 0000000000..abfbde10ff
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/badserver.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKeZ1e1y29cmBKaW
+oIUwJ5neOJUjx+eD/3nRPe+dvLXEd9+db0fG5RYRR0S3mF1Ywuj4PIxlTW2YprUS
+oGSw+tcqWNIzxv94HjwYFkkvER3AblXcDBh0P2zAkzg+nf9AcAsMh0QpDTyrXtMl
+gqryjq1/vkhFofKMMbY+aXJdG6OBAgMBAAECgYAAgaB51S0A22aMMkxN2rVj6530
+JWWHN4jgD1fGj41wZyWNkWYyq1Ep3ed/N6bIMWp1VbqpGe0/9YQba/D8HOTFHGRt
+72YXnP1e/ds8cxU4x4j1vvqSPtXpMmkiXfXijOvCl9mrMH2xjghFAt6/1Nb9xo1m
+VdcOB8OdSuOIw6CI+QJBAN5FZUbS+bRXDWII/FaAih1DBpwCxhYEN+TXPJBxSen6
+kOzGt5g+mB6YqRMZ/qshshwPq7bsgFGfJ2lIdS2t3GsCQQDBCKifV5AAkOdOUrkK
+HvoX3qnVmyIA8CyvWLcIWpfZ76QAYh0q0StedKdOMXaB1jTeSJ2KU1nlss7UD1Yw
+VbrDAkAwjMHpbW3jiVw//Kx5jIwehiRscWKpLnSzBJyTBFvbwsJjJai2lX2OuVO8
++2GYKb0Iyhd81j3VFkl6grwtpRtPAkB7+n+yt555fpfRKjhGU9b09cHGu7h/OcK5
+bBVCfE0DYHLI/DsXgPiF1g6Onh4rDdUu3xyv9xDKAqnscV099hHZAkEAvcFBfXZs
+tk18N+bUcvXTdZjzZbfLCHlJmwPIspZ8G/6Pn63deg4GVYoCvTwGruah+8y734Ph
+7PskfPgUQlB7Ag==
+-----END PRIVATE KEY-----
diff --git a/src/core/lib/tsi/test_creds/badserver.pem b/src/core/lib/tsi/test_creds/badserver.pem
new file mode 100644
index 0000000000..983c979f31
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/badserver.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICoDCCAgmgAwIBAgIJAPdqwqsKNy81MA0GCSqGSIb3DQEBBQUAMGkxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxIjAgBgNVBAMMGWJhZHNlcnZlci50ZXN0Lmdvb2dsZS5j
+b20wHhcNMTQwNzI4MjAwODU0WhcNMjQwNzI1MjAwODU0WjBpMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMSIwIAYDVQQDDBliYWRzZXJ2ZXIudGVzdC5nb29nbGUuY29tMIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnmdXtctvXJgSmlqCFMCeZ3jiVI8fn
+g/950T3vnby1xHffnW9HxuUWEUdEt5hdWMLo+DyMZU1tmKa1EqBksPrXKljSM8b/
+eB48GBZJLxEdwG5V3AwYdD9swJM4Pp3/QHALDIdEKQ08q17TJYKq8o6tf75IRaHy
+jDG2PmlyXRujgQIDAQABo1AwTjAdBgNVHQ4EFgQU3u/qvHr9knMBeZyAD7mAA/ec
+8cUwHwYDVR0jBBgwFoAU3u/qvHr9knMBeZyAD7mAA/ec8cUwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQUFAAOBgQA/FmR1SGLguxCCfhp4CYCbrAePSyPWDi48gTwj
+vVZf/OMxdVu/H8sBYFf27BjbrEugAw16DElFtgTZ83pLb2BvkUgb6vBUK5sEkgmh
+z88zBsgDp8aCf4STDOLFZMBh/E9ZKkm1zogbEmlTjFp/ceSpa2gNv7OuN4WiorOh
+Wvw40g==
+-----END CERTIFICATE-----
diff --git a/src/core/lib/tsi/test_creds/ca-openssl.cnf b/src/core/lib/tsi/test_creds/ca-openssl.cnf
new file mode 100644
index 0000000000..e97b945e4b
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/ca-openssl.cnf
@@ -0,0 +1,17 @@
+[req]
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+
+[req_distinguished_name]
+countryName = Country Name (2 letter code)
+countryName_default = AU
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = Some-State
+organizationName = Organization Name (eg, company)
+organizationName_default = Internet Widgits Pty Ltd
+commonName = Common Name (eg, YOUR name)
+commonName_default = testca
+
+[v3_req]
+basicConstraints = CA:true
+keyUsage = critical, keyCertSign
diff --git a/src/core/lib/tsi/test_creds/ca.key b/src/core/lib/tsi/test_creds/ca.key
new file mode 100644
index 0000000000..03c4f950e3
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/ca.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMBA3wVeTGHZR1Ry
+e/i+J8a2cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBl
+LoNY0lSvxDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JA
+HUGodjx+QAi6yCAurUZGvYXGgZSBAgMBAAECgYAxRi8i9BlFlufGSBVoGmydbJOm
+bwLKl9dP3o33ODSP9hok5y6A0w5plWk3AJSF1hPLleK9VcSKYGYnt0clmPVHF35g
+bx2rVK8dOT0mn7rz9Zr70jcSz1ETA2QonHZ+Y+niLmcic9At6hRtWiewblUmyFQm
+GwggIzi7LOyEUHrEcQJBAOXxyQvnLvtKzXiqcsW/K6rExqVJVk+KF0fzzVyMzTJx
+HRBxUVgvGdEJT7j+7P2kcTyafve0BBzDSPIaDyiJ+Y0CQQDWCb7jASFSbu5M3Zcd
+Gkr4ZKN1XO3VLQX10b22bQYdF45hrTN2tnzRvVUR4q86VVnXmiGiTqmLkXcA2WWf
+pHfFAkAhv9olUBo6MeF0i3frBEMRfm41hk0PwZHnMqZ6pgPcGnQMnMU2rzsXzkkQ
+OwJnvAIOxhJKovZTjmofdqmw5odlAkBYVUdRWjsNUTjJwj3GRf6gyq/nFMYWz3EB
+RWFdM1ttkDYzu45ctO2IhfHg4sPceDMO1s6AtKQmNI9/azkUjITdAkApNa9yFRzc
+TBaDNPd5KVd58LVIzoPQ6i7uMHteLXJUWqSroji6S3s4gKMFJ/dO+ZXIlgQgfJJJ
+ZDL4cdrdkeoM
+-----END PRIVATE KEY-----
diff --git a/src/core/lib/tsi/test_creds/ca.pem b/src/core/lib/tsi/test_creds/ca.pem
new file mode 100644
index 0000000000..6c8511a73c
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSjCCAbOgAwIBAgIJAJHGGR4dGioHMA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNV
+BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
+aWRnaXRzIFB0eSBMdGQxDzANBgNVBAMTBnRlc3RjYTAeFw0xNDExMTEyMjMxMjla
+Fw0yNDExMDgyMjMxMjlaMFYxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0
+YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMT
+BnRlc3RjYTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwEDfBV5MYdlHVHJ7
++L4nxrZy7mBfAVXpOc5vMYztssUI7mL2/iYujiIXM+weZYNTEpLdjyJdu7R5gGUu
+g1jSVK/EPHfc74O7AyZU34PNIP4Sh33N+/A5YexrNgJlPY+E3GdVYi4ldWJjgkAd
+Qah2PH5ACLrIIC6tRka9hcaBlIECAwEAAaMgMB4wDAYDVR0TBAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAgQwDQYJKoZIhvcNAQELBQADgYEAHzC7jdYlzAVmddi/gdAeKPau
+sPBG/C2HCWqHzpCUHcKuvMzDVkY/MP2o6JIW2DBbY64bO/FceExhjcykgaYtCH/m
+oIU63+CFOTtR7otyQAWHqXa7q4SbCDlG7DyRFxqG0txPtGvy12lgldA2+RgcigQG
+Dfcog5wrJytaQ6UA0wE=
+-----END CERTIFICATE-----
diff --git a/src/core/lib/tsi/test_creds/client.key b/src/core/lib/tsi/test_creds/client.key
new file mode 100644
index 0000000000..f48d0735d9
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/client.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICeQIBADANBgkqhkiG9w0BAQEFAASCAmMwggJfAgEAAoGBAOxUR9uhvhbeVUIM
+s5WbH0px0mehl2+6sZpNjzvE2KimZpHzMJHukVH0Ffkvhs0b8+S5Ut9VNUAqd3IM
+JCCAEGtRNoQhM1t9Yr2zAckSvbRacp+FL/Cj9eDmyo00KsVGaeefA4Dh4OW+ZhkT
+NKcldXqkSuj1sEf244JZYuqZp6/tAgMBAAECgYEAi2NSVqpZMafE5YYUTcMGe6QS
+k2jtpsqYgggI2RnLJ/2tNZwYI5pwP8QVSbnMaiF4gokD5hGdrNDfTnb2v+yIwYEH
+0w8+oG7Z81KodsiZSIDJfTGsAZhVNwOz9y0VD8BBZZ1/274Zh52AUKLjZS/ZwIbS
+W2ywya855dPnH/wj+0ECQQD9X8D920kByTNHhBG18biAEZ4pxs9f0OAG8333eVcI
+w2lJDLsYDZrCB2ocgA3lUdozlzPC7YDYw8reg0tkiRY5AkEA7sdNzOeQsQRn7++5
+0bP9DtT/iON1gbfxRzCfCfXdoOtfQWIzTePWtURt9X/5D9NofI0Rg5W2oGy/MLe5
+/sXHVQJBAIup5XrJDkQywNZyAUU2ecn2bCWBFjwtqd+LBmuMciI9fOKsZtEKZrz/
+U0lkeMRoSwvXE8wmGLjjrAbdfohrXFkCQQDZEx/LtIl6JINJQiswVe0tWr6k+ASP
+1WXoTm+HYpoF/XUvv9LccNF1IazFj34hwRQwhx7w/V52Ieb+p0jUMYGxAkEAjDhd
+9pBO1fKXWiXzi9ZKfoyTNcUq3eBSVKwPG2nItg5ycXengjT5sgcWDnciIzW7BIVI
+JiqOszq9GWESErAatg==
+-----END PRIVATE KEY-----
diff --git a/src/core/lib/tsi/test_creds/client.pem b/src/core/lib/tsi/test_creds/client.pem
new file mode 100644
index 0000000000..e332091019
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/client.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICHzCCAYgCAQEwDQYJKoZIhvcNAQEFBQAwVjELMAkGA1UEBhMCQVUxEzARBgNV
+BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
+ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTE0MDcxNzIzNTYwMloXDTI0MDcxNDIzNTYw
+MlowWjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKdGVzdGNsaWVudDCB
+nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA7FRH26G+Ft5VQgyzlZsfSnHSZ6GX
+b7qxmk2PO8TYqKZmkfMwke6RUfQV+S+GzRvz5LlS31U1QCp3cgwkIIAQa1E2hCEz
+W31ivbMByRK9tFpyn4Uv8KP14ObKjTQqxUZp558DgOHg5b5mGRM0pyV1eqRK6PWw
+R/bjglli6pmnr+0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAStSm5PM7ubROiKK6/
+T2FkKlhiTOx+Ryenm3Eio59emq+jXl+1nhPySX5G2PQzSR5vd1dIhwgZSR4Gyttk
+tRZ57k/NI1brUW8joiEOMJA/Mr7H7asx7wIRYDE91Fs8GkKWd5LhoPAQj+qdG35C
+OO+svdkmqH0KZo320ZUqdl2ooQ==
+-----END CERTIFICATE-----
diff --git a/src/core/lib/tsi/test_creds/server0.key b/src/core/lib/tsi/test_creds/server0.key
new file mode 100644
index 0000000000..add153c9ae
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/server0.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANOmffupIGC8YDau
+rOF4eKnHwPszgpkkhWzKsVxhNDBxCVYx4TEjG0XWIO0iyRXupZbUC+7N/8HnEVNa
+8F1jYhng14Iiq99cNQbbnuHHhIztmpocrJTxmnhGzoAnRa1Tb+GnAuRoIHRA/V2c
+VUE9tbikQugFx/SPgXAw6tfWB+YvAgMBAAECgYEAoEq9qzUBgoHoVEGiSPiWWe8g
+5p6yUA1qx2QTQyWTAwT4z0DjjfVKmG99bFsl8+hTnJFnoCp/gnjflEOROwkjp5kG
+m0drqOPx1jeipJjpXYTBu49h+WpZ1PF+KhVtxsIm3OOCvh67iWaKyyOVb5Og8aiR
+jl6dn/TdG/dlGD8AfUECQQDuNMle6p0oU8amC6O9wIMBroxx2nFstzE6O35PLEzG
+/tj0kxxn9Jp2TS9mGaLCzSuXmpjlF4+NOWiBPkrLC2TfAkEA43Xg7uEUkaJAz2/W
+m1lIBTLt+4rIQY/2emh33bDcA+rv8rwwrMMIv17/xPx7bs49YqGG5xufD+Rwl6TL
+qFXYsQJAPrOwagax1aKvwJeBw3oAQhoTKAkLIEXcdGqipe6QSzVcIIz0xjxxyEAr
+AOIwoLxnBCISqwMXq2H4K0UdZPMb2wJAdhdYLY1L6YRMk6XjzImg25oidisKZweA
+FvMv8DgHMj2CUAqmVrt3SivfLH1M9C09L3zfFhOAFHcsgX58gav4MQJBANSBnrHj
+tIq4l8z79CPUIuu3QyeEh+XwY8s5qE5CNTck0U59lzp9NvENHbkx3KO896TTerko
++8bXHMLkJkHPXms=
+-----END PRIVATE KEY-----
diff --git a/src/core/lib/tsi/test_creds/server0.pem b/src/core/lib/tsi/test_creds/server0.pem
new file mode 100644
index 0000000000..ade75d8563
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/server0.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICHDCCAYUCAQQwDQYJKoZIhvcNAQEFBQAwVjELMAkGA1UEBhMCQVUxEzARBgNV
+BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
+ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTE0MDcyMjE3NTk0OVoXDTI0MDcxOTE3NTk0
+OVowVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxFDASBgNVBAoM
+C0dvb2dsZSBJbmMuMR0wGwYDVQQDDBQqLnRlc3QuZ29vZ2xlLmNvbS5hdTCBnzAN
+BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA06Z9+6kgYLxgNq6s4Xh4qcfA+zOCmSSF
+bMqxXGE0MHEJVjHhMSMbRdYg7SLJFe6lltQL7s3/wecRU1rwXWNiGeDXgiKr31w1
+Btue4ceEjO2amhyslPGaeEbOgCdFrVNv4acC5GggdED9XZxVQT21uKRC6AXH9I+B
+cDDq19YH5i8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBtfR5qXG9TTI8YcYh7sA4V
+GeNoplp0x6p7OG0NLvbJqAkUnkvjIkk1m1R2AUHhbkxzx6G75JIOoNJcWrCzywBA
+BIsaTdmnNysf/s1hQJuD3IHiVb+7Ji0jhttnJlYcMid4o0tJO/a2E9YUxR+9cg0i
+obb+Ql3qsvKdWBC1dDLDLw==
+-----END CERTIFICATE-----
diff --git a/src/core/lib/tsi/test_creds/server1-openssl.cnf b/src/core/lib/tsi/test_creds/server1-openssl.cnf
new file mode 100644
index 0000000000..8a02108289
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/server1-openssl.cnf
@@ -0,0 +1,26 @@
+[req]
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+
+[req_distinguished_name]
+countryName = Country Name (2 letter code)
+countryName_default = US
+stateOrProvinceName = State or Province Name (full name)
+stateOrProvinceName_default = Illinois
+localityName = Locality Name (eg, city)
+localityName_default = Chicago
+organizationName = Organization Name (eg, company)
+organizationName_default = Example, Co.
+commonName = Common Name (eg, YOUR name)
+commonName_max = 64
+
+[v3_req]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = *.test.google.fr
+DNS.2 = waterzooi.test.google.be
+DNS.3 = *.test.youtube.com
+IP.1 = "192.168.1.3"
diff --git a/src/core/lib/tsi/test_creds/server1.key b/src/core/lib/tsi/test_creds/server1.key
new file mode 100644
index 0000000000..143a5b8765
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/server1.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOHDFScoLCVJpYDD
+M4HYtIdV6Ake/sMNaaKdODjDMsux/4tDydlumN+fm+AjPEK5GHhGn1BgzkWF+slf
+3BxhrA/8dNsnunstVA7ZBgA/5qQxMfGAq4wHNVX77fBZOgp9VlSMVfyd9N8YwbBY
+AckOeUQadTi2X1S6OgJXgQ0m3MWhAgMBAAECgYAn7qGnM2vbjJNBm0VZCkOkTIWm
+V10okw7EPJrdL2mkre9NasghNXbE1y5zDshx5Nt3KsazKOxTT8d0Jwh/3KbaN+YY
+tTCbKGW0pXDRBhwUHRcuRzScjli8Rih5UOCiZkhefUTcRb6xIhZJuQy71tjaSy0p
+dHZRmYyBYO2YEQ8xoQJBAPrJPhMBkzmEYFtyIEqAxQ/o/A6E+E4w8i+KM7nQCK7q
+K4JXzyXVAjLfyBZWHGM2uro/fjqPggGD6QH1qXCkI4MCQQDmdKeb2TrKRh5BY1LR
+81aJGKcJ2XbcDu6wMZK4oqWbTX2KiYn9GB0woM6nSr/Y6iy1u145YzYxEV/iMwff
+DJULAkB8B2MnyzOg0pNFJqBJuH29bKCcHa8gHJzqXhNO5lAlEbMK95p/P2Wi+4Hd
+aiEIAF1BF326QJcvYKmwSmrORp85AkAlSNxRJ50OWrfMZnBgzVjDx3xG6KsFQVk2
+ol6VhqL6dFgKUORFUWBvnKSyhjJxurlPEahV6oo6+A+mPhFY8eUvAkAZQyTdupP3
+XEFQKctGz+9+gKkemDp7LBBMEMBXrGTLPhpEfcjv/7KPdnFHYmhYeBTBnuVmTVWe
+F98XJ7tIFfJq
+-----END PRIVATE KEY-----
diff --git a/src/core/lib/tsi/test_creds/server1.pem b/src/core/lib/tsi/test_creds/server1.pem
new file mode 100644
index 0000000000..f3d43fcc5b
--- /dev/null
+++ b/src/core/lib/tsi/test_creds/server1.pem
@@ -0,0 +1,16 @@
+-----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/src/core/lib/tsi/transport_security.c b/src/core/lib/tsi/transport_security.c
new file mode 100644
index 0000000000..a2c0d46196
--- /dev/null
+++ b/src/core/lib/tsi/transport_security.c
@@ -0,0 +1,284 @@
+/*
+ *
+ * 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/lib/tsi/transport_security.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* --- 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/src/core/lib/tsi/transport_security.h b/src/core/lib/tsi/transport_security.h
new file mode 100644
index 0000000000..349dd0ae9c
--- /dev/null
+++ b/src/core/lib/tsi/transport_security.h
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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_CORE_LIB_TSI_TRANSPORT_SECURITY_H
+#define GRPC_CORE_LIB_TSI_TRANSPORT_SECURITY_H
+
+#include "src/core/lib/tsi/transport_security_interface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int tsi_tracing_enabled;
+
+/* Base for tsi_frame_protector implementations.
+ See transport_security_interface.h for documentation. */
+typedef struct {
+ tsi_result (*protect)(tsi_frame_protector *self,
+ const unsigned char *unprotected_bytes,
+ size_t *unprotected_bytes_size,
+ unsigned char *protected_output_frames,
+ size_t *protected_output_frames_size);
+ tsi_result (*protect_flush)(tsi_frame_protector *self,
+ unsigned char *protected_output_frames,
+ size_t *protected_output_frames_size,
+ size_t *still_pending_size);
+ tsi_result (*unprotect)(tsi_frame_protector *self,
+ const unsigned char *protected_frames_bytes,
+ size_t *protected_frames_bytes_size,
+ unsigned char *unprotected_bytes,
+ size_t *unprotected_bytes_size);
+ void (*destroy)(tsi_frame_protector *self);
+} tsi_frame_protector_vtable;
+
+struct tsi_frame_protector {
+ const tsi_frame_protector_vtable *vtable;
+};
+
+/* Base for tsi_handshaker implementations.
+ See transport_security_interface.h for documentation. */
+typedef struct {
+ tsi_result (*get_bytes_to_send_to_peer)(tsi_handshaker *self,
+ unsigned char *bytes,
+ size_t *bytes_size);
+ tsi_result (*process_bytes_from_peer)(tsi_handshaker *self,
+ const unsigned char *bytes,
+ size_t *bytes_size);
+ tsi_result (*get_result)(tsi_handshaker *self);
+ tsi_result (*extract_peer)(tsi_handshaker *self, tsi_peer *peer);
+ tsi_result (*create_frame_protector)(tsi_handshaker *self,
+ size_t *max_protected_frame_size,
+ tsi_frame_protector **protector);
+ void (*destroy)(tsi_handshaker *self);
+} tsi_handshaker_vtable;
+
+struct tsi_handshaker {
+ const tsi_handshaker_vtable *vtable;
+ int frame_protector_created;
+};
+
+/* Peer and property construction/destruction functions. */
+tsi_result tsi_construct_peer(size_t property_count, tsi_peer *peer);
+tsi_peer_property tsi_init_peer_property(void);
+void tsi_peer_property_destruct(tsi_peer_property *property);
+tsi_result tsi_construct_string_peer_property(const char *name,
+ const char *value,
+ size_t value_length,
+ tsi_peer_property *property);
+tsi_result tsi_construct_allocated_string_peer_property(
+ const char *name, size_t value_length, tsi_peer_property *property);
+tsi_result tsi_construct_string_peer_property_from_cstring(
+ const char *name, const char *value, tsi_peer_property *property);
+
+/* Utils. */
+char *tsi_strdup(const char *src); /* Sadly, no strdup in C89. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_TSI_TRANSPORT_SECURITY_H */
diff --git a/src/core/lib/tsi/transport_security_interface.h b/src/core/lib/tsi/transport_security_interface.h
new file mode 100644
index 0000000000..f88f1516a9
--- /dev/null
+++ b/src/core/lib/tsi/transport_security_interface.h
@@ -0,0 +1,344 @@
+/*
+ *
+ * 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_CORE_LIB_TSI_TRANSPORT_SECURITY_INTERFACE_H
+#define GRPC_CORE_LIB_TSI_TRANSPORT_SECURITY_INTERFACE_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --- tsi result --- */
+
+typedef enum {
+ TSI_OK = 0,
+ TSI_UNKNOWN_ERROR = 1,
+ TSI_INVALID_ARGUMENT = 2,
+ TSI_PERMISSION_DENIED = 3,
+ TSI_INCOMPLETE_DATA = 4,
+ TSI_FAILED_PRECONDITION = 5,
+ TSI_UNIMPLEMENTED = 6,
+ TSI_INTERNAL_ERROR = 7,
+ TSI_DATA_CORRUPTED = 8,
+ TSI_NOT_FOUND = 9,
+ TSI_PROTOCOL_FAILURE = 10,
+ TSI_HANDSHAKE_IN_PROGRESS = 11,
+ TSI_OUT_OF_RESOURCES = 12
+} tsi_result;
+
+const char *tsi_result_to_string(tsi_result result);
+
+/* --- tsi tracing --- */
+
+/* Set this early to avoid races */
+extern int tsi_tracing_enabled;
+
+/* --- tsi_frame_protector object ---
+
+ This object protects and unprotects buffers once the handshake is done.
+ Implementations of this object must be thread compatible. */
+
+typedef struct tsi_frame_protector tsi_frame_protector;
+
+/* Outputs protected frames.
+ - unprotected_bytes is an input only parameter and points to the data
+ to be protected.
+ - unprotected_bytes_size is an input/output parameter used by the caller to
+ specify how many bytes are available in unprotected_bytes. The output
+ value is the number of bytes consumed during the call.
+ - protected_output_frames points to a buffer allocated by the caller that
+ will be written.
+ - protected_output_frames_size is an input/output parameter used by the
+ caller to specify how many bytes are available in protected_output_frames.
+ As an output, this value indicates the number of bytes written.
+ - This method returns TSI_OK in case of success or a specific error code in
+ case of failure. Note that even if all the input unprotected bytes are
+ consumed, they may not have been processed into the returned protected
+ output frames. The caller should call the protect_flush method
+ to make sure that there are no more protected bytes buffered in the
+ protector.
+
+ A typical way to call this method would be:
+
+ ------------------------------------------------------------------------
+ unsigned char protected_buffer[4096];
+ size_t protected_buffer_size = sizeof(protected_buffer);
+ tsi_result result = TSI_OK;
+ while (message_size > 0) {
+ size_t protected_buffer_size_to_send = protected_buffer_size;
+ size_t processed_message_size = message_size;
+ result = tsi_frame_protector_protect(protector,
+ message_bytes,
+ &processed_message_size,
+ protected_buffer,
+ &protected_buffer_size_to_send);
+ if (result != TSI_OK) break;
+ send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send);
+ message_bytes += processed_message_size;
+ message_size -= processed_message_size;
+
+ // Don't forget to flush.
+ if (message_size == 0) {
+ size_t still_pending_size;
+ do {
+ protected_buffer_size_to_send = protected_buffer_size;
+ result = tsi_frame_protector_protect_flush(
+ protector, protected_buffer,
+ &protected_buffer_size_to_send, &still_pending_size);
+ if (result != TSI_OK) break;
+ send_bytes_to_peer(protected_buffer, protected_buffer_size_to_send);
+ } while (still_pending_size > 0);
+ }
+ }
+
+ if (result != TSI_OK) HandleError(result);
+ ------------------------------------------------------------------------ */
+tsi_result tsi_frame_protector_protect(tsi_frame_protector *self,
+ const unsigned char *unprotected_bytes,
+ size_t *unprotected_bytes_size,
+ unsigned char *protected_output_frames,
+ size_t *protected_output_frames_size);
+
+/* Indicates that we need to flush the bytes buffered in the protector and get
+ the resulting frame.
+ - protected_output_frames points to a buffer allocated by the caller that
+ will be written.
+ - protected_output_frames_size is an input/output parameter used by the
+ caller to specify how many bytes are available in protected_output_frames.
+ - still_pending_bytes is an output parameter indicating the number of bytes
+ that still need to be flushed from the protector.*/
+tsi_result tsi_frame_protector_protect_flush(
+ tsi_frame_protector *self, unsigned char *protected_output_frames,
+ size_t *protected_output_frames_size, size_t *still_pending_size);
+
+/* Outputs unprotected bytes.
+ - protected_frames_bytes is an input only parameter and points to the
+ protected frames to be unprotected.
+ - protected_frames_bytes_size is an input/output only parameter used by the
+ caller to specify how many bytes are available in protected_bytes. The
+ output value is the number of bytes consumed during the call.
+ Implementations will buffer up to a frame of protected data.
+ - unprotected_bytes points to a buffer allocated by the caller that will be
+ written.
+ - unprotected_bytes_size is an input/output parameter used by the caller to
+ specify how many bytes are available in unprotected_bytes. This
+ value is expected to be at most max_protected_frame_size minus overhead
+ which means that max_protected_frame_size is a safe bet. The output value
+ is the number of bytes actually written.
+ If *unprotected_bytes_size is unchanged, there may be more data remaining
+ to unprotect, and the caller should call this function again.
+
+ - This method returns TSI_OK in case of success. Success includes cases where
+ there is not enough data to output a frame in which case
+ unprotected_bytes_size will be set to 0 and cases where the internal buffer
+ needs to be read before new protected data can be processed in which case
+ protected_frames_size will be set to 0. */
+tsi_result tsi_frame_protector_unprotect(
+ tsi_frame_protector *self, const unsigned char *protected_frames_bytes,
+ size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes,
+ size_t *unprotected_bytes_size);
+
+/* Destroys the tsi_frame_protector object. */
+void tsi_frame_protector_destroy(tsi_frame_protector *self);
+
+/* --- tsi_peer objects ---
+
+ tsi_peer objects are a set of properties. The peer owns the properties. */
+
+/* This property is of type TSI_PEER_PROPERTY_STRING. */
+#define TSI_CERTIFICATE_TYPE_PEER_PROPERTY "certificate_type"
+
+/* Property values may contain NULL characters just like C++ strings.
+ The length field gives the length of the string. */
+typedef struct tsi_peer_property {
+ char *name;
+ struct {
+ char *data;
+ size_t length;
+ } value;
+} tsi_peer_property;
+
+typedef struct {
+ tsi_peer_property *properties;
+ size_t property_count;
+} tsi_peer;
+
+/* Destructs the tsi_peer object. */
+void tsi_peer_destruct(tsi_peer *self);
+
+/* --- tsi_handshaker objects ----
+
+ Implementations of this object must be thread compatible.
+
+ A typical usage of this object would be:
+
+ ------------------------------------------------------------------------
+ tsi_result result = TSI_OK;
+ unsigned char buf[4096];
+ size_t buf_offset;
+ size_t buf_size;
+ while (1) {
+ // See if we need to send some bytes to the peer.
+ do {
+ size_t buf_size_to_send = sizeof(buf);
+ result = tsi_handshaker_get_bytes_to_send_to_peer(handshaker, buf,
+ &buf_size_to_send);
+ if (buf_size_to_send > 0) send_bytes_to_peer(buf, buf_size_to_send);
+ } while (result == TSI_INCOMPLETE_DATA);
+ if (result != TSI_OK) return result;
+ if (!tsi_handshaker_is_in_progress(handshaker)) break;
+
+ do {
+ // Read bytes from the peer.
+ buf_size = sizeof(buf);
+ buf_offset = 0;
+ read_bytes_from_peer(buf, &buf_size);
+ if (buf_size == 0) break;
+
+ // Process the bytes from the peer. We have to be careful as these bytes
+ // may contain non-handshake data (protected data). If this is the case,
+ // we will exit from the loop with buf_size > 0.
+ size_t consumed_by_handshaker = buf_size;
+ result = tsi_handshaker_process_bytes_from_peer(
+ handshaker, buf, &consumed_by_handshaker);
+ buf_size -= consumed_by_handshaker;
+ buf_offset += consumed_by_handshaker;
+ } while (result == TSI_INCOMPLETE_DATA);
+
+ if (result != TSI_OK) return result;
+ if (!tsi_handshaker_is_in_progress(handshaker)) break;
+ }
+
+ // Check the Peer.
+ tsi_peer peer;
+ do {
+ result = tsi_handshaker_extract_peer(handshaker, &peer);
+ if (result != TSI_OK) break;
+ result = check_peer(&peer);
+ } while (0);
+ tsi_peer_destruct(&peer);
+ if (result != TSI_OK) return result;
+
+ // Create the protector.
+ tsi_frame_protector* protector = NULL;
+ result = tsi_handshaker_create_frame_protector(handshaker, NULL,
+ &protector);
+ if (result != TSI_OK) return result;
+
+ // Do not forget to unprotect outstanding data if any.
+ if (buf_size > 0) {
+ result = tsi_frame_protector_unprotect(protector, buf + buf_offset,
+ buf_size, ..., ...);
+ ....
+ }
+ ...
+ ------------------------------------------------------------------------ */
+typedef struct tsi_handshaker tsi_handshaker;
+
+/* Gets bytes that need to be sent to the peer.
+ - bytes is the buffer that will be written with the data to be sent to the
+ peer.
+ - bytes_size is an input/output parameter specifying the capacity of the
+ bytes parameter as input and the number of bytes written as output.
+ Returns TSI_OK if all the data to send to the peer has been written or if
+ nothing has to be sent to the peer (in which base bytes_size outputs to 0),
+ otherwise returns TSI_INCOMPLETE_DATA which indicates that this method
+ needs to be called again to get all the bytes to send to the peer (there
+ was more data to write than the specified bytes_size). In case of a fatal
+ error in the handshake, another specific error code is returned. */
+tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self,
+ unsigned char *bytes,
+ size_t *bytes_size);
+
+/* Processes bytes received from the peer.
+ - bytes is the buffer containing the data.
+ - bytes_size is an input/output parameter specifying the size of the data as
+ input and the number of bytes consumed as output.
+ Return TSI_OK if the handshake has all the data it needs to process,
+ otherwise return TSI_INCOMPLETE_DATA which indicates that this method
+ needs to be called again to complete the data needed for processing. In
+ case of a fatal error in the handshake, another specific error code is
+ returned. */
+tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self,
+ const unsigned char *bytes,
+ size_t *bytes_size);
+
+/* Gets the result of the handshaker.
+ Returns TSI_OK if the hanshake completed successfully and there has been no
+ errors. Returns TSI_HANDSHAKE_IN_PROGRESS if the handshaker is not done yet
+ but no error has been encountered so far. Otherwise the handshaker failed
+ with the returned error. */
+tsi_result tsi_handshaker_get_result(tsi_handshaker *self);
+
+/* Returns 1 if the handshake is in progress, 0 otherwise. */
+#define tsi_handshaker_is_in_progress(h) \
+ (tsi_handshaker_get_result((h)) == TSI_HANDSHAKE_IN_PROGRESS)
+
+/* This method may return TSI_FAILED_PRECONDITION if
+ tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise
+ assuming the handshaker is not in a fatal error state.
+ The caller is responsible for destructing the peer. */
+tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer);
+
+/* This method creates a tsi_frame_protector object after the handshake phase
+ is done. After this method has been called successfully, the only method
+ that can be called on this object is Destroy.
+ - max_output_protected_frame_size is an input/output parameter specifying the
+ desired max output protected frame size as input and outputing the actual
+ max output frame size as the output. Passing NULL is OK and will result in
+ the implementation choosing the default maximum protected frame size. Note
+ that this size only applies to outgoing frames (generated with
+ tsi_frame_protector_protect) and not incoming frames (input of
+ tsi_frame_protector_unprotect).
+ - protector is an output parameter pointing to the newly created
+ tsi_frame_protector object.
+ This method may return TSI_FAILED_PRECONDITION if
+ tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise assuming
+ the handshaker is not in a fatal error state.
+ The caller is responsible for destroying the protector. */
+tsi_result tsi_handshaker_create_frame_protector(
+ tsi_handshaker *self, size_t *max_output_protected_frame_size,
+ tsi_frame_protector **protector);
+
+/* This method releases the tsi_handshaker object. After this method is called,
+ no other method can be called on the object. */
+void tsi_handshaker_destroy(tsi_handshaker *self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_LIB_TSI_TRANSPORT_SECURITY_INTERFACE_H */