Merge branch 'master' into zookeeper

pull/2882/head
Hongwei Wang 10 years ago
commit b307ae280b
  1. 6
      .gitignore
  2. 3
      .travis.yml
  3. 30
      BUILD
  4. 1317
      Makefile
  5. 2
      README.md
  6. 123
      build.json
  7. 57
      doc/connection-backoff.md
  8. 96
      doc/interop-test-descriptions.md
  9. 7
      gRPC.podspec
  10. 65
      include/grpc++/auth_context.h
  11. 77
      include/grpc++/auth_property_iterator.h
  12. 10
      include/grpc++/client_context.h
  13. 6
      include/grpc++/credentials.h
  14. 8
      include/grpc++/fixed_size_thread_pool.h
  15. 141
      include/grpc++/impl/call.h
  16. 4
      include/grpc++/server.h
  17. 33
      include/grpc++/server_builder.h
  18. 14
      include/grpc++/server_context.h
  19. 26
      include/grpc++/stream.h
  20. 2
      include/grpc++/thread_pool_interface.h
  21. 4
      include/grpc/census.h
  22. 3
      include/grpc/grpc.h
  23. 22
      include/grpc/grpc_security.h
  24. 2
      include/grpc/support/port_platform.h
  25. 22
      include/grpc/support/time.h
  26. 20
      include/grpc/support/useful.h
  27. 50
      src/compiler/csharp_generator.cc
  28. 74
      src/compiler/objective_c_generator.cc
  29. 28
      src/core/census/grpc_context.c
  30. 19
      src/core/census/grpc_context.h
  31. 2
      src/core/census/initialize.c
  32. 10
      src/core/channel/census_filter.c
  33. 2
      src/core/channel/client_channel.c
  34. 18
      src/core/channel/http_client_filter.c
  35. 30
      src/core/channel/http_server_filter.c
  36. 2
      src/core/client_config/lb_policies/pick_first.h
  37. 4
      src/core/client_config/resolvers/zookeeper_resolver.c
  38. 4
      src/core/client_config/subchannel.c
  39. 1
      src/core/httpcli/httpcli.c
  40. 4
      src/core/httpcli/httpcli.h
  41. 2
      src/core/iomgr/alarm.h
  42. 1
      src/core/iomgr/iocp_windows.c
  43. 39
      src/core/iomgr/iomgr.c
  44. 3
      src/core/iomgr/pollset_multipoller_with_epoll.c
  45. 9
      src/core/iomgr/pollset_posix.c
  46. 2
      src/core/iomgr/pollset_set.h
  47. 6
      src/core/iomgr/pollset_windows.c
  48. 35
      src/core/iomgr/socket_windows.c
  49. 10
      src/core/iomgr/tcp_client_posix.c
  50. 3
      src/core/iomgr/tcp_client_windows.c
  51. 8
      src/core/iomgr/tcp_posix.c
  52. 80
      src/core/iomgr/tcp_server_windows.c
  53. 24
      src/core/iomgr/tcp_windows.c
  54. 2
      src/core/json/json.h
  55. 2
      src/core/profiling/timers_preciseclock.h
  56. 38
      src/core/security/client_auth_filter.c
  57. 169
      src/core/security/credentials.c
  58. 112
      src/core/security/credentials.h
  59. 89
      src/core/security/google_default_credentials.c
  60. 54
      src/core/security/json_token.c
  61. 15
      src/core/security/json_token.h
  62. 832
      src/core/security/jwt_verifier.c
  63. 136
      src/core/security/jwt_verifier.h
  64. 8
      src/core/security/secure_endpoint.c
  65. 29
      src/core/security/secure_transport_setup.c
  66. 2
      src/core/security/secure_transport_setup.h
  67. 14
      src/core/security/security_context.c
  68. 8
      src/core/security/security_context.h
  69. 47
      src/core/security/server_secure_chttp2.c
  70. 4
      src/core/statistics/census_rpc_stats.c
  71. 16
      src/core/statistics/census_tracing.c
  72. 6
      src/core/statistics/window_stats.h
  73. 4
      src/core/support/cancellable.c
  74. 13
      src/core/support/log_linux.c
  75. 2
      src/core/support/log_posix.c
  76. 2
      src/core/support/log_win32.c
  77. 7
      src/core/support/slice.c
  78. 130
      src/core/support/stack_lockfree.c
  79. 50
      src/core/support/stack_lockfree.h
  80. 57
      src/core/support/string.c
  81. 15
      src/core/support/string.h
  82. 2
      src/core/support/sync_win32.c
  83. 44
      src/core/support/time_posix.c
  84. 35
      src/core/support/time_win32.c
  85. 8
      src/core/surface/byte_buffer_queue.c
  86. 2
      src/core/surface/byte_buffer_queue.h
  87. 103
      src/core/surface/call.c
  88. 2
      src/core/surface/call.h
  89. 4
      src/core/surface/call_log_batch.c
  90. 35
      src/core/surface/channel.c
  91. 230
      src/core/surface/completion_queue.c
  92. 18
      src/core/surface/completion_queue.h
  93. 2
      src/core/surface/init.c
  94. 1
      src/core/surface/secure_channel_create.c
  95. 397
      src/core/surface/server.c
  96. 41
      src/core/surface/version.c
  97. 9
      src/core/transport/chttp2/frame_data.c
  98. 4
      src/core/transport/chttp2/frame_window_update.c
  99. 19
      src/core/transport/chttp2/hpack_parser.c
  100. 6
      src/core/transport/chttp2/hpack_table.c
  101. Some files were not shown because too many files have changed in this diff Show More

6
.gitignore vendored

@ -4,8 +4,8 @@ gens
libs libs
objs objs
# Python virtual environment (pre-3.4 only) # Python virtual environments
python2.7_virtual_environment python*_virtual_environment
# gcov coverage data # gcov coverage data
coverage coverage
@ -31,3 +31,5 @@ coverage
# vim temp files # vim temp files
.*.swp .*.swp
# Makefile's cache
cache.mk

@ -6,7 +6,8 @@ before_install:
- echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list - echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
- echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list - echo "deb http://download.mono-project.com/repo/debian wheezy-libtiff-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
- sudo apt-get update -qq - sudo apt-get update -qq
- sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv clang-3.5 - sudo apt-get install -qq libgtest-dev libgflags-dev python-virtualenv python-dev python3-dev clang-3.5
- sudo pip install --upgrade virtualenv
- sudo pip install cpp-coveralls mako simplejson - sudo pip install cpp-coveralls mako simplejson
- sudo apt-get install -qq mono-devel nunit - sudo apt-get install -qq mono-devel nunit
- wget www.nuget.org/NuGet.exe -O nuget.exe - wget www.nuget.org/NuGet.exe -O nuget.exe

30
BUILD

@ -47,6 +47,7 @@ cc_library(
"src/core/support/env.h", "src/core/support/env.h",
"src/core/support/file.h", "src/core/support/file.h",
"src/core/support/murmur_hash.h", "src/core/support/murmur_hash.h",
"src/core/support/stack_lockfree.h",
"src/core/support/string.h", "src/core/support/string.h",
"src/core/support/string_win32.h", "src/core/support/string_win32.h",
"src/core/support/thd_internal.h", "src/core/support/thd_internal.h",
@ -73,6 +74,7 @@ cc_library(
"src/core/support/murmur_hash.c", "src/core/support/murmur_hash.c",
"src/core/support/slice.c", "src/core/support/slice.c",
"src/core/support/slice_buffer.c", "src/core/support/slice_buffer.c",
"src/core/support/stack_lockfree.c",
"src/core/support/string.c", "src/core/support/string.c",
"src/core/support/string_posix.c", "src/core/support/string_posix.c",
"src/core/support/string_win32.c", "src/core/support/string_win32.c",
@ -138,6 +140,7 @@ cc_library(
"src/core/security/base64.h", "src/core/security/base64.h",
"src/core/security/credentials.h", "src/core/security/credentials.h",
"src/core/security/json_token.h", "src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h", "src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h", "src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h", "src/core/security/security_connector.h",
@ -254,6 +257,7 @@ cc_library(
"src/core/security/credentials_win32.c", "src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c", "src/core/security/google_default_credentials.c",
"src/core/security/json_token.c", "src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c", "src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c", "src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c", "src/core/security/security_connector.c",
@ -346,6 +350,7 @@ cc_library(
"src/core/surface/server_chttp2.c", "src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c", "src/core/surface/server_create.c",
"src/core/surface/surface_trace.c", "src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c", "src/core/transport/chttp2/frame_data.c",
@ -576,6 +581,7 @@ cc_library(
"src/core/surface/server_chttp2.c", "src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c", "src/core/surface/server_create.c",
"src/core/surface/surface_trace.c", "src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c", "src/core/transport/chttp2/frame_data.c",
@ -647,11 +653,15 @@ cc_library(
name = "grpc++", name = "grpc++",
srcs = [ srcs = [
"src/cpp/client/secure_credentials.h", "src/cpp/client/secure_credentials.h",
"src/cpp/common/secure_auth_context.h",
"src/cpp/server/secure_server_credentials.h", "src/cpp/server/secure_server_credentials.h",
"src/cpp/client/channel.h", "src/cpp/client/channel.h",
"src/cpp/server/thread_pool.h", "src/cpp/common/create_auth_context.h",
"src/cpp/client/secure_channel_arguments.cc", "src/cpp/client/secure_channel_arguments.cc",
"src/cpp/client/secure_credentials.cc", "src/cpp/client/secure_credentials.cc",
"src/cpp/common/auth_property_iterator.cc",
"src/cpp/common/secure_auth_context.cc",
"src/cpp/common/secure_create_auth_context.cc",
"src/cpp/server/secure_server_credentials.cc", "src/cpp/server/secure_server_credentials.cc",
"src/cpp/client/channel.cc", "src/cpp/client/channel.cc",
"src/cpp/client/channel_arguments.cc", "src/cpp/client/channel_arguments.cc",
@ -667,12 +677,12 @@ cc_library(
"src/cpp/proto/proto_utils.cc", "src/cpp/proto/proto_utils.cc",
"src/cpp/server/async_generic_service.cc", "src/cpp/server/async_generic_service.cc",
"src/cpp/server/create_default_thread_pool.cc", "src/cpp/server/create_default_thread_pool.cc",
"src/cpp/server/fixed_size_thread_pool.cc",
"src/cpp/server/insecure_server_credentials.cc", "src/cpp/server/insecure_server_credentials.cc",
"src/cpp/server/server.cc", "src/cpp/server/server.cc",
"src/cpp/server/server_builder.cc", "src/cpp/server/server_builder.cc",
"src/cpp/server/server_context.cc", "src/cpp/server/server_context.cc",
"src/cpp/server/server_credentials.cc", "src/cpp/server/server_credentials.cc",
"src/cpp/server/thread_pool.cc",
"src/cpp/util/byte_buffer.cc", "src/cpp/util/byte_buffer.cc",
"src/cpp/util/slice.cc", "src/cpp/util/slice.cc",
"src/cpp/util/status.cc", "src/cpp/util/status.cc",
@ -681,6 +691,8 @@ cc_library(
hdrs = [ hdrs = [
"include/grpc++/async_generic_service.h", "include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h", "include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/auth_property_iterator.h",
"include/grpc++/byte_buffer.h", "include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h", "include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h", "include/grpc++/channel_interface.h",
@ -690,6 +702,7 @@ cc_library(
"include/grpc++/config_protobuf.h", "include/grpc++/config_protobuf.h",
"include/grpc++/create_channel.h", "include/grpc++/create_channel.h",
"include/grpc++/credentials.h", "include/grpc++/credentials.h",
"include/grpc++/fixed_size_thread_pool.h",
"include/grpc++/generic_stub.h", "include/grpc++/generic_stub.h",
"include/grpc++/impl/call.h", "include/grpc++/impl/call.h",
"include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/client_unary_call.h",
@ -733,7 +746,8 @@ cc_library(
name = "grpc++_unsecure", name = "grpc++_unsecure",
srcs = [ srcs = [
"src/cpp/client/channel.h", "src/cpp/client/channel.h",
"src/cpp/server/thread_pool.h", "src/cpp/common/create_auth_context.h",
"src/cpp/common/insecure_create_auth_context.cc",
"src/cpp/client/channel.cc", "src/cpp/client/channel.cc",
"src/cpp/client/channel_arguments.cc", "src/cpp/client/channel_arguments.cc",
"src/cpp/client/client_context.cc", "src/cpp/client/client_context.cc",
@ -748,12 +762,12 @@ cc_library(
"src/cpp/proto/proto_utils.cc", "src/cpp/proto/proto_utils.cc",
"src/cpp/server/async_generic_service.cc", "src/cpp/server/async_generic_service.cc",
"src/cpp/server/create_default_thread_pool.cc", "src/cpp/server/create_default_thread_pool.cc",
"src/cpp/server/fixed_size_thread_pool.cc",
"src/cpp/server/insecure_server_credentials.cc", "src/cpp/server/insecure_server_credentials.cc",
"src/cpp/server/server.cc", "src/cpp/server/server.cc",
"src/cpp/server/server_builder.cc", "src/cpp/server/server_builder.cc",
"src/cpp/server/server_context.cc", "src/cpp/server/server_context.cc",
"src/cpp/server/server_credentials.cc", "src/cpp/server/server_credentials.cc",
"src/cpp/server/thread_pool.cc",
"src/cpp/util/byte_buffer.cc", "src/cpp/util/byte_buffer.cc",
"src/cpp/util/slice.cc", "src/cpp/util/slice.cc",
"src/cpp/util/status.cc", "src/cpp/util/status.cc",
@ -762,6 +776,8 @@ cc_library(
hdrs = [ hdrs = [
"include/grpc++/async_generic_service.h", "include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h", "include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/auth_property_iterator.h",
"include/grpc++/byte_buffer.h", "include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h", "include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h", "include/grpc++/channel_interface.h",
@ -771,6 +787,7 @@ cc_library(
"include/grpc++/config_protobuf.h", "include/grpc++/config_protobuf.h",
"include/grpc++/create_channel.h", "include/grpc++/create_channel.h",
"include/grpc++/credentials.h", "include/grpc++/credentials.h",
"include/grpc++/fixed_size_thread_pool.h",
"include/grpc++/generic_stub.h", "include/grpc++/generic_stub.h",
"include/grpc++/impl/call.h", "include/grpc++/impl/call.h",
"include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/client_unary_call.h",
@ -891,6 +908,7 @@ objc_library(
"src/core/support/murmur_hash.c", "src/core/support/murmur_hash.c",
"src/core/support/slice.c", "src/core/support/slice.c",
"src/core/support/slice_buffer.c", "src/core/support/slice_buffer.c",
"src/core/support/stack_lockfree.c",
"src/core/support/string.c", "src/core/support/string.c",
"src/core/support/string_posix.c", "src/core/support/string_posix.c",
"src/core/support/string_win32.c", "src/core/support/string_win32.c",
@ -938,6 +956,7 @@ objc_library(
"src/core/support/env.h", "src/core/support/env.h",
"src/core/support/file.h", "src/core/support/file.h",
"src/core/support/murmur_hash.h", "src/core/support/murmur_hash.h",
"src/core/support/stack_lockfree.h",
"src/core/support/string.h", "src/core/support/string.h",
"src/core/support/string_win32.h", "src/core/support/string_win32.h",
"src/core/support/thd_internal.h", "src/core/support/thd_internal.h",
@ -966,6 +985,7 @@ objc_library(
"src/core/security/credentials_win32.c", "src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c", "src/core/security/google_default_credentials.c",
"src/core/security/json_token.c", "src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c", "src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c", "src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c", "src/core/security/security_connector.c",
@ -1058,6 +1078,7 @@ objc_library(
"src/core/surface/server_chttp2.c", "src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c", "src/core/surface/server_create.c",
"src/core/surface/surface_trace.c", "src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c", "src/core/transport/chttp2/frame_data.c",
@ -1103,6 +1124,7 @@ objc_library(
"src/core/security/base64.h", "src/core/security/base64.h",
"src/core/security/credentials.h", "src/core/security/credentials.h",
"src/core/security/json_token.h", "src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h", "src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h", "src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h", "src/core/security/security_connector.h",

1317
Makefile

File diff suppressed because one or more lines are too long

@ -40,8 +40,8 @@ Libraries in different languages are in different state of development. We are s
* NodeJS Library: [src/node] (src/node) : Early adopter ready - Alpha. * NodeJS Library: [src/node] (src/node) : Early adopter ready - Alpha.
* Python Library: [src/python] (src/python) : Early adopter ready - Alpha. * Python Library: [src/python] (src/python) : Early adopter ready - Alpha.
* C# Library: [src/csharp] (src/csharp) : Early adopter ready - Alpha. * C# Library: [src/csharp] (src/csharp) : Early adopter ready - Alpha.
* Objective-C Library: [src/objective-c] (src/objective-c): Early adopter ready - Alpha.
* PHP Library: [src/php] (src/php) : Pre-Alpha. * PHP Library: [src/php] (src/php) : Pre-Alpha.
* Objective-C Library: [src/objective-c] (src/objective-c): Pre-Alpha.
#Overview #Overview

@ -30,6 +30,8 @@
"public_headers": [ "public_headers": [
"include/grpc++/async_generic_service.h", "include/grpc++/async_generic_service.h",
"include/grpc++/async_unary_call.h", "include/grpc++/async_unary_call.h",
"include/grpc++/auth_context.h",
"include/grpc++/auth_property_iterator.h",
"include/grpc++/byte_buffer.h", "include/grpc++/byte_buffer.h",
"include/grpc++/channel_arguments.h", "include/grpc++/channel_arguments.h",
"include/grpc++/channel_interface.h", "include/grpc++/channel_interface.h",
@ -39,6 +41,7 @@
"include/grpc++/config_protobuf.h", "include/grpc++/config_protobuf.h",
"include/grpc++/create_channel.h", "include/grpc++/create_channel.h",
"include/grpc++/credentials.h", "include/grpc++/credentials.h",
"include/grpc++/fixed_size_thread_pool.h",
"include/grpc++/generic_stub.h", "include/grpc++/generic_stub.h",
"include/grpc++/impl/call.h", "include/grpc++/impl/call.h",
"include/grpc++/impl/client_unary_call.h", "include/grpc++/impl/client_unary_call.h",
@ -68,7 +71,7 @@
], ],
"headers": [ "headers": [
"src/cpp/client/channel.h", "src/cpp/client/channel.h",
"src/cpp/server/thread_pool.h" "src/cpp/common/create_auth_context.h"
], ],
"src": [ "src": [
"src/cpp/client/channel.cc", "src/cpp/client/channel.cc",
@ -85,12 +88,12 @@
"src/cpp/proto/proto_utils.cc", "src/cpp/proto/proto_utils.cc",
"src/cpp/server/async_generic_service.cc", "src/cpp/server/async_generic_service.cc",
"src/cpp/server/create_default_thread_pool.cc", "src/cpp/server/create_default_thread_pool.cc",
"src/cpp/server/fixed_size_thread_pool.cc",
"src/cpp/server/insecure_server_credentials.cc", "src/cpp/server/insecure_server_credentials.cc",
"src/cpp/server/server.cc", "src/cpp/server/server.cc",
"src/cpp/server/server_builder.cc", "src/cpp/server/server_builder.cc",
"src/cpp/server/server_context.cc", "src/cpp/server/server_context.cc",
"src/cpp/server/server_credentials.cc", "src/cpp/server/server_credentials.cc",
"src/cpp/server/thread_pool.cc",
"src/cpp/util/byte_buffer.cc", "src/cpp/util/byte_buffer.cc",
"src/cpp/util/slice.cc", "src/cpp/util/slice.cc",
"src/cpp/util/status.cc", "src/cpp/util/status.cc",
@ -285,6 +288,7 @@
"src/core/surface/server_chttp2.c", "src/core/surface/server_chttp2.c",
"src/core/surface/server_create.c", "src/core/surface/server_create.c",
"src/core/surface/surface_trace.c", "src/core/surface/surface_trace.c",
"src/core/surface/version.c",
"src/core/transport/chttp2/alpn.c", "src/core/transport/chttp2/alpn.c",
"src/core/transport/chttp2/bin_encoder.c", "src/core/transport/chttp2/bin_encoder.c",
"src/core/transport/chttp2/frame_data.c", "src/core/transport/chttp2/frame_data.c",
@ -373,6 +377,7 @@
"src/core/support/env.h", "src/core/support/env.h",
"src/core/support/file.h", "src/core/support/file.h",
"src/core/support/murmur_hash.h", "src/core/support/murmur_hash.h",
"src/core/support/stack_lockfree.h",
"src/core/support/string.h", "src/core/support/string.h",
"src/core/support/string_win32.h", "src/core/support/string_win32.h",
"src/core/support/thd_internal.h" "src/core/support/thd_internal.h"
@ -401,6 +406,7 @@
"src/core/support/murmur_hash.c", "src/core/support/murmur_hash.c",
"src/core/support/slice.c", "src/core/support/slice.c",
"src/core/support/slice_buffer.c", "src/core/support/slice_buffer.c",
"src/core/support/stack_lockfree.c",
"src/core/support/string.c", "src/core/support/string.c",
"src/core/support/string_posix.c", "src/core/support/string_posix.c",
"src/core/support/string_win32.c", "src/core/support/string_win32.c",
@ -451,6 +457,7 @@
"src/core/security/base64.h", "src/core/security/base64.h",
"src/core/security/credentials.h", "src/core/security/credentials.h",
"src/core/security/json_token.h", "src/core/security/json_token.h",
"src/core/security/jwt_verifier.h",
"src/core/security/secure_endpoint.h", "src/core/security/secure_endpoint.h",
"src/core/security/secure_transport_setup.h", "src/core/security/secure_transport_setup.h",
"src/core/security/security_connector.h", "src/core/security/security_connector.h",
@ -473,6 +480,7 @@
"src/core/security/credentials_win32.c", "src/core/security/credentials_win32.c",
"src/core/security/google_default_credentials.c", "src/core/security/google_default_credentials.c",
"src/core/security/json_token.c", "src/core/security/json_token.c",
"src/core/security/jwt_verifier.c",
"src/core/security/secure_endpoint.c", "src/core/security/secure_endpoint.c",
"src/core/security/secure_transport_setup.c", "src/core/security/secure_transport_setup.c",
"src/core/security/security_connector.c", "src/core/security/security_connector.c",
@ -580,11 +588,15 @@
"language": "c++", "language": "c++",
"headers": [ "headers": [
"src/cpp/client/secure_credentials.h", "src/cpp/client/secure_credentials.h",
"src/cpp/common/secure_auth_context.h",
"src/cpp/server/secure_server_credentials.h" "src/cpp/server/secure_server_credentials.h"
], ],
"src": [ "src": [
"src/cpp/client/secure_channel_arguments.cc", "src/cpp/client/secure_channel_arguments.cc",
"src/cpp/client/secure_credentials.cc", "src/cpp/client/secure_credentials.cc",
"src/cpp/common/auth_property_iterator.cc",
"src/cpp/common/secure_auth_context.cc",
"src/cpp/common/secure_create_auth_context.cc",
"src/cpp/server/secure_server_credentials.cc" "src/cpp/server/secure_server_credentials.cc"
], ],
"deps": [ "deps": [
@ -637,6 +649,9 @@
"name": "grpc++_unsecure", "name": "grpc++_unsecure",
"build": "all", "build": "all",
"language": "c++", "language": "c++",
"src": [
"src/cpp/common/insecure_create_auth_context.cc"
],
"deps": [ "deps": [
"gpr", "gpr",
"grpc_unsecure" "grpc_unsecure"
@ -1181,6 +1196,18 @@
"gpr" "gpr"
] ]
}, },
{
"name": "gpr_stack_lockfree_test",
"build": "test",
"language": "c",
"src": [
"test/core/support/stack_lockfree_test.c"
],
"deps": [
"gpr_test_util",
"gpr"
]
},
{ {
"name": "gpr_string_test", "name": "gpr_string_test",
"build": "test", "build": "test",
@ -1379,6 +1406,20 @@
"gpr" "gpr"
] ]
}, },
{
"name": "grpc_jwt_verifier_test",
"build": "test",
"language": "c",
"src": [
"test/core/security/jwt_verifier_test.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "grpc_print_google_default_creds_token", "name": "grpc_print_google_default_creds_token",
"build": "tool", "build": "tool",
@ -1421,6 +1462,20 @@
"gpr" "gpr"
] ]
}, },
{
"name": "grpc_verify_jwt",
"build": "tool",
"language": "c",
"src": [
"test/core/security/verify_jwt.c"
],
"deps": [
"grpc_test_util",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "hpack_parser_test", "name": "hpack_parser_test",
"build": "test", "build": "test",
@ -1889,6 +1944,19 @@
"gpr" "gpr"
] ]
}, },
{
"name": "auth_property_iterator_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/common/auth_property_iterator_test.cc"
],
"deps": [
"grpc++",
"grpc",
"gpr"
]
},
{ {
"name": "channel_arguments_test", "name": "channel_arguments_test",
"build": "test", "build": "test",
@ -2025,6 +2093,21 @@
"gpr" "gpr"
] ]
}, },
{
"name": "fixed_size_thread_pool_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/server/fixed_size_thread_pool_test.cc"
],
"deps": [
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "generic_end2end_test", "name": "generic_end2end_test",
"build": "test", "build": "test",
@ -2279,11 +2362,11 @@
] ]
}, },
{ {
"name": "qps_test", "name": "qps_openloop_test",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/qps_test.cc" "test/cpp/qps/qps_openloop_test.cc"
], ],
"deps": [ "deps": [
"qps", "qps",
@ -2297,11 +2380,11 @@
] ]
}, },
{ {
"name": "qps_test_openloop", "name": "qps_test",
"build": "test", "build": "test",
"language": "c++", "language": "c++",
"src": [ "src": [
"test/cpp/qps/qps_test_openloop.cc" "test/cpp/qps/qps_test.cc"
], ],
"deps": [ "deps": [
"qps", "qps",
@ -2336,6 +2419,19 @@
"grpc++_test_config" "grpc++_test_config"
] ]
}, },
{
"name": "secure_auth_context_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/common/secure_auth_context_test.cc"
],
"deps": [
"grpc++",
"grpc",
"gpr"
]
},
{ {
"name": "server_crash_test", "name": "server_crash_test",
"build": "test", "build": "test",
@ -2418,21 +2514,6 @@
"gpr" "gpr"
] ]
}, },
{
"name": "thread_pool_test",
"build": "test",
"language": "c++",
"src": [
"test/cpp/server/thread_pool_test.cc"
],
"deps": [
"grpc_test_util",
"grpc++",
"grpc",
"gpr_test_util",
"gpr"
]
},
{ {
"name": "thread_stress_test", "name": "thread_stress_test",
"build": "test", "build": "test",

@ -8,58 +8,39 @@ requests) and instead do some form of exponential backoff.
We have several parameters: We have several parameters:
1. INITIAL_BACKOFF (how long to wait after the first failure before retrying) 1. INITIAL_BACKOFF (how long to wait after the first failure before retrying)
2. MULTIPLIER (factor with which to multiply backoff after a failed retry) 2. MULTIPLIER (factor with which to multiply backoff after a failed retry)
3. MAX_BACKOFF (Upper bound on backoff) 3. MAX_BACKOFF (upper bound on backoff)
4. MIN_CONNECTION_TIMEOUT 4. MIN_CONNECT_TIMEOUT (minimum time we're willing to give a connection to
complete)
## Proposed Backoff Algorithm ## Proposed Backoff Algorithm
Exponentially back off the start time of connection attempts up to a limit of Exponentially back off the start time of connection attempts up to a limit of
MAX_BACKOFF. MAX_BACKOFF, with jitter.
``` ```
ConnectWithBackoff() ConnectWithBackoff()
current_backoff = INITIAL_BACKOFF current_backoff = INITIAL_BACKOFF
current_deadline = now() + INITIAL_BACKOFF current_deadline = now() + INITIAL_BACKOFF
while (TryConnect(Max(current_deadline, MIN_CONNECT_TIMEOUT)) while (TryConnect(Max(current_deadline, now() + MIN_CONNECT_TIMEOUT))
!= SUCCESS) != SUCCESS)
SleepUntil(current_deadline) SleepUntil(current_deadline)
current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF) current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF)
current_deadline = now() + current_backoff current_deadline = now() + current_backoff +
``` UniformRandom(-JITTER * current_backoff, JITTER * current_backoff)
## Historical Algorithm in Stubby
Exponentially increase up to a limit of MAX_BACKOFF the intervals between
connection attempts. This is what stubby 2 uses, and is equivalent if
TryConnect() fails instantly.
``` ```
LegacyConnectWithBackoff()
current_backoff = INITIAL_BACKOFF
while (TryConnect(MIN_CONNECT_TIMEOUT) != SUCCESS)
SleepFor(current_backoff)
current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF)
```
The grpc C implementation currently uses this approach with an initial backoff
of 1 second, multiplier of 2, and maximum backoff of 120 seconds. (This will
change)
Stubby, or at least rpc2, uses exactly this algorithm with an initial backoff With specific parameters of
of 1 second, multiplier of 1.2, and a maximum backoff of 120 seconds. MIN_CONNECT_TIMEOUT = 20 seconds
INITIAL_BACKOFF = 1 second
MULTIPLIER = 1.6
MAX_BACKOFF = 120 seconds
JITTER = 0.2
## Use Cases to Consider Implementations with pressing concerns (such as minimizing the number of wakeups
on a mobile phone) may wish to use a different algorithm, and in particular
different jitter logic.
* Client tries to connect to a server which is down for multiple hours, eg for Alternate implementations must ensure that connection backoffs started at the
maintenance same time disperse, and must not attempt connections substantially more often
* Client tries to connect to a server which is overloaded than the above algorithm.
* User is bringing up both a client and a server at the same time
* In particular, we would like to avoid a large unnecessary delay if the
client connects to a server which is about to come up
* Client/server are misconfigured such that connection attempts always fail
* We want to make sure these don’t put too much load on the server by
default.
* Server is overloaded and wants to transiently make clients back off
* Application has out of band reason to believe a server is back
* We should consider an out of band mechanism for the client to hint that
we should short circuit the backoff.

@ -392,6 +392,97 @@ Asserts:
* clients are free to assert that the response payload body contents are zero * clients are free to assert that the response payload body contents are zero
and comparing the entire response message against a golden response and comparing the entire response message against a golden response
### oauth2_auth_token
Similar to the other auth tests, this test is only for cloud-to-prod path.
This test verifies unary calls succeed in sending messages using an OAuth2 token
that is obtained out of band. For the purpose of the test, the OAuth2 token is
actually obtained from the service account credentials via the
language-specific authorization library.
The difference between this test and the other auth tests is that rather than
configuring the test client with ServiceAccountCredentials directly, the test
first uses the authorization library to obtain an authorization token.
The test
- uses the flag `--service_account_key_file` with the path to a json key file
downloaded from https://console.developers.google.com. Alternately, if using a
usable auth implementation, it may specify the file location in the environment
variable GOOGLE_APPLICATION_CREDENTIALS
- uses the flag `--oauth_scope` for the oauth scope. For testing against
grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should
be passed as the `--oauth_scope`.
Server features:
* [UnaryCall][]
* [Compressable Payload][]
* [Echo Authenticated Username][]
* [Echo OAuth Scope][]
Procedure:
1. Client uses the auth library to obtain an authorization token
2. Client configures the channel to use AccessTokenCredentials with the access token obtained in step 1.
3. Client calls UnaryCall with the following message
```
{
fill_username: true
fill_oauth_scope: true
}
```
Asserts:
* call was successful
* received SimpleResponse.username is in the json key file used by the auth
library to obtain the authorization token
* received SimpleResponse.oauth_scope is in `--oauth_scope`
### per_rpc_creds
Similar to the other auth tests, this test is only for cloud-to-prod path.
This test verifies unary calls succeed in sending messages using an OAuth2 token
that is obtained out of band. For the purpose of the test, the OAuth2 token is
actually obtained from the service account credentials via the
language-specific authorization library.
The test
- uses the flag `--service_account_key_file` with the path to a json key file
downloaded from https://console.developers.google.com. Alternately, if using a
usable auth implementation, it may specify the file location in the environment
variable GOOGLE_APPLICATION_CREDENTIALS
- uses the flag `--oauth_scope` for the oauth scope. For testing against
grpc-test.sandbox.google.com, "https://www.googleapis.com/auth/xapi.zoo" should
be passed as the `--oauth_scope`.
Server features:
* [UnaryCall][]
* [Compressable Payload][]
* [Echo Authenticated Username][]
* [Echo OAuth Scope][]
Procedure:
1. Client uses the auth library to obtain an authorization token
2. Client configures the channel with just SSL credentials.
3. Client calls UnaryCall, setting per-call credentials to
AccessTokenCredentials with the access token obtained in step 1. The request is
the following message
```
{
fill_username: true
fill_oauth_scope: true
}
```
Asserts:
* call was successful
* received SimpleResponse.username is in the json key file used by the auth
library to obtain the authorization token
* received SimpleResponse.oauth_scope is in `--oauth_scope`
### Metadata (TODO: fix name) ### Metadata (TODO: fix name)
Status: Not yet implementable Status: Not yet implementable
@ -560,11 +651,6 @@ Propagation of status code and message (yangg)
Multiple thousand simultaneous calls on same Channel (ctiller) Multiple thousand simultaneous calls on same Channel (ctiller)
OAuth2 tokens + Service Credentials from GCE metadata server (GCE->prod only)
(abhishek)
OAuth2 tokens + JWT signing key (GCE->prod only) (abhishek)
Metadata: client headers, server headers + trailers, binary+ascii Metadata: client headers, server headers + trailers, binary+ascii
#### Normal priority: #### Normal priority:

@ -64,6 +64,7 @@ Pod::Spec.new do |s|
ss.source_files = 'src/core/support/env.h', ss.source_files = 'src/core/support/env.h',
'src/core/support/file.h', 'src/core/support/file.h',
'src/core/support/murmur_hash.h', 'src/core/support/murmur_hash.h',
'src/core/support/stack_lockfree.h',
'src/core/support/grpc_string.h', 'src/core/support/grpc_string.h',
'src/core/support/string_win32.h', 'src/core/support/string_win32.h',
'src/core/support/thd_internal.h', 'src/core/support/thd_internal.h',
@ -118,6 +119,7 @@ Pod::Spec.new do |s|
'src/core/support/murmur_hash.c', 'src/core/support/murmur_hash.c',
'src/core/support/slice.c', 'src/core/support/slice.c',
'src/core/support/slice_buffer.c', 'src/core/support/slice_buffer.c',
'src/core/support/stack_lockfree.c',
'src/core/support/string.c', 'src/core/support/string.c',
'src/core/support/string_posix.c', 'src/core/support/string_posix.c',
'src/core/support/string_win32.c', 'src/core/support/string_win32.c',
@ -140,6 +142,7 @@ Pod::Spec.new do |s|
'src/core/security/base64.h', 'src/core/security/base64.h',
'src/core/security/credentials.h', 'src/core/security/credentials.h',
'src/core/security/json_token.h', 'src/core/security/json_token.h',
'src/core/security/jwt_verifier.h',
'src/core/security/secure_endpoint.h', 'src/core/security/secure_endpoint.h',
'src/core/security/secure_transport_setup.h', 'src/core/security/secure_transport_setup.h',
'src/core/security/security_connector.h', 'src/core/security/security_connector.h',
@ -263,6 +266,7 @@ Pod::Spec.new do |s|
'src/core/security/credentials_win32.c', 'src/core/security/credentials_win32.c',
'src/core/security/google_default_credentials.c', 'src/core/security/google_default_credentials.c',
'src/core/security/json_token.c', 'src/core/security/json_token.c',
'src/core/security/jwt_verifier.c',
'src/core/security/secure_endpoint.c', 'src/core/security/secure_endpoint.c',
'src/core/security/secure_transport_setup.c', 'src/core/security/secure_transport_setup.c',
'src/core/security/security_connector.c', 'src/core/security/security_connector.c',
@ -355,6 +359,7 @@ Pod::Spec.new do |s|
'src/core/surface/server_chttp2.c', 'src/core/surface/server_chttp2.c',
'src/core/surface/server_create.c', 'src/core/surface/server_create.c',
'src/core/surface/surface_trace.c', 'src/core/surface/surface_trace.c',
'src/core/surface/version.c',
'src/core/transport/chttp2/alpn.c', 'src/core/transport/chttp2/alpn.c',
'src/core/transport/chttp2/bin_encoder.c', 'src/core/transport/chttp2/bin_encoder.c',
'src/core/transport/chttp2/frame_data.c', 'src/core/transport/chttp2/frame_data.c',
@ -387,6 +392,7 @@ Pod::Spec.new do |s|
ss.private_header_files = 'src/core/support/env.h', ss.private_header_files = 'src/core/support/env.h',
'src/core/support/file.h', 'src/core/support/file.h',
'src/core/support/murmur_hash.h', 'src/core/support/murmur_hash.h',
'src/core/support/stack_lockfree.h',
'src/core/support/string.h', 'src/core/support/string.h',
'src/core/support/string_win32.h', 'src/core/support/string_win32.h',
'src/core/support/thd_internal.h', 'src/core/support/thd_internal.h',
@ -398,6 +404,7 @@ Pod::Spec.new do |s|
'src/core/security/base64.h', 'src/core/security/base64.h',
'src/core/security/credentials.h', 'src/core/security/credentials.h',
'src/core/security/json_token.h', 'src/core/security/json_token.h',
'src/core/security/jwt_verifier.h',
'src/core/security/secure_endpoint.h', 'src/core/security/secure_endpoint.h',
'src/core/security/secure_transport_setup.h', 'src/core/security/secure_transport_setup.h',
'src/core/security/security_connector.h', 'src/core/security/security_connector.h',

@ -0,0 +1,65 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPCXX_AUTH_CONTEXT_H
#define GRPCXX_AUTH_CONTEXT_H
#include <vector>
#include <grpc++/auth_property_iterator.h>
#include <grpc++/config.h>
namespace grpc {
class AuthContext {
public:
virtual ~AuthContext() {}
// A peer identity, in general is one or more properties (in which case they
// have the same name).
virtual std::vector<grpc::string> GetPeerIdentity() const = 0;
virtual grpc::string GetPeerIdentityPropertyName() const = 0;
// Returns all the property values with the given name.
virtual std::vector<grpc::string> FindPropertyValues(
const grpc::string& name) const = 0;
// Iteration over all the properties.
virtual AuthPropertyIterator begin() const = 0;
virtual AuthPropertyIterator end() const = 0;
};
} // namespace grpc
#endif // GRPCXX_AUTH_CONTEXT_H

@ -0,0 +1,77 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPCXX_AUTH_PROPERTY_ITERATOR_H
#define GRPCXX_AUTH_PROPERTY_ITERATOR_H
#include <iterator>
#include <vector>
#include <grpc++/config.h>
struct grpc_auth_context;
struct grpc_auth_property;
struct grpc_auth_property_iterator;
namespace grpc {
class SecureAuthContext;
typedef std::pair<grpc::string, grpc::string> AuthProperty;
class AuthPropertyIterator
: public std::iterator<std::input_iterator_tag, const AuthProperty> {
public:
~AuthPropertyIterator();
AuthPropertyIterator& operator++();
AuthPropertyIterator operator++(int);
bool operator==(const AuthPropertyIterator& rhs) const;
bool operator!=(const AuthPropertyIterator& rhs) const;
const AuthProperty operator*();
protected:
AuthPropertyIterator();
AuthPropertyIterator(const grpc_auth_property* property,
const grpc_auth_property_iterator* iter);
private:
friend class SecureAuthContext;
const grpc_auth_property* property_;
// The following items form a grpc_auth_property_iterator.
const grpc_auth_context* ctx_;
size_t index_;
const char* name_;
};
} // namespace grpc
#endif // GRPCXX_AUTH_PROPERTY_ITERATOR_H

@ -40,12 +40,14 @@
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include <grpc++/auth_context.h>
#include <grpc++/config.h> #include <grpc++/config.h>
#include <grpc++/status.h> #include <grpc++/status.h>
#include <grpc++/time.h> #include <grpc++/time.h>
struct grpc_call; struct grpc_call;
struct grpc_completion_queue; struct grpc_completion_queue;
struct census_context;
namespace grpc { namespace grpc {
@ -107,6 +109,12 @@ class ClientContext {
creds_ = creds; creds_ = creds;
} }
std::shared_ptr<const AuthContext> auth_context() const;
// Get and set census context
void set_census_context(census_context* ccp) { census_context_ = ccp; }
census_context* get_census_context() const { return census_context_; }
void TryCancel(); void TryCancel();
private: private:
@ -154,6 +162,8 @@ class ClientContext {
gpr_timespec deadline_; gpr_timespec deadline_;
grpc::string authority_; grpc::string authority_;
std::shared_ptr<Credentials> creds_; std::shared_ptr<Credentials> creds_;
mutable std::shared_ptr<const AuthContext> auth_context_;
census_context* census_context_;
std::multimap<grpc::string, grpc::string> send_initial_metadata_; std::multimap<grpc::string, grpc::string> send_initial_metadata_;
std::multimap<grpc::string, grpc::string> recv_initial_metadata_; std::multimap<grpc::string, grpc::string> recv_initial_metadata_;
std::multimap<grpc::string, grpc::string> trailing_metadata_; std::multimap<grpc::string, grpc::string> trailing_metadata_;

@ -120,6 +120,12 @@ std::shared_ptr<Credentials> JWTCredentials(const grpc::string& json_key,
std::shared_ptr<Credentials> RefreshTokenCredentials( std::shared_ptr<Credentials> RefreshTokenCredentials(
const grpc::string& json_refresh_token); const grpc::string& json_refresh_token);
// Builds access token credentials.
// access_token is an oauth2 access token that was fetched using an out of band
// mechanism.
std::shared_ptr<Credentials> AccessTokenCredentials(
const grpc::string& access_token);
// Builds IAM credentials. // Builds IAM credentials.
std::shared_ptr<Credentials> IAMCredentials( std::shared_ptr<Credentials> IAMCredentials(
const grpc::string& authorization_token, const grpc::string& authorization_token,

@ -45,10 +45,10 @@
namespace grpc { namespace grpc {
class ThreadPool GRPC_FINAL : public ThreadPoolInterface { class FixedSizeThreadPool GRPC_FINAL : public ThreadPoolInterface {
public: public:
explicit ThreadPool(int num_threads); explicit FixedSizeThreadPool(int num_threads);
~ThreadPool(); ~FixedSizeThreadPool();
void ScheduleCallback(const std::function<void()>& callback) GRPC_OVERRIDE; void ScheduleCallback(const std::function<void()>& callback) GRPC_OVERRIDE;
@ -62,8 +62,6 @@ class ThreadPool GRPC_FINAL : public ThreadPoolInterface {
void ThreadFunc(); void ThreadFunc();
}; };
ThreadPoolInterface* CreateDefaultThreadPool();
} // namespace grpc } // namespace grpc
#endif // GRPC_INTERNAL_CPP_SERVER_THREAD_POOL_H #endif // GRPC_INTERNAL_CPP_SERVER_THREAD_POOL_H

@ -60,6 +60,93 @@ void FillMetadataMap(grpc_metadata_array* arr,
grpc_metadata* FillMetadataArray( grpc_metadata* FillMetadataArray(
const std::multimap<grpc::string, grpc::string>& metadata); const std::multimap<grpc::string, grpc::string>& metadata);
/// Per-message write options.
class WriteOptions {
public:
WriteOptions() : flags_(0) {}
WriteOptions(const WriteOptions& other) : flags_(other.flags_) {}
/// Clear all flags.
inline void Clear() {
flags_ = 0;
}
/// Returns raw flags bitset.
inline gpr_uint32 flags() const {
return flags_;
}
/// Sets flag for the disabling of compression for the next message write.
///
/// \sa GRPC_WRITE_NO_COMPRESS
inline WriteOptions& set_no_compression() {
SetBit(GRPC_WRITE_NO_COMPRESS);
return *this;
}
/// Clears flag for the disabling of compression for the next message write.
///
/// \sa GRPC_WRITE_NO_COMPRESS
inline WriteOptions& clear_no_compression() {
ClearBit(GRPC_WRITE_NO_COMPRESS);
return *this;
}
/// Get value for the flag indicating whether compression for the next
/// message write is forcefully disabled.
///
/// \sa GRPC_WRITE_NO_COMPRESS
inline bool get_no_compression() const {
return GetBit(GRPC_WRITE_NO_COMPRESS);
}
/// Sets flag indicating that the write may be buffered and need not go out on
/// the wire immediately.
///
/// \sa GRPC_WRITE_BUFFER_HINT
inline WriteOptions& set_buffer_hint() {
SetBit(GRPC_WRITE_BUFFER_HINT);
return *this;
}
/// Clears flag indicating that the write may be buffered and need not go out
/// on the wire immediately.
///
/// \sa GRPC_WRITE_BUFFER_HINT
inline WriteOptions& clear_buffer_hint() {
ClearBit(GRPC_WRITE_BUFFER_HINT);
return *this;
}
/// Get value for the flag indicating that the write may be buffered and need
/// not go out on the wire immediately.
///
/// \sa GRPC_WRITE_BUFFER_HINT
inline bool get_buffer_hint() const {
return GetBit(GRPC_WRITE_BUFFER_HINT);
}
WriteOptions& operator=(const WriteOptions& rhs) {
flags_ = rhs.flags_;
return *this;
}
private:
void SetBit(const gpr_int32 mask) {
flags_ |= mask;
}
void ClearBit(const gpr_int32 mask) {
flags_ &= ~mask;
}
bool GetBit(const gpr_int32 mask) const {
return flags_ & mask;
}
gpr_uint32 flags_;
};
/// Default argument for CallOpSet. I is unused by the class, but can be /// Default argument for CallOpSet. I is unused by the class, but can be
/// used for generating multiple names for the same thing. /// used for generating multiple names for the same thing.
template <int I> template <int I>
@ -104,6 +191,12 @@ class CallOpSendMessage {
public: public:
CallOpSendMessage() : send_buf_(nullptr), own_buf_(false) {} CallOpSendMessage() : send_buf_(nullptr), own_buf_(false) {}
/// Send \a message using \a options for the write. The \a options are cleared
/// after use.
template <class M>
Status SendMessage(const M& message,
const WriteOptions& options) GRPC_MUST_USE_RESULT;
template <class M> template <class M>
Status SendMessage(const M& message) GRPC_MUST_USE_RESULT; Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
@ -112,8 +205,10 @@ class CallOpSendMessage {
if (send_buf_ == nullptr) return; if (send_buf_ == nullptr) return;
grpc_op* op = &ops[(*nops)++]; grpc_op* op = &ops[(*nops)++];
op->op = GRPC_OP_SEND_MESSAGE; op->op = GRPC_OP_SEND_MESSAGE;
op->flags = 0; op->flags = write_options_.flags();
op->data.send_message = send_buf_; op->data.send_message = send_buf_;
// Flags are per-message: clear them after use.
write_options_.Clear();
} }
void FinishOp(bool* status, int max_message_size) { void FinishOp(bool* status, int max_message_size) {
if (own_buf_) grpc_byte_buffer_destroy(send_buf_); if (own_buf_) grpc_byte_buffer_destroy(send_buf_);
@ -122,14 +217,22 @@ class CallOpSendMessage {
private: private:
grpc_byte_buffer* send_buf_; grpc_byte_buffer* send_buf_;
WriteOptions write_options_;
bool own_buf_; bool own_buf_;
}; };
template <class M> template <class M>
Status CallOpSendMessage::SendMessage(const M& message) { Status CallOpSendMessage::SendMessage(const M& message,
const WriteOptions& options) {
write_options_ = options;
return SerializationTraits<M>::Serialize(message, &send_buf_, &own_buf_); return SerializationTraits<M>::Serialize(message, &send_buf_, &own_buf_);
} }
template <class M>
Status CallOpSendMessage::SendMessage(const M& message) {
return SendMessage(message, WriteOptions());
}
template <class R> template <class R>
class CallOpRecvMessage { class CallOpRecvMessage {
public: public:
@ -172,17 +275,34 @@ class CallOpRecvMessage {
grpc_byte_buffer* recv_buf_; grpc_byte_buffer* recv_buf_;
}; };
namespace CallOpGenericRecvMessageHelper {
class DeserializeFunc {
public:
virtual Status Deserialize(grpc_byte_buffer* buf, int max_message_size) = 0;
};
template <class R>
class DeserializeFuncType GRPC_FINAL : public DeserializeFunc {
public:
DeserializeFuncType(R* message) : message_(message) {}
Status Deserialize(grpc_byte_buffer* buf,
int max_message_size) GRPC_OVERRIDE {
return SerializationTraits<R>::Deserialize(buf, message_, max_message_size);
}
private:
R* message_; // Not a managed pointer because management is external to this
};
} // namespace CallOpGenericRecvMessageHelper
class CallOpGenericRecvMessage { class CallOpGenericRecvMessage {
public: public:
CallOpGenericRecvMessage() : got_message(false) {} CallOpGenericRecvMessage() : got_message(false) {}
template <class R> template <class R>
void RecvMessage(R* message) { void RecvMessage(R* message) {
deserialize_ = [message](grpc_byte_buffer* buf, deserialize_.reset(
int max_message_size) -> Status { new CallOpGenericRecvMessageHelper::DeserializeFuncType<R>(message));
return SerializationTraits<R>::Deserialize(buf, message,
max_message_size);
};
} }
bool got_message; bool got_message;
@ -201,7 +321,7 @@ class CallOpGenericRecvMessage {
if (recv_buf_) { if (recv_buf_) {
if (*status) { if (*status) {
got_message = true; got_message = true;
*status = deserialize_(recv_buf_, max_message_size).ok(); *status = deserialize_->Deserialize(recv_buf_, max_message_size).ok();
} else { } else {
got_message = false; got_message = false;
grpc_byte_buffer_destroy(recv_buf_); grpc_byte_buffer_destroy(recv_buf_);
@ -210,12 +330,11 @@ class CallOpGenericRecvMessage {
got_message = false; got_message = false;
*status = false; *status = false;
} }
deserialize_ = DeserializeFunc(); deserialize_.reset();
} }
private: private:
typedef std::function<Status(grpc_byte_buffer*, int)> DeserializeFunc; std::unique_ptr<CallOpGenericRecvMessageHelper::DeserializeFunc> deserialize_;
DeserializeFunc deserialize_;
grpc_byte_buffer* recv_buf_; grpc_byte_buffer* recv_buf_;
}; };

@ -84,8 +84,8 @@ class Server GRPC_FINAL : public GrpcLibrary, private CallHook {
int max_message_size); int max_message_size);
// Register a service. This call does not take ownership of the service. // Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the Server instance. // The service must exist for the lifetime of the Server instance.
bool RegisterService(RpcService* service); bool RegisterService(const grpc::string *host, RpcService* service);
bool RegisterAsyncService(AsynchronousService* service); bool RegisterAsyncService(const grpc::string *host, AsynchronousService* service);
void RegisterAsyncGenericService(AsyncGenericService* service); void RegisterAsyncGenericService(AsyncGenericService* service);
// Add a listening port. Can be called multiple times. // Add a listening port. Can be called multiple times.
int AddListeningPort(const grpc::string& addr, ServerCredentials* creds); int AddListeningPort(const grpc::string& addr, ServerCredentials* creds);

@ -58,17 +58,35 @@ class ServerBuilder {
// Register a service. This call does not take ownership of the service. // Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the Server instance returned by // The service must exist for the lifetime of the Server instance returned by
// BuildAndStart(). // BuildAndStart().
// Matches requests with any :authority
void RegisterService(SynchronousService* service); void RegisterService(SynchronousService* service);
// Register an asynchronous service. New calls will be delevered to cq. // Register an asynchronous service.
// This call does not take ownership of the service or completion queue. // This call does not take ownership of the service or completion queue.
// The service and completion queuemust exist for the lifetime of the Server // The service and completion queuemust exist for the lifetime of the Server
// instance returned by BuildAndStart(). // instance returned by BuildAndStart().
// Matches requests with any :authority
void RegisterAsyncService(AsynchronousService* service); void RegisterAsyncService(AsynchronousService* service);
// Register a generic service. // Register a generic service.
// Matches requests with any :authority
void RegisterAsyncGenericService(AsyncGenericService* service); void RegisterAsyncGenericService(AsyncGenericService* service);
// Register a service. This call does not take ownership of the service.
// The service must exist for the lifetime of the Server instance returned by
// BuildAndStart().
// Only matches requests with :authority \a host
void RegisterService(const grpc::string& host,
SynchronousService* service);
// Register an asynchronous service.
// This call does not take ownership of the service or completion queue.
// The service and completion queuemust exist for the lifetime of the Server
// instance returned by BuildAndStart().
// Only matches requests with :authority \a host
void RegisterAsyncService(const grpc::string& host,
AsynchronousService* service);
// Set max message size in bytes. // Set max message size in bytes.
void SetMaxMessageSize(int max_message_size) { void SetMaxMessageSize(int max_message_size) {
max_message_size_ = max_message_size; max_message_size_ = max_message_size;
@ -98,9 +116,18 @@ class ServerBuilder {
int* selected_port; int* selected_port;
}; };
typedef std::unique_ptr<grpc::string> HostString;
template <class T> struct NamedService {
explicit NamedService(T* s) : service(s) {}
NamedService(const grpc::string& h, T *s)
: host(new grpc::string(h)), service(s) {}
HostString host;
T* service;
};
int max_message_size_; int max_message_size_;
std::vector<RpcService*> services_; std::vector<std::unique_ptr<NamedService<RpcService>>> services_;
std::vector<AsynchronousService*> async_services_; std::vector<std::unique_ptr<NamedService<AsynchronousService>>> async_services_;
std::vector<Port> ports_; std::vector<Port> ports_;
std::vector<ServerCompletionQueue*> cqs_; std::vector<ServerCompletionQueue*> cqs_;
std::shared_ptr<ServerCredentials> creds_; std::shared_ptr<ServerCredentials> creds_;

@ -35,8 +35,10 @@
#define GRPCXX_SERVER_CONTEXT_H #define GRPCXX_SERVER_CONTEXT_H
#include <map> #include <map>
#include <memory>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include <grpc++/auth_context.h>
#include <grpc++/config.h> #include <grpc++/config.h>
#include <grpc++/time.h> #include <grpc++/time.h>
@ -74,6 +76,10 @@ class CallOpBuffer;
class CompletionQueue; class CompletionQueue;
class Server; class Server;
namespace testing {
class InteropContextInspector;
} // namespace testing
// Interface of server side rpc context. // Interface of server side rpc context.
class ServerContext { class ServerContext {
public: public:
@ -91,13 +97,16 @@ class ServerContext {
void AddInitialMetadata(const grpc::string& key, const grpc::string& value); void AddInitialMetadata(const grpc::string& key, const grpc::string& value);
void AddTrailingMetadata(const grpc::string& key, const grpc::string& value); void AddTrailingMetadata(const grpc::string& key, const grpc::string& value);
bool IsCancelled(); bool IsCancelled() const;
const std::multimap<grpc::string, grpc::string>& client_metadata() { const std::multimap<grpc::string, grpc::string>& client_metadata() {
return client_metadata_; return client_metadata_;
} }
std::shared_ptr<const AuthContext> auth_context() const;
private: private:
friend class ::grpc::testing::InteropContextInspector;
friend class ::grpc::Server; friend class ::grpc::Server;
template <class W, class R> template <class W, class R>
friend class ::grpc::ServerAsyncReader; friend class ::grpc::ServerAsyncReader;
@ -133,12 +142,15 @@ class ServerContext {
ServerContext(gpr_timespec deadline, grpc_metadata* metadata, ServerContext(gpr_timespec deadline, grpc_metadata* metadata,
size_t metadata_count); size_t metadata_count);
void set_call(grpc_call* call);
CompletionOp* completion_op_; CompletionOp* completion_op_;
gpr_timespec deadline_; gpr_timespec deadline_;
grpc_call* call_; grpc_call* call_;
CompletionQueue* cq_; CompletionQueue* cq_;
bool sent_initial_metadata_; bool sent_initial_metadata_;
mutable std::shared_ptr<const AuthContext> auth_context_;
std::multimap<grpc::string, grpc::string> client_metadata_; std::multimap<grpc::string, grpc::string> client_metadata_;
std::multimap<grpc::string, grpc::string> initial_metadata_; std::multimap<grpc::string, grpc::string> initial_metadata_;
std::multimap<grpc::string, grpc::string> trailing_metadata_; std::multimap<grpc::string, grpc::string> trailing_metadata_;

@ -79,7 +79,11 @@ class WriterInterface {
// Blocking write msg to the stream. Returns true on success. // Blocking write msg to the stream. Returns true on success.
// Returns false when the stream has been closed. // Returns false when the stream has been closed.
virtual bool Write(const W& msg) = 0; virtual bool Write(const W& msg, const WriteOptions& options) = 0;
inline bool Write(const W& msg) {
return Write(msg, WriteOptions());
}
}; };
template <class R> template <class R>
@ -168,9 +172,10 @@ class ClientWriter : public ClientWriterInterface<W> {
cq_.Pluck(&ops); cq_.Pluck(&ops);
} }
bool Write(const W& msg) GRPC_OVERRIDE { using WriterInterface<W>::Write;
bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendMessage> ops; CallOpSet<CallOpSendMessage> ops;
if (!ops.SendMessage(msg).ok()) { if (!ops.SendMessage(msg, options).ok()) {
return false; return false;
} }
call_.PerformOps(&ops); call_.PerformOps(&ops);
@ -246,9 +251,10 @@ class ClientReaderWriter GRPC_FINAL : public ClientReaderWriterInterface<W, R> {
return cq_.Pluck(&ops) && ops.got_message; return cq_.Pluck(&ops) && ops.got_message;
} }
bool Write(const W& msg) GRPC_OVERRIDE { using WriterInterface<W>::Write;
bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendMessage> ops; CallOpSet<CallOpSendMessage> ops;
if (!ops.SendMessage(msg).ok()) return false; if (!ops.SendMessage(msg, options).ok()) return false;
call_.PerformOps(&ops); call_.PerformOps(&ops);
return cq_.Pluck(&ops); return cq_.Pluck(&ops);
} }
@ -317,9 +323,10 @@ class ServerWriter GRPC_FINAL : public WriterInterface<W> {
call_->cq()->Pluck(&ops); call_->cq()->Pluck(&ops);
} }
bool Write(const W& msg) GRPC_OVERRIDE { using WriterInterface<W>::Write;
bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops;
if (!ops.SendMessage(msg).ok()) { if (!ops.SendMessage(msg, options).ok()) {
return false; return false;
} }
if (!ctx_->sent_initial_metadata_) { if (!ctx_->sent_initial_metadata_) {
@ -359,9 +366,10 @@ class ServerReaderWriter GRPC_FINAL : public WriterInterface<W>,
return call_->cq()->Pluck(&ops) && ops.got_message; return call_->cq()->Pluck(&ops) && ops.got_message;
} }
bool Write(const W& msg) GRPC_OVERRIDE { using WriterInterface<W>::Write;
bool Write(const W& msg, const WriteOptions& options) GRPC_OVERRIDE {
CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops; CallOpSet<CallOpSendInitialMetadata, CallOpSendMessage> ops;
if (!ops.SendMessage(msg).ok()) { if (!ops.SendMessage(msg, options).ok()) {
return false; return false;
} }
if (!ctx_->sent_initial_metadata_) { if (!ctx_->sent_initial_metadata_) {

@ -47,6 +47,8 @@ class ThreadPoolInterface {
virtual void ScheduleCallback(const std::function<void()>& callback) = 0; virtual void ScheduleCallback(const std::function<void()>& callback) = 0;
}; };
ThreadPoolInterface* CreateDefaultThreadPool();
} // namespace grpc } // namespace grpc
#endif // GRPCXX_THREAD_POOL_INTERFACE_H #endif // GRPCXX_THREAD_POOL_INTERFACE_H

@ -61,6 +61,10 @@ enum census_functions {
int census_initialize(int functions); int census_initialize(int functions);
void census_shutdown(); void census_shutdown();
/* If any census feature has been initialized, this funtion will return a
* non-zero value. */
int census_available();
/* Internally, Census relies on a context, which should be propagated across /* Internally, Census relies on a context, which should be propagated across
* RPC's. From the RPC subsystems viewpoint, this is an opaque data structure. * RPC's. From the RPC subsystems viewpoint, this is an opaque data structure.
* A context must be used as the first argument to all other census * A context must be used as the first argument to all other census

@ -357,6 +357,9 @@ void grpc_init(void);
destroyed. */ destroyed. */
void grpc_shutdown(void); void grpc_shutdown(void);
/** Return a string representing the current version of grpc */
const char *grpc_version_string(void);
/** Create a completion queue */ /** Create a completion queue */
grpc_completion_queue *grpc_completion_queue_create(void); grpc_completion_queue *grpc_completion_queue_create(void);

@ -51,6 +51,11 @@ typedef struct grpc_credentials grpc_credentials;
The creator of the credentials object is responsible for its release. */ The creator of the credentials object is responsible for its release. */
void grpc_credentials_release(grpc_credentials *creds); void grpc_credentials_release(grpc_credentials *creds);
/* Environment variable that points to the google default application
credentials json key or refresh token. Used in the
grpc_google_default_credentials_create function. */
#define GRPC_GOOGLE_CREDENTIALS_ENV_VAR "GOOGLE_APPLICATION_CREDENTIALS"
/* Creates default credentials to connect to a google gRPC service. /* Creates default credentials to connect to a google gRPC service.
WARNING: Do NOT use this credentials to connect to a non-google service as WARNING: Do NOT use this credentials to connect to a non-google service as
this could result in an oauth2 token leak. */ this could result in an oauth2 token leak. */
@ -126,13 +131,18 @@ grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
grpc_credentials *grpc_refresh_token_credentials_create( grpc_credentials *grpc_refresh_token_credentials_create(
const char *json_refresh_token); const char *json_refresh_token);
/* Creates a fake transport security credentials object for testing. */ /* Creates an Oauth2 Access Token credentials with an access token that was
grpc_credentials *grpc_fake_transport_security_credentials_create(void); aquired by an out of band mechanism. */
grpc_credentials *grpc_access_token_credentials_create(
const char *access_token);
/* Creates an IAM credentials object. */ /* Creates an IAM credentials object. */
grpc_credentials *grpc_iam_credentials_create(const char *authorization_token, grpc_credentials *grpc_iam_credentials_create(const char *authorization_token,
const char *authority_selector); const char *authority_selector);
/* Creates a fake transport security credentials object for testing. */
grpc_credentials *grpc_fake_transport_security_credentials_create(void);
/* --- Secure channel creation. --- */ /* --- Secure channel creation. --- */
/* The caller of the secure_channel_create functions may override the target /* The caller of the secure_channel_create functions may override the target
@ -243,8 +253,12 @@ const char *grpc_auth_context_peer_identity_property_name(
/* Returns 1 if the peer is authenticated, 0 otherwise. */ /* Returns 1 if the peer is authenticated, 0 otherwise. */
int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx); int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx);
/* Gets the auth context from the call. */ /* Gets the auth context from the call. Caller needs to call
const grpc_auth_context *grpc_call_auth_context(grpc_call *call); grpc_auth_context_release on the returned context. */
grpc_auth_context *grpc_call_auth_context(grpc_call *call);
/* Releases the auth context returned from grpc_call_auth_context. */
void grpc_auth_context_release(grpc_auth_context *context);
#ifdef __cplusplus #ifdef __cplusplus
} }

@ -82,6 +82,7 @@
#define GPR_WIN32_ATOMIC 1 #define GPR_WIN32_ATOMIC 1
#define GPR_MSVC_TLS 1 #define GPR_MSVC_TLS 1
#endif #endif
#define GPR_WINDOWS_CRASH_HANDLER 1
#elif defined(_WIN32) || defined(WIN32) #elif defined(_WIN32) || defined(WIN32)
#define GPR_ARCH_32 1 #define GPR_ARCH_32 1
#define GPR_WIN32 1 #define GPR_WIN32 1
@ -94,6 +95,7 @@
#define GPR_WIN32_ATOMIC 1 #define GPR_WIN32_ATOMIC 1
#define GPR_MSVC_TLS 1 #define GPR_MSVC_TLS 1
#endif #endif
#define GPR_WINDOWS_CRASH_HANDLER 1
#elif defined(ANDROID) || defined(__ANDROID__) #elif defined(ANDROID) || defined(__ANDROID__)
#define GPR_ANDROID 1 #define GPR_ANDROID 1
#define GPR_ARCH_32 1 #define GPR_ARCH_32 1

@ -46,8 +46,8 @@ extern "C" {
#endif #endif
typedef struct gpr_timespec { typedef struct gpr_timespec {
time_t tv_sec; time_t tv_sec;
int tv_nsec; int tv_nsec;
} gpr_timespec; } gpr_timespec;
/* Time constants. */ /* Time constants. */
@ -62,8 +62,20 @@ extern const gpr_timespec gpr_inf_past; /* The far past. */
#define GPR_NS_PER_US 1000 #define GPR_NS_PER_US 1000
#define GPR_US_PER_MS 1000 #define GPR_US_PER_MS 1000
/* Return the current time measured from the system's default epoch. */ /* The clocks we support. */
gpr_timespec gpr_now(void); typedef enum {
/* Monotonic clock. Epoch undefined. Always moves forwards. */
GPR_CLOCK_MONOTONIC = 0,
/* Realtime clock. May jump forwards or backwards. Settable by
the system administrator. Has its epoch at 0:00:00 UTC 1 Jan 1970. */
GPR_CLOCK_REALTIME
} gpr_clock_type;
/* initialize time subsystem */
void gpr_time_init(void);
/* Return the current time measured from the given clocks epoch. */
gpr_timespec gpr_now(gpr_clock_type clock);
/* Return -ve, 0, or +ve according to whether a < b, a == b, or a > b /* Return -ve, 0, or +ve according to whether a < b, a == b, or a > b
respectively. */ respectively. */
@ -100,4 +112,4 @@ double gpr_timespec_to_micros(gpr_timespec t);
} }
#endif #endif
#endif /* GRPC_SUPPORT_TIME_H */ #endif /* GRPC_SUPPORT_TIME_H */

@ -52,4 +52,24 @@
b = x; \ b = x; \
} while (0) } while (0)
/** Set the \a n-th bit of \a i (a mutable pointer). */
#define GPR_BITSET(i, n) ((*(i)) |= (1u << (n)))
/** Clear the \a n-th bit of \a i (a mutable pointer). */
#define GPR_BITCLEAR(i, n) ((*(i)) &= ~(1u << (n)))
/** Get the \a n-th bit of \a i */
#define GPR_BITGET(i, n) (((i) & (1u << (n))) != 0)
#define GPR_INTERNAL_HEXDIGIT_BITCOUNT(x) \
((x) - (((x) >> 1) & 0x77777777) - (((x) >> 2) & 0x33333333) - \
(((x) >> 3) & 0x11111111))
/** Returns number of bits set in bitset \a i */
#define GPR_BITCOUNT(i) \
(((GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) + \
(GPR_INTERNAL_HEXDIGIT_BITCOUNT(i) >> 4)) & \
0x0f0f0f0f) % \
255)
#endif /* GRPC_SUPPORT_USEFUL_H */ #endif /* GRPC_SUPPORT_USEFUL_H */

@ -257,7 +257,7 @@ void GenerateStaticMethodField(Printer* out, const MethodDescriptor *method) {
} }
void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) { void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
out->Print("// client-side stub interface\n"); out->Print("// client interface\n");
out->Print("public interface $name$\n", "name", out->Print("public interface $name$\n", "name",
GetClientInterfaceName(service)); GetClientInterfaceName(service));
out->Print("{\n"); out->Print("{\n");
@ -269,7 +269,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
if (method_type == METHODTYPE_NO_STREAMING) { if (method_type == METHODTYPE_NO_STREAMING) {
// unary calls have an extra synchronous stub method // unary calls have an extra synchronous stub method
out->Print( out->Print(
"$response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken));\n", "$response$ $methodname$($request$ request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken));\n",
"methodname", method->name(), "request", "methodname", method->name(), "request",
GetClassName(method->input_type()), "response", GetClassName(method->input_type()), "response",
GetClassName(method->output_type())); GetClassName(method->output_type()));
@ -280,7 +280,7 @@ void GenerateClientInterface(Printer* out, const ServiceDescriptor *service) {
method_name += "Async"; // prevent name clash with synchronous method. method_name += "Async"; // prevent name clash with synchronous method.
} }
out->Print( out->Print(
"$returntype$ $methodname$($request_maybe$CancellationToken token = default(CancellationToken));\n", "$returntype$ $methodname$($request_maybe$Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken));\n",
"methodname", method_name, "request_maybe", "methodname", method_name, "request_maybe",
GetMethodRequestParamMaybe(method), "returntype", GetMethodRequestParamMaybe(method), "returntype",
GetMethodReturnTypeClient(method)); GetMethodReturnTypeClient(method));
@ -312,7 +312,7 @@ void GenerateServerInterface(Printer* out, const ServiceDescriptor *service) {
void GenerateClientStub(Printer* out, const ServiceDescriptor *service) { void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
out->Print("// client stub\n"); out->Print("// client stub\n");
out->Print( out->Print(
"public class $name$ : AbstractStub<$name$, StubConfiguration>, $interface$\n", "public class $name$ : ClientBase, $interface$\n",
"name", GetClientClassName(service), "interface", "name", GetClientClassName(service), "interface",
GetClientInterfaceName(service)); GetClientInterfaceName(service));
out->Print("{\n"); out->Print("{\n");
@ -320,12 +320,7 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
// constructors // constructors
out->Print( out->Print(
"public $name$(Channel channel) : this(channel, StubConfiguration.Default)\n", "public $name$(Channel channel) : base(channel)\n",
"name", GetClientClassName(service));
out->Print("{\n");
out->Print("}\n");
out->Print(
"public $name$(Channel channel, StubConfiguration config) : base(channel, config)\n",
"name", GetClientClassName(service)); "name", GetClientClassName(service));
out->Print("{\n"); out->Print("{\n");
out->Print("}\n"); out->Print("}\n");
@ -337,16 +332,16 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
if (method_type == METHODTYPE_NO_STREAMING) { if (method_type == METHODTYPE_NO_STREAMING) {
// unary calls have an extra synchronous stub method // unary calls have an extra synchronous stub method
out->Print( out->Print(
"public $response$ $methodname$($request$ request, CancellationToken token = default(CancellationToken))\n", "public $response$ $methodname$($request$ request, Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken))\n",
"methodname", method->name(), "request", "methodname", method->name(), "request",
GetClassName(method->input_type()), "response", GetClassName(method->input_type()), "response",
GetClassName(method->output_type())); GetClassName(method->output_type()));
out->Print("{\n"); out->Print("{\n");
out->Indent(); out->Indent();
out->Print("var call = CreateCall($servicenamefield$, $methodfield$);\n", out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers);\n",
"servicenamefield", GetServiceNameFieldName(), "methodfield", "servicenamefield", GetServiceNameFieldName(), "methodfield",
GetMethodFieldName(method)); GetMethodFieldName(method));
out->Print("return Calls.BlockingUnaryCall(call, request, token);\n"); out->Print("return Calls.BlockingUnaryCall(call, request, cancellationToken);\n");
out->Outdent(); out->Outdent();
out->Print("}\n"); out->Print("}\n");
} }
@ -356,28 +351,28 @@ void GenerateClientStub(Printer* out, const ServiceDescriptor *service) {
method_name += "Async"; // prevent name clash with synchronous method. method_name += "Async"; // prevent name clash with synchronous method.
} }
out->Print( out->Print(
"public $returntype$ $methodname$($request_maybe$CancellationToken token = default(CancellationToken))\n", "public $returntype$ $methodname$($request_maybe$Metadata headers = null, CancellationToken cancellationToken = default(CancellationToken))\n",
"methodname", method_name, "request_maybe", "methodname", method_name, "request_maybe",
GetMethodRequestParamMaybe(method), "returntype", GetMethodRequestParamMaybe(method), "returntype",
GetMethodReturnTypeClient(method)); GetMethodReturnTypeClient(method));
out->Print("{\n"); out->Print("{\n");
out->Indent(); out->Indent();
out->Print("var call = CreateCall($servicenamefield$, $methodfield$);\n", out->Print("var call = CreateCall($servicenamefield$, $methodfield$, headers);\n",
"servicenamefield", GetServiceNameFieldName(), "methodfield", "servicenamefield", GetServiceNameFieldName(), "methodfield",
GetMethodFieldName(method)); GetMethodFieldName(method));
switch (GetMethodType(method)) { switch (GetMethodType(method)) {
case METHODTYPE_NO_STREAMING: case METHODTYPE_NO_STREAMING:
out->Print("return Calls.AsyncUnaryCall(call, request, token);\n"); out->Print("return Calls.AsyncUnaryCall(call, request, cancellationToken);\n");
break; break;
case METHODTYPE_CLIENT_STREAMING: case METHODTYPE_CLIENT_STREAMING:
out->Print("return Calls.AsyncClientStreamingCall(call, token);\n"); out->Print("return Calls.AsyncClientStreamingCall(call, cancellationToken);\n");
break; break;
case METHODTYPE_SERVER_STREAMING: case METHODTYPE_SERVER_STREAMING:
out->Print( out->Print(
"return Calls.AsyncServerStreamingCall(call, request, token);\n"); "return Calls.AsyncServerStreamingCall(call, request, cancellationToken);\n");
break; break;
case METHODTYPE_BIDI_STREAMING: case METHODTYPE_BIDI_STREAMING:
out->Print("return Calls.AsyncDuplexStreamingCall(call, token);\n"); out->Print("return Calls.AsyncDuplexStreamingCall(call, cancellationToken);\n");
break; break;
default: default:
GOOGLE_LOG(FATAL)<< "Can't get here."; GOOGLE_LOG(FATAL)<< "Can't get here.";
@ -423,9 +418,9 @@ void GenerateBindServiceMethod(Printer* out, const ServiceDescriptor *service) {
} }
void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) { void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) {
out->Print("// creates a new client stub\n"); out->Print("// creates a new client\n");
out->Print("public static $interface$ NewStub(Channel channel)\n", out->Print("public static $classname$ NewClient(Channel channel)\n",
"interface", GetClientInterfaceName(service)); "classname", GetClientClassName(service));
out->Print("{\n"); out->Print("{\n");
out->Indent(); out->Indent();
out->Print("return new $classname$(channel);\n", "classname", out->Print("return new $classname$(channel);\n", "classname",
@ -433,17 +428,6 @@ void GenerateNewStubMethods(Printer* out, const ServiceDescriptor *service) {
out->Outdent(); out->Outdent();
out->Print("}\n"); out->Print("}\n");
out->Print("\n"); out->Print("\n");
out->Print("// creates a new client stub\n");
out->Print(
"public static $interface$ NewStub(Channel channel, StubConfiguration config)\n",
"interface", GetClientInterfaceName(service));
out->Print("{\n");
out->Indent();
out->Print("return new $classname$(channel, config);\n", "classname",
GetClientClassName(service));
out->Outdent();
out->Print("}\n");
} }
void GenerateService(Printer* out, const ServiceDescriptor *service) { void GenerateService(Printer* out, const ServiceDescriptor *service) {

@ -57,34 +57,34 @@ void PrintProtoRpcDeclarationAsPragma(Printer *printer,
vars["server_stream"] = method->server_streaming() ? "stream " : ""; vars["server_stream"] = method->server_streaming() ? "stream " : "";
printer->Print(vars, printer->Print(vars,
"#pragma mark $method_name$($client_stream$$request_type$)" "#pragma mark $method_name$($client_stream$$request_type$)"
" returns ($server_stream$$response_type$)\n\n"); " returns ($server_stream$$response_type$)\n\n");
} }
void PrintMethodSignature(Printer *printer, void PrintMethodSignature(Printer *printer, const MethodDescriptor *method,
const MethodDescriptor *method, const map<string, string> &vars) {
const map<string, string>& vars) {
// TODO(jcanizales): Print method comments. // TODO(jcanizales): Print method comments.
printer->Print(vars, "- ($return_type$)$method_name$With"); printer->Print(vars, "- ($return_type$)$method_name$With");
if (method->client_streaming()) { if (method->client_streaming()) {
printer->Print("RequestsWriter:(id<GRXWriter>)requestWriter"); printer->Print("RequestsWriter:(GRXWriter *)requestWriter");
} else { } else {
printer->Print(vars, "Request:($request_class$ *)request"); printer->Print(vars, "Request:($request_class$ *)request");
} }
// TODO(jcanizales): Put this on a new line and align colons. // TODO(jcanizales): Put this on a new line and align colons.
if (method->server_streaming()) { if (method->server_streaming()) {
printer->Print(vars, " eventHandler:(void(^)(BOOL done, " printer->Print(vars,
"$response_class$ *response, NSError *error))eventHandler"); " eventHandler:(void(^)(BOOL done, "
"$response_class$ *response, NSError *error))eventHandler");
} else { } else {
printer->Print(vars, " handler:(void(^)($response_class$ *response, " printer->Print(vars,
"NSError *error))handler"); " handler:(void(^)($response_class$ *response, "
"NSError *error))handler");
} }
} }
void PrintSimpleSignature(Printer *printer, void PrintSimpleSignature(Printer *printer, const MethodDescriptor *method,
const MethodDescriptor *method,
map<string, string> vars) { map<string, string> vars) {
vars["method_name"] = vars["method_name"] =
grpc_generator::LowercaseFirstLetter(vars["method_name"]); grpc_generator::LowercaseFirstLetter(vars["method_name"]);
@ -92,8 +92,7 @@ void PrintSimpleSignature(Printer *printer,
PrintMethodSignature(printer, method, vars); PrintMethodSignature(printer, method, vars);
} }
void PrintAdvancedSignature(Printer *printer, void PrintAdvancedSignature(Printer *printer, const MethodDescriptor *method,
const MethodDescriptor *method,
map<string, string> vars) { map<string, string> vars) {
vars["method_name"] = "RPCTo" + vars["method_name"]; vars["method_name"] = "RPCTo" + vars["method_name"];
vars["return_type"] = "ProtoRPC *"; vars["return_type"] = "ProtoRPC *";
@ -101,15 +100,16 @@ void PrintAdvancedSignature(Printer *printer,
} }
inline map<string, string> GetMethodVars(const MethodDescriptor *method) { inline map<string, string> GetMethodVars(const MethodDescriptor *method) {
return {{ "method_name", method->name() }, map<string, string> res;
{ "request_type", method->input_type()->name() }, res["method_name"] = method->name();
{ "response_type", method->output_type()->name() }, res["request_type"] = method->input_type()->name();
{ "request_class", ClassName(method->input_type()) }, res["response_type"] = method->output_type()->name();
{ "response_class", ClassName(method->output_type()) }}; res["request_class"] = ClassName(method->input_type());
res["response_class"] = ClassName(method->output_type());
return res;
} }
void PrintMethodDeclarations(Printer *printer, void PrintMethodDeclarations(Printer *printer, const MethodDescriptor *method) {
const MethodDescriptor *method) {
map<string, string> vars = GetMethodVars(method); map<string, string> vars = GetMethodVars(method);
PrintProtoRpcDeclarationAsPragma(printer, method, vars); PrintProtoRpcDeclarationAsPragma(printer, method, vars);
@ -120,8 +120,7 @@ void PrintMethodDeclarations(Printer *printer,
printer->Print(";\n\n\n"); printer->Print(";\n\n\n");
} }
void PrintSimpleImplementation(Printer *printer, void PrintSimpleImplementation(Printer *printer, const MethodDescriptor *method,
const MethodDescriptor *method,
map<string, string> vars) { map<string, string> vars) {
printer->Print("{\n"); printer->Print("{\n");
printer->Print(vars, " [[self RPCTo$method_name$With"); printer->Print(vars, " [[self RPCTo$method_name$With");
@ -178,7 +177,7 @@ void PrintMethodImplementations(Printer *printer,
PrintAdvancedImplementation(printer, method, vars); PrintAdvancedImplementation(printer, method, vars);
} }
} // namespace } // namespace
string GetHeader(const ServiceDescriptor *service) { string GetHeader(const ServiceDescriptor *service) {
string output; string output;
@ -199,12 +198,15 @@ string GetHeader(const ServiceDescriptor *service) {
} }
printer.Print("@end\n\n"); printer.Print("@end\n\n");
printer.Print("// Basic service implementation, over gRPC, that only does" printer.Print(
"// Basic service implementation, over gRPC, that only does"
" marshalling and parsing.\n"); " marshalling and parsing.\n");
printer.Print(vars, "@interface $service_class$ :" printer.Print(vars,
" ProtoService<$service_class$>\n"); "@interface $service_class$ :"
printer.Print("- (instancetype)initWithHost:(NSString *)host" " ProtoService<$service_class$>\n");
" NS_DESIGNATED_INITIALIZER;\n"); printer.Print(
"- (instancetype)initWithHost:(NSString *)host"
" NS_DESIGNATED_INITIALIZER;\n");
printer.Print("@end\n"); printer.Print("@end\n");
} }
return output; return output;
@ -222,18 +224,20 @@ string GetSource(const ServiceDescriptor *service) {
{"package", service->file()->package()}}; {"package", service->file()->package()}};
printer.Print(vars, printer.Print(vars,
"static NSString *const kPackageName = @\"$package$\";\n"); "static NSString *const kPackageName = @\"$package$\";\n");
printer.Print(vars, printer.Print(
"static NSString *const kServiceName = @\"$service_name$\";\n\n"); vars, "static NSString *const kServiceName = @\"$service_name$\";\n\n");
printer.Print(vars, "@implementation $service_class$\n\n"); printer.Print(vars, "@implementation $service_class$\n\n");
printer.Print("// Designated initializer\n"); printer.Print("// Designated initializer\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host {\n"); printer.Print("- (instancetype)initWithHost:(NSString *)host {\n");
printer.Print(" return (self = [super initWithHost:host" printer.Print(
" return (self = [super initWithHost:host"
" packageName:kPackageName serviceName:kServiceName]);\n"); " packageName:kPackageName serviceName:kServiceName]);\n");
printer.Print("}\n\n"); printer.Print("}\n\n");
printer.Print("// Override superclass initializer to disallow different" printer.Print(
"// Override superclass initializer to disallow different"
" package and service names.\n"); " package and service names.\n");
printer.Print("- (instancetype)initWithHost:(NSString *)host\n"); printer.Print("- (instancetype)initWithHost:(NSString *)host\n");
printer.Print(" packageName:(NSString *)packageName\n"); printer.Print(" packageName:(NSString *)packageName\n");
@ -250,4 +254,4 @@ string GetSource(const ServiceDescriptor *service) {
return output; return output;
} }
} // namespace grpc_objective_c_generator } // namespace grpc_objective_c_generator

@ -34,12 +34,28 @@
#include <grpc/census.h> #include <grpc/census.h>
#include "src/core/census/grpc_context.h" #include "src/core/census/grpc_context.h"
void *grpc_census_context_create() { static void grpc_census_context_destroy(void *context) {
census_context *context; census_context_destroy((census_context *)context);
census_context_deserialize(NULL, &context);
return (void *)context;
} }
void grpc_census_context_destroy(void *context) { void grpc_census_call_set_context(grpc_call *call, census_context *context) {
census_context_destroy((census_context *)context); if (!census_available()) {
return;
}
if (context == NULL) {
if (grpc_call_is_client(call)) {
census_context *context_ptr;
census_context_deserialize(NULL, &context_ptr);
grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context_ptr,
grpc_census_context_destroy);
} else {
/* TODO(aveitch): server side context code to be implemented. */
}
} else {
grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, NULL);
}
}
census_context *grpc_census_call_get_context(grpc_call *call) {
return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING);
} }

@ -36,7 +36,22 @@
#ifndef CENSUS_GRPC_CONTEXT_H #ifndef CENSUS_GRPC_CONTEXT_H
#define CENSUS_GRPC_CONTEXT_H #define CENSUS_GRPC_CONTEXT_H
void *grpc_census_context_create(); #include <grpc/census.h>
void grpc_census_context_destroy(void *context); #include "src/core/surface/call.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Set census context for the call; Must be called before first call to
grpc_call_start_batch(). */
void grpc_census_call_set_context(grpc_call *call, census_context *context);
/* Retrieve the calls current census context. */
census_context *grpc_census_call_get_context(grpc_call *call);
#ifdef __cplusplus
}
#endif
#endif /* CENSUS_GRPC_CONTEXT_H */ #endif /* CENSUS_GRPC_CONTEXT_H */

@ -48,3 +48,5 @@ int census_initialize(int functions) {
} }
void census_shutdown() { census_fns_enabled = CENSUS_NONE; } void census_shutdown() { census_fns_enabled = CENSUS_NONE; }
int census_available() { return (census_fns_enabled != CENSUS_NONE); }

@ -151,7 +151,7 @@ static void client_init_call_elem(grpc_call_element* elem,
call_data* d = elem->call_data; call_data* d = elem->call_data;
GPR_ASSERT(d != NULL); GPR_ASSERT(d != NULL);
init_rpc_stats(&d->stats); init_rpc_stats(&d->stats);
d->start_ts = gpr_now(); d->start_ts = gpr_now(GPR_CLOCK_REALTIME);
d->op_id = census_tracing_start_op(); d->op_id = census_tracing_start_op();
if (initial_op) client_mutate_op(elem, initial_op); if (initial_op) client_mutate_op(elem, initial_op);
} }
@ -169,7 +169,7 @@ static void server_init_call_elem(grpc_call_element* elem,
call_data* d = elem->call_data; call_data* d = elem->call_data;
GPR_ASSERT(d != NULL); GPR_ASSERT(d != NULL);
init_rpc_stats(&d->stats); init_rpc_stats(&d->stats);
d->start_ts = gpr_now(); d->start_ts = gpr_now(GPR_CLOCK_REALTIME);
d->op_id = census_tracing_start_op(); d->op_id = census_tracing_start_op();
if (initial_op) server_mutate_op(elem, initial_op); if (initial_op) server_mutate_op(elem, initial_op);
} }
@ -177,8 +177,8 @@ static void server_init_call_elem(grpc_call_element* elem,
static void server_destroy_call_elem(grpc_call_element* elem) { static void server_destroy_call_elem(grpc_call_element* elem) {
call_data* d = elem->call_data; call_data* d = elem->call_data;
GPR_ASSERT(d != NULL); GPR_ASSERT(d != NULL);
d->stats.elapsed_time_ms = d->stats.elapsed_time_ms = gpr_timespec_to_micros(
gpr_timespec_to_micros(gpr_time_sub(gpr_now(), d->start_ts)); gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), d->start_ts));
census_record_rpc_server_stats(d->op_id, &d->stats); census_record_rpc_server_stats(d->op_id, &d->stats);
census_tracing_end_op(d->op_id); census_tracing_end_op(d->op_id);
} }
@ -197,7 +197,7 @@ static void destroy_channel_elem(grpc_channel_element* elem) {
channel_data* chand = elem->channel_data; channel_data* chand = elem->channel_data;
GPR_ASSERT(chand != NULL); GPR_ASSERT(chand != NULL);
if (chand->path_str != NULL) { if (chand->path_str != NULL) {
grpc_mdstr_unref(chand->path_str); GRPC_MDSTR_UNREF(chand->path_str);
} }
} }

@ -427,7 +427,7 @@ static void cc_on_config_changed(void *arg, int iomgr_success) {
GRPC_RESOLVER_REF(resolver, "channel-next"); GRPC_RESOLVER_REF(resolver, "channel-next");
gpr_mu_unlock(&chand->mu_config); gpr_mu_unlock(&chand->mu_config);
GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver"); GRPC_CHANNEL_INTERNAL_REF(chand->master, "resolver");
grpc_resolver_next(chand->resolver, &chand->incoming_configuration, grpc_resolver_next(resolver, &chand->incoming_configuration,
&chand->on_config_changed); &chand->on_config_changed);
GRPC_RESOLVER_UNREF(resolver, "channel-next"); GRPC_RESOLVER_UNREF(resolver, "channel-next");
} else { } else {

@ -108,13 +108,13 @@ static void hc_mutate_op(grpc_call_element *elem,
/* Send : prefixed headers, which have to be before any application /* Send : prefixed headers, which have to be before any application
layer headers. */ layer headers. */
grpc_metadata_batch_add_head(&op->data.metadata, &calld->method, grpc_metadata_batch_add_head(&op->data.metadata, &calld->method,
grpc_mdelem_ref(channeld->method)); GRPC_MDELEM_REF(channeld->method));
grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme, grpc_metadata_batch_add_head(&op->data.metadata, &calld->scheme,
grpc_mdelem_ref(channeld->scheme)); GRPC_MDELEM_REF(channeld->scheme));
grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers, grpc_metadata_batch_add_tail(&op->data.metadata, &calld->te_trailers,
grpc_mdelem_ref(channeld->te_trailers)); GRPC_MDELEM_REF(channeld->te_trailers));
grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type, grpc_metadata_batch_add_tail(&op->data.metadata, &calld->content_type,
grpc_mdelem_ref(channeld->content_type)); GRPC_MDELEM_REF(channeld->content_type));
break; break;
} }
} }
@ -196,11 +196,11 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
/* grab pointers to our data from the channel element */ /* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data; channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers); GRPC_MDELEM_UNREF(channeld->te_trailers);
grpc_mdelem_unref(channeld->method); GRPC_MDELEM_UNREF(channeld->method);
grpc_mdelem_unref(channeld->scheme); GRPC_MDELEM_UNREF(channeld->scheme);
grpc_mdelem_unref(channeld->content_type); GRPC_MDELEM_UNREF(channeld->content_type);
grpc_mdelem_unref(channeld->status); GRPC_MDELEM_UNREF(channeld->status);
} }
const grpc_channel_filter grpc_http_client_filter = { const grpc_channel_filter grpc_http_client_filter = {

@ -129,9 +129,9 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
/* translate host to :authority since :authority may be /* translate host to :authority since :authority may be
omitted */ omitted */
grpc_mdelem *authority = grpc_mdelem_from_metadata_strings( grpc_mdelem *authority = grpc_mdelem_from_metadata_strings(
channeld->mdctx, grpc_mdstr_ref(channeld->authority_key), channeld->mdctx, GRPC_MDSTR_REF(channeld->authority_key),
grpc_mdstr_ref(md->value)); GRPC_MDSTR_REF(md->value));
grpc_mdelem_unref(md); GRPC_MDELEM_UNREF(md);
return authority; return authority;
} else { } else {
return md; return md;
@ -193,7 +193,7 @@ static void hs_mutate_op(grpc_call_element *elem,
if (op->type != GRPC_OP_METADATA) continue; if (op->type != GRPC_OP_METADATA) continue;
calld->sent_status = 1; calld->sent_status = 1;
grpc_metadata_batch_add_head(&op->data.metadata, &calld->status, grpc_metadata_batch_add_head(&op->data.metadata, &calld->status,
grpc_mdelem_ref(channeld->status_ok)); GRPC_MDELEM_REF(channeld->status_ok));
break; break;
} }
} }
@ -264,17 +264,17 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
/* grab pointers to our data from the channel element */ /* grab pointers to our data from the channel element */
channel_data *channeld = elem->channel_data; channel_data *channeld = elem->channel_data;
grpc_mdelem_unref(channeld->te_trailers); GRPC_MDELEM_UNREF(channeld->te_trailers);
grpc_mdelem_unref(channeld->status_ok); GRPC_MDELEM_UNREF(channeld->status_ok);
grpc_mdelem_unref(channeld->status_not_found); GRPC_MDELEM_UNREF(channeld->status_not_found);
grpc_mdelem_unref(channeld->method_post); GRPC_MDELEM_UNREF(channeld->method_post);
grpc_mdelem_unref(channeld->http_scheme); GRPC_MDELEM_UNREF(channeld->http_scheme);
grpc_mdelem_unref(channeld->https_scheme); GRPC_MDELEM_UNREF(channeld->https_scheme);
grpc_mdelem_unref(channeld->grpc_scheme); GRPC_MDELEM_UNREF(channeld->grpc_scheme);
grpc_mdelem_unref(channeld->content_type); GRPC_MDELEM_UNREF(channeld->content_type);
grpc_mdstr_unref(channeld->path_key); GRPC_MDSTR_UNREF(channeld->path_key);
grpc_mdstr_unref(channeld->authority_key); GRPC_MDSTR_UNREF(channeld->authority_key);
grpc_mdstr_unref(channeld->host_key); GRPC_MDSTR_UNREF(channeld->host_key);
} }
const grpc_channel_filter grpc_http_server_filter = { const grpc_channel_filter grpc_http_server_filter = {

@ -36,6 +36,8 @@
#include "src/core/client_config/lb_policy.h" #include "src/core/client_config/lb_policy.h"
/** Returns a load balancing policy instance that picks up the first subchannel
* from \a subchannels to succesfully connect */
grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels, grpc_lb_policy *grpc_create_pick_first_lb_policy(grpc_subchannel **subchannels,
size_t num_subchannels); size_t num_subchannels);

@ -207,7 +207,6 @@ static char *zookeeper_parse_address(char *buffer, int buffer_len) {
grpc_json *json; grpc_json *json;
grpc_json *cur; grpc_json *cur;
gpr_log(GPR_INFO, buffer);
address = NULL; address = NULL;
json = grpc_json_parse_string_with_len(buffer, buffer_len); json = grpc_json_parse_string_with_len(buffer, buffer_len);
if (json != NULL) { if (json != NULL) {
@ -231,7 +230,6 @@ static char *zookeeper_parse_address(char *buffer, int buffer_len) {
strcat(address, host); strcat(address, host);
strcat(address, ":"); strcat(address, ":");
strcat(address, port); strcat(address, port);
gpr_log(GPR_INFO, address);
} }
grpc_json_destroy(json); grpc_json_destroy(json);
} }
@ -259,7 +257,6 @@ static void zookeeper_resolve_address(zookeeper_resolver *r) {
/* Get zookeeper node of given path r->name /* Get zookeeper node of given path r->name
If not containing address(i.e. service node), get its children */ If not containing address(i.e. service node), get its children */
gpr_log(GPR_INFO, r->name);
status = zoo_get(r->zookeeper_handle, r->name, GRPC_ZOOKEEPER_WATCH, status = zoo_get(r->zookeeper_handle, r->name, GRPC_ZOOKEEPER_WATCH,
buffer, &buffer_len, NULL); buffer, &buffer_len, NULL);
if (!status) { if (!status) {
@ -291,7 +288,6 @@ static void zookeeper_resolve_address(zookeeper_resolver *r) {
strcat(path, r->name); strcat(path, r->name);
strcat(path, "/"); strcat(path, "/");
strcat(path, children.data[i]); strcat(path, children.data[i]);
gpr_log(GPR_INFO, path);
memset(buffer, 0, GRPC_MAX_ZOOKEEPER_BUFFER_SIZE); memset(buffer, 0, GRPC_MAX_ZOOKEEPER_BUFFER_SIZE);
status = zoo_get(r->zookeeper_handle, path, GRPC_ZOOKEEPER_WATCH, status = zoo_get(r->zookeeper_handle, path, GRPC_ZOOKEEPER_WATCH,
buffer, &buffer_len, NULL); buffer, &buffer_len, NULL);

@ -300,7 +300,7 @@ static void continue_connect(grpc_subchannel *c) {
} }
static void start_connect(grpc_subchannel *c) { static void start_connect(grpc_subchannel *c) {
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
c->next_attempt = now; c->next_attempt = now;
c->backoff_delta = gpr_time_from_seconds(1); c->backoff_delta = gpr_time_from_seconds(1);
@ -585,7 +585,7 @@ static void subchannel_connected(void *arg, int iomgr_success) {
c->have_alarm = 1; c->have_alarm = 1;
c->next_attempt = gpr_time_add(c->next_attempt, c->backoff_delta); c->next_attempt = gpr_time_add(c->next_attempt, c->backoff_delta);
c->backoff_delta = gpr_time_add(c->backoff_delta, c->backoff_delta); c->backoff_delta = gpr_time_add(c->backoff_delta, c->backoff_delta);
grpc_alarm_init(&c->alarm, c->next_attempt, on_alarm, c, gpr_now()); grpc_alarm_init(&c->alarm, c->next_attempt, on_alarm, c, gpr_now(GPR_CLOCK_REALTIME));
gpr_mu_unlock(&c->mu); gpr_mu_unlock(&c->mu);
} }
} }

@ -165,6 +165,7 @@ static void start_write(internal_request *req) {
static void on_secure_transport_setup_done(void *rp, static void on_secure_transport_setup_done(void *rp,
grpc_security_status status, grpc_security_status status,
grpc_endpoint *wrapped_endpoint,
grpc_endpoint *secure_endpoint) { grpc_endpoint *secure_endpoint) {
internal_request *req = rp; internal_request *req = rp;
if (status != GRPC_SECURITY_OK) { if (status != GRPC_SECURITY_OK) {

@ -85,7 +85,7 @@ typedef struct grpc_httpcli_response {
char *body; char *body;
} grpc_httpcli_response; } grpc_httpcli_response;
/* Callback for grpc_httpcli_get */ /* Callback for grpc_httpcli_get and grpc_httpcli_post. */
typedef void (*grpc_httpcli_response_cb)(void *user_data, typedef void (*grpc_httpcli_response_cb)(void *user_data,
const grpc_httpcli_response *response); const grpc_httpcli_response *response);
@ -100,8 +100,6 @@ void grpc_httpcli_context_destroy(grpc_httpcli_context *context);
'request' contains request parameters - these are caller owned and can be 'request' contains request parameters - these are caller owned and can be
destroyed once the call returns destroyed once the call returns
'deadline' contains a deadline for the request (or gpr_inf_future) '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 'on_response' is a callback to report results to (and 'user_data' is a user
supplied pointer to pass to said call) */ supplied pointer to pass to said call) */
void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset, void grpc_httpcli_get(grpc_httpcli_context *context, grpc_pollset *pollset,

@ -41,9 +41,9 @@
typedef struct grpc_alarm { typedef struct grpc_alarm {
gpr_timespec deadline; gpr_timespec deadline;
gpr_uint32 heap_index; /* INVALID_HEAP_INDEX if not in heap */ gpr_uint32 heap_index; /* INVALID_HEAP_INDEX if not in heap */
int triggered;
struct grpc_alarm *next; struct grpc_alarm *next;
struct grpc_alarm *prev; struct grpc_alarm *prev;
int triggered;
grpc_iomgr_cb_func cb; grpc_iomgr_cb_func cb;
void *cb_arg; void *cb_arg;
} grpc_alarm; } grpc_alarm;

@ -127,7 +127,6 @@ static void iocp_loop(void *p) {
grpc_maybe_call_delayed_callbacks(NULL, 1); grpc_maybe_call_delayed_callbacks(NULL, 1);
do_iocp_work(); do_iocp_work();
} }
gpr_log(GPR_DEBUG, "iocp_loop is done");
gpr_event_set(&g_iocp_done, (void *)1); gpr_event_set(&g_iocp_done, (void *)1);
} }

@ -59,7 +59,7 @@ static void background_callback_executor(void *ignored) {
while (!g_shutdown) { while (!g_shutdown) {
gpr_timespec deadline = gpr_inf_future; gpr_timespec deadline = gpr_inf_future;
gpr_timespec short_deadline = gpr_timespec short_deadline =
gpr_time_add(gpr_now(), gpr_time_from_millis(100)); gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100));
if (g_cbs_head) { if (g_cbs_head) {
grpc_iomgr_closure *closure = g_cbs_head; grpc_iomgr_closure *closure = g_cbs_head;
g_cbs_head = closure->next; g_cbs_head = closure->next;
@ -67,7 +67,8 @@ static void background_callback_executor(void *ignored) {
gpr_mu_unlock(&g_mu); gpr_mu_unlock(&g_mu);
closure->cb(closure->cb_arg, closure->success); closure->cb(closure->cb_arg, closure->success);
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_mu);
} else if (grpc_alarm_check(&g_mu, gpr_now(), &deadline)) { } else if (grpc_alarm_check(&g_mu, gpr_now(GPR_CLOCK_REALTIME),
&deadline)) {
} else { } else {
gpr_mu_unlock(&g_mu); gpr_mu_unlock(&g_mu);
gpr_sleep_until(gpr_time_min(short_deadline, deadline)); gpr_sleep_until(gpr_time_min(short_deadline, deadline));
@ -89,7 +90,7 @@ void grpc_iomgr_init(void) {
gpr_thd_id id; gpr_thd_id id;
gpr_mu_init(&g_mu); gpr_mu_init(&g_mu);
gpr_cv_init(&g_rcv); gpr_cv_init(&g_rcv);
grpc_alarm_list_init(gpr_now()); grpc_alarm_list_init(gpr_now(GPR_CLOCK_REALTIME));
g_root_object.next = g_root_object.prev = &g_root_object; g_root_object.next = g_root_object.prev = &g_root_object;
g_root_object.name = "root"; g_root_object.name = "root";
grpc_iomgr_platform_init(); grpc_iomgr_platform_init();
@ -110,21 +111,27 @@ void grpc_iomgr_shutdown(void) {
grpc_iomgr_object *obj; grpc_iomgr_object *obj;
grpc_iomgr_closure *closure; grpc_iomgr_closure *closure;
gpr_timespec shutdown_deadline = gpr_timespec shutdown_deadline =
gpr_time_add(gpr_now(), gpr_time_from_seconds(10)); gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10));
gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_mu);
g_shutdown = 1; g_shutdown = 1;
while (g_cbs_head != NULL || g_root_object.next != &g_root_object) { while (g_cbs_head != NULL || g_root_object.next != &g_root_object) {
if (g_cbs_head != NULL && g_root_object.next != &g_root_object) { if (gpr_time_cmp(
gpr_log(GPR_DEBUG, gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time),
"Waiting for %d iomgr objects to be destroyed and executing " gpr_time_from_seconds(1)) >= 0) {
"final callbacks", if (g_cbs_head != NULL && g_root_object.next != &g_root_object) {
count_objects()); gpr_log(GPR_DEBUG,
} else if (g_cbs_head != NULL) { "Waiting for %d iomgr objects to be destroyed and executing "
gpr_log(GPR_DEBUG, "Executing final iomgr callbacks"); "final callbacks",
} else { count_objects());
gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed", } else if (g_cbs_head != NULL) {
count_objects()); gpr_log(GPR_DEBUG, "Executing final iomgr callbacks");
} else {
gpr_log(GPR_DEBUG, "Waiting for %d iomgr objects to be destroyed",
count_objects());
}
last_warning_time = gpr_now(GPR_CLOCK_REALTIME);
} }
if (g_cbs_head) { if (g_cbs_head) {
do { do {
@ -145,9 +152,9 @@ void grpc_iomgr_shutdown(void) {
if (g_root_object.next != &g_root_object) { if (g_root_object.next != &g_root_object) {
int timeout = 0; int timeout = 0;
gpr_timespec short_deadline = gpr_timespec short_deadline =
gpr_time_add(gpr_now(), gpr_time_from_millis(100)); gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100));
while (gpr_cv_wait(&g_rcv, &g_mu, short_deadline) && g_cbs_head == NULL) { while (gpr_cv_wait(&g_rcv, &g_mu, short_deadline) && g_cbs_head == NULL) {
if (gpr_time_cmp(gpr_now(), shutdown_deadline) > 0) { if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) {
timeout = 1; timeout = 1;
break; break;
} }

@ -105,10 +105,11 @@ static void multipoll_with_epoll_pollset_maybe_work(
* here. * here.
*/ */
timeout_ms = grpc_poll_deadline_to_millis_timeout(deadline, now);
pollset->counter += 1; pollset->counter += 1;
gpr_mu_unlock(&pollset->mu); gpr_mu_unlock(&pollset->mu);
timeout_ms = grpc_poll_deadline_to_millis_timeout(deadline, now);
do { do {
ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms); ep_rv = epoll_wait(h->epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms);
if (ep_rv < 0) { if (ep_rv < 0) {

@ -122,7 +122,7 @@ static void finish_shutdown(grpc_pollset *pollset) {
int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
/* pollset->mu already held */ /* pollset->mu already held */
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(now, deadline) > 0) { if (gpr_time_cmp(now, deadline) > 0) {
return 0; return 0;
} }
@ -187,15 +187,16 @@ void grpc_pollset_destroy(grpc_pollset *pollset) {
gpr_mu_destroy(&pollset->mu); gpr_mu_destroy(&pollset->mu);
} }
int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline, gpr_timespec now) { int grpc_poll_deadline_to_millis_timeout(gpr_timespec deadline,
gpr_timespec now) {
gpr_timespec timeout; gpr_timespec timeout;
static const int max_spin_polling_us = 10; static const int max_spin_polling_us = 10;
if (gpr_time_cmp(deadline, gpr_inf_future) == 0) { if (gpr_time_cmp(deadline, gpr_inf_future) == 0) {
return -1; return -1;
} }
if (gpr_time_cmp( if (gpr_time_cmp(
deadline, deadline,
gpr_time_add(now, gpr_time_from_micros(max_spin_polling_us))) <= 0) { gpr_time_add(now, gpr_time_from_micros(max_spin_polling_us))) <= 0) {
return 0; return 0;
} }
timeout = gpr_time_sub(deadline, now); timeout = gpr_time_sub(deadline, now);

@ -38,7 +38,7 @@
/* A grpc_pollset_set is a set of pollsets that are interested in an /* 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 action. Adding a pollset to a pollset_set automatically adds any
fd's (etc) that have been registered with the set_set with that pollset. fd's (etc) that have been registered with the set_set to that pollset.
Registering fd's automatically adds them to all current pollsets. */ Registering fd's automatically adds them to all current pollsets. */
#ifdef GPR_POSIX_SOCKET #ifdef GPR_POSIX_SOCKET

@ -70,7 +70,7 @@ void grpc_pollset_destroy(grpc_pollset *pollset) {
int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) { int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
gpr_timespec now; gpr_timespec now;
now = gpr_now(); now = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(now, deadline) > 0) { if (gpr_time_cmp(now, deadline) > 0) {
return 0 /* GPR_FALSE */; return 0 /* GPR_FALSE */;
} }
@ -86,8 +86,6 @@ int grpc_pollset_work(grpc_pollset *pollset, gpr_timespec deadline) {
return 1 /* GPR_TRUE */; return 1 /* GPR_TRUE */;
} }
void grpc_pollset_kick(grpc_pollset *p) { void grpc_pollset_kick(grpc_pollset *p) { gpr_cv_signal(&p->cv); }
gpr_cv_signal(&p->cv);
}
#endif /* GPR_WINSOCK_SOCKET */ #endif /* GPR_WINSOCK_SOCKET */

@ -37,6 +37,7 @@
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/iomgr/iocp_windows.h" #include "src/core/iomgr/iocp_windows.h"
#include "src/core/iomgr/iomgr_internal.h" #include "src/core/iomgr/iomgr_internal.h"
@ -45,11 +46,14 @@
#include "src/core/iomgr/socket_windows.h" #include "src/core/iomgr/socket_windows.h"
grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) { grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) {
char *final_name;
grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket)); grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket));
memset(r, 0, sizeof(grpc_winsocket)); memset(r, 0, sizeof(grpc_winsocket));
r->socket = socket; r->socket = socket;
gpr_mu_init(&r->state_mu); gpr_mu_init(&r->state_mu);
grpc_iomgr_register_object(&r->iomgr_object, name); 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); grpc_iocp_add_socket(r);
return r; return r;
} }
@ -58,22 +62,27 @@ grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) {
operations to abort them. We need to do that this way because of the 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 various callsites of that function, which happens to be in various
mutex hold states, and that'd be unsafe to call them directly. */ mutex hold states, and that'd be unsafe to call them directly. */
int grpc_winsocket_shutdown(grpc_winsocket *socket) { int grpc_winsocket_shutdown(grpc_winsocket *winsocket) {
int callbacks_set = 0; int callbacks_set = 0;
gpr_mu_lock(&socket->state_mu); SOCKET socket;
if (socket->read_info.cb) { gpr_mu_lock(&winsocket->state_mu);
socket = winsocket->socket;
if (winsocket->read_info.cb) {
callbacks_set++; callbacks_set++;
grpc_iomgr_closure_init(&socket->shutdown_closure, socket->read_info.cb, grpc_iomgr_closure_init(&winsocket->shutdown_closure,
socket->read_info.opaque); winsocket->read_info.cb,
grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0); winsocket->read_info.opaque);
grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0);
} }
if (socket->write_info.cb) { if (winsocket->write_info.cb) {
callbacks_set++; callbacks_set++;
grpc_iomgr_closure_init(&socket->shutdown_closure, socket->write_info.cb, grpc_iomgr_closure_init(&winsocket->shutdown_closure,
socket->write_info.opaque); winsocket->write_info.cb,
grpc_iomgr_add_delayed_callback(&socket->shutdown_closure, 0); winsocket->write_info.opaque);
grpc_iomgr_add_delayed_callback(&winsocket->shutdown_closure, 0);
} }
gpr_mu_unlock(&socket->state_mu); gpr_mu_unlock(&winsocket->state_mu);
closesocket(socket);
return callbacks_set; return callbacks_set;
} }
@ -84,14 +93,12 @@ int grpc_winsocket_shutdown(grpc_winsocket *socket) {
an "idle" socket which is neither trying to read or write, we'd start leaking an "idle" socket which is neither trying to read or write, we'd start leaking
both memory and sockets. */ both memory and sockets. */
void grpc_winsocket_orphan(grpc_winsocket *winsocket) { void grpc_winsocket_orphan(grpc_winsocket *winsocket) {
SOCKET socket = winsocket->socket;
grpc_iomgr_unregister_object(&winsocket->iomgr_object); grpc_iomgr_unregister_object(&winsocket->iomgr_object);
if (winsocket->read_info.outstanding || winsocket->write_info.outstanding) { if (winsocket->read_info.outstanding || winsocket->write_info.outstanding) {
grpc_iocp_socket_orphan(winsocket); grpc_iocp_socket_orphan(winsocket);
} else { } else {
grpc_winsocket_destroy(winsocket); grpc_winsocket_destroy(winsocket);
} }
closesocket(socket);
} }
void grpc_winsocket_destroy(grpc_winsocket *winsocket) { void grpc_winsocket_destroy(grpc_winsocket *winsocket) {

@ -114,6 +114,7 @@ static void on_writable(void *acp, int success) {
void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb; void (*cb)(void *arg, grpc_endpoint *tcp) = ac->cb;
void *cb_arg = ac->cb_arg; void *cb_arg = ac->cb_arg;
gpr_mu_lock(&ac->mu);
if (success) { if (success) {
do { do {
so_error_size = sizeof(so_error); so_error_size = sizeof(so_error);
@ -139,6 +140,7 @@ static void on_writable(void *acp, int success) {
opened too many network connections. The "easy" fix: opened too many network connections. The "easy" fix:
don't do that! */ don't do that! */
gpr_log(GPR_ERROR, "kernel out of buffers"); gpr_log(GPR_ERROR, "kernel out of buffers");
gpr_mu_unlock(&ac->mu);
grpc_fd_notify_on_write(ac->fd, &ac->write_closure); grpc_fd_notify_on_write(ac->fd, &ac->write_closure);
return; return;
} else { } else {
@ -165,10 +167,11 @@ static void on_writable(void *acp, int success) {
abort(); abort();
finish: finish:
gpr_mu_lock(&ac->mu); if (ep == NULL) {
if (!ep) {
grpc_pollset_set_del_fd(ac->interested_parties, ac->fd); grpc_pollset_set_del_fd(ac->interested_parties, ac->fd);
grpc_fd_orphan(ac->fd, NULL, "tcp_client_orphan"); grpc_fd_orphan(ac->fd, NULL, "tcp_client_orphan");
} else {
ac->fd = NULL;
} }
done = (--ac->refs == 0); done = (--ac->refs == 0);
gpr_mu_unlock(&ac->mu); gpr_mu_unlock(&ac->mu);
@ -250,7 +253,8 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *ep),
ac->write_closure.cb_arg = ac; ac->write_closure.cb_arg = ac;
gpr_mu_lock(&ac->mu); gpr_mu_lock(&ac->mu);
grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now()); grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac,
gpr_now(GPR_CLOCK_REALTIME));
grpc_fd_notify_on_write(ac->fd, &ac->write_closure); grpc_fd_notify_on_write(ac->fd, &ac->write_closure);
gpr_mu_unlock(&ac->mu); gpr_mu_unlock(&ac->mu);

@ -215,7 +215,8 @@ void grpc_tcp_client_connect(void (*cb)(void *arg, grpc_endpoint *tcp),
ac->refs = 2; ac->refs = 2;
ac->aborted = 0; ac->aborted = 0;
grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac, gpr_now()); grpc_alarm_init(&ac->alarm, deadline, on_alarm, ac,
gpr_now(GPR_CLOCK_REALTIME));
socket->write_info.outstanding = 1; socket->write_info.outstanding = 1;
grpc_socket_notify_on_write(socket, on_connect, ac); grpc_socket_notify_on_write(socket, on_connect, ac);
return; return;

@ -313,9 +313,7 @@ static void call_read_cb(grpc_tcp *tcp, gpr_slice *slices, size_t nslices,
size_t i; size_t i;
gpr_log(GPR_DEBUG, "read: status=%d", status); gpr_log(GPR_DEBUG, "read: status=%d", status);
for (i = 0; i < nslices; i++) { for (i = 0; i < nslices; i++) {
char *dump = char *dump = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
gpr_log(GPR_DEBUG, "READ: %s", dump); gpr_log(GPR_DEBUG, "READ: %s", dump);
gpr_free(dump); gpr_free(dump);
} }
@ -540,9 +538,7 @@ static grpc_endpoint_write_status grpc_tcp_write(grpc_endpoint *ep,
size_t i; size_t i;
for (i = 0; i < nslices; i++) { for (i = 0; i < nslices; i++) {
char *data = char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
gpr_log(GPR_DEBUG, "WRITE %p: %s", tcp, data); gpr_log(GPR_DEBUG, "WRITE %p: %s", tcp, data);
gpr_free(data); gpr_free(data);
} }

@ -108,9 +108,10 @@ void grpc_tcp_server_destroy(grpc_tcp_server *s,
size_t i; size_t i;
gpr_mu_lock(&s->mu); gpr_mu_lock(&s->mu);
/* First, shutdown all fd's. This will queue abortion calls for all /* First, shutdown all fd's. This will queue abortion calls for all
of the pending accepts. */ of the pending accepts due to the normal operation mechanism. */
for (i = 0; i < s->nports; i++) { for (i = 0; i < s->nports; i++) {
server_port *sp = &s->ports[i]; server_port *sp = &s->ports[i];
sp->shutting_down = 1;
grpc_winsocket_shutdown(sp->socket); grpc_winsocket_shutdown(sp->socket);
} }
/* This happens asynchronously. Wait while that happens. */ /* This happens asynchronously. Wait while that happens. */
@ -242,63 +243,52 @@ static void on_accept(void *arg, int from_iocp) {
SOCKET sock = sp->new_socket; SOCKET sock = sp->new_socket;
grpc_winsocket_callback_info *info = &sp->socket->read_info; grpc_winsocket_callback_info *info = &sp->socket->read_info;
grpc_endpoint *ep = NULL; grpc_endpoint *ep = NULL;
DWORD transfered_bytes;
/* The shutdown sequence is done in two parts. This is the second DWORD flags;
part here, acknowledging the IOCP notification, and doing nothing BOOL wsa_success;
else, especially not queuing a new accept. */
if (sp->shutting_down) { /* The general mechanism for shutting down is to queue abortion calls. While
GPR_ASSERT(from_iocp); this is necessary in the read/write case, it's useless for the accept
sp->shutting_down = 0; case. Let's do nothing. */
sp->socket->read_info.outstanding = 0; if (!from_iocp) return;
gpr_mu_lock(&sp->server->mu);
if (0 == --sp->server->active_ports) { /* The IOCP notified us of a completed operation. Let's grab the results,
gpr_cv_broadcast(&sp->server->cv); and act accordingly. */
} transfered_bytes = 0;
gpr_mu_unlock(&sp->server->mu); wsa_success = WSAGetOverlappedResult(sock, &info->overlapped,
return; &transfered_bytes, FALSE, &flags);
} if (!wsa_success) {
if (sp->shutting_down) {
if (from_iocp) { /* During the shutdown case, we ARE expecting an error. So that's swell,
/* The IOCP notified us of a completed operation. Let's grab the results, and we can wake up the shutdown thread. */
and act accordingly. */ sp->shutting_down = 0;
DWORD transfered_bytes = 0; sp->socket->read_info.outstanding = 0;
DWORD flags; gpr_mu_lock(&sp->server->mu);
BOOL wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, if (0 == --sp->server->active_ports) {
&transfered_bytes, FALSE, &flags); gpr_cv_broadcast(&sp->server->cv);
if (!wsa_success) { }
gpr_mu_unlock(&sp->server->mu);
return;
} else {
char *utf8_message = gpr_format_message(WSAGetLastError()); char *utf8_message = gpr_format_message(WSAGetLastError());
gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message);
gpr_free(utf8_message); gpr_free(utf8_message);
closesocket(sock); closesocket(sock);
} else {
/* TODO(ctiller): add sockaddr address to label */
ep = grpc_tcp_create(grpc_winsocket_create(sock, "server"));
} }
} else { } else {
/* If we're not notified from the IOCP, it means we are asked to shutdown. if (!sp->shutting_down) {
This will initiate that shutdown. Calling closesocket will trigger an /* TODO(ctiller): add sockaddr address to label */
IOCP notification, that will call this function a second time, from ep = grpc_tcp_create(grpc_winsocket_create(sock, "server"));
the IOCP thread. Of course, this only works if the socket was, in fact,
listening. If that's not the case, we'd wait indefinitely. That's a bit
of a degenerate case, but it can happen if you create a server, but
don't start it. So let's support that by recursing once. */
sp->shutting_down = 1;
sp->new_socket = INVALID_SOCKET;
if (sock != INVALID_SOCKET) {
closesocket(sock);
} else {
on_accept(sp, 1);
} }
return;
} }
/* The only time we should call our callback, is where we successfully /* The only time we should call our callback, is where we successfully
managed to accept a connection, and created an endpoint. */ managed to accept a connection, and created an endpoint. */
if (ep) sp->server->cb(sp->server->cb_arg, ep); if (ep) sp->server->cb(sp->server->cb_arg, ep);
/* As we were notified from the IOCP of one and exactly one accept, /* 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 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 to the new connection. We need to create a new one for the next
connection. */ connection. */
start_accept(sp); start_accept(sp);
} }

@ -148,9 +148,11 @@ static void on_read(void *tcpp, int from_iocp) {
GPR_ASSERT(tcp->socket->read_info.outstanding); GPR_ASSERT(tcp->socket->read_info.outstanding);
if (socket->read_info.wsa_error != 0) { if (socket->read_info.wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error); if (socket->read_info.wsa_error != WSAECONNRESET) {
gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message); char *utf8_message = gpr_format_message(info->wsa_error);
gpr_free(utf8_message); gpr_log(GPR_ERROR, "ReadFile overlapped error: %s", utf8_message);
gpr_free(utf8_message);
}
status = GRPC_ENDPOINT_CB_ERROR; status = GRPC_ENDPOINT_CB_ERROR;
} else { } else {
if (info->bytes_transfered != 0) { if (info->bytes_transfered != 0) {
@ -259,9 +261,11 @@ static void on_write(void *tcpp, int from_iocp) {
GPR_ASSERT(tcp->socket->write_info.outstanding); GPR_ASSERT(tcp->socket->write_info.outstanding);
if (info->wsa_error != 0) { if (info->wsa_error != 0) {
char *utf8_message = gpr_format_message(info->wsa_error); if (info->wsa_error != WSAECONNRESET) {
gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message); char *utf8_message = gpr_format_message(info->wsa_error);
gpr_free(utf8_message); gpr_log(GPR_ERROR, "WSASend overlapped error: %s", utf8_message);
gpr_free(utf8_message);
}
status = GRPC_ENDPOINT_CB_ERROR; status = GRPC_ENDPOINT_CB_ERROR;
} else { } else {
GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length); GPR_ASSERT(info->bytes_transfered == tcp->write_slices.length);
@ -325,9 +329,11 @@ static grpc_endpoint_write_status win_write(grpc_endpoint *ep,
ret = GRPC_ENDPOINT_WRITE_DONE; ret = GRPC_ENDPOINT_WRITE_DONE;
GPR_ASSERT(bytes_sent == tcp->write_slices.length); GPR_ASSERT(bytes_sent == tcp->write_slices.length);
} else { } else {
char *utf8_message = gpr_format_message(info->wsa_error); if (socket->read_info.wsa_error != WSAECONNRESET) {
gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message); char *utf8_message = gpr_format_message(info->wsa_error);
gpr_free(utf8_message); gpr_log(GPR_ERROR, "WSASend error: %s", utf8_message);
gpr_free(utf8_message);
}
} }
if (allocated) gpr_free(allocated); if (allocated) gpr_free(allocated);
gpr_slice_buffer_reset_and_unref(&tcp->write_slices); gpr_slice_buffer_reset_and_unref(&tcp->write_slices);

@ -53,7 +53,7 @@ typedef struct grpc_json {
} grpc_json; } grpc_json;
/* The next two functions are going to parse the input string, and /* The next two functions are going to parse the input string, and
* destroy it in the process, in order to use its space to store * modify it in the process, in order to use its space to store
* all of the keys and values for the returned object tree. * all of the keys and values for the returned object tree.
* *
* They assume UTF-8 input stream, and will output UTF-8 encoded * They assume UTF-8 input stream, and will output UTF-8 encoded

@ -82,7 +82,7 @@ struct grpc_precise_clock {
gpr_timespec clock; gpr_timespec clock;
}; };
static void grpc_precise_clock_now(grpc_precise_clock* clk) { static void grpc_precise_clock_now(grpc_precise_clock* clk) {
clk->clock = gpr_now(); clk->clock = gpr_now(GPR_CLOCK_REALTIME);
} }
#define GRPC_PRECISE_CLOCK_FORMAT "%ld.%09d" #define GRPC_PRECISE_CLOCK_FORMAT "%ld.%09d"
#define GRPC_PRECISE_CLOCK_PRINTF_ARGS(clk) \ #define GRPC_PRECISE_CLOCK_PRINTF_ARGS(clk) \

@ -61,6 +61,7 @@ typedef struct {
grpc_transport_stream_op op; grpc_transport_stream_op op;
size_t op_md_idx; size_t op_md_idx;
int sent_initial_metadata; int sent_initial_metadata;
gpr_uint8 security_context_set;
grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
} call_data; } call_data;
@ -199,8 +200,22 @@ static void auth_start_transport_op(grpc_call_element *elem,
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
grpc_linked_mdelem *l; grpc_linked_mdelem *l;
size_t i; size_t i;
grpc_client_security_context* sec_ctx = NULL;
/* TODO(jboeuf): write the call auth context. */ if (calld->security_context_set == 0) {
calld->security_context_set = 1;
GPR_ASSERT(op->context);
if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) {
op->context[GRPC_CONTEXT_SECURITY].value =
grpc_client_security_context_create();
op->context[GRPC_CONTEXT_SECURITY].destroy =
grpc_client_security_context_destroy;
}
sec_ctx = op->context[GRPC_CONTEXT_SECURITY].value;
GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter");
sec_ctx->auth_context = GRPC_AUTH_CONTEXT_REF(
chand->security_connector->base.auth_context, "client_auth_filter");
}
if (op->bind_pollset) { if (op->bind_pollset) {
calld->pollset = op->bind_pollset; calld->pollset = op->bind_pollset;
@ -219,11 +234,11 @@ static void auth_start_transport_op(grpc_call_element *elem,
/* Pointer comparison is OK for md_elems created from the same context. /* Pointer comparison is OK for md_elems created from the same context.
*/ */
if (md->key == chand->authority_string) { if (md->key == chand->authority_string) {
if (calld->host != NULL) grpc_mdstr_unref(calld->host); if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host);
calld->host = grpc_mdstr_ref(md->value); calld->host = GRPC_MDSTR_REF(md->value);
} else if (md->key == chand->path_string) { } else if (md->key == chand->path_string) {
if (calld->method != NULL) grpc_mdstr_unref(calld->method); if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method);
calld->method = grpc_mdstr_ref(md->value); calld->method = GRPC_MDSTR_REF(md->value);
} }
} }
if (calld->host != NULL) { if (calld->host != NULL) {
@ -263,6 +278,7 @@ static void init_call_elem(grpc_call_element *elem,
calld->method = NULL; calld->method = NULL;
calld->pollset = NULL; calld->pollset = NULL;
calld->sent_initial_metadata = 0; calld->sent_initial_metadata = 0;
calld->security_context_set = 0;
GPR_ASSERT(!initial_op || !initial_op->send_ops); GPR_ASSERT(!initial_op || !initial_op->send_ops);
} }
@ -272,10 +288,10 @@ static void destroy_call_elem(grpc_call_element *elem) {
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
grpc_credentials_unref(calld->creds); grpc_credentials_unref(calld->creds);
if (calld->host != NULL) { if (calld->host != NULL) {
grpc_mdstr_unref(calld->host); GRPC_MDSTR_UNREF(calld->host);
} }
if (calld->method != NULL) { if (calld->method != NULL) {
grpc_mdstr_unref(calld->method); GRPC_MDSTR_UNREF(calld->method);
} }
} }
@ -314,16 +330,16 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
if (ctx != NULL) if (ctx != NULL)
GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter"); GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter");
if (chand->authority_string != NULL) { if (chand->authority_string != NULL) {
grpc_mdstr_unref(chand->authority_string); GRPC_MDSTR_UNREF(chand->authority_string);
} }
if (chand->error_msg_key != NULL) { if (chand->error_msg_key != NULL) {
grpc_mdstr_unref(chand->error_msg_key); GRPC_MDSTR_UNREF(chand->error_msg_key);
} }
if (chand->status_key != NULL) { if (chand->status_key != NULL) {
grpc_mdstr_unref(chand->status_key); GRPC_MDSTR_UNREF(chand->status_key);
} }
if (chand->path_string != NULL) { if (chand->path_string != NULL) {
grpc_mdstr_unref(chand->path_string); GRPC_MDSTR_UNREF(chand->path_string);
} }
} }

@ -41,7 +41,6 @@
#include "src/core/json/json.h" #include "src/core/json/json.h"
#include "src/core/httpcli/httpcli.h" #include "src/core/httpcli/httpcli.h"
#include "src/core/iomgr/iomgr.h" #include "src/core/iomgr/iomgr.h"
#include "src/core/security/json_token.h"
#include "src/core/support/string.h" #include "src/core/support/string.h"
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
@ -52,12 +51,12 @@
/* -- Common. -- */ /* -- Common. -- */
typedef struct { struct grpc_credentials_metadata_request {
grpc_credentials *creds; grpc_credentials *creds;
grpc_credentials_metadata_cb cb; grpc_credentials_metadata_cb cb;
grpc_iomgr_closure *on_simulated_token_fetch_done_closure; grpc_iomgr_closure *on_simulated_token_fetch_done_closure;
void *user_data; void *user_data;
} grpc_credentials_metadata_request; };
static grpc_credentials_metadata_request * static grpc_credentials_metadata_request *
grpc_credentials_metadata_request_create(grpc_credentials *creds, grpc_credentials_metadata_request_create(grpc_credentials *creds,
@ -152,16 +151,6 @@ grpc_security_status grpc_server_credentials_create_security_connector(
/* -- Ssl credentials. -- */ /* -- Ssl credentials. -- */
typedef struct {
grpc_credentials base;
grpc_ssl_config config;
} grpc_ssl_credentials;
typedef struct {
grpc_server_credentials base;
grpc_ssl_server_config config;
} grpc_ssl_server_credentials;
static void ssl_destroy(grpc_credentials *creds) { static void ssl_destroy(grpc_credentials *creds) {
grpc_ssl_credentials *c = (grpc_ssl_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_root_certs != NULL) gpr_free(c->config.pem_root_certs);
@ -326,22 +315,6 @@ grpc_server_credentials *grpc_ssl_server_credentials_create(
/* -- Jwt credentials -- */ /* -- Jwt credentials -- */
typedef struct {
grpc_credentials base;
/* Have a simple cache for now with just 1 entry. We could have a map based on
the service_url for a more sophisticated one. */
gpr_mu cache_mu;
struct {
grpc_credentials_md_store *jwt_md;
char *service_url;
gpr_timespec jwt_expiration;
} cached;
grpc_auth_json_key key;
gpr_timespec jwt_lifetime;
} grpc_jwt_credentials;
static void jwt_reset_cache(grpc_jwt_credentials *c) { static void jwt_reset_cache(grpc_jwt_credentials *c) {
if (c->cached.jwt_md != NULL) { if (c->cached.jwt_md != NULL) {
grpc_credentials_md_store_unref(c->cached.jwt_md); grpc_credentials_md_store_unref(c->cached.jwt_md);
@ -384,7 +357,8 @@ static void jwt_get_request_metadata(grpc_credentials *creds,
if (c->cached.service_url != NULL && if (c->cached.service_url != NULL &&
strcmp(c->cached.service_url, service_url) == 0 && strcmp(c->cached.service_url, service_url) == 0 &&
c->cached.jwt_md != NULL && c->cached.jwt_md != NULL &&
(gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, gpr_now()), (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration,
gpr_now(GPR_CLOCK_REALTIME)),
refresh_threshold) > 0)) { refresh_threshold) > 0)) {
jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md); jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md);
} }
@ -401,7 +375,8 @@ static void jwt_get_request_metadata(grpc_credentials *creds,
char *md_value; char *md_value;
gpr_asprintf(&md_value, "Bearer %s", jwt); gpr_asprintf(&md_value, "Bearer %s", jwt);
gpr_free(jwt); gpr_free(jwt);
c->cached.jwt_expiration = gpr_time_add(gpr_now(), c->jwt_lifetime); c->cached.jwt_expiration =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime);
c->cached.service_url = gpr_strdup(service_url); c->cached.service_url = gpr_strdup(service_url);
c->cached.jwt_md = grpc_credentials_md_store_create(1); c->cached.jwt_md = grpc_credentials_md_store_create(1);
grpc_credentials_md_store_add_cstrings( grpc_credentials_md_store_add_cstrings(
@ -424,10 +399,9 @@ static grpc_credentials_vtable jwt_vtable = {
jwt_destroy, jwt_has_request_metadata, jwt_has_request_metadata_only, jwt_destroy, jwt_has_request_metadata, jwt_has_request_metadata_only,
jwt_get_request_metadata, NULL}; jwt_get_request_metadata, NULL};
grpc_credentials *grpc_jwt_credentials_create(const char *json_key, grpc_credentials *grpc_jwt_credentials_create_from_auth_json_key(
gpr_timespec token_lifetime) { grpc_auth_json_key key, gpr_timespec token_lifetime) {
grpc_jwt_credentials *c; grpc_jwt_credentials *c;
grpc_auth_json_key key = grpc_auth_json_key_create_from_string(json_key);
if (!grpc_auth_json_key_is_valid(&key)) { if (!grpc_auth_json_key_is_valid(&key)) {
gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation"); gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation");
return NULL; return NULL;
@ -444,26 +418,13 @@ grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
return &c->base; return &c->base;
} }
/* -- Oauth2TokenFetcher credentials -- */ grpc_credentials *grpc_jwt_credentials_create(const char *json_key,
gpr_timespec token_lifetime) {
/* This object is a base for credentials that need to acquire an oauth2 token return grpc_jwt_credentials_create_from_auth_json_key(
from an http service. */ grpc_auth_json_key_create_from_string(json_key), token_lifetime);
}
typedef void (*grpc_fetch_oauth2_func)(grpc_credentials_metadata_request *req,
grpc_httpcli_context *http_context,
grpc_pollset *pollset,
grpc_httpcli_response_cb response_cb,
gpr_timespec deadline);
typedef struct { /* -- Oauth2TokenFetcher credentials -- */
grpc_credentials base;
gpr_mu mu;
grpc_credentials_md_store *access_token_md;
gpr_timespec token_expiration;
grpc_httpcli_context httpcli_context;
grpc_pollset_set pollset_set;
grpc_fetch_oauth2_func fetch_func;
} grpc_oauth2_token_fetcher_credentials;
static void oauth2_token_fetcher_destroy(grpc_credentials *creds) { static void oauth2_token_fetcher_destroy(grpc_credentials *creds) {
grpc_oauth2_token_fetcher_credentials *c = grpc_oauth2_token_fetcher_credentials *c =
@ -586,7 +547,8 @@ static void on_oauth2_token_fetcher_http_response(
status = grpc_oauth2_token_fetcher_credentials_parse_server_response( status = grpc_oauth2_token_fetcher_credentials_parse_server_response(
response, &c->access_token_md, &token_lifetime); response, &c->access_token_md, &token_lifetime);
if (status == GRPC_CREDENTIALS_OK) { if (status == GRPC_CREDENTIALS_OK) {
c->token_expiration = gpr_time_add(gpr_now(), token_lifetime); c->token_expiration =
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime);
r->cb(r->user_data, c->access_token_md->entries, r->cb(r->user_data, c->access_token_md->entries,
c->access_token_md->num_entries, status); c->access_token_md->num_entries, status);
} else { } else {
@ -608,8 +570,9 @@ static void oauth2_token_fetcher_get_request_metadata(
{ {
gpr_mu_lock(&c->mu); gpr_mu_lock(&c->mu);
if (c->access_token_md != NULL && if (c->access_token_md != NULL &&
(gpr_time_cmp(gpr_time_sub(c->token_expiration, gpr_now()), (gpr_time_cmp(
refresh_threshold) > 0)) { gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)),
refresh_threshold) > 0)) {
cached_access_token_md = cached_access_token_md =
grpc_credentials_md_store_ref(c->access_token_md); grpc_credentials_md_store_ref(c->access_token_md);
} }
@ -623,7 +586,7 @@ static void oauth2_token_fetcher_get_request_metadata(
c->fetch_func( c->fetch_func(
grpc_credentials_metadata_request_create(creds, cb, user_data), grpc_credentials_metadata_request_create(creds, cb, user_data),
&c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response, &c->httpcli_context, pollset, on_oauth2_token_fetcher_http_response,
gpr_time_add(gpr_now(), refresh_threshold)); gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold));
} }
} }
@ -635,7 +598,7 @@ static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c,
gpr_mu_init(&c->mu); gpr_mu_init(&c->mu);
c->token_expiration = gpr_inf_past; c->token_expiration = gpr_inf_past;
c->fetch_func = fetch_func; c->fetch_func = fetch_func;
grpc_pollset_set_init(&c->pollset_set); grpc_httpcli_context_init(&c->httpcli_context);
} }
/* -- ComputeEngine credentials. -- */ /* -- ComputeEngine credentials. -- */
@ -670,13 +633,6 @@ grpc_credentials *grpc_compute_engine_credentials_create(void) {
/* -- ServiceAccount credentials. -- */ /* -- ServiceAccount credentials. -- */
typedef struct {
grpc_oauth2_token_fetcher_credentials base;
grpc_auth_json_key key;
char *scope;
gpr_timespec token_lifetime;
} grpc_service_account_credentials;
static void service_account_destroy(grpc_credentials *creds) { static void service_account_destroy(grpc_credentials *creds) {
grpc_service_account_credentials *c = grpc_service_account_credentials *c =
(grpc_service_account_credentials *)creds; (grpc_service_account_credentials *)creds;
@ -747,11 +703,6 @@ grpc_credentials *grpc_service_account_credentials_create(
/* -- RefreshToken credentials. -- */ /* -- RefreshToken credentials. -- */
typedef struct {
grpc_oauth2_token_fetcher_credentials base;
grpc_auth_refresh_token refresh_token;
} grpc_refresh_token_credentials;
static void refresh_token_destroy(grpc_credentials *creds) { static void refresh_token_destroy(grpc_credentials *creds) {
grpc_refresh_token_credentials *c = (grpc_refresh_token_credentials *)creds; grpc_refresh_token_credentials *c = (grpc_refresh_token_credentials *)creds;
grpc_auth_refresh_token_destruct(&c->refresh_token); grpc_auth_refresh_token_destruct(&c->refresh_token);
@ -787,12 +738,9 @@ static void refresh_token_fetch_oauth2(
gpr_free(body); gpr_free(body);
} }
grpc_credentials *grpc_refresh_token_credentials_create( grpc_credentials *grpc_refresh_token_credentials_create_from_auth_refresh_token(
const char *json_refresh_token) { grpc_auth_refresh_token refresh_token) {
grpc_refresh_token_credentials *c; grpc_refresh_token_credentials *c;
grpc_auth_refresh_token refresh_token =
grpc_auth_refresh_token_create_from_string(json_refresh_token);
if (!grpc_auth_refresh_token_is_valid(&refresh_token)) { if (!grpc_auth_refresh_token_is_valid(&refresh_token)) {
gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation"); gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation");
return NULL; return NULL;
@ -805,13 +753,13 @@ grpc_credentials *grpc_refresh_token_credentials_create(
return &c->base.base; return &c->base.base;
} }
/* -- Fake Oauth2 credentials. -- */ grpc_credentials *grpc_refresh_token_credentials_create(
const char *json_refresh_token) {
return grpc_refresh_token_credentials_create_from_auth_refresh_token(
grpc_auth_refresh_token_create_from_string(json_refresh_token));
}
typedef struct { /* -- Fake Oauth2 credentials. -- */
grpc_credentials base;
grpc_credentials_md_store *access_token_md;
int is_async;
} grpc_fake_oauth2_credentials;
static void fake_oauth2_destroy(grpc_credentials *creds) { static void fake_oauth2_destroy(grpc_credentials *creds) {
grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds; grpc_fake_oauth2_credentials *c = (grpc_fake_oauth2_credentials *)creds;
@ -876,6 +824,54 @@ grpc_credentials *grpc_fake_oauth2_credentials_create(
return &c->base; return &c->base;
} }
/* -- Oauth2 Access Token credentials. -- */
static void access_token_destroy(grpc_credentials *creds) {
grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
grpc_credentials_md_store_unref(c->access_token_md);
gpr_free(c);
}
static int access_token_has_request_metadata(const grpc_credentials *creds) {
return 1;
}
static int access_token_has_request_metadata_only(
const grpc_credentials *creds) {
return 1;
}
static void access_token_get_request_metadata(grpc_credentials *creds,
grpc_pollset *pollset,
const char *service_url,
grpc_credentials_metadata_cb cb,
void *user_data) {
grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds;
cb(user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK);
}
static grpc_credentials_vtable access_token_vtable = {
access_token_destroy, access_token_has_request_metadata,
access_token_has_request_metadata_only, access_token_get_request_metadata,
NULL};
grpc_credentials *grpc_access_token_credentials_create(
const char *access_token) {
grpc_access_token_credentials *c =
gpr_malloc(sizeof(grpc_access_token_credentials));
char *token_md_value;
memset(c, 0, sizeof(grpc_access_token_credentials));
c->base.type = GRPC_CREDENTIALS_TYPE_OAUTH2;
c->base.vtable = &access_token_vtable;
gpr_ref_init(&c->base.refcount, 1);
c->access_token_md = grpc_credentials_md_store_create(1);
gpr_asprintf(&token_md_value, "Bearer %s", access_token);
grpc_credentials_md_store_add_cstrings(
c->access_token_md, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value);
gpr_free(token_md_value);
return &c->base;
}
/* -- Fake transport security credentials. -- */ /* -- Fake transport security credentials. -- */
static void fake_transport_security_credentials_destroy( static void fake_transport_security_credentials_destroy(
@ -944,12 +940,6 @@ grpc_server_credentials *grpc_fake_transport_security_server_credentials_create(
/* -- Composite credentials. -- */ /* -- Composite credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_array inner;
grpc_credentials *connector_creds;
} grpc_composite_credentials;
typedef struct { typedef struct {
grpc_composite_credentials *composite_creds; grpc_composite_credentials *composite_creds;
size_t creds_index; size_t creds_index;
@ -1180,11 +1170,6 @@ grpc_credentials *grpc_credentials_contains_type(
/* -- IAM credentials. -- */ /* -- IAM credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_md_store *iam_md;
} grpc_iam_credentials;
static void iam_destroy(grpc_credentials *creds) { static void iam_destroy(grpc_credentials *creds) {
grpc_iam_credentials *c = (grpc_iam_credentials *)creds; grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
grpc_credentials_md_store_unref(c->iam_md); grpc_credentials_md_store_unref(c->iam_md);

@ -39,6 +39,8 @@
#include <grpc/grpc_security.h> #include <grpc/grpc_security.h>
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
#include "src/core/httpcli/httpcli.h"
#include "src/core/security/json_token.h"
#include "src/core/security/security_connector.h" #include "src/core/security/security_connector.h"
struct grpc_httpcli_response; struct grpc_httpcli_response;
@ -178,11 +180,22 @@ grpc_credentials_status
grpc_oauth2_token_fetcher_credentials_parse_server_response( grpc_oauth2_token_fetcher_credentials_parse_server_response(
const struct grpc_httpcli_response *response, const struct grpc_httpcli_response *response,
grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime); grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime);
void grpc_flush_cached_google_default_credentials(void);
/* Simulates an oauth2 token fetch with the specified value for testing. */ /* Simulates an oauth2 token fetch with the specified value for testing. */
grpc_credentials *grpc_fake_oauth2_credentials_create( grpc_credentials *grpc_fake_oauth2_credentials_create(
const char *token_md_value, int is_async); const char *token_md_value, int is_async);
/* Private constructor for jwt credentials from an already parsed json key.
Takes ownership of the key. */
grpc_credentials *grpc_jwt_credentials_create_from_auth_json_key(
grpc_auth_json_key key, gpr_timespec token_lifetime);
/* Private constructor for refresh token credentials from an already parsed
refresh token. Takes ownership of the refresh token. */
grpc_credentials *grpc_refresh_token_credentials_create_from_auth_refresh_token(
grpc_auth_refresh_token token);
/* --- grpc_server_credentials. --- */ /* --- grpc_server_credentials. --- */
typedef struct { typedef struct {
@ -199,4 +212,103 @@ struct grpc_server_credentials {
grpc_security_status grpc_server_credentials_create_security_connector( grpc_security_status grpc_server_credentials_create_security_connector(
grpc_server_credentials *creds, grpc_security_connector **sc); grpc_server_credentials *creds, grpc_security_connector **sc);
/* -- Ssl credentials. -- */
typedef struct {
grpc_credentials base;
grpc_ssl_config config;
} grpc_ssl_credentials;
typedef struct {
grpc_server_credentials base;
grpc_ssl_server_config config;
} grpc_ssl_server_credentials;
/* -- Jwt credentials -- */
typedef struct {
grpc_credentials base;
/* Have a simple cache for now with just 1 entry. We could have a map based on
the service_url for a more sophisticated one. */
gpr_mu cache_mu;
struct {
grpc_credentials_md_store *jwt_md;
char *service_url;
gpr_timespec jwt_expiration;
} cached;
grpc_auth_json_key key;
gpr_timespec jwt_lifetime;
} grpc_jwt_credentials;
/* -- Oauth2TokenFetcher credentials --
This object is a base for credentials that need to acquire an oauth2 token
from an http service. */
typedef struct grpc_credentials_metadata_request
grpc_credentials_metadata_request;
typedef void (*grpc_fetch_oauth2_func)(grpc_credentials_metadata_request *req,
grpc_httpcli_context *http_context,
grpc_pollset *pollset,
grpc_httpcli_response_cb response_cb,
gpr_timespec deadline);
typedef struct {
grpc_credentials base;
gpr_mu mu;
grpc_credentials_md_store *access_token_md;
gpr_timespec token_expiration;
grpc_httpcli_context httpcli_context;
grpc_fetch_oauth2_func fetch_func;
} grpc_oauth2_token_fetcher_credentials;
/* -- ServiceAccount credentials. -- */
typedef struct {
grpc_oauth2_token_fetcher_credentials base;
grpc_auth_json_key key;
char *scope;
gpr_timespec token_lifetime;
} grpc_service_account_credentials;
/* -- RefreshToken credentials. -- */
typedef struct {
grpc_oauth2_token_fetcher_credentials base;
grpc_auth_refresh_token refresh_token;
} grpc_refresh_token_credentials;
/* -- Oauth2 Access Token credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_md_store *access_token_md;
} grpc_access_token_credentials;
/* -- Fake Oauth2 credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_md_store *access_token_md;
int is_async;
} grpc_fake_oauth2_credentials;
/* -- IAM credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_md_store *iam_md;
} grpc_iam_credentials;
/* -- Composite credentials. -- */
typedef struct {
grpc_credentials base;
grpc_credentials_array inner;
grpc_credentials *connector_creds;
} grpc_composite_credentials;
#endif /* GRPC_INTERNAL_CORE_SECURITY_CREDENTIALS_H */ #endif /* GRPC_INTERNAL_CORE_SECURITY_CREDENTIALS_H */

@ -46,7 +46,6 @@
/* -- Constants. -- */ /* -- Constants. -- */
#define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal" #define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal"
#define GRPC_GOOGLE_CREDENTIALS_ENV_VAR "GOOGLE_APPLICATION_CREDENTIALS"
/* -- Default credentials. -- */ /* -- Default credentials. -- */
@ -104,9 +103,10 @@ static int is_stack_running_on_compute_engine(void) {
grpc_httpcli_context_init(&context); grpc_httpcli_context_init(&context);
grpc_httpcli_get(&context, &detector.pollset, &request, grpc_httpcli_get(
gpr_time_add(gpr_now(), max_detection_delay), &context, &detector.pollset, &request,
on_compute_engine_detection_http_response, &detector); gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay),
on_compute_engine_detection_http_response, &detector);
/* Block until we get the response. This is not ideal but this should only be /* 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. */ called once for the lifetime of the process by the default credentials. */
@ -123,36 +123,40 @@ static int is_stack_running_on_compute_engine(void) {
} }
/* Takes ownership of creds_path if not NULL. */ /* Takes ownership of creds_path if not NULL. */
static grpc_credentials *create_jwt_creds_from_path(char *creds_path) { static grpc_credentials *create_default_creds_from_path(char *creds_path) {
grpc_json *json = NULL;
grpc_auth_json_key key;
grpc_auth_refresh_token token;
grpc_credentials *result = NULL; grpc_credentials *result = NULL;
gpr_slice creds_data; gpr_slice creds_data = gpr_empty_slice();
int file_ok = 0; int file_ok = 0;
if (creds_path == NULL) return NULL; if (creds_path == NULL) goto end;
creds_data = gpr_load_file(creds_path, 1, &file_ok); creds_data = gpr_load_file(creds_path, 0, &file_ok);
gpr_free(creds_path); if (!file_ok) goto end;
if (file_ok) { json = grpc_json_parse_string_with_len(
result = grpc_jwt_credentials_create( (char *)GPR_SLICE_START_PTR(creds_data), GPR_SLICE_LENGTH(creds_data));
(const char *)GPR_SLICE_START_PTR(creds_data), if (json == NULL) goto end;
grpc_max_auth_token_lifetime);
gpr_slice_unref(creds_data); /* 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_jwt_credentials_create_from_auth_json_key(
key, grpc_max_auth_token_lifetime);
goto end;
} }
return result;
}
/* Takes ownership of creds_path if not NULL. */ /* Then try a refresh token if the auth json key was invalid. */
static grpc_credentials *create_refresh_token_creds_from_path( token = grpc_auth_refresh_token_create_from_json(json);
char *creds_path) { if (grpc_auth_refresh_token_is_valid(&token)) {
grpc_credentials *result = NULL; result =
gpr_slice creds_data; grpc_refresh_token_credentials_create_from_auth_refresh_token(token);
int file_ok = 0; goto end;
if (creds_path == NULL) return NULL;
creds_data = gpr_load_file(creds_path, 1, &file_ok);
gpr_free(creds_path);
if (file_ok) {
result = grpc_refresh_token_credentials_create(
(const char *)GPR_SLICE_START_PTR(creds_data));
gpr_slice_unref(creds_data);
} }
end:
if (creds_path != NULL) gpr_free(creds_path);
gpr_slice_unref(creds_data);
if (json != NULL) grpc_json_destroy(json);
return result; return result;
} }
@ -170,12 +174,12 @@ grpc_credentials *grpc_google_default_credentials_create(void) {
} }
/* First, try the environment variable. */ /* First, try the environment variable. */
result = result = create_default_creds_from_path(
create_jwt_creds_from_path(gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR)); gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR));
if (result != NULL) goto end; if (result != NULL) goto end;
/* Then the well-known file. */ /* Then the well-known file. */
result = create_refresh_token_creds_from_path( result = create_default_creds_from_path(
grpc_get_well_known_google_credentials_file_path()); grpc_get_well_known_google_credentials_file_path());
if (result != NULL) goto end; if (result != NULL) goto end;
@ -193,11 +197,24 @@ end:
if (!serving_cached_credentials && result != NULL) { if (!serving_cached_credentials && result != NULL) {
/* Blend with default ssl credentials and add a global reference so that it /* Blend with default ssl credentials and add a global reference so that it
can be cached and re-served. */ can be cached and re-served. */
result = grpc_composite_credentials_create( grpc_credentials *ssl_creds = grpc_ssl_credentials_create(NULL, NULL);
grpc_ssl_credentials_create(NULL, NULL), result); default_credentials = grpc_credentials_ref(grpc_composite_credentials_create(
GPR_ASSERT(result != NULL); ssl_creds, result));
default_credentials = grpc_credentials_ref(result); GPR_ASSERT(default_credentials != NULL);
grpc_credentials_unref(ssl_creds);
grpc_credentials_unref(result);
result = default_credentials;
} }
gpr_mu_unlock(&g_mu); gpr_mu_unlock(&g_mu);
return result; return result;
} }
void grpc_flush_cached_google_default_credentials(void) {
gpr_once_init(&g_once, init_default_credentials);
gpr_mu_lock(&g_mu);
if (default_credentials != NULL) {
grpc_credentials_unref(default_credentials);
default_credentials = NULL;
}
gpr_mu_unlock(&g_mu);
}

@ -46,17 +46,11 @@
#include <openssl/evp.h> #include <openssl/evp.h>
#include <openssl/pem.h> #include <openssl/pem.h>
#include "src/core/json/json.h"
/* --- Constants. --- */ /* --- Constants. --- */
/* 1 hour max. */ /* 1 hour max. */
const gpr_timespec grpc_max_auth_token_lifetime = {3600, 0}; const gpr_timespec grpc_max_auth_token_lifetime = {3600, 0};
#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"
#define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256" #define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256"
#define GRPC_JWT_TYPE "JWT" #define GRPC_JWT_TYPE "JWT"
@ -66,7 +60,7 @@ static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL;
/* --- grpc_auth_json_key. --- */ /* --- grpc_auth_json_key. --- */
static const char *json_get_string_property(grpc_json *json, static const char *json_get_string_property(const grpc_json *json,
const char *prop_name) { const char *prop_name) {
grpc_json *child; grpc_json *child;
for (child = json->child; child != NULL; child = child->next) { for (child = json->child; child != NULL; child = child->next) {
@ -79,7 +73,8 @@ static const char *json_get_string_property(grpc_json *json,
return child->value; return child->value;
} }
static int set_json_key_string_property(grpc_json *json, const char *prop_name, static int set_json_key_string_property(const grpc_json *json,
const char *prop_name,
char **json_key_field) { char **json_key_field) {
const char *prop_value = json_get_string_property(json, prop_name); const char *prop_value = json_get_string_property(json, prop_name);
if (prop_value == NULL) return 0; if (prop_value == NULL) return 0;
@ -92,11 +87,8 @@ int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) {
strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID); strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID);
} }
grpc_auth_json_key grpc_auth_json_key_create_from_string( grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json *json) {
const char *json_string) {
grpc_auth_json_key result; grpc_auth_json_key result;
char *scratchpad = gpr_strdup(json_string);
grpc_json *json = grpc_json_parse_string(scratchpad);
BIO *bio = NULL; BIO *bio = NULL;
const char *prop_value; const char *prop_value;
int success = 0; int success = 0;
@ -104,7 +96,7 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
memset(&result, 0, sizeof(grpc_auth_json_key)); memset(&result, 0, sizeof(grpc_auth_json_key));
result.type = GRPC_AUTH_JSON_TYPE_INVALID; result.type = GRPC_AUTH_JSON_TYPE_INVALID;
if (json == NULL) { if (json == NULL) {
gpr_log(GPR_ERROR, "Invalid json string %s", json_string); gpr_log(GPR_ERROR, "Invalid json.");
goto end; goto end;
} }
@ -142,8 +134,16 @@ grpc_auth_json_key grpc_auth_json_key_create_from_string(
end: end:
if (bio != NULL) BIO_free(bio); if (bio != NULL) BIO_free(bio);
if (json != NULL) grpc_json_destroy(json);
if (!success) grpc_auth_json_key_destruct(&result); 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); gpr_free(scratchpad);
return result; return result;
} }
@ -207,7 +207,7 @@ static char *encoded_jwt_claim(const grpc_auth_json_key *json_key,
grpc_json *child = NULL; grpc_json *child = NULL;
char *json_str = NULL; char *json_str = NULL;
char *result = NULL; char *result = NULL;
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
gpr_timespec expiration = gpr_time_add(now, token_lifetime); gpr_timespec expiration = gpr_time_add(now, token_lifetime);
char now_str[GPR_LTOA_MIN_BUFSIZE]; char now_str[GPR_LTOA_MIN_BUFSIZE];
char expiration_str[GPR_LTOA_MIN_BUFSIZE]; char expiration_str[GPR_LTOA_MIN_BUFSIZE];
@ -218,8 +218,8 @@ static char *encoded_jwt_claim(const grpc_auth_json_key *json_key,
gpr_ltoa(now.tv_sec, now_str); gpr_ltoa(now.tv_sec, now_str);
gpr_ltoa(expiration.tv_sec, expiration_str); gpr_ltoa(expiration.tv_sec, expiration_str);
child = create_child(NULL, json, "iss", json_key->client_email, child =
GRPC_JSON_STRING); create_child(NULL, json, "iss", json_key->client_email, GRPC_JSON_STRING);
if (scope != NULL) { if (scope != NULL) {
child = create_child(child, json, "scope", scope, GRPC_JSON_STRING); child = create_child(child, json, "scope", scope, GRPC_JSON_STRING);
} else { } else {
@ -342,18 +342,16 @@ int grpc_auth_refresh_token_is_valid(
strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID); strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID);
} }
grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string( grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json(
const char *json_string) { const grpc_json *json) {
grpc_auth_refresh_token result; grpc_auth_refresh_token result;
char *scratchpad = gpr_strdup(json_string);
grpc_json *json = grpc_json_parse_string(scratchpad);
const char *prop_value; const char *prop_value;
int success = 0; int success = 0;
memset(&result, 0, sizeof(grpc_auth_refresh_token)); memset(&result, 0, sizeof(grpc_auth_refresh_token));
result.type = GRPC_AUTH_JSON_TYPE_INVALID; result.type = GRPC_AUTH_JSON_TYPE_INVALID;
if (json == NULL) { if (json == NULL) {
gpr_log(GPR_ERROR, "Invalid json string %s", json_string); gpr_log(GPR_ERROR, "Invalid json.");
goto end; goto end;
} }
@ -374,8 +372,17 @@ grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
success = 1; success = 1;
end: end:
if (json != NULL) grpc_json_destroy(json);
if (!success) grpc_auth_refresh_token_destruct(&result); 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); gpr_free(scratchpad);
return result; return result;
} }
@ -396,4 +403,3 @@ void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) {
refresh_token->refresh_token = NULL; refresh_token->refresh_token = NULL;
} }
} }

@ -37,10 +37,16 @@
#include <grpc/support/slice.h> #include <grpc/support/slice.h>
#include <openssl/rsa.h> #include <openssl/rsa.h>
#include "src/core/json/json.h"
/* --- Constants. --- */ /* --- Constants. --- */
#define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token" #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. --- */ /* --- auth_json_key parsing. --- */
typedef struct { typedef struct {
@ -59,6 +65,10 @@ int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key);
grpc_auth_json_key grpc_auth_json_key_create_from_string( grpc_auth_json_key grpc_auth_json_key_create_from_string(
const char *json_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. */ /* Destructs the object. */
void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key); void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key);
@ -97,6 +107,11 @@ int grpc_auth_refresh_token_is_valid(
grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string( grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string(
const char *json_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. */ /* Destructs the object. */
void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token); void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token);

@ -0,0 +1,832 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimser.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimser
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/security/jwt_verifier.h"
#include <string.h>
#include "src/core/httpcli/httpcli.h"
#include "src/core/security/base64.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;
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;
return claims->iat;
}
gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims) {
if (claims == NULL) return gpr_inf_future;
return claims->exp;
}
gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims) {
if (claims == NULL) return gpr_inf_past;
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;
claims->nbf = gpr_inf_past;
claims->exp = gpr_inf_future;
/* 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) == 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) == 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) == 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};
/* Max delay defaults to one minute. */
gpr_timespec grpc_jwt_verifier_max_delay = {60, 0};
typedef struct {
char *email_domain;
char *key_url_prefix;
} email_key_mapping;
struct grpc_jwt_verifier {
email_key_mapping *mappings;
size_t num_mappings; /* Should be very few, linear search ok. */
size_t allocated_mappings;
grpc_httpcli_context http_ctx;
};
static grpc_json *json_from_http(const grpc_httpcli_response *response) {
grpc_json *json = NULL;
if (response == NULL) {
gpr_log(GPR_ERROR, "HTTP response is NULL.");
return NULL;
}
if (response->status != 200) {
gpr_log(GPR_ERROR, "Call to http server failed with error %d.",
response->status);
return NULL;
}
json = grpc_json_parse_string_with_len(response->body, response->body_length);
if (json == NULL) {
gpr_log(GPR_ERROR, "Invalid JSON found in response.");
}
return json;
}
static const grpc_json *find_property_by_name(const grpc_json *json,
const char *name) {
const grpc_json *cur;
for (cur = json->child; cur != NULL; cur = cur->next) {
if (strcmp(cur->key, name) == 0) return cur;
}
return NULL;
}
static EVP_PKEY *extract_pkey_from_x509(const char *x509_str) {
X509 *x509 = NULL;
EVP_PKEY *result = NULL;
BIO *bio = BIO_new(BIO_s_mem());
BIO_write(bio, x509_str, strlen(x509_str));
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (x509 == NULL) {
gpr_log(GPR_ERROR, "Unable to parse x509 cert.");
goto end;
}
result = X509_get_pubkey(x509);
if (result == NULL) {
gpr_log(GPR_ERROR, "Cannot find public key in X509 cert.");
}
end:
BIO_free(bio);
if (x509 != NULL) X509_free(x509);
return result;
}
static BIGNUM *bignum_from_base64(const char *b64) {
BIGNUM *result = NULL;
gpr_slice bin;
if (b64 == NULL) return NULL;
bin = grpc_base64_decode(b64, 1);
if (GPR_SLICE_IS_EMPTY(bin)) {
gpr_log(GPR_ERROR, "Invalid base64 for big num.");
return NULL;
}
result = BN_bin2bn(GPR_SLICE_START_PTR(bin), GPR_SLICE_LENGTH(bin), NULL);
gpr_slice_unref(bin);
return result;
}
static EVP_PKEY *pkey_from_jwk(const grpc_json *json, const char *kty) {
const grpc_json *key_prop;
RSA *rsa = NULL;
EVP_PKEY *result = NULL;
GPR_ASSERT(kty != NULL && json != NULL);
if (strcmp(kty, "RSA") != 0) {
gpr_log(GPR_ERROR, "Unsupported key type %s.", kty);
goto end;
}
rsa = RSA_new();
if (rsa == NULL) {
gpr_log(GPR_ERROR, "Could not create rsa key.");
goto end;
}
for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) {
if (strcmp(key_prop->key, "n") == 0) {
rsa->n = bignum_from_base64(validate_string_field(key_prop, "n"));
if (rsa->n == NULL) goto end;
} else if (strcmp(key_prop->key, "e") == 0) {
rsa->e = bignum_from_base64(validate_string_field(key_prop, "e"));
if (rsa->e == NULL) goto end;
}
}
if (rsa->e == NULL || rsa->n == NULL) {
gpr_log(GPR_ERROR, "Missing RSA public key field.");
goto end;
}
result = EVP_PKEY_new();
EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */
end:
if (rsa != NULL) RSA_free(rsa);
return result;
}
static EVP_PKEY *find_verification_key(const grpc_json *json,
const char *header_alg,
const char *header_kid) {
const grpc_json *jkey;
const grpc_json *jwk_keys;
/* Try to parse the json as a JWK set:
https://tools.ietf.org/html/rfc7517#section-5. */
jwk_keys = find_property_by_name(json, "keys");
if (jwk_keys == NULL) {
/* Use the google proprietary format which is:
{ <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(void *user_data,
const grpc_httpcli_response *response) {
grpc_json *json = json_from_http(response);
verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
EVP_PKEY *verification_key = NULL;
grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR;
grpc_jwt_claims *claims = NULL;
if (json == NULL) {
status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
goto end;
}
verification_key =
find_verification_key(json, ctx->header->alg, ctx->header->kid);
if (verification_key == NULL) {
gpr_log(GPR_ERROR, "Could not find verification key with kid %s.",
ctx->header->kid);
status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR;
goto end;
}
if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature,
ctx->signed_data)) {
status = GRPC_JWT_VERIFIER_BAD_SIGNATURE;
goto end;
}
status = grpc_jwt_claims_check(ctx->claims, ctx->audience);
if (status == GRPC_JWT_VERIFIER_OK) {
/* Pass ownership. */
claims = ctx->claims;
ctx->claims = NULL;
}
end:
if (json != NULL) grpc_json_destroy(json);
if (verification_key != NULL) EVP_PKEY_free(verification_key);
ctx->user_cb(ctx->user_data, status, claims);
verifier_cb_ctx_destroy(ctx);
}
static void on_openid_config_retrieved(void *user_data,
const grpc_httpcli_response *response) {
const grpc_json *cur;
grpc_json *json = json_from_http(response);
verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data;
grpc_httpcli_request req;
const char *jwks_uri;
/* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time.*/
if (json == NULL) goto error;
cur = find_property_by_name(json, "jwks_uri");
if (cur == NULL) {
gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config.");
goto error;
}
jwks_uri = validate_string_field(cur, "jwks_uri");
if (jwks_uri == NULL) goto error;
if (strstr(jwks_uri, "https://") != jwks_uri) {
gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri);
goto error;
}
jwks_uri += 8;
req.use_ssl = 1;
req.host = gpr_strdup(jwks_uri);
req.path = strchr(jwks_uri, '/');
if (req.path == NULL) {
req.path = "";
} else {
*(req.host + (req.path - jwks_uri)) = '\0';
}
grpc_httpcli_get(
&ctx->verifier->http_ctx, ctx->pollset, &req,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
on_keys_retrieved, ctx);
grpc_json_destroy(json);
gpr_free(req.host);
return;
error:
if (json != NULL) grpc_json_destroy(json);
ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
verifier_cb_ctx_destroy(ctx);
}
static email_key_mapping *verifier_get_mapping(grpc_jwt_verifier *v,
const char *email_domain) {
size_t i;
if (v->mappings == NULL) return NULL;
for (i = 0; i < v->num_mappings; i++) {
if (strcmp(email_domain, v->mappings[i].email_domain) == 0) {
return &v->mappings[i];
}
}
return NULL;
}
static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain,
const char *key_url_prefix) {
email_key_mapping *mapping = verifier_get_mapping(v, email_domain);
GPR_ASSERT(v->num_mappings < v->allocated_mappings);
if (mapping != NULL) {
gpr_free(mapping->key_url_prefix);
mapping->key_url_prefix = gpr_strdup(key_url_prefix);
return;
}
v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain);
v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix);
v->num_mappings++;
GPR_ASSERT(v->num_mappings <= v->allocated_mappings);
}
/* Takes ownership of ctx. */
static void retrieve_key_and_verify(verifier_cb_ctx *ctx) {
const char *at_sign;
grpc_httpcli_response_cb http_cb;
char *path_prefix = NULL;
const char *iss;
grpc_httpcli_request req;
memset(&req, 0, sizeof(grpc_httpcli_request));
req.use_ssl = 1;
GPR_ASSERT(ctx != NULL && ctx->header != NULL && ctx->claims != NULL);
iss = ctx->claims->iss;
if (ctx->header->kid == NULL) {
gpr_log(GPR_ERROR, "Missing kid in jose header.");
goto error;
}
if (iss == NULL) {
gpr_log(GPR_ERROR, "Missing iss in claims.");
goto error;
}
/* This code relies on:
https://openid.net/specs/openid-connect-discovery-1_0.html
Nobody seems to implement the account/email/webfinger part 2. of the spec
so we will rely instead on email/url mappings if we detect such an issuer.
Part 4, on the other hand is implemented by both google and salesforce. */
/* Very non-sophisticated way to detect an email address. Should be good
enough for now... */
at_sign = strchr(iss, '@');
if (at_sign != NULL) {
email_key_mapping *mapping;
const char *email_domain = at_sign + 1;
GPR_ASSERT(ctx->verifier != NULL);
mapping = verifier_get_mapping(ctx->verifier, email_domain);
if (mapping == NULL) {
gpr_log(GPR_ERROR, "Missing mapping for issuer email.");
goto error;
}
req.host = gpr_strdup(mapping->key_url_prefix);
path_prefix = strchr(req.host, '/');
if (path_prefix == NULL) {
gpr_asprintf(&req.path, "/%s", iss);
} else {
*(path_prefix++) = '\0';
gpr_asprintf(&req.path, "/%s/%s", path_prefix, iss);
}
http_cb = on_keys_retrieved;
} else {
req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss);
path_prefix = strchr(req.host, '/');
if (path_prefix == NULL) {
req.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX);
} else {
*(path_prefix++) = 0;
gpr_asprintf(&req.path, "/%s%s", path_prefix,
GRPC_OPENID_CONFIG_URL_SUFFIX);
}
http_cb = on_openid_config_retrieved;
}
grpc_httpcli_get(
&ctx->verifier->http_ctx, ctx->pollset, &req,
gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay),
http_cb, ctx);
gpr_free(req.host);
gpr_free(req.path);
return;
error:
ctx->user_cb(ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, NULL);
verifier_cb_ctx_destroy(ctx);
}
void grpc_jwt_verifier_verify(grpc_jwt_verifier *verifier,
grpc_pollset *pollset, const char *jwt,
const char *audience,
grpc_jwt_verification_done_cb cb,
void *user_data) {
const char *dot = NULL;
grpc_json *json;
jose_header *header = NULL;
grpc_jwt_claims *claims = NULL;
gpr_slice header_buffer;
gpr_slice claims_buffer;
gpr_slice signature;
size_t signed_jwt_len;
const char *cur = jwt;
GPR_ASSERT(verifier != NULL && jwt != NULL && audience != NULL && cb != NULL);
dot = strchr(cur, '.');
if (dot == NULL) goto error;
json = parse_json_part_from_jwt(cur, dot - cur, &header_buffer);
if (json == NULL) goto error;
header = jose_header_from_json(json, header_buffer);
if (header == NULL) goto error;
cur = dot + 1;
dot = strchr(cur, '.');
if (dot == NULL) goto error;
json = parse_json_part_from_jwt(cur, dot - cur, &claims_buffer);
if (json == NULL) goto error;
claims = grpc_jwt_claims_from_json(json, claims_buffer);
if (claims == NULL) goto error;
signed_jwt_len = (size_t)(dot - jwt);
cur = dot + 1;
signature = grpc_base64_decode(cur, 1);
if (GPR_SLICE_IS_EMPTY(signature)) goto error;
retrieve_key_and_verify(
verifier_cb_ctx_create(verifier, pollset, header, claims, audience,
signature, jwt, signed_jwt_len, user_data, cb));
return;
error:
if (header != NULL) jose_header_destroy(header);
if (claims != NULL) grpc_jwt_claims_destroy(claims);
cb(user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, NULL);
}
grpc_jwt_verifier *grpc_jwt_verifier_create(
const grpc_jwt_verifier_email_domain_key_url_mapping *mappings,
size_t num_mappings) {
grpc_jwt_verifier *v = gpr_malloc(sizeof(grpc_jwt_verifier));
memset(v, 0, sizeof(grpc_jwt_verifier));
grpc_httpcli_context_init(&v->http_ctx);
/* We know at least of one mapping. */
v->allocated_mappings = 1 + num_mappings;
v->mappings = gpr_malloc(v->allocated_mappings * sizeof(email_key_mapping));
verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN,
GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX);
/* User-Provided mappings. */
if (mappings != NULL) {
size_t i;
for (i = 0; i < num_mappings; i++) {
verifier_put_mapping(v, mappings[i].email_domain,
mappings[i].key_url_prefix);
}
}
return v;
}
void grpc_jwt_verifier_destroy(grpc_jwt_verifier *v) {
size_t i;
if (v == NULL) return;
grpc_httpcli_context_destroy(&v->http_ctx);
if (v->mappings != NULL) {
for (i = 0; i < v->num_mappings; i++) {
gpr_free(v->mappings[i].email_domain);
gpr_free(v->mappings[i].key_url_prefix);
}
gpr_free(v->mappings);
}
gpr_free(v);
}

@ -0,0 +1,136 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimser.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimser
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H
#define GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H
#include "src/core/iomgr/pollset.h"
#include "src/core/json/json.h"
#include <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_jwt_verifier *verifier,
grpc_pollset *pollset, const char *jwt,
const char *audience,
grpc_jwt_verification_done_cb cb,
void *user_data);
/* --- TESTING ONLY exposed functions. --- */
grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_json *json, gpr_slice buffer);
grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims,
const char *audience);
#endif /* GRPC_INTERNAL_CORE_SECURITY_JWT_VERIFIER_H */

@ -101,9 +101,7 @@ static void call_read_cb(secure_endpoint *ep, gpr_slice *slices, size_t nslices,
if (grpc_trace_secure_endpoint) { if (grpc_trace_secure_endpoint) {
size_t i; size_t i;
for (i = 0; i < nslices; i++) { for (i = 0; i < nslices; i++) {
char *data = char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
gpr_log(GPR_DEBUG, "READ %p: %s", ep, data); gpr_log(GPR_DEBUG, "READ %p: %s", ep, data);
gpr_free(data); gpr_free(data);
} }
@ -235,9 +233,7 @@ static grpc_endpoint_write_status endpoint_write(grpc_endpoint *secure_ep,
if (grpc_trace_secure_endpoint) { if (grpc_trace_secure_endpoint) {
for (i = 0; i < nslices; i++) { for (i = 0; i < nslices; i++) {
char *data = char *data = gpr_dump_slice(slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_hexdump((char *)GPR_SLICE_START_PTR(slices[i]),
GPR_SLICE_LENGTH(slices[i]), GPR_HEXDUMP_PLAINTEXT);
gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data); gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data);
gpr_free(data); gpr_free(data);
} }

@ -47,7 +47,8 @@ typedef struct {
tsi_handshaker *handshaker; tsi_handshaker *handshaker;
unsigned char *handshake_buffer; unsigned char *handshake_buffer;
size_t handshake_buffer_size; size_t handshake_buffer_size;
grpc_endpoint *endpoint; grpc_endpoint *wrapped_endpoint;
grpc_endpoint *secure_endpoint;
gpr_slice_buffer left_overs; gpr_slice_buffer left_overs;
grpc_secure_transport_setup_done_cb cb; grpc_secure_transport_setup_done_cb cb;
void *user_data; void *user_data;
@ -63,13 +64,16 @@ static void on_handshake_data_sent_to_peer(void *setup,
static void secure_transport_setup_done(grpc_secure_transport_setup *s, static void secure_transport_setup_done(grpc_secure_transport_setup *s,
int is_success) { int is_success) {
if (is_success) { if (is_success) {
s->cb(s->user_data, GRPC_SECURITY_OK, s->endpoint); s->cb(s->user_data, GRPC_SECURITY_OK, s->wrapped_endpoint,
s->secure_endpoint);
} else { } else {
if (s->endpoint != NULL) { if (s->secure_endpoint != NULL) {
grpc_endpoint_shutdown(s->endpoint); grpc_endpoint_shutdown(s->secure_endpoint);
grpc_endpoint_destroy(s->endpoint); grpc_endpoint_destroy(s->secure_endpoint);
} else {
grpc_endpoint_destroy(s->wrapped_endpoint);
} }
s->cb(s->user_data, GRPC_SECURITY_ERROR, NULL); s->cb(s->user_data, GRPC_SECURITY_ERROR, s->wrapped_endpoint, NULL);
} }
if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker); if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker);
if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer); if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer);
@ -95,8 +99,9 @@ static void on_peer_checked(void *user_data, grpc_security_status status) {
secure_transport_setup_done(s, 0); secure_transport_setup_done(s, 0);
return; return;
} }
s->endpoint = grpc_secure_endpoint_create( s->secure_endpoint =
protector, s->endpoint, s->left_overs.slices, s->left_overs.count); grpc_secure_endpoint_create(protector, s->wrapped_endpoint,
s->left_overs.slices, s->left_overs.count);
secure_transport_setup_done(s, 1); secure_transport_setup_done(s, 1);
return; return;
} }
@ -152,7 +157,7 @@ static void send_handshake_bytes_to_peer(grpc_secure_transport_setup *s) {
gpr_slice_from_copied_buffer((const char *)s->handshake_buffer, offset); gpr_slice_from_copied_buffer((const char *)s->handshake_buffer, offset);
/* TODO(klempner,jboeuf): This should probably use the client setup /* TODO(klempner,jboeuf): This should probably use the client setup
deadline */ deadline */
write_status = grpc_endpoint_write(s->endpoint, &to_send, 1, write_status = grpc_endpoint_write(s->wrapped_endpoint, &to_send, 1,
on_handshake_data_sent_to_peer, s); on_handshake_data_sent_to_peer, s);
if (write_status == GRPC_ENDPOINT_WRITE_ERROR) { if (write_status == GRPC_ENDPOINT_WRITE_ERROR) {
gpr_log(GPR_ERROR, "Could not send handshake data to peer."); gpr_log(GPR_ERROR, "Could not send handshake data to peer.");
@ -198,7 +203,7 @@ static void on_handshake_data_received_from_peer(
if (result == TSI_INCOMPLETE_DATA) { if (result == TSI_INCOMPLETE_DATA) {
/* TODO(klempner,jboeuf): This should probably use the client setup /* TODO(klempner,jboeuf): This should probably use the client setup
deadline */ deadline */
grpc_endpoint_notify_on_read(s->endpoint, grpc_endpoint_notify_on_read(s->wrapped_endpoint,
on_handshake_data_received_from_peer, setup); on_handshake_data_received_from_peer, setup);
cleanup_slices(slices, nslices); cleanup_slices(slices, nslices);
return; return;
@ -256,7 +261,7 @@ static void on_handshake_data_sent_to_peer(void *setup,
if (tsi_handshaker_is_in_progress(s->handshaker)) { if (tsi_handshaker_is_in_progress(s->handshaker)) {
/* TODO(klempner,jboeuf): This should probably use the client setup /* TODO(klempner,jboeuf): This should probably use the client setup
deadline */ deadline */
grpc_endpoint_notify_on_read(s->endpoint, grpc_endpoint_notify_on_read(s->wrapped_endpoint,
on_handshake_data_received_from_peer, setup); on_handshake_data_received_from_peer, setup);
} else { } else {
check_peer(s); check_peer(s);
@ -280,7 +285,7 @@ void grpc_setup_secure_transport(grpc_security_connector *connector,
GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup"); GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup");
s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE;
s->handshake_buffer = gpr_malloc(s->handshake_buffer_size); s->handshake_buffer = gpr_malloc(s->handshake_buffer_size);
s->endpoint = nonsecure_endpoint; s->wrapped_endpoint = nonsecure_endpoint;
s->user_data = user_data; s->user_data = user_data;
s->cb = cb; s->cb = cb;
gpr_slice_buffer_init(&s->left_overs); gpr_slice_buffer_init(&s->left_overs);

@ -42,7 +42,7 @@
/* Ownership of the secure_endpoint is transfered. */ /* Ownership of the secure_endpoint is transfered. */
typedef void (*grpc_secure_transport_setup_done_cb)( typedef void (*grpc_secure_transport_setup_done_cb)(
void *user_data, grpc_security_status status, void *user_data, grpc_security_status status,
grpc_endpoint *secure_endpoint); grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint);
/* Calls the callback upon completion. */ /* Calls the callback upon completion. */
void grpc_setup_secure_transport(grpc_security_connector *connector, void grpc_setup_secure_transport(grpc_security_connector *connector,

@ -69,12 +69,20 @@ grpc_call_error grpc_call_set_credentials(grpc_call *call,
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
const grpc_auth_context *grpc_call_auth_context(grpc_call *call) { grpc_auth_context *grpc_call_auth_context(grpc_call *call) {
void *sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY); void *sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY);
if (sec_ctx == NULL) return NULL; if (sec_ctx == NULL) return NULL;
return grpc_call_is_client(call) return grpc_call_is_client(call)
? ((grpc_client_security_context *)sec_ctx)->auth_context ? GRPC_AUTH_CONTEXT_REF(
: ((grpc_server_security_context *)sec_ctx)->auth_context; ((grpc_client_security_context *)sec_ctx)->auth_context,
"grpc_call_auth_context client")
: GRPC_AUTH_CONTEXT_REF(
((grpc_server_security_context *)sec_ctx)->auth_context,
"grpc_call_auth_context server");
}
void grpc_auth_context_release(grpc_auth_context *context) {
GRPC_AUTH_CONTEXT_UNREF(context, "grpc_auth_context_unref");
} }
/* --- grpc_client_security_context --- */ /* --- grpc_client_security_context --- */

@ -36,6 +36,10 @@
#include "src/core/security/credentials.h" #include "src/core/security/credentials.h"
#ifdef __cplusplus
extern "C" {
#endif
/* --- grpc_auth_context --- /* --- grpc_auth_context ---
High level authentication context object. Can optionally be chained. */ High level authentication context object. Can optionally be chained. */
@ -103,5 +107,9 @@ typedef struct {
grpc_server_security_context *grpc_server_security_context_create(void); grpc_server_security_context *grpc_server_security_context_create(void);
void grpc_server_security_context_destroy(void *ctx); void grpc_server_security_context_destroy(void *ctx);
#ifdef __cplusplus
}
#endif
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */ #endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */

@ -51,10 +51,16 @@
#include <grpc/support/sync.h> #include <grpc/support/sync.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
typedef struct tcp_endpoint_list {
grpc_endpoint *tcp_endpoint;
struct tcp_endpoint_list *next;
} tcp_endpoint_list;
typedef struct grpc_server_secure_state { typedef struct grpc_server_secure_state {
grpc_server *server; grpc_server *server;
grpc_tcp_server *tcp; grpc_tcp_server *tcp;
grpc_security_connector *sc; grpc_security_connector *sc;
tcp_endpoint_list *handshaking_tcp_endpoints;
int is_shutdown; int is_shutdown;
gpr_mu mu; gpr_mu mu;
gpr_refcount refcount; gpr_refcount refcount;
@ -88,14 +94,37 @@ static void setup_transport(void *statep, grpc_transport *transport,
grpc_channel_args_destroy(args_copy); grpc_channel_args_destroy(args_copy);
} }
static int remove_tcp_from_list_locked(grpc_server_secure_state *state,
grpc_endpoint *tcp) {
tcp_endpoint_list *node = state->handshaking_tcp_endpoints;
tcp_endpoint_list *tmp = NULL;
if (node && node->tcp_endpoint == tcp) {
state->handshaking_tcp_endpoints = state->handshaking_tcp_endpoints->next;
gpr_free(node);
return 0;
}
while (node) {
if (node->next->tcp_endpoint == tcp) {
tmp = node->next;
node->next = node->next->next;
gpr_free(tmp);
return 0;
}
node = node->next;
}
return -1;
}
static void on_secure_transport_setup_done(void *statep, static void on_secure_transport_setup_done(void *statep,
grpc_security_status status, grpc_security_status status,
grpc_endpoint *wrapped_endpoint,
grpc_endpoint *secure_endpoint) { grpc_endpoint *secure_endpoint) {
grpc_server_secure_state *state = statep; grpc_server_secure_state *state = statep;
grpc_transport *transport; grpc_transport *transport;
grpc_mdctx *mdctx; grpc_mdctx *mdctx;
if (status == GRPC_SECURITY_OK) { if (status == GRPC_SECURITY_OK) {
gpr_mu_lock(&state->mu); gpr_mu_lock(&state->mu);
remove_tcp_from_list_locked(state, wrapped_endpoint);
if (!state->is_shutdown) { if (!state->is_shutdown) {
mdctx = grpc_mdctx_create(); mdctx = grpc_mdctx_create();
transport = grpc_create_chttp2_transport( transport = grpc_create_chttp2_transport(
@ -110,6 +139,9 @@ static void on_secure_transport_setup_done(void *statep,
} }
gpr_mu_unlock(&state->mu); gpr_mu_unlock(&state->mu);
} else { } else {
gpr_mu_lock(&state->mu);
remove_tcp_from_list_locked(state, wrapped_endpoint);
gpr_mu_unlock(&state->mu);
gpr_log(GPR_ERROR, "Secure transport failed with error %d", status); gpr_log(GPR_ERROR, "Secure transport failed with error %d", status);
} }
state_unref(state); state_unref(state);
@ -117,7 +149,14 @@ static void on_secure_transport_setup_done(void *statep,
static void on_accept(void *statep, grpc_endpoint *tcp) { static void on_accept(void *statep, grpc_endpoint *tcp) {
grpc_server_secure_state *state = statep; grpc_server_secure_state *state = statep;
tcp_endpoint_list *node;
state_ref(state); state_ref(state);
node = gpr_malloc(sizeof(tcp_endpoint_list));
node->tcp_endpoint = tcp;
gpr_mu_lock(&state->mu);
node->next = state->handshaking_tcp_endpoints;
state->handshaking_tcp_endpoints = node;
gpr_mu_unlock(&state->mu);
grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done, grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done,
state); state);
} }
@ -132,6 +171,13 @@ static void start(grpc_server *server, void *statep, grpc_pollset **pollsets,
static void destroy_done(void *statep) { static void destroy_done(void *statep) {
grpc_server_secure_state *state = statep; grpc_server_secure_state *state = statep;
grpc_server_listener_destroy_done(state->server); grpc_server_listener_destroy_done(state->server);
gpr_mu_lock(&state->mu);
while (state->handshaking_tcp_endpoints != NULL) {
grpc_endpoint_shutdown(state->handshaking_tcp_endpoints->tcp_endpoint);
remove_tcp_from_list_locked(state,
state->handshaking_tcp_endpoints->tcp_endpoint);
}
gpr_mu_unlock(&state->mu);
state_unref(state); state_unref(state);
} }
@ -209,6 +255,7 @@ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr,
state->server = server; state->server = server;
state->tcp = tcp; state->tcp = tcp;
state->sc = sc; state->sc = sc;
state->handshaking_tcp_endpoints = NULL;
state->is_shutdown = 0; state->is_shutdown = 0;
gpr_mu_init(&state->mu); gpr_mu_init(&state->mu);
gpr_ref_init(&state->refcount, 1); gpr_ref_init(&state->refcount, 1);

@ -157,7 +157,7 @@ static void record_stats(census_ht* store, census_op_id op_id,
key.ptr = gpr_strdup(key.ptr); key.ptr = gpr_strdup(key.ptr);
census_ht_insert(store, key, (void*)window_stats); census_ht_insert(store, key, (void*)window_stats);
} }
census_window_stats_add(window_stats, gpr_now(), stats); census_window_stats_add(window_stats, gpr_now(GPR_CLOCK_REALTIME), stats);
} else { } else {
census_internal_unlock_trace_store(); census_internal_unlock_trace_store();
} }
@ -185,7 +185,7 @@ static void get_stats(census_ht* store, census_aggregated_rpc_stats* data) {
if (store != NULL) { if (store != NULL) {
size_t n; size_t n;
unsigned i, j; unsigned i, j;
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
census_ht_kv* kv = census_ht_get_all_elements(store, &n); census_ht_kv* kv = census_ht_get_all_elements(store, &n);
if (kv != NULL) { if (kv != NULL) {
data->num_entries = n; data->num_entries = n;

@ -94,7 +94,7 @@ census_op_id census_tracing_start_op(void) {
g_id++; g_id++;
memcpy(&ret->id, &g_id, sizeof(census_op_id)); memcpy(&ret->id, &g_id, sizeof(census_op_id));
ret->rpc_stats.cnt = 1; ret->rpc_stats.cnt = 1;
ret->ts = gpr_now(); ret->ts = gpr_now(GPR_CLOCK_REALTIME);
census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void*)ret); 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_log(GPR_DEBUG, "Start tracing for id %lu", g_id);
gpr_mu_unlock(&g_mu); gpr_mu_unlock(&g_mu);
@ -122,7 +122,7 @@ void census_tracing_print(census_op_id op_id, const char* anno_txt) {
trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
if (trace != NULL) { if (trace != NULL) {
census_trace_annotation* anno = gpr_malloc(sizeof(census_trace_annotation)); census_trace_annotation* anno = gpr_malloc(sizeof(census_trace_annotation));
anno->ts = gpr_now(); anno->ts = gpr_now(GPR_CLOCK_REALTIME);
{ {
char* d = anno->txt; char* d = anno->txt;
const char* s = anno_txt; const char* s = anno_txt;
@ -143,8 +143,8 @@ void census_tracing_end_op(census_op_id op_id) {
gpr_mu_lock(&g_mu); gpr_mu_lock(&g_mu);
trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); trace = census_ht_find(g_trace_store, op_id_as_key(&op_id));
if (trace != NULL) { if (trace != NULL) {
trace->rpc_stats.elapsed_time_ms = trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros(
gpr_timespec_to_micros(gpr_time_sub(gpr_now(), trace->ts)); gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), trace->ts));
gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us", gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us",
op_id_2_uint64(&op_id), trace->method, op_id_2_uint64(&op_id), trace->method,
trace->rpc_stats.elapsed_time_ms); trace->rpc_stats.elapsed_time_ms);
@ -194,8 +194,8 @@ const char* census_get_trace_method_name(const census_trace_obj* trace) {
static census_trace_annotation* dup_annotation_chain( static census_trace_annotation* dup_annotation_chain(
census_trace_annotation* from) { census_trace_annotation* from) {
census_trace_annotation *ret = NULL; census_trace_annotation* ret = NULL;
census_trace_annotation **to = &ret; census_trace_annotation** to = &ret;
for (; from != NULL; from = from->next) { for (; from != NULL; from = from->next) {
*to = gpr_malloc(sizeof(census_trace_annotation)); *to = gpr_malloc(sizeof(census_trace_annotation));
memcpy(*to, from, sizeof(census_trace_annotation)); memcpy(*to, from, sizeof(census_trace_annotation));
@ -223,9 +223,9 @@ census_trace_obj** census_get_active_ops(int* num_active_ops) {
size_t n = 0; size_t n = 0;
census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n); census_ht_kv* all_kvs = census_ht_get_all_elements(g_trace_store, &n);
*num_active_ops = (int)n; *num_active_ops = (int)n;
if (n != 0 ) { if (n != 0) {
size_t i = 0; size_t i = 0;
ret = gpr_malloc(sizeof(census_trace_obj *) * n); ret = gpr_malloc(sizeof(census_trace_obj*) * n);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
ret[i] = trace_obj_dup((census_trace_obj*)all_kvs[i].v); ret[i] = trace_obj_dup((census_trace_obj*)all_kvs[i].v);
} }

@ -90,11 +90,11 @@
// Record a new event, taking 15.3ms, transferring 1784 bytes. // Record a new event, taking 15.3ms, transferring 1784 bytes.
stat.latency = 0.153; stat.latency = 0.153;
stat.bytes = 1784; stat.bytes = 1784;
census_window_stats_add(stats, gpr_now(), &stat); census_window_stats_add(stats, gpr_now(GPR_CLOCK_REALTIME), &stat);
// Get sums and print them out // Get sums and print them out
result[kMinInterval].statistic = &sums[kMinInterval]; result[kMinInterval].statistic = &sums[kMinInterval];
result[kHourInterval].statistic = &sums[kHourInterval]; result[kHourInterval].statistic = &sums[kHourInterval];
census_window_stats_get_sums(stats, gpr_now(), result); census_window_stats_get_sums(stats, gpr_now(GPR_CLOCK_REALTIME), result);
printf("%d events/min, average time %gs, average bytes %g\n", printf("%d events/min, average time %gs, average bytes %g\n",
result[kMinInterval].count, result[kMinInterval].count,
(my_stat*)result[kMinInterval].statistic->latency / (my_stat*)result[kMinInterval].statistic->latency /
@ -170,4 +170,4 @@ void census_window_stats_get_sums(const struct census_window_stats* wstats,
assertion failure). This function is thread-compatible. */ assertion failure). This function is thread-compatible. */
void census_window_stats_destroy(struct census_window_stats* wstats); void census_window_stats_destroy(struct census_window_stats* wstats);
#endif /* GRPC_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H */ #endif /* GRPC_INTERNAL_CORE_STATISTICS_WINDOW_STATS_H */

@ -121,8 +121,8 @@ void gpr_cancellable_cancel(gpr_cancellable *c) {
} else { } else {
gpr_event ev; gpr_event ev;
gpr_event_init(&ev); gpr_event_init(&ev);
gpr_event_wait(&ev, gpr_event_wait(&ev, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_add(gpr_now(), gpr_time_from_micros(1000))); gpr_time_from_micros(1000)));
} }
} }
} while (failures != 0); } while (failures != 0);

@ -43,7 +43,9 @@
#ifdef GPR_LINUX #ifdef GPR_LINUX
#include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
@ -71,9 +73,10 @@ void gpr_log(const char *file, int line, gpr_log_severity severity,
void gpr_default_log(gpr_log_func_args *args) { void gpr_default_log(gpr_log_func_args *args) {
char *final_slash; char *final_slash;
char *prefix;
const char *display_file; const char *display_file;
char time_buffer[64]; char time_buffer[64];
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
struct tm tm; struct tm tm;
final_slash = strrchr(args->file, '/'); final_slash = strrchr(args->file, '/');
@ -89,10 +92,12 @@ void gpr_default_log(gpr_log_func_args *args) {
strcpy(time_buffer, "error:strftime"); strcpy(time_buffer, "error:strftime");
} }
fprintf(stderr, "%s%s.%09d %7ld %s:%d] %s\n", gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]",
gpr_log_severity_string(args->severity), time_buffer, gpr_log_severity_string(args->severity), time_buffer,
(int)(now.tv_nsec), gettid(), display_file, args->line, (int)(now.tv_nsec), gettid(), display_file, args->line);
args->message);
fprintf(stderr, "%-60s %s\n", prefix, args->message);
gpr_free(prefix);
} }
#endif #endif

@ -75,7 +75,7 @@ void gpr_default_log(gpr_log_func_args *args) {
char *final_slash; char *final_slash;
const char *display_file; const char *display_file;
char time_buffer[64]; char time_buffer[64];
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
struct tm tm; struct tm tm;
final_slash = strrchr(args->file, '/'); final_slash = strrchr(args->file, '/');

@ -82,7 +82,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity,
/* Simple starter implementation */ /* Simple starter implementation */
void gpr_default_log(gpr_log_func_args *args) { void gpr_default_log(gpr_log_func_args *args) {
char time_buffer[64]; char time_buffer[64];
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
struct tm tm; struct tm tm;
if (localtime_s(&tm, &now.tv_sec)) { if (localtime_s(&tm, &now.tv_sec)) {

@ -325,3 +325,10 @@ int gpr_slice_str_cmp(gpr_slice a, const char *b) {
if (d != 0) return d; if (d != 0) return d;
return memcmp(GPR_SLICE_START_PTR(a), b, b_length); return memcmp(GPR_SLICE_START_PTR(a), b, b_length);
} }
char *gpr_slice_to_cstring(gpr_slice slice) {
char *result = gpr_malloc(GPR_SLICE_LENGTH(slice) + 1);
memcpy(result, GPR_SLICE_START_PTR(slice), GPR_SLICE_LENGTH(slice));
result[GPR_SLICE_LENGTH(slice)] = '\0';
return result;
}

@ -0,0 +1,130 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "src/core/support/stack_lockfree.h"
#include <stdlib.h>
#include <string.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/alloc.h>
#include <grpc/support/atm.h>
#include <grpc/support/log.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 */
gpr_uint16 index;
#ifdef GPR_ARCH_64
gpr_uint16 pad;
gpr_uint32 aba_ctr;
#else
#ifdef GPR_ARCH_32
gpr_uint16 aba_ctr;
#else
#error Unsupported bit width architecture
#endif
#endif
};
/* Use a union to make sure that these are in the same bits as an atm word */
typedef union lockfree_node {
gpr_atm atm;
struct lockfree_node_contents contents;
} lockfree_node;
#define ENTRY_ALIGNMENT_BITS 3 /* make sure that entries aligned to 8-bytes */
#define INVALID_ENTRY_INDEX ((1 << 16) - 1) /* reserve this entry as invalid \
*/
struct gpr_stack_lockfree {
lockfree_node *entries;
lockfree_node head; /* An atomic entry describing curr head */
};
gpr_stack_lockfree *gpr_stack_lockfree_create(int entries) {
gpr_stack_lockfree *stack;
stack = gpr_malloc(sizeof(*stack));
/* Since we only allocate 16 bits to represent an entry number,
* make sure that we are within the desired range */
/* Reserve the highest entry number as a dummy */
GPR_ASSERT(entries < INVALID_ENTRY_INDEX);
stack->entries = gpr_malloc_aligned(entries * sizeof(stack->entries[0]),
ENTRY_ALIGNMENT_BITS);
/* Clear out all entries */
memset(stack->entries, 0, entries * sizeof(stack->entries[0]));
memset(&stack->head, 0, sizeof(stack->head));
/* Point the head at reserved dummy entry */
stack->head.contents.index = INVALID_ENTRY_INDEX;
return stack;
}
void gpr_stack_lockfree_destroy(gpr_stack_lockfree *stack) {
gpr_free_aligned(stack->entries);
gpr_free(stack);
}
void gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) {
lockfree_node head;
lockfree_node newhead;
/* First fill in the entry's index and aba ctr for new head */
newhead.contents.index = (gpr_uint16)entry;
/* Also post-increment the aba_ctr */
newhead.contents.aba_ctr = stack->entries[entry].contents.aba_ctr++;
do {
/* Atomically get the existing head value for use */
head.atm = gpr_atm_no_barrier_load(&(stack->head.atm));
/* Point to it */
stack->entries[entry].contents.index = head.contents.index;
} 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 */
}
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));
return head.contents.index;
}

@ -0,0 +1,50 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H
#define GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H
typedef struct gpr_stack_lockfree gpr_stack_lockfree;
/* This stack must specify the maximum number of entries to track.
The current implementation only allows up to 65534 entries */
gpr_stack_lockfree* gpr_stack_lockfree_create(int entries);
void gpr_stack_lockfree_destroy(gpr_stack_lockfree* stack);
/* Pass in a valid entry number for the next stack entry */
void gpr_stack_lockfree_push(gpr_stack_lockfree* stack, int entry);
/* Returns -1 on empty or the actual entry number */
int gpr_stack_lockfree_pop(gpr_stack_lockfree* stack);
#endif /* GRPC_INTERNAL_CORE_SUPPORT_STACK_LOCKFREE_H */

@ -61,14 +61,14 @@ typedef struct {
size_t capacity; size_t capacity;
size_t length; size_t length;
char *data; char *data;
} hexout; } dump_out;
static hexout hexout_create(void) { static dump_out dump_out_create(void) {
hexout r = {0, 0, NULL}; dump_out r = {0, 0, NULL};
return r; return r;
} }
static void hexout_append(hexout *out, char c) { static void dump_out_append(dump_out *out, char c) {
if (out->length == out->capacity) { if (out->length == out->capacity) {
out->capacity = GPR_MAX(8, 2 * out->capacity); out->capacity = GPR_MAX(8, 2 * out->capacity);
out->data = gpr_realloc(out->data, out->capacity); out->data = gpr_realloc(out->data, out->capacity);
@ -76,34 +76,55 @@ static void hexout_append(hexout *out, char c) {
out->data[out->length++] = c; out->data[out->length++] = c;
} }
char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags) { static void hexdump(dump_out *out, const char *buf, size_t len) {
static const char hex[16] = "0123456789abcdef"; static const char hex[16] = "0123456789abcdef";
hexout out = hexout_create();
const gpr_uint8 *const beg = (const gpr_uint8 *)buf; const gpr_uint8 *const beg = (const gpr_uint8 *)buf;
const gpr_uint8 *const end = beg + len; const gpr_uint8 *const end = beg + len;
const gpr_uint8 *cur; const gpr_uint8 *cur;
for (cur = beg; cur != end; ++cur) { for (cur = beg; cur != end; ++cur) {
if (cur != beg) hexout_append(&out, ' '); if (cur != beg) dump_out_append(out, ' ');
hexout_append(&out, hex[*cur >> 4]); dump_out_append(out, hex[*cur >> 4]);
hexout_append(&out, hex[*cur & 0xf]); dump_out_append(out, hex[*cur & 0xf]);
} }
}
if (flags & GPR_HEXDUMP_PLAINTEXT) { static void asciidump(dump_out *out, const char *buf, size_t len) {
if (len) hexout_append(&out, ' '); const gpr_uint8 *const beg = (const gpr_uint8 *)buf;
hexout_append(&out, '\''); const gpr_uint8 *const end = beg + len;
for (cur = beg; cur != end; ++cur) { const gpr_uint8 *cur;
hexout_append(&out, isprint(*cur) ? *(char*)cur : '.'); int out_was_empty = (out->length == 0);
} if (!out_was_empty) {
hexout_append(&out, '\''); dump_out_append(out, ' ');
dump_out_append(out, '\'');
} }
for (cur = beg; cur != end; ++cur) {
dump_out_append(out, isprint(*cur) ? *(char *)cur : '.');
}
if (!out_was_empty) {
dump_out_append(out, '\'');
}
}
hexout_append(&out, 0); char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags) {
dump_out out = dump_out_create();
if (flags & GPR_DUMP_HEX) {
hexdump(&out, buf, len);
}
if (flags & GPR_DUMP_ASCII) {
asciidump(&out, buf, len);
}
dump_out_append(&out, 0);
return out.data; return out.data;
} }
char *gpr_dump_slice(gpr_slice s, gpr_uint32 flags) {
return gpr_dump((const char *)GPR_SLICE_START_PTR(s), GPR_SLICE_LENGTH(s),
flags);
}
int gpr_parse_bytes_to_uint32(const char *buf, size_t len, gpr_uint32 *result) { int gpr_parse_bytes_to_uint32(const char *buf, size_t len, gpr_uint32 *result) {
gpr_uint32 out = 0; gpr_uint32 out = 0;
gpr_uint32 new; gpr_uint32 new;

@ -37,6 +37,7 @@
#include <stddef.h> #include <stddef.h>
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#include <grpc/support/slice.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -44,12 +45,16 @@ extern "C" {
/* String utility functions */ /* String utility functions */
/* flag to include plaintext after a hexdump */ /* Flags for gpr_dump function. */
#define GPR_HEXDUMP_PLAINTEXT 0x00000001 #define GPR_DUMP_HEX 0x00000001
#define GPR_DUMP_ASCII 0x00000002
/* Converts array buf, of length len, into a hexadecimal dump. Result should /* Converts array buf, of length len, into a C string according to the flags.
be freed with gpr_free() */ Result should be freed with gpr_free() */
char *gpr_hexdump(const char *buf, size_t len, gpr_uint32 flags); char *gpr_dump(const char *buf, size_t len, gpr_uint32 flags);
/* Calls gpr_dump on a slice. */
char *gpr_dump_slice(gpr_slice slice, gpr_uint32 flags);
/* Parses an array of bytes into an integer (base 10). Returns 1 on success, /* Parses an array of bytes into an integer (base 10). Returns 1 on success,
0 on failure. */ 0 on failure. */

@ -86,7 +86,7 @@ int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) {
if (gpr_time_cmp(abs_deadline, gpr_inf_future) == 0) { if (gpr_time_cmp(abs_deadline, gpr_inf_future) == 0) {
SleepConditionVariableCS(cv, &mu->cs, INFINITE); SleepConditionVariableCS(cv, &mu->cs, INFINITE);
} else { } else {
gpr_timespec now = gpr_now(); gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
gpr_int64 now_ms = now.tv_sec * 1000 + now.tv_nsec / 1000000; gpr_int64 now_ms = now.tv_sec * 1000 + now.tv_nsec / 1000000;
gpr_int64 deadline_ms = gpr_int64 deadline_ms =
abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000;

@ -55,22 +55,52 @@ static gpr_timespec gpr_from_timespec(struct timespec ts) {
return rv; return rv;
} }
gpr_timespec gpr_now(void) { /** maps gpr_clock_type --> clockid_t for clock_gettime */
static clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, CLOCK_REALTIME};
void gpr_time_init(void) {}
gpr_timespec gpr_now(gpr_clock_type clock) {
struct timespec now; struct timespec now;
clock_gettime(CLOCK_REALTIME, &now); clock_gettime(clockid_for_gpr_clock[clock], &now);
return gpr_from_timespec(now); return gpr_from_timespec(now);
} }
#else #else
/* For some reason Apple's OSes haven't implemented clock_gettime. */ /* For some reason Apple's OSes haven't implemented clock_gettime. */
#include <sys/time.h> #include <sys/time.h>
#include <mach/mach.h>
#include <mach/mach_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};
mach_timebase_info(&tb);
g_time_scale = tb.numer;
g_time_scale /= tb.denom;
g_time_start = mach_absolute_time();
}
gpr_timespec gpr_now(void) { gpr_timespec gpr_now(gpr_clock_type clock) {
gpr_timespec now; gpr_timespec now;
struct timeval now_tv; struct timeval now_tv;
gettimeofday(&now_tv, NULL); double now_dbl;
now.tv_sec = now_tv.tv_sec;
now.tv_nsec = now_tv.tv_usec * 1000; switch (clock) {
case GPR_CLOCK_REALTIME:
gettimeofday(&now_tv, NULL);
now.tv_sec = now_tv.tv_sec;
now.tv_nsec = now_tv.tv_usec * 1000;
break;
case GPR_CLOCK_MONOTONIC:
now_dbl = (mach_absolute_time() - g_time_start) * g_time_scale;
now.tv_sec = now_dbl * 1e-9;
now.tv_nsec = now_dbl - now.tv_sec * 1e9;
break;
}
return now; return now;
} }
#endif #endif
@ -83,7 +113,7 @@ void gpr_sleep_until(gpr_timespec until) {
for (;;) { for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be /* We could simplify by using clock_nanosleep instead, but it might be
* slightly less portable. */ * slightly less portable. */
now = gpr_now(); now = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(until, now) <= 0) { if (gpr_time_cmp(until, now) <= 0) {
return; return;
} }

@ -40,12 +40,34 @@
#include <grpc/support/time.h> #include <grpc/support/time.h>
#include <sys/timeb.h> #include <sys/timeb.h>
gpr_timespec gpr_now(void) { static LARGE_INTEGER g_start_time;
static double g_time_scale;
void gpr_time_init(void) {
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&g_start_time);
g_time_scale = 1.0 / frequency.QuadPart;
}
gpr_timespec gpr_now(gpr_clock_type clock) {
gpr_timespec now_tv; gpr_timespec now_tv;
struct _timeb now_tb; struct _timeb now_tb;
_ftime_s(&now_tb); LARGE_INTEGER timestamp;
now_tv.tv_sec = now_tb.time; double now_dbl;
now_tv.tv_nsec = now_tb.millitm * 1000000; switch (clock) {
case GPR_CLOCK_REALTIME:
_ftime_s(&now_tb);
now_tv.tv_sec = now_tb.time;
now_tv.tv_nsec = now_tb.millitm * 1000000;
break;
case GPR_CLOCK_MONOTONIC:
QueryPerformanceCounter(&timestamp);
now_dbl = (timestamp.QuadPart - g_start_time.QuadPart) * g_time_scale;
now_tv.tv_sec = (time_t)now_dbl;
now_tv.tv_nsec = (int)((now_dbl - (double)now_tv.tv_sec) * 1e9);
break;
}
return now_tv; return now_tv;
} }
@ -57,13 +79,14 @@ void gpr_sleep_until(gpr_timespec until) {
for (;;) { for (;;) {
/* We could simplify by using clock_nanosleep instead, but it might be /* We could simplify by using clock_nanosleep instead, but it might be
* slightly less portable. */ * slightly less portable. */
now = gpr_now(); now = gpr_now(GPR_CLOCK_REALTIME);
if (gpr_time_cmp(until, now) <= 0) { if (gpr_time_cmp(until, now) <= 0) {
return; return;
} }
delta = gpr_time_sub(until, now); delta = gpr_time_sub(until, now);
sleep_millis = (DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; sleep_millis =
(DWORD)delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS;
Sleep(sleep_millis); Sleep(sleep_millis);
} }
} }

@ -62,6 +62,7 @@ int grpc_bbq_empty(grpc_byte_buffer_queue *q) {
} }
void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) { void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *buffer) {
q->bytes += grpc_byte_buffer_length(buffer);
bba_push(&q->filling, buffer); bba_push(&q->filling, buffer);
} }
@ -72,8 +73,11 @@ void grpc_bbq_flush(grpc_byte_buffer_queue *q) {
} }
} }
size_t grpc_bbq_bytes(grpc_byte_buffer_queue *q) { return q->bytes; }
grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) { grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) {
grpc_bbq_array temp_array; grpc_bbq_array temp_array;
grpc_byte_buffer *out;
if (q->drain_pos == q->draining.count) { if (q->drain_pos == q->draining.count) {
if (q->filling.count == 0) { if (q->filling.count == 0) {
@ -87,5 +91,7 @@ grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q) {
q->draining = temp_array; q->draining = temp_array;
} }
return q->draining.data[q->drain_pos++]; out = q->draining.data[q->drain_pos++];
q->bytes -= grpc_byte_buffer_length(out);
return out;
} }

@ -49,6 +49,7 @@ typedef struct {
size_t drain_pos; size_t drain_pos;
grpc_bbq_array filling; grpc_bbq_array filling;
grpc_bbq_array draining; grpc_bbq_array draining;
size_t bytes;
} grpc_byte_buffer_queue; } grpc_byte_buffer_queue;
void grpc_bbq_destroy(grpc_byte_buffer_queue *q); void grpc_bbq_destroy(grpc_byte_buffer_queue *q);
@ -56,5 +57,6 @@ grpc_byte_buffer *grpc_bbq_pop(grpc_byte_buffer_queue *q);
void grpc_bbq_flush(grpc_byte_buffer_queue *q); void grpc_bbq_flush(grpc_byte_buffer_queue *q);
int grpc_bbq_empty(grpc_byte_buffer_queue *q); int grpc_bbq_empty(grpc_byte_buffer_queue *q);
void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *bb); void grpc_bbq_push(grpc_byte_buffer_queue *q, grpc_byte_buffer *bb);
size_t grpc_bbq_bytes(grpc_byte_buffer_queue *q);
#endif /* GRPC_INTERNAL_CORE_SURFACE_BYTE_BUFFER_QUEUE_H */ #endif /* GRPC_INTERNAL_CORE_SURFACE_BYTE_BUFFER_QUEUE_H */

@ -49,6 +49,17 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
/** The maximum number of completions possible.
Based upon the maximum number of individually queueable ops in the batch
api:
- initial metadata send
- message send
- status/close send (depending on client/server)
- initial metadata recv
- message recv
- status/close recv (depending on client/server) */
#define MAX_CONCURRENT_COMPLETIONS 6
typedef enum { REQ_INITIAL = 0, REQ_READY, REQ_DONE } req_state; typedef enum { REQ_INITIAL = 0, REQ_READY, REQ_DONE } req_state;
typedef enum { typedef enum {
@ -76,14 +87,14 @@ typedef struct {
typedef struct { typedef struct {
/* Overall status of the operation: starts OK, may degrade to /* Overall status of the operation: starts OK, may degrade to
non-OK */ non-OK */
int success; gpr_uint8 success;
/* Completion function to call at the end of the operation */
grpc_ioreq_completion_func on_complete;
void *user_data;
/* a bit mask of which request ops are needed (1u << opid) */ /* a bit mask of which request ops are needed (1u << opid) */
gpr_uint16 need_mask; gpr_uint16 need_mask;
/* a bit mask of which request ops are now completed */ /* a bit mask of which request ops are now completed */
gpr_uint16 complete_mask; gpr_uint16 complete_mask;
/* Completion function to call at the end of the operation */
grpc_ioreq_completion_func on_complete;
void *user_data;
} reqinfo_master; } reqinfo_master;
/* Status data for a request can come from several sources; this /* Status data for a request can come from several sources; this
@ -135,6 +146,7 @@ struct grpc_call {
grpc_mdctx *metadata_context; grpc_mdctx *metadata_context;
/* TODO(ctiller): share with cq if possible? */ /* TODO(ctiller): share with cq if possible? */
gpr_mu mu; gpr_mu mu;
gpr_mu completion_mu;
/* how far through the stream have we read? */ /* how far through the stream have we read? */
read_state read_state; read_state read_state;
@ -162,6 +174,8 @@ struct grpc_call {
gpr_uint8 error_status_set; gpr_uint8 error_status_set;
/** should the alarm be cancelled */ /** should the alarm be cancelled */
gpr_uint8 cancel_alarm; gpr_uint8 cancel_alarm;
/** bitmask of allocated completion events in completions */
gpr_uint8 allocated_completions;
/* flags with bits corresponding to write states allowing us to determine /* flags with bits corresponding to write states allowing us to determine
what was sent */ what was sent */
@ -250,6 +264,9 @@ struct grpc_call {
grpc_iomgr_closure on_done_recv; grpc_iomgr_closure on_done_recv;
grpc_iomgr_closure on_done_send; grpc_iomgr_closure on_done_send;
grpc_iomgr_closure on_done_bind; grpc_iomgr_closure on_done_bind;
/** completion events - for completion queue use */
grpc_cq_completion completions[MAX_CONCURRENT_COMPLETIONS];
}; };
#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1)) #define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1))
@ -286,6 +303,7 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size); gpr_malloc(sizeof(grpc_call) + channel_stack->call_stack_size);
memset(call, 0, sizeof(grpc_call)); memset(call, 0, sizeof(grpc_call));
gpr_mu_init(&call->mu); gpr_mu_init(&call->mu);
gpr_mu_init(&call->completion_mu);
call->channel = channel; call->channel = channel;
call->cq = cq; call->cq = cq;
if (cq) { if (cq) {
@ -298,8 +316,6 @@ grpc_call *grpc_call_create(grpc_channel *channel, grpc_completion_queue *cq,
if (call->is_client) { if (call->is_client) {
call->request_set[GRPC_IOREQ_SEND_TRAILING_METADATA] = REQSET_DONE; call->request_set[GRPC_IOREQ_SEND_TRAILING_METADATA] = REQSET_DONE;
call->request_set[GRPC_IOREQ_SEND_STATUS] = REQSET_DONE; call->request_set[GRPC_IOREQ_SEND_STATUS] = REQSET_DONE;
call->context[GRPC_CONTEXT_TRACING].value = grpc_census_context_create();
call->context[GRPC_CONTEXT_TRACING].destroy = grpc_census_context_destroy;
} }
GPR_ASSERT(add_initial_metadata_count < MAX_SEND_INITIAL_METADATA_COUNT); GPR_ASSERT(add_initial_metadata_count < MAX_SEND_INITIAL_METADATA_COUNT);
for (i = 0; i < add_initial_metadata_count; i++) { for (i = 0; i < add_initial_metadata_count; i++) {
@ -351,6 +367,29 @@ grpc_completion_queue *grpc_call_get_completion_queue(grpc_call *call) {
return call->cq; return call->cq;
} }
static grpc_cq_completion *allocate_completion(grpc_call *call) {
gpr_uint8 i;
gpr_mu_lock(&call->completion_mu);
for (i = 0; i < GPR_ARRAY_SIZE(call->completions); i++) {
if (call->allocated_completions & (1u << i)) {
continue;
}
call->allocated_completions |= 1u << i;
gpr_mu_unlock(&call->completion_mu);
return &call->completions[i];
}
gpr_log(GPR_ERROR, "should never reach here");
abort();
}
static void done_completion(void *call, grpc_cq_completion *completion) {
grpc_call *c = call;
gpr_mu_lock(&c->completion_mu);
c->allocated_completions &= ~(1u << (completion - c->completions));
gpr_mu_unlock(&c->completion_mu);
GRPC_CALL_INTERNAL_UNREF(c, "completion", 1);
}
#ifdef GRPC_CALL_REF_COUNT_DEBUG #ifdef GRPC_CALL_REF_COUNT_DEBUG
void grpc_call_internal_ref(grpc_call *c, const char *reason) { void grpc_call_internal_ref(grpc_call *c, const char *reason) {
gpr_log(GPR_DEBUG, "CALL: ref %p %d -> %d [%s]", c, gpr_log(GPR_DEBUG, "CALL: ref %p %d -> %d [%s]", c,
@ -367,20 +406,21 @@ static void destroy_call(void *call, int ignored_success) {
grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c)); grpc_call_stack_destroy(CALL_STACK_FROM_CALL(c));
GRPC_CHANNEL_INTERNAL_UNREF(c->channel, "call"); GRPC_CHANNEL_INTERNAL_UNREF(c->channel, "call");
gpr_mu_destroy(&c->mu); gpr_mu_destroy(&c->mu);
gpr_mu_destroy(&c->completion_mu);
for (i = 0; i < STATUS_SOURCE_COUNT; i++) { for (i = 0; i < STATUS_SOURCE_COUNT; i++) {
if (c->status[i].details) { if (c->status[i].details) {
grpc_mdstr_unref(c->status[i].details); GRPC_MDSTR_UNREF(c->status[i].details);
} }
} }
for (i = 0; i < c->owned_metadata_count; i++) { for (i = 0; i < c->owned_metadata_count; i++) {
grpc_mdelem_unref(c->owned_metadata[i]); GRPC_MDELEM_UNREF(c->owned_metadata[i]);
} }
gpr_free(c->owned_metadata); gpr_free(c->owned_metadata);
for (i = 0; i < GPR_ARRAY_SIZE(c->buffered_metadata); i++) { for (i = 0; i < GPR_ARRAY_SIZE(c->buffered_metadata); i++) {
gpr_free(c->buffered_metadata[i].metadata); gpr_free(c->buffered_metadata[i].metadata);
} }
for (i = 0; i < c->send_initial_metadata_count; i++) { for (i = 0; i < c->send_initial_metadata_count; i++) {
grpc_mdelem_unref(c->send_initial_metadata[i].md); GRPC_MDELEM_UNREF(c->send_initial_metadata[i].md);
} }
for (i = 0; i < GRPC_CONTEXT_COUNT; i++) { for (i = 0; i < GRPC_CONTEXT_COUNT; i++) {
if (c->context[i].destroy) { if (c->context[i].destroy) {
@ -437,7 +477,7 @@ static void set_decode_compression_level(grpc_call *call,
static void set_status_details(grpc_call *call, status_source source, static void set_status_details(grpc_call *call, status_source source,
grpc_mdstr *status) { grpc_mdstr *status) {
if (call->status[source].details != NULL) { if (call->status[source].details != NULL) {
grpc_mdstr_unref(call->status[source].details); GRPC_MDSTR_UNREF(call->status[source].details);
} }
call->status[source].details = status; call->status[source].details = status;
} }
@ -473,6 +513,8 @@ static void unlock(grpc_call *call) {
int completing_requests = 0; int completing_requests = 0;
int start_op = 0; int start_op = 0;
int i; int i;
const gpr_uint32 MAX_RECV_PEEK_AHEAD = 65536;
size_t buffered_bytes;
int cancel_alarm = 0; int cancel_alarm = 0;
memset(&op, 0, sizeof(op)); memset(&op, 0, sizeof(op));
@ -488,6 +530,17 @@ static void unlock(grpc_call *call) {
op.recv_ops = &call->recv_ops; op.recv_ops = &call->recv_ops;
op.recv_state = &call->recv_state; op.recv_state = &call->recv_state;
op.on_done_recv = &call->on_done_recv; op.on_done_recv = &call->on_done_recv;
if (grpc_bbq_empty(&call->incoming_queue) && call->reading_message) {
op.max_recv_bytes = call->incoming_message_length -
call->incoming_message.length + MAX_RECV_PEEK_AHEAD;
} else {
buffered_bytes = grpc_bbq_bytes(&call->incoming_queue);
if (buffered_bytes > MAX_RECV_PEEK_AHEAD) {
op.max_recv_bytes = 0;
} else {
op.max_recv_bytes = MAX_RECV_PEEK_AHEAD - buffered_bytes;
}
}
call->receiving = 1; call->receiving = 1;
GRPC_CALL_INTERNAL_REF(call, "receiving"); GRPC_CALL_INTERNAL_REF(call, "receiving");
start_op = 1; start_op = 1;
@ -616,7 +669,7 @@ static void finish_live_ioreq_op(grpc_call *call, grpc_ioreq_op op,
case GRPC_IOREQ_SEND_STATUS: case GRPC_IOREQ_SEND_STATUS:
if (call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details != if (call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details !=
NULL) { NULL) {
grpc_mdstr_unref( GRPC_MDSTR_UNREF(
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details); call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details);
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details = call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
NULL; NULL;
@ -945,7 +998,7 @@ static int fill_send_ops(grpc_call *call, grpc_transport_stream_op *op) {
&mdb, &call->details_link, &mdb, &call->details_link,
grpc_mdelem_from_metadata_strings( grpc_mdelem_from_metadata_strings(
call->metadata_context, call->metadata_context,
grpc_mdstr_ref( GRPC_MDSTR_REF(
grpc_channel_get_message_string(call->channel)), grpc_channel_get_message_string(call->channel)),
data.send_status.details)); data.send_status.details));
call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details = call->request_data[GRPC_IOREQ_SEND_STATUS].send_status.details =
@ -1053,7 +1106,7 @@ static grpc_call_error start_ioreq(grpc_call *call, const grpc_ioreq *reqs,
reqs[i].data.send_status.code); reqs[i].data.send_status.code);
if (reqs[i].data.send_status.details) { if (reqs[i].data.send_status.details) {
set_status_details(call, STATUS_FROM_SERVER_STATUS, set_status_details(call, STATUS_FROM_SERVER_STATUS,
grpc_mdstr_ref(reqs[i].data.send_status.details)); GRPC_MDSTR_REF(reqs[i].data.send_status.details));
} }
} }
have_ops |= 1u << op; have_ops |= 1u << op;
@ -1190,7 +1243,8 @@ static void set_deadline_alarm(grpc_call *call, gpr_timespec deadline) {
} }
GRPC_CALL_INTERNAL_REF(call, "alarm"); GRPC_CALL_INTERNAL_REF(call, "alarm");
call->have_alarm = 1; call->have_alarm = 1;
grpc_alarm_init(&call->alarm, deadline, call_alarm, call, gpr_now()); grpc_alarm_init(&call->alarm, deadline, call_alarm, call,
gpr_now(GPR_CLOCK_REALTIME));
} }
/* we offset status by a small amount when storing it into transport metadata /* we offset status by a small amount when storing it into transport metadata
@ -1257,7 +1311,7 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) {
if (key == grpc_channel_get_status_string(call->channel)) { if (key == grpc_channel_get_status_string(call->channel)) {
set_status_code(call, STATUS_FROM_WIRE, decode_status(md)); set_status_code(call, STATUS_FROM_WIRE, decode_status(md));
} else if (key == grpc_channel_get_message_string(call->channel)) { } else if (key == grpc_channel_get_message_string(call->channel)) {
set_status_details(call, STATUS_FROM_WIRE, grpc_mdstr_ref(md->value)); set_status_details(call, STATUS_FROM_WIRE, GRPC_MDSTR_REF(md->value));
} else if (key == } else if (key ==
grpc_channel_get_compresssion_level_string(call->channel)) { grpc_channel_get_compresssion_level_string(call->channel)) {
set_decode_compression_level(call, decode_compression(md)); set_decode_compression_level(call, decode_compression(md));
@ -1293,10 +1347,10 @@ static void recv_metadata(grpc_call *call, grpc_metadata_batch *md) {
grpc_mdctx_lock(mdctx); grpc_mdctx_lock(mdctx);
for (l = md->list.head; l; l = l->next) { for (l = md->list.head; l; l = l->next) {
if (l->md) grpc_mdctx_locked_mdelem_unref(mdctx, l->md); if (l->md) GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
} }
for (l = md->garbage.head; l; l = l->next) { for (l = md->garbage.head; l; l = l->next) {
grpc_mdctx_locked_mdelem_unref(mdctx, l->md); GRPC_MDCTX_LOCKED_MDELEM_UNREF(mdctx, l->md);
} }
grpc_mdctx_unlock(mdctx); grpc_mdctx_unlock(mdctx);
} }
@ -1318,11 +1372,13 @@ static void set_cancelled_value(grpc_status_code status, void *dest) {
} }
static void finish_batch(grpc_call *call, int success, void *tag) { static void finish_batch(grpc_call *call, int success, void *tag) {
grpc_cq_end_op(call->cq, tag, call, success); grpc_cq_end_op(call->cq, tag, success, done_completion, call,
allocate_completion(call));
} }
static void finish_batch_with_close(grpc_call *call, int success, void *tag) { static void finish_batch_with_close(grpc_call *call, int success, void *tag) {
grpc_cq_end_op(call->cq, tag, call, 1); grpc_cq_end_op(call->cq, tag, 1, done_completion, call,
allocate_completion(call));
} }
static int are_write_flags_valid(gpr_uint32 flags) { static int are_write_flags_valid(gpr_uint32 flags) {
@ -1345,8 +1401,10 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, tag); GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, tag);
if (nops == 0) { if (nops == 0) {
grpc_cq_begin_op(call->cq, call); grpc_cq_begin_op(call->cq);
grpc_cq_end_op(call->cq, tag, call, 1); GRPC_CALL_INTERNAL_REF(call, "completion");
grpc_cq_end_op(call->cq, tag, 1, done_completion, call,
allocate_completion(call));
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
@ -1468,7 +1526,8 @@ grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops,
} }
} }
grpc_cq_begin_op(call->cq, call); GRPC_CALL_INTERNAL_REF(call, "completion");
grpc_cq_begin_op(call->cq);
return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_func, tag); return grpc_call_start_ioreq_and_call_back(call, reqs, out, finish_func, tag);
} }

@ -78,8 +78,8 @@ typedef union {
typedef struct { typedef struct {
grpc_ioreq_op op; grpc_ioreq_op op;
grpc_ioreq_data data;
gpr_uint32 flags; /**< A copy of the write flags from grpc_op */ gpr_uint32 flags; /**< A copy of the write flags from grpc_op */
grpc_ioreq_data data;
} grpc_ioreq; } grpc_ioreq;
typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success, typedef void (*grpc_ioreq_completion_func)(grpc_call *call, int success,

@ -46,8 +46,8 @@ static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) {
gpr_strvec_add(b, gpr_strdup(md[i].key)); gpr_strvec_add(b, gpr_strdup(md[i].key));
gpr_strvec_add(b, gpr_strdup(" value=")); gpr_strvec_add(b, gpr_strdup(" value="));
gpr_strvec_add(b, gpr_hexdump(md[i].value, md[i].value_length, gpr_strvec_add(b, gpr_dump(md[i].value, md[i].value_length,
GPR_HEXDUMP_PLAINTEXT)); GPR_DUMP_HEX | GPR_DUMP_ASCII));
} }
} }

@ -91,6 +91,7 @@ grpc_channel *grpc_channel_create_from_filters(
size_t size = size_t size =
sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters); sizeof(grpc_channel) + grpc_channel_stack_size(filters, num_filters);
grpc_channel *channel = gpr_malloc(size); grpc_channel *channel = gpr_malloc(size);
memset(channel, 0, sizeof(*channel));
GPR_ASSERT(grpc_is_initialized() && "call grpc_init()"); GPR_ASSERT(grpc_is_initialized() && "call grpc_init()");
channel->is_client = is_client; channel->is_client = is_client;
/* decremented by grpc_channel_destroy */ /* decremented by grpc_channel_destroy */
@ -104,7 +105,7 @@ grpc_channel *grpc_channel_create_from_filters(
char buf[GPR_LTOA_MIN_BUFSIZE]; char buf[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(i, buf); gpr_ltoa(i, buf);
channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings( channel->grpc_status_elem[i] = grpc_mdelem_from_metadata_strings(
mdctx, grpc_mdstr_ref(channel->grpc_status_string), mdctx, GRPC_MDSTR_REF(channel->grpc_status_string),
grpc_mdstr_from_string(mdctx, buf)); grpc_mdstr_from_string(mdctx, buf));
} }
channel->path_string = grpc_mdstr_from_string(mdctx, ":path"); channel->path_string = grpc_mdstr_from_string(mdctx, ":path");
@ -157,10 +158,10 @@ grpc_call *grpc_channel_create_call(grpc_channel *channel,
return grpc_channel_create_call_internal( return grpc_channel_create_call_internal(
channel, cq, channel, cq,
grpc_mdelem_from_metadata_strings( grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->path_string), channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
grpc_mdstr_from_string(channel->metadata_context, method)), grpc_mdstr_from_string(channel->metadata_context, method)),
grpc_mdelem_from_metadata_strings( grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->authority_string), channel->metadata_context, GRPC_MDSTR_REF(channel->authority_string),
grpc_mdstr_from_string(channel->metadata_context, host)), grpc_mdstr_from_string(channel->metadata_context, host)),
deadline); deadline);
} }
@ -169,10 +170,10 @@ void *grpc_channel_register_call(grpc_channel *channel, const char *method,
const char *host) { const char *host) {
registered_call *rc = gpr_malloc(sizeof(registered_call)); registered_call *rc = gpr_malloc(sizeof(registered_call));
rc->path = grpc_mdelem_from_metadata_strings( rc->path = grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->path_string), channel->metadata_context, GRPC_MDSTR_REF(channel->path_string),
grpc_mdstr_from_string(channel->metadata_context, method)); grpc_mdstr_from_string(channel->metadata_context, method));
rc->authority = grpc_mdelem_from_metadata_strings( rc->authority = grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->authority_string), channel->metadata_context, GRPC_MDSTR_REF(channel->authority_string),
grpc_mdstr_from_string(channel->metadata_context, host)); grpc_mdstr_from_string(channel->metadata_context, host));
gpr_mu_lock(&channel->registered_call_mu); gpr_mu_lock(&channel->registered_call_mu);
rc->next = channel->registered_calls; rc->next = channel->registered_calls;
@ -186,8 +187,8 @@ grpc_call *grpc_channel_create_registered_call(
void *registered_call_handle, gpr_timespec deadline) { void *registered_call_handle, gpr_timespec deadline) {
registered_call *rc = registered_call_handle; registered_call *rc = registered_call_handle;
return grpc_channel_create_call_internal( return grpc_channel_create_call_internal(
channel, completion_queue, grpc_mdelem_ref(rc->path), channel, completion_queue, GRPC_MDELEM_REF(rc->path),
grpc_mdelem_ref(rc->authority), deadline); GRPC_MDELEM_REF(rc->authority), deadline);
} }
#ifdef GRPC_CHANNEL_REF_COUNT_DEBUG #ifdef GRPC_CHANNEL_REF_COUNT_DEBUG
@ -205,18 +206,18 @@ static void destroy_channel(void *p, int ok) {
size_t i; size_t i;
grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel)); grpc_channel_stack_destroy(CHANNEL_STACK_FROM_CHANNEL(channel));
for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) { for (i = 0; i < NUM_CACHED_STATUS_ELEMS; i++) {
grpc_mdelem_unref(channel->grpc_status_elem[i]); GRPC_MDELEM_UNREF(channel->grpc_status_elem[i]);
} }
grpc_mdstr_unref(channel->grpc_status_string); GRPC_MDSTR_UNREF(channel->grpc_status_string);
grpc_mdstr_unref(channel->grpc_compression_level_string); GRPC_MDSTR_UNREF(channel->grpc_compression_level_string);
grpc_mdstr_unref(channel->grpc_message_string); GRPC_MDSTR_UNREF(channel->grpc_message_string);
grpc_mdstr_unref(channel->path_string); GRPC_MDSTR_UNREF(channel->path_string);
grpc_mdstr_unref(channel->authority_string); GRPC_MDSTR_UNREF(channel->authority_string);
while (channel->registered_calls) { while (channel->registered_calls) {
registered_call *rc = channel->registered_calls; registered_call *rc = channel->registered_calls;
channel->registered_calls = rc->next; channel->registered_calls = rc->next;
grpc_mdelem_unref(rc->path); GRPC_MDELEM_UNREF(rc->path);
grpc_mdelem_unref(rc->authority); GRPC_MDELEM_UNREF(rc->authority);
gpr_free(rc); gpr_free(rc);
} }
grpc_mdctx_unref(channel->metadata_context); grpc_mdctx_unref(channel->metadata_context);
@ -267,12 +268,12 @@ grpc_mdstr *grpc_channel_get_compresssion_level_string(grpc_channel *channel) {
grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) { grpc_mdelem *grpc_channel_get_reffed_status_elem(grpc_channel *channel, int i) {
if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) { if (i >= 0 && i < NUM_CACHED_STATUS_ELEMS) {
return grpc_mdelem_ref(channel->grpc_status_elem[i]); return GRPC_MDELEM_REF(channel->grpc_status_elem[i]);
} else { } else {
char tmp[GPR_LTOA_MIN_BUFSIZE]; char tmp[GPR_LTOA_MIN_BUFSIZE];
gpr_ltoa(i, tmp); gpr_ltoa(i, tmp);
return grpc_mdelem_from_metadata_strings( return grpc_mdelem_from_metadata_strings(
channel->metadata_context, grpc_mdstr_ref(channel->grpc_status_string), channel->metadata_context, GRPC_MDSTR_REF(channel->grpc_status_string),
grpc_mdstr_from_string(channel->metadata_context, tmp)); grpc_mdstr_from_string(channel->metadata_context, tmp));
} }
} }

@ -45,34 +45,20 @@
#include <grpc/support/atm.h> #include <grpc/support/atm.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#define NUM_TAG_BUCKETS 31
/* A single event: extends grpc_event to form a linked list with a destruction
function (on_finish) that is hidden from outside this module */
typedef struct event {
grpc_event base;
struct event *queue_next;
struct event *queue_prev;
struct event *bucket_next;
struct event *bucket_prev;
} event;
/* Completion queue structure */ /* Completion queue structure */
struct grpc_completion_queue { struct grpc_completion_queue {
/* When refs drops to zero, we are in shutdown mode, and will be destroyable /** completed events */
once all queued events are drained */ grpc_cq_completion completed_head;
gpr_refcount refs; grpc_cq_completion *completed_tail;
/* Once owning_refs drops to zero, we will destroy the cq */ /** 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; gpr_refcount owning_refs;
/* the set of low level i/o things that concern this cq */ /** the set of low level i/o things that concern this cq */
grpc_pollset pollset; grpc_pollset pollset;
/* 0 initially, 1 once we've begun shutting down */ /** 0 initially, 1 once we've begun shutting down */
int shutdown; int shutdown;
int shutdown_called; int shutdown_called;
/* Head of a linked list of queued events (prev points to the last element) */
event *queue;
/* Fixed size chained hash table of events for pluck() */
event *buckets[NUM_TAG_BUCKETS];
int is_server_cq; int is_server_cq;
}; };
@ -80,19 +66,20 @@ grpc_completion_queue *grpc_completion_queue_create(void) {
grpc_completion_queue *cc = gpr_malloc(sizeof(grpc_completion_queue)); grpc_completion_queue *cc = gpr_malloc(sizeof(grpc_completion_queue));
memset(cc, 0, sizeof(*cc)); memset(cc, 0, sizeof(*cc));
/* Initial ref is dropped by grpc_completion_queue_shutdown */ /* Initial ref is dropped by grpc_completion_queue_shutdown */
gpr_ref_init(&cc->refs, 1); gpr_ref_init(&cc->pending_events, 1);
/* One for destroy(), one for pollset_shutdown */ /* One for destroy(), one for pollset_shutdown */
gpr_ref_init(&cc->owning_refs, 2); gpr_ref_init(&cc->owning_refs, 2);
grpc_pollset_init(&cc->pollset); grpc_pollset_init(&cc->pollset);
cc->completed_tail = &cc->completed_head;
cc->completed_head.next = (gpr_uintptr)cc->completed_tail;
return cc; return cc;
} }
#ifdef GRPC_CQ_REF_COUNT_DEBUG #ifdef GRPC_CQ_REF_COUNT_DEBUG
void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason, void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason,
const char *file, int line) { const char *file, int line) {
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s", gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p ref %d -> %d %s", cc,
cc, (int)cc->owning_refs.count, (int)cc->owning_refs.count + 1, (int)cc->owning_refs.count, (int)cc->owning_refs.count + 1, reason);
reason);
#else #else
void grpc_cq_internal_ref(grpc_completion_queue *cc) { void grpc_cq_internal_ref(grpc_completion_queue *cc) {
#endif #endif
@ -107,186 +94,135 @@ static void on_pollset_destroy_done(void *arg) {
#ifdef GRPC_CQ_REF_COUNT_DEBUG #ifdef GRPC_CQ_REF_COUNT_DEBUG
void grpc_cq_internal_unref(grpc_completion_queue *cc, const char *reason, void grpc_cq_internal_unref(grpc_completion_queue *cc, const char *reason,
const char *file, int line) { const char *file, int line) {
gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s", gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, "CQ:%p unref %d -> %d %s", cc,
cc, (int)cc->owning_refs.count, (int)cc->owning_refs.count - 1, (int)cc->owning_refs.count, (int)cc->owning_refs.count - 1, reason);
reason);
#else #else
void grpc_cq_internal_unref(grpc_completion_queue *cc) { void grpc_cq_internal_unref(grpc_completion_queue *cc) {
#endif #endif
if (gpr_unref(&cc->owning_refs)) { if (gpr_unref(&cc->owning_refs)) {
GPR_ASSERT(cc->queue == NULL); GPR_ASSERT(cc->completed_head.next == (gpr_uintptr)&cc->completed_head);
grpc_pollset_destroy(&cc->pollset); grpc_pollset_destroy(&cc->pollset);
gpr_free(cc); gpr_free(cc);
} }
} }
/* Create and append an event to the queue. Returns the event so that its data void grpc_cq_begin_op(grpc_completion_queue *cc) {
members can be filled in. gpr_ref(&cc->pending_events);
Requires GRPC_POLLSET_MU(&cc->pollset) locked. */
static event *add_locked(grpc_completion_queue *cc, grpc_completion_type type,
void *tag, grpc_call *call) {
event *ev = gpr_malloc(sizeof(event));
gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS;
ev->base.type = type;
ev->base.tag = tag;
if (cc->queue == NULL) {
cc->queue = ev->queue_next = ev->queue_prev = ev;
} else {
ev->queue_next = cc->queue;
ev->queue_prev = cc->queue->queue_prev;
ev->queue_next->queue_prev = ev->queue_prev->queue_next = ev;
}
if (cc->buckets[bucket] == NULL) {
cc->buckets[bucket] = ev->bucket_next = ev->bucket_prev = ev;
} else {
ev->bucket_next = cc->buckets[bucket];
ev->bucket_prev = cc->buckets[bucket]->bucket_prev;
ev->bucket_next->bucket_prev = ev->bucket_prev->bucket_next = ev;
}
grpc_pollset_kick(&cc->pollset);
return ev;
}
void grpc_cq_begin_op(grpc_completion_queue *cc, grpc_call *call) {
gpr_ref(&cc->refs);
if (call) GRPC_CALL_INTERNAL_REF(call, "cq");
} }
/* Signal the end of an operation - if this is the last waiting-to-be-queued /* Signal the end of an operation - if this is the last waiting-to-be-queued
event, then enter shutdown mode */ event, then enter shutdown mode */
void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call, /* Queue a GRPC_OP_COMPLETED operation */
int success) { void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success,
event *ev; void (*done)(void *done_arg, grpc_cq_completion *storage),
int shutdown = 0; void *done_arg, grpc_cq_completion *storage) {
int shutdown;
storage->tag = tag;
storage->done = done;
storage->done_arg = done_arg;
storage->next =
((gpr_uintptr)&cc->completed_head) | ((gpr_uintptr)(success != 0));
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
ev = add_locked(cc, GRPC_OP_COMPLETE, tag, call); shutdown = gpr_unref(&cc->pending_events);
ev->base.success = success; if (!shutdown) {
if (gpr_unref(&cc->refs)) { cc->completed_tail->next =
((gpr_uintptr)storage) | (1u & (gpr_uintptr)cc->completed_tail->next);
cc->completed_tail = storage;
grpc_pollset_kick(&cc->pollset);
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
} else {
cc->completed_tail->next =
((gpr_uintptr)storage) | (1u & (gpr_uintptr)cc->completed_tail->next);
cc->completed_tail = storage;
GPR_ASSERT(!cc->shutdown); GPR_ASSERT(!cc->shutdown);
GPR_ASSERT(cc->shutdown_called); GPR_ASSERT(cc->shutdown_called);
cc->shutdown = 1; cc->shutdown = 1;
shutdown = 1; gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
if (call) GRPC_CALL_INTERNAL_UNREF(call, "cq", 0);
if (shutdown) {
grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc); grpc_pollset_shutdown(&cc->pollset, on_pollset_destroy_done, cc);
} }
} }
/* Create a GRPC_QUEUE_SHUTDOWN event without queuing it anywhere */
static event *create_shutdown_event(void) {
event *ev = gpr_malloc(sizeof(event));
ev->base.type = GRPC_QUEUE_SHUTDOWN;
ev->base.tag = NULL;
return ev;
}
grpc_event grpc_completion_queue_next(grpc_completion_queue *cc, grpc_event grpc_completion_queue_next(grpc_completion_queue *cc,
gpr_timespec deadline) { gpr_timespec deadline) {
event *ev = NULL;
grpc_event ret; grpc_event ret;
GRPC_CQ_INTERNAL_REF(cc, "next"); GRPC_CQ_INTERNAL_REF(cc, "next");
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
for (;;) { for (;;) {
if (cc->queue != NULL) { if (cc->completed_tail != &cc->completed_head) {
gpr_uintptr bucket; grpc_cq_completion *c = (grpc_cq_completion *)cc->completed_head.next;
ev = cc->queue; cc->completed_head.next = c->next & ~(gpr_uintptr)1;
bucket = ((gpr_uintptr)ev->base.tag) % NUM_TAG_BUCKETS; if (c == cc->completed_tail) {
cc->queue = ev->queue_next; cc->completed_tail = &cc->completed_head;
ev->queue_next->queue_prev = ev->queue_prev;
ev->queue_prev->queue_next = ev->queue_next;
ev->bucket_next->bucket_prev = ev->bucket_prev;
ev->bucket_prev->bucket_next = ev->bucket_next;
if (ev == cc->buckets[bucket]) {
cc->buckets[bucket] = ev->bucket_next;
if (ev == cc->buckets[bucket]) {
cc->buckets[bucket] = NULL;
}
}
if (cc->queue == ev) {
cc->queue = NULL;
} }
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret.type = GRPC_OP_COMPLETE;
ret.success = c->next & 1u;
ret.tag = c->tag;
c->done(c->done_arg, c);
break; break;
} }
if (cc->shutdown) { if (cc->shutdown) {
ev = create_shutdown_event(); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_SHUTDOWN;
break; break;
} }
if (!grpc_pollset_work(&cc->pollset, deadline)) { if (!grpc_pollset_work(&cc->pollset, deadline)) {
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_TIMEOUT; ret.type = GRPC_QUEUE_TIMEOUT;
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); break;
GRPC_CQ_INTERNAL_UNREF(cc, "next");
return ret;
} }
} }
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret = ev->base;
gpr_free(ev);
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
GRPC_CQ_INTERNAL_UNREF(cc, "next"); GRPC_CQ_INTERNAL_UNREF(cc, "next");
return ret; return ret;
} }
static event *pluck_event(grpc_completion_queue *cc, void *tag) {
gpr_uintptr bucket = ((gpr_uintptr)tag) % NUM_TAG_BUCKETS;
event *ev = cc->buckets[bucket];
if (ev == NULL) return NULL;
do {
if (ev->base.tag == tag) {
ev->queue_next->queue_prev = ev->queue_prev;
ev->queue_prev->queue_next = ev->queue_next;
ev->bucket_next->bucket_prev = ev->bucket_prev;
ev->bucket_prev->bucket_next = ev->bucket_next;
if (ev == cc->buckets[bucket]) {
cc->buckets[bucket] = ev->bucket_next;
if (ev == cc->buckets[bucket]) {
cc->buckets[bucket] = NULL;
}
}
if (cc->queue == ev) {
cc->queue = ev->queue_next;
if (cc->queue == ev) {
cc->queue = NULL;
}
}
return ev;
}
ev = ev->bucket_next;
} while (ev != cc->buckets[bucket]);
return NULL;
}
grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag, grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cc, void *tag,
gpr_timespec deadline) { gpr_timespec deadline) {
event *ev = NULL;
grpc_event ret; grpc_event ret;
grpc_cq_completion *c;
grpc_cq_completion *prev;
GRPC_CQ_INTERNAL_REF(cc, "pluck"); GRPC_CQ_INTERNAL_REF(cc, "pluck");
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
for (;;) { for (;;) {
if ((ev = pluck_event(cc, tag))) { prev = &cc->completed_head;
break; while ((c = (grpc_cq_completion *)(prev->next & ~(gpr_uintptr)1)) !=
&cc->completed_head) {
if (c->tag == tag) {
prev->next =
(prev->next & (gpr_uintptr)1) | (c->next & ~(gpr_uintptr)1);
if (c == cc->completed_tail) {
cc->completed_tail = prev;
}
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
ret.type = GRPC_OP_COMPLETE;
ret.success = c->next & 1u;
ret.tag = c->tag;
c->done(c->done_arg, c);
goto done;
}
prev = c;
} }
if (cc->shutdown) { if (cc->shutdown) {
ev = create_shutdown_event(); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_SHUTDOWN;
break; break;
} }
if (!grpc_pollset_work(&cc->pollset, deadline)) { if (!grpc_pollset_work(&cc->pollset, deadline)) {
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
ret.type = GRPC_QUEUE_TIMEOUT; ret.type = GRPC_QUEUE_TIMEOUT;
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); break;
GRPC_CQ_INTERNAL_UNREF(cc, "pluck");
return ret;
} }
} }
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); done:
ret = ev->base;
gpr_free(ev);
GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret); GRPC_SURFACE_TRACE_RETURNED_EVENT(cc, &ret);
GRPC_CQ_INTERNAL_UNREF(cc, "pluck"); GRPC_CQ_INTERNAL_UNREF(cc, "pluck");
return ret; return ret;
@ -303,7 +239,7 @@ void grpc_completion_queue_shutdown(grpc_completion_queue *cc) {
cc->shutdown_called = 1; cc->shutdown_called = 1;
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
if (gpr_unref(&cc->refs)) { if (gpr_unref(&cc->pending_events)) {
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
GPR_ASSERT(!cc->shutdown); GPR_ASSERT(!cc->shutdown);
cc->shutdown = 1; cc->shutdown = 1;
@ -324,8 +260,8 @@ grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc) {
void grpc_cq_hack_spin_pollset(grpc_completion_queue *cc) { void grpc_cq_hack_spin_pollset(grpc_completion_queue *cc) {
gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_lock(GRPC_POLLSET_MU(&cc->pollset));
grpc_pollset_kick(&cc->pollset); grpc_pollset_kick(&cc->pollset);
grpc_pollset_work(&cc->pollset, grpc_pollset_work(&cc->pollset, gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
gpr_time_add(gpr_now(), gpr_time_from_millis(100))); gpr_time_from_millis(100)));
gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset)); gpr_mu_unlock(GRPC_POLLSET_MU(&cc->pollset));
} }

@ -39,6 +39,17 @@
#include "src/core/iomgr/pollset.h" #include "src/core/iomgr/pollset.h"
#include <grpc/grpc.h> #include <grpc/grpc.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)(void *done_arg, struct grpc_cq_completion *c);
void *done_arg;
/** next pointer; low bit is used to indicate success or not */
gpr_uintptr next;
} grpc_cq_completion;
#ifdef GRPC_CQ_REF_COUNT_DEBUG #ifdef GRPC_CQ_REF_COUNT_DEBUG
void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason, void grpc_cq_internal_ref(grpc_completion_queue *cc, const char *reason,
const char *file, int line); const char *file, int line);
@ -57,11 +68,12 @@ void grpc_cq_internal_unref(grpc_completion_queue *cc);
/* Flag that an operation is beginning: the completion channel will not finish /* Flag that an operation is beginning: the completion channel will not finish
shutdown until a corrensponding grpc_cq_end_* call is made */ shutdown until a corrensponding grpc_cq_end_* call is made */
void grpc_cq_begin_op(grpc_completion_queue *cc, grpc_call *call); void grpc_cq_begin_op(grpc_completion_queue *cc);
/* Queue a GRPC_OP_COMPLETED operation */ /* Queue a GRPC_OP_COMPLETED operation */
void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, grpc_call *call, void grpc_cq_end_op(grpc_completion_queue *cc, void *tag, int success,
int success); void (*done)(void *done_arg, grpc_cq_completion *storage),
void *done_arg, grpc_cq_completion *storage);
grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc); grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cc);

@ -38,6 +38,7 @@
#include <grpc/census.h> #include <grpc/census.h>
#include <grpc/grpc.h> #include <grpc/grpc.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/time.h>
#include "src/core/channel/channel_stack.h" #include "src/core/channel/channel_stack.h"
#include "src/core/client_config/resolver_registry.h" #include "src/core/client_config/resolver_registry.h"
#include "src/core/client_config/resolvers/dns_resolver.h" #include "src/core/client_config/resolvers/dns_resolver.h"
@ -91,6 +92,7 @@ void grpc_init(void) {
gpr_mu_lock(&g_init_mu); gpr_mu_lock(&g_init_mu);
if (++g_initializations == 1) { if (++g_initializations == 1) {
gpr_time_init();
grpc_resolver_registry_init("dns:///"); grpc_resolver_registry_init("dns:///");
grpc_register_resolver_type("dns", grpc_dns_resolver_factory_create()); grpc_register_resolver_type("dns", grpc_dns_resolver_factory_create());
#ifdef GPR_POSIX_SOCKET #ifdef GPR_POSIX_SOCKET

@ -75,6 +75,7 @@ static void connector_unref(grpc_connector *con) {
static void on_secure_transport_setup_done(void *arg, static void on_secure_transport_setup_done(void *arg,
grpc_security_status status, grpc_security_status status,
grpc_endpoint *wrapped_endpoint,
grpc_endpoint *secure_endpoint) { grpc_endpoint *secure_endpoint) {
connector *c = arg; connector *c = arg;
grpc_iomgr_closure *notify; grpc_iomgr_closure *notify;

@ -51,7 +51,7 @@
#include <grpc/support/string_util.h> #include <grpc/support/string_util.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
typedef enum { PENDING_START, ALL_CALLS, CALL_LIST_COUNT } call_list; typedef enum { PENDING_START, CALL_LIST_COUNT } call_list;
typedef struct listener { typedef struct listener {
void *arg; void *arg;
@ -72,12 +72,14 @@ typedef struct {
typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type; typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type;
typedef struct { typedef struct requested_call {
requested_call_type type; requested_call_type type;
struct requested_call *next;
void *tag; void *tag;
grpc_completion_queue *cq_bound_to_call; grpc_completion_queue *cq_bound_to_call;
grpc_completion_queue *cq_for_notification; grpc_completion_queue *cq_for_notification;
grpc_call **call; grpc_call **call;
grpc_cq_completion completion;
union { union {
struct { struct {
grpc_call_details *details; grpc_call_details *details;
@ -92,17 +94,11 @@ typedef struct {
} data; } data;
} requested_call; } requested_call;
typedef struct {
requested_call *calls;
size_t count;
size_t capacity;
} requested_call_array;
struct registered_method { struct registered_method {
char *method; char *method;
char *host; char *host;
call_data *pending; call_data *pending;
requested_call_array requested; requested_call *requests;
registered_method *next; registered_method *next;
}; };
@ -114,7 +110,6 @@ typedef struct channel_registered_method {
struct channel_data { struct channel_data {
grpc_server *server; grpc_server *server;
size_t num_calls;
grpc_connectivity_state connectivity_state; grpc_connectivity_state connectivity_state;
grpc_channel *channel; grpc_channel *channel;
grpc_mdstr *path_key; grpc_mdstr *path_key;
@ -132,6 +127,7 @@ struct channel_data {
typedef struct shutdown_tag { typedef struct shutdown_tag {
void *tag; void *tag;
grpc_completion_queue *cq; grpc_completion_queue *cq;
grpc_cq_completion completion;
} shutdown_tag; } shutdown_tag;
struct grpc_server { struct grpc_server {
@ -154,7 +150,7 @@ struct grpc_server {
gpr_mu mu_call; /* mutex for call-specific state */ gpr_mu mu_call; /* mutex for call-specific state */
registered_method *registered_methods; registered_method *registered_methods;
requested_call_array requested_calls; requested_call *requests;
gpr_uint8 shutdown; gpr_uint8 shutdown;
gpr_uint8 shutdown_published; gpr_uint8 shutdown_published;
@ -167,6 +163,9 @@ struct grpc_server {
listener *listeners; listener *listeners;
int listeners_destroyed; int listeners_destroyed;
gpr_refcount internal_refcount; gpr_refcount internal_refcount;
/** when did we print the last shutdown progress message */
gpr_timespec last_shutdown_message_time;
}; };
typedef enum { typedef enum {
@ -183,7 +182,11 @@ typedef enum {
struct call_data { struct call_data {
grpc_call *call; grpc_call *call;
/** protects state */
gpr_mu mu_state;
/** the current state of a call - see call_state */
call_state state; call_state state;
grpc_mdstr *path; grpc_mdstr *path;
grpc_mdstr *host; grpc_mdstr *host;
gpr_timespec deadline; gpr_timespec deadline;
@ -204,9 +207,7 @@ struct call_data {
typedef struct { typedef struct {
grpc_channel **channels; grpc_channel **channels;
grpc_channel **disconnects;
size_t num_channels; size_t num_channels;
size_t num_disconnects;
} channel_broadcaster; } channel_broadcaster;
#define SERVER_FROM_CALL_ELEM(elem) \ #define SERVER_FROM_CALL_ELEM(elem) \
@ -225,26 +226,15 @@ static void maybe_finish_shutdown(grpc_server *server);
static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) { static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) {
channel_data *c; channel_data *c;
size_t count = 0; size_t count = 0;
size_t dc_count = 0;
for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) {
count++; count++;
if (c->num_calls == 0) {
dc_count++;
}
} }
cb->num_channels = count; cb->num_channels = count;
cb->num_disconnects = dc_count;
cb->channels = gpr_malloc(sizeof(*cb->channels) * cb->num_channels); cb->channels = gpr_malloc(sizeof(*cb->channels) * cb->num_channels);
cb->disconnects = gpr_malloc(sizeof(*cb->channels) * cb->num_disconnects);
count = 0; count = 0;
dc_count = 0;
for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) {
cb->channels[count++] = c->channel; cb->channels[count++] = c->channel;
GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast"); GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast");
if (c->num_calls == 0) {
cb->disconnects[dc_count++] = c->channel;
GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast-disconnect");
}
} }
} }
@ -280,19 +270,15 @@ static void send_shutdown(grpc_channel *channel, int send_goaway,
} }
static void channel_broadcaster_shutdown(channel_broadcaster *cb, static void channel_broadcaster_shutdown(channel_broadcaster *cb,
int send_goaway, int send_disconnect) { int send_goaway,
int force_disconnect) {
size_t i; size_t i;
for (i = 0; i < cb->num_channels; i++) { for (i = 0; i < cb->num_channels; i++) {
send_shutdown(cb->channels[i], 1, 0); send_shutdown(cb->channels[i], send_goaway, force_disconnect);
GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast"); GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast");
} }
for (i = 0; i < cb->num_disconnects; i++) {
send_shutdown(cb->disconnects[i], 0, 1);
GRPC_CHANNEL_INTERNAL_UNREF(cb->channels[i], "broadcast-disconnect");
}
gpr_free(cb->channels); gpr_free(cb->channels);
gpr_free(cb->disconnects);
} }
/* call list */ /* call list */
@ -344,22 +330,6 @@ static int call_list_remove(call_data *call, call_list list) {
return 1; return 1;
} }
static void requested_call_array_destroy(requested_call_array *array) {
gpr_free(array->calls);
}
static requested_call *requested_call_array_add(requested_call_array *array) {
requested_call *rc;
if (array->count == array->capacity) {
array->capacity = GPR_MAX(array->capacity + 8, array->capacity * 2);
array->calls =
gpr_realloc(array->calls, sizeof(requested_call) * array->capacity);
}
rc = &array->calls[array->count++];
memset(rc, 0, sizeof(*rc));
return rc;
}
static void server_ref(grpc_server *server) { static void server_ref(grpc_server *server) {
gpr_ref(&server->internal_refcount); gpr_ref(&server->internal_refcount);
} }
@ -371,12 +341,10 @@ static void server_delete(grpc_server *server) {
gpr_mu_destroy(&server->mu_global); gpr_mu_destroy(&server->mu_global);
gpr_mu_destroy(&server->mu_call); gpr_mu_destroy(&server->mu_call);
gpr_free(server->channel_filters); gpr_free(server->channel_filters);
requested_call_array_destroy(&server->requested_calls);
while ((rm = server->registered_methods) != NULL) { while ((rm = server->registered_methods) != NULL) {
server->registered_methods = rm->next; server->registered_methods = rm->next;
gpr_free(rm->method); gpr_free(rm->method);
gpr_free(rm->host); gpr_free(rm->host);
requested_call_array_destroy(&rm->requested);
gpr_free(rm); gpr_free(rm);
} }
for (i = 0; i < server->cq_count; i++) { for (i = 0; i < server->cq_count; i++) {
@ -422,21 +390,26 @@ static void destroy_channel(channel_data *chand) {
grpc_iomgr_add_callback(&chand->finish_destroy_channel_closure); grpc_iomgr_add_callback(&chand->finish_destroy_channel_closure);
} }
static void finish_start_new_rpc_and_unlock(grpc_server *server, static void finish_start_new_rpc(grpc_server *server, grpc_call_element *elem,
grpc_call_element *elem, call_data **pending_root,
call_data **pending_root, requested_call **requests) {
requested_call_array *array) { requested_call *rc;
requested_call rc;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (array->count == 0) { gpr_mu_lock(&server->mu_call);
rc = *requests;
if (rc == NULL) {
gpr_mu_lock(&calld->mu_state);
calld->state = PENDING; calld->state = PENDING;
gpr_mu_unlock(&calld->mu_state);
call_list_join(pending_root, calld, PENDING_START); call_list_join(pending_root, calld, PENDING_START);
gpr_mu_unlock(&server->mu_call); gpr_mu_unlock(&server->mu_call);
} else { } else {
rc = array->calls[--array->count]; *requests = rc->next;
gpr_mu_lock(&calld->mu_state);
calld->state = ACTIVATED; calld->state = ACTIVATED;
gpr_mu_unlock(&calld->mu_state);
gpr_mu_unlock(&server->mu_call); gpr_mu_unlock(&server->mu_call);
begin_call(server, calld, &rc); begin_call(server, calld, rc);
} }
} }
@ -448,20 +421,18 @@ static void start_new_rpc(grpc_call_element *elem) {
gpr_uint32 hash; gpr_uint32 hash;
channel_registered_method *rm; channel_registered_method *rm;
gpr_mu_lock(&server->mu_call);
if (chand->registered_methods && calld->path && calld->host) { if (chand->registered_methods && calld->path && calld->host) {
/* TODO(ctiller): unify these two searches */ /* TODO(ctiller): unify these two searches */
/* check for an exact match with host */ /* check for an exact match with host */
hash = GRPC_MDSTR_KV_HASH(calld->host->hash, calld->path->hash); hash = GRPC_MDSTR_KV_HASH(calld->host->hash, calld->path->hash);
for (i = 0; i < chand->registered_method_max_probes; i++) { for (i = 0; i <= chand->registered_method_max_probes; i++) {
rm = &chand->registered_methods[(hash + i) % rm = &chand->registered_methods[(hash + i) %
chand->registered_method_slots]; chand->registered_method_slots];
if (!rm) break; if (!rm) break;
if (rm->host != calld->host) continue; if (rm->host != calld->host) continue;
if (rm->method != calld->path) continue; if (rm->method != calld->path) continue;
finish_start_new_rpc_and_unlock(server, elem, finish_start_new_rpc(server, elem, &rm->server_registered_method->pending,
&rm->server_registered_method->pending, &rm->server_registered_method->requests);
&rm->server_registered_method->requested);
return; return;
} }
/* check for a wildcard method definition (no host set) */ /* check for a wildcard method definition (no host set) */
@ -472,14 +443,13 @@ static void start_new_rpc(grpc_call_element *elem) {
if (!rm) break; if (!rm) break;
if (rm->host != NULL) continue; if (rm->host != NULL) continue;
if (rm->method != calld->path) continue; if (rm->method != calld->path) continue;
finish_start_new_rpc_and_unlock(server, elem, finish_start_new_rpc(server, elem, &rm->server_registered_method->pending,
&rm->server_registered_method->pending, &rm->server_registered_method->requests);
&rm->server_registered_method->requested);
return; return;
} }
} }
finish_start_new_rpc_and_unlock(server, elem, &server->lists[PENDING_START], finish_start_new_rpc(server, elem, &server->lists[PENDING_START],
&server->requested_calls); &server->requests);
} }
static void kill_zombie(void *elem, int success) { static void kill_zombie(void *elem, int success) {
@ -495,35 +465,47 @@ static int num_listeners(grpc_server *server) {
return n; return n;
} }
static void done_shutdown_event(void *server, grpc_cq_completion *completion) {
server_unref(server);
}
static int num_channels(grpc_server *server) {
channel_data *chand;
int n = 0;
for (chand = server->root_channel_data.next;
chand != &server->root_channel_data; chand = chand->next) {
n++;
}
return n;
}
static void maybe_finish_shutdown(grpc_server *server) { static void maybe_finish_shutdown(grpc_server *server) {
size_t i; size_t i;
if (!server->shutdown || server->shutdown_published) { if (!server->shutdown || server->shutdown_published) {
return; return;
} }
gpr_mu_lock(&server->mu_call); if (server->root_channel_data.next != &server->root_channel_data ||
if (server->lists[ALL_CALLS] != NULL) { server->listeners_destroyed < num_listeners(server)) {
gpr_log(GPR_DEBUG, if (gpr_time_cmp(
"Waiting for all calls to finish before destroying server"); gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), server->last_shutdown_message_time),
gpr_mu_unlock(&server->mu_call); gpr_time_from_seconds(1)) >= 0) {
return; server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
} gpr_log(GPR_DEBUG,
gpr_mu_unlock(&server->mu_call); "Waiting for %d channels and %d/%d listeners to be destroyed"
" before shutting down server",
if (server->root_channel_data.next != &server->root_channel_data) { num_channels(server),
gpr_log(GPR_DEBUG, num_listeners(server) - server->listeners_destroyed,
"Waiting for all channels to close before destroying server"); num_listeners(server));
return; }
}
if (server->listeners_destroyed < num_listeners(server)) {
gpr_log(GPR_DEBUG, "Waiting for all listeners to be destroyed (@ %d/%d)",
server->listeners_destroyed, num_listeners(server));
return; return;
} }
server->shutdown_published = 1; server->shutdown_published = 1;
for (i = 0; i < server->num_shutdown_tags; i++) { for (i = 0; i < server->num_shutdown_tags; i++) {
grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag, server_ref(server);
NULL, 1); grpc_cq_end_op(server->shutdown_tags[i].cq, server->shutdown_tags[i].tag, 1,
done_shutdown_event, server,
&server->shutdown_tags[i].completion);
} }
} }
@ -532,31 +514,19 @@ static grpc_mdelem *server_filter(void *user_data, grpc_mdelem *md) {
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
if (md->key == chand->path_key) { if (md->key == chand->path_key) {
calld->path = grpc_mdstr_ref(md->value); calld->path = GRPC_MDSTR_REF(md->value);
return NULL; return NULL;
} else if (md->key == chand->authority_key) { } else if (md->key == chand->authority_key) {
calld->host = grpc_mdstr_ref(md->value); calld->host = GRPC_MDSTR_REF(md->value);
return NULL; return NULL;
} }
return md; return md;
} }
static int decrement_call_count(channel_data *chand) {
int disconnect = 0;
chand->num_calls--;
if (0 == chand->num_calls && chand->server->shutdown) {
disconnect = 1;
}
maybe_finish_shutdown(chand->server);
return disconnect;
}
static void server_on_recv(void *ptr, int success) { static void server_on_recv(void *ptr, int success) {
grpc_call_element *elem = ptr; grpc_call_element *elem = ptr;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
int remove_res;
int disconnect = 0;
if (success && !calld->got_initial_metadata) { if (success && !calld->got_initial_metadata) {
size_t i; size_t i;
@ -581,39 +551,33 @@ static void server_on_recv(void *ptr, int success) {
case GRPC_STREAM_SEND_CLOSED: case GRPC_STREAM_SEND_CLOSED:
break; break;
case GRPC_STREAM_RECV_CLOSED: case GRPC_STREAM_RECV_CLOSED:
gpr_mu_lock(&chand->server->mu_call); gpr_mu_lock(&calld->mu_state);
if (calld->state == NOT_STARTED) { if (calld->state == NOT_STARTED) {
calld->state = ZOMBIED; calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure); grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} else {
gpr_mu_unlock(&calld->mu_state);
} }
gpr_mu_unlock(&chand->server->mu_call);
break; break;
case GRPC_STREAM_CLOSED: case GRPC_STREAM_CLOSED:
gpr_mu_lock(&chand->server->mu_call); gpr_mu_lock(&calld->mu_state);
if (calld->state == NOT_STARTED) { if (calld->state == NOT_STARTED) {
calld->state = ZOMBIED; calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure); grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} else if (calld->state == PENDING) { } else if (calld->state == PENDING) {
call_list_remove(calld, PENDING_START);
calld->state = ZOMBIED; calld->state = ZOMBIED;
gpr_mu_unlock(&calld->mu_state);
gpr_mu_lock(&chand->server->mu_call);
call_list_remove(calld, PENDING_START);
gpr_mu_unlock(&chand->server->mu_call);
grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem); grpc_iomgr_closure_init(&calld->kill_zombie_closure, kill_zombie, elem);
grpc_iomgr_add_callback(&calld->kill_zombie_closure); grpc_iomgr_add_callback(&calld->kill_zombie_closure);
} } else {
remove_res = call_list_remove(calld, ALL_CALLS); gpr_mu_unlock(&calld->mu_state);
gpr_mu_unlock(&chand->server->mu_call);
gpr_mu_lock(&chand->server->mu_global);
if (remove_res) {
disconnect = decrement_call_count(chand);
if (disconnect) {
GRPC_CHANNEL_INTERNAL_REF(chand->channel, "send-disconnect");
}
}
gpr_mu_unlock(&chand->server->mu_global);
if (disconnect) {
send_shutdown(chand->channel, 0, 1);
GRPC_CHANNEL_INTERNAL_UNREF(chand->channel, "send-disconnect");
} }
break; break;
} }
@ -676,17 +640,10 @@ static void init_call_elem(grpc_call_element *elem,
memset(calld, 0, sizeof(call_data)); memset(calld, 0, sizeof(call_data));
calld->deadline = gpr_inf_future; calld->deadline = gpr_inf_future;
calld->call = grpc_call_from_top_element(elem); calld->call = grpc_call_from_top_element(elem);
gpr_mu_init(&calld->mu_state);
grpc_iomgr_closure_init(&calld->server_on_recv, server_on_recv, elem); grpc_iomgr_closure_init(&calld->server_on_recv, server_on_recv, elem);
gpr_mu_lock(&chand->server->mu_call);
call_list_join(&chand->server->lists[ALL_CALLS], calld, ALL_CALLS);
gpr_mu_unlock(&chand->server->mu_call);
gpr_mu_lock(&chand->server->mu_global);
chand->num_calls++;
gpr_mu_unlock(&chand->server->mu_global);
server_ref(chand->server); server_ref(chand->server);
if (initial_op) server_mutate_op(elem, initial_op); if (initial_op) server_mutate_op(elem, initial_op);
@ -695,27 +652,22 @@ static void init_call_elem(grpc_call_element *elem,
static void destroy_call_elem(grpc_call_element *elem) { static void destroy_call_elem(grpc_call_element *elem) {
channel_data *chand = elem->channel_data; channel_data *chand = elem->channel_data;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
int removed[CALL_LIST_COUNT];
size_t i;
gpr_mu_lock(&chand->server->mu_call); if (calld->state == PENDING) {
for (i = 0; i < CALL_LIST_COUNT; i++) { gpr_mu_lock(&chand->server->mu_call);
removed[i] = call_list_remove(elem->call_data, i); call_list_remove(elem->call_data, PENDING_START);
} gpr_mu_unlock(&chand->server->mu_call);
gpr_mu_unlock(&chand->server->mu_call);
if (removed[ALL_CALLS]) {
gpr_mu_lock(&chand->server->mu_global);
decrement_call_count(chand);
gpr_mu_unlock(&chand->server->mu_global);
} }
if (calld->host) { if (calld->host) {
grpc_mdstr_unref(calld->host); GRPC_MDSTR_UNREF(calld->host);
} }
if (calld->path) { if (calld->path) {
grpc_mdstr_unref(calld->path); GRPC_MDSTR_UNREF(calld->path);
} }
gpr_mu_destroy(&calld->mu_state);
server_unref(chand->server); server_unref(chand->server);
} }
@ -727,7 +679,6 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
GPR_ASSERT(is_first); GPR_ASSERT(is_first);
GPR_ASSERT(!is_last); GPR_ASSERT(!is_last);
chand->server = NULL; chand->server = NULL;
chand->num_calls = 0;
chand->channel = NULL; chand->channel = NULL;
chand->path_key = grpc_mdstr_from_string(metadata_context, ":path"); chand->path_key = grpc_mdstr_from_string(metadata_context, ":path");
chand->authority_key = grpc_mdstr_from_string(metadata_context, ":authority"); chand->authority_key = grpc_mdstr_from_string(metadata_context, ":authority");
@ -744,10 +695,10 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
if (chand->registered_methods) { if (chand->registered_methods) {
for (i = 0; i < chand->registered_method_slots; i++) { for (i = 0; i < chand->registered_method_slots; i++) {
if (chand->registered_methods[i].method) { if (chand->registered_methods[i].method) {
grpc_mdstr_unref(chand->registered_methods[i].method); GRPC_MDSTR_UNREF(chand->registered_methods[i].method);
} }
if (chand->registered_methods[i].host) { if (chand->registered_methods[i].host) {
grpc_mdstr_unref(chand->registered_methods[i].host); GRPC_MDSTR_UNREF(chand->registered_methods[i].host);
} }
} }
gpr_free(chand->registered_methods); gpr_free(chand->registered_methods);
@ -759,8 +710,8 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
chand->next = chand->prev = chand; chand->next = chand->prev = chand;
maybe_finish_shutdown(chand->server); maybe_finish_shutdown(chand->server);
gpr_mu_unlock(&chand->server->mu_global); gpr_mu_unlock(&chand->server->mu_global);
grpc_mdstr_unref(chand->path_key); GRPC_MDSTR_UNREF(chand->path_key);
grpc_mdstr_unref(chand->authority_key); GRPC_MDSTR_UNREF(chand->authority_key);
server_unref(chand->server); server_unref(chand->server);
} }
} }
@ -978,15 +929,14 @@ void grpc_server_setup_transport(grpc_server *s, grpc_transport *transport,
void grpc_server_shutdown_and_notify(grpc_server *server, void grpc_server_shutdown_and_notify(grpc_server *server,
grpc_completion_queue *cq, void *tag) { grpc_completion_queue *cq, void *tag) {
listener *l; listener *l;
requested_call_array requested_calls; requested_call *requests = NULL;
size_t i;
registered_method *rm; registered_method *rm;
shutdown_tag *sdt; shutdown_tag *sdt;
channel_broadcaster broadcaster; channel_broadcaster broadcaster;
/* lock, and gather up some stuff to do */ /* lock, and gather up some stuff to do */
gpr_mu_lock(&server->mu_global); gpr_mu_lock(&server->mu_global);
grpc_cq_begin_op(cq, NULL); grpc_cq_begin_op(cq);
server->shutdown_tags = server->shutdown_tags =
gpr_realloc(server->shutdown_tags, gpr_realloc(server->shutdown_tags,
sizeof(shutdown_tag) * (server->num_shutdown_tags + 1)); sizeof(shutdown_tag) * (server->num_shutdown_tags + 1));
@ -998,27 +948,21 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
return; return;
} }
server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME);
channel_broadcaster_init(server, &broadcaster); channel_broadcaster_init(server, &broadcaster);
/* collect all unregistered then registered calls */ /* collect all unregistered then registered calls */
gpr_mu_lock(&server->mu_call); gpr_mu_lock(&server->mu_call);
requested_calls = server->requested_calls; requests = server->requests;
memset(&server->requested_calls, 0, sizeof(server->requested_calls)); server->requests = NULL;
for (rm = server->registered_methods; rm; rm = rm->next) { for (rm = server->registered_methods; rm; rm = rm->next) {
if (requested_calls.count + rm->requested.count > while (rm->requests != NULL) {
requested_calls.capacity) { requested_call *c = rm->requests;
requested_calls.capacity = rm->requests = c->next;
GPR_MAX(requested_calls.count + rm->requested.count, c->next = requests;
2 * requested_calls.capacity); requests = c;
requested_calls.calls =
gpr_realloc(requested_calls.calls, sizeof(*requested_calls.calls) *
requested_calls.capacity);
} }
memcpy(requested_calls.calls + requested_calls.count, rm->requested.calls,
sizeof(*requested_calls.calls) * rm->requested.count);
requested_calls.count += rm->requested.count;
gpr_free(rm->requested.calls);
memset(&rm->requested, 0, sizeof(rm->requested));
} }
gpr_mu_unlock(&server->mu_call); gpr_mu_unlock(&server->mu_call);
@ -1027,10 +971,11 @@ void grpc_server_shutdown_and_notify(grpc_server *server,
gpr_mu_unlock(&server->mu_global); gpr_mu_unlock(&server->mu_global);
/* terminate all the requested calls */ /* terminate all the requested calls */
for (i = 0; i < requested_calls.count; i++) { while (requests != NULL) {
fail_call(server, &requested_calls.calls[i]); requested_call *next = requests->next;
fail_call(server, requests);
requests = next;
} }
gpr_free(requested_calls.calls);
/* Shutdown listeners */ /* Shutdown listeners */
for (l = server->listeners; l; l = l->next) { for (l = server->listeners; l; l = l->next) {
@ -1049,47 +994,13 @@ void grpc_server_listener_destroy_done(void *s) {
} }
void grpc_server_cancel_all_calls(grpc_server *server) { void grpc_server_cancel_all_calls(grpc_server *server) {
call_data *calld; channel_broadcaster broadcaster;
grpc_call **calls;
size_t call_count;
size_t call_capacity;
int is_first = 1;
size_t i;
gpr_mu_lock(&server->mu_call);
GPR_ASSERT(server->shutdown);
if (!server->lists[ALL_CALLS]) {
gpr_mu_unlock(&server->mu_call);
return;
}
call_capacity = 8;
call_count = 0;
calls = gpr_malloc(sizeof(grpc_call *) * call_capacity);
for (calld = server->lists[ALL_CALLS];
calld != server->lists[ALL_CALLS] || is_first;
calld = calld->links[ALL_CALLS].next) {
if (call_count == call_capacity) {
call_capacity *= 2;
calls = gpr_realloc(calls, sizeof(grpc_call *) * call_capacity);
}
calls[call_count++] = calld->call;
GRPC_CALL_INTERNAL_REF(calld->call, "cancel_all");
is_first = 0;
}
gpr_mu_unlock(&server->mu_call);
for (i = 0; i < call_count; i++) { gpr_mu_lock(&server->mu_global);
grpc_call_cancel_with_status(calls[i], GRPC_STATUS_UNAVAILABLE, channel_broadcaster_init(server, &broadcaster);
"Unavailable"); gpr_mu_unlock(&server->mu_global);
GRPC_CALL_INTERNAL_UNREF(calls[i], "cancel_all", 1);
}
gpr_free(calls); channel_broadcaster_shutdown(&broadcaster, 0, 1);
} }
void grpc_server_destroy(grpc_server *server) { void grpc_server_destroy(grpc_server *server) {
@ -1126,7 +1037,7 @@ void grpc_server_add_listener(grpc_server *server, void *arg,
static grpc_call_error queue_call_request(grpc_server *server, static grpc_call_error queue_call_request(grpc_server *server,
requested_call *rc) { requested_call *rc) {
call_data *calld = NULL; call_data *calld = NULL;
requested_call_array *requested_calls = NULL; requested_call **requests = NULL;
gpr_mu_lock(&server->mu_call); gpr_mu_lock(&server->mu_call);
if (server->shutdown) { if (server->shutdown) {
gpr_mu_unlock(&server->mu_call); gpr_mu_unlock(&server->mu_call);
@ -1137,22 +1048,25 @@ static grpc_call_error queue_call_request(grpc_server *server,
case BATCH_CALL: case BATCH_CALL:
calld = calld =
call_list_remove_head(&server->lists[PENDING_START], PENDING_START); call_list_remove_head(&server->lists[PENDING_START], PENDING_START);
requested_calls = &server->requested_calls; requests = &server->requests;
break; break;
case REGISTERED_CALL: case REGISTERED_CALL:
calld = call_list_remove_head( calld = call_list_remove_head(
&rc->data.registered.registered_method->pending, PENDING_START); &rc->data.registered.registered_method->pending, PENDING_START);
requested_calls = &rc->data.registered.registered_method->requested; requests = &rc->data.registered.registered_method->requests;
break; break;
} }
if (calld) { if (calld != NULL) {
gpr_mu_unlock(&server->mu_call);
gpr_mu_lock(&calld->mu_state);
GPR_ASSERT(calld->state == PENDING); GPR_ASSERT(calld->state == PENDING);
calld->state = ACTIVATED; calld->state = ACTIVATED;
gpr_mu_unlock(&server->mu_call); gpr_mu_unlock(&calld->mu_state);
begin_call(server, calld, rc); begin_call(server, calld, rc);
return GRPC_CALL_OK; return GRPC_CALL_OK;
} else { } else {
*requested_call_array_add(requested_calls) = *rc; rc->next = *requests;
*requests = rc;
gpr_mu_unlock(&server->mu_call); gpr_mu_unlock(&server->mu_call);
return GRPC_CALL_OK; return GRPC_CALL_OK;
} }
@ -1163,22 +1077,23 @@ grpc_call_error grpc_server_request_call(
grpc_metadata_array *initial_metadata, grpc_metadata_array *initial_metadata,
grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_bound_to_call,
grpc_completion_queue *cq_for_notification, void *tag) { grpc_completion_queue *cq_for_notification, void *tag) {
requested_call rc; requested_call *rc = gpr_malloc(sizeof(*rc));
GRPC_SERVER_LOG_REQUEST_CALL(GPR_INFO, server, call, details, GRPC_SERVER_LOG_REQUEST_CALL(GPR_INFO, server, call, details,
initial_metadata, cq_bound_to_call, initial_metadata, cq_bound_to_call,
cq_for_notification, tag); cq_for_notification, tag);
if (!grpc_cq_is_server_cq(cq_for_notification)) { if (!grpc_cq_is_server_cq(cq_for_notification)) {
gpr_free(rc);
return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
} }
grpc_cq_begin_op(cq_for_notification, NULL); grpc_cq_begin_op(cq_for_notification);
rc.type = BATCH_CALL; rc->type = BATCH_CALL;
rc.tag = tag; rc->tag = tag;
rc.cq_bound_to_call = cq_bound_to_call; rc->cq_bound_to_call = cq_bound_to_call;
rc.cq_for_notification = cq_for_notification; rc->cq_for_notification = cq_for_notification;
rc.call = call; rc->call = call;
rc.data.batch.details = details; rc->data.batch.details = details;
rc.data.batch.initial_metadata = initial_metadata; rc->data.batch.initial_metadata = initial_metadata;
return queue_call_request(server, &rc); return queue_call_request(server, rc);
} }
grpc_call_error grpc_server_request_registered_call( grpc_call_error grpc_server_request_registered_call(
@ -1186,22 +1101,23 @@ grpc_call_error grpc_server_request_registered_call(
grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload, grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload,
grpc_completion_queue *cq_bound_to_call, grpc_completion_queue *cq_bound_to_call,
grpc_completion_queue *cq_for_notification, void *tag) { grpc_completion_queue *cq_for_notification, void *tag) {
requested_call rc; requested_call *rc = gpr_malloc(sizeof(*rc));
registered_method *registered_method = rm; registered_method *registered_method = rm;
if (!grpc_cq_is_server_cq(cq_for_notification)) { if (!grpc_cq_is_server_cq(cq_for_notification)) {
gpr_free(rc);
return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; return GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE;
} }
grpc_cq_begin_op(cq_for_notification, NULL); grpc_cq_begin_op(cq_for_notification);
rc.type = REGISTERED_CALL; rc->type = REGISTERED_CALL;
rc.tag = tag; rc->tag = tag;
rc.cq_bound_to_call = cq_bound_to_call; rc->cq_bound_to_call = cq_bound_to_call;
rc.cq_for_notification = cq_for_notification; rc->cq_for_notification = cq_for_notification;
rc.call = call; rc->call = call;
rc.data.registered.registered_method = registered_method; rc->data.registered.registered_method = registered_method;
rc.data.registered.deadline = deadline; rc->data.registered.deadline = deadline;
rc.data.registered.initial_metadata = initial_metadata; rc->data.registered.initial_metadata = initial_metadata;
rc.data.registered.optional_payload = optional_payload; rc->data.registered.optional_payload = optional_payload;
return queue_call_request(server, &rc); return queue_call_request(server, rc);
} }
static void publish_registered_or_batch(grpc_call *call, int success, static void publish_registered_or_batch(grpc_call *call, int success,
@ -1268,8 +1184,11 @@ static void begin_call(grpc_server *server, call_data *calld,
} }
GRPC_CALL_INTERNAL_REF(calld->call, "server"); GRPC_CALL_INTERNAL_REF(calld->call, "server");
grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish, grpc_call_start_ioreq_and_call_back(calld->call, req, r - req, publish, rc);
rc->tag); }
static void done_request_event(void *req, grpc_cq_completion *c) {
gpr_free(req);
} }
static void fail_call(grpc_server *server, requested_call *rc) { static void fail_call(grpc_server *server, requested_call *rc) {
@ -1282,15 +1201,19 @@ static void fail_call(grpc_server *server, requested_call *rc) {
rc->data.registered.initial_metadata->count = 0; rc->data.registered.initial_metadata->count = 0;
break; break;
} }
grpc_cq_end_op(rc->cq_for_notification, rc->tag, NULL, 0); grpc_cq_end_op(rc->cq_for_notification, rc->tag, 0, done_request_event, rc,
&rc->completion);
} }
static void publish_registered_or_batch(grpc_call *call, int success, static void publish_registered_or_batch(grpc_call *call, int success,
void *tag) { void *prc) {
grpc_call_element *elem = grpc_call_element *elem =
grpc_call_stack_element(grpc_call_get_call_stack(call), 0); grpc_call_stack_element(grpc_call_get_call_stack(call), 0);
requested_call *rc = prc;
call_data *calld = elem->call_data; call_data *calld = elem->call_data;
grpc_cq_end_op(calld->cq_new, tag, call, success); grpc_cq_end_op(calld->cq_new, rc->tag, success, done_request_event, rc,
&rc->completion);
GRPC_CALL_INTERNAL_UNREF(call, "server", 0);
} }
const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) { const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) {

@ -0,0 +1,41 @@
/*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/* This file is autogenerated from:
templates/src/core/surface/version.c.template */
#include <grpc/grpc.h>
const char *grpc_version_string(void) {
return "0.10.0.0";
}

@ -89,12 +89,9 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
fh_0: fh_0:
case GRPC_CHTTP2_DATA_FH_0: case GRPC_CHTTP2_DATA_FH_0:
p->frame_type = *cur; p->frame_type = *cur;
if (++cur == end) {
p->state = GRPC_CHTTP2_DATA_FH_1;
return GRPC_CHTTP2_PARSE_OK;
}
switch (p->frame_type) { switch (p->frame_type) {
case 0: case 0:
/* noop */
break; break;
case 1: case 1:
gpr_log(GPR_ERROR, "Compressed GRPC frames not yet supported"); gpr_log(GPR_ERROR, "Compressed GRPC frames not yet supported");
@ -103,6 +100,10 @@ grpc_chttp2_parse_error grpc_chttp2_data_parser_parse(
gpr_log(GPR_ERROR, "Bad GRPC frame type 0x%02x", p->frame_type); gpr_log(GPR_ERROR, "Bad GRPC frame type 0x%02x", p->frame_type);
return GRPC_CHTTP2_STREAM_ERROR; return GRPC_CHTTP2_STREAM_ERROR;
} }
if (++cur == end) {
p->state = GRPC_CHTTP2_DATA_FH_1;
return GRPC_CHTTP2_PARSE_OK;
}
/* fallthrough */ /* fallthrough */
case GRPC_CHTTP2_DATA_FH_1: case GRPC_CHTTP2_DATA_FH_1:
p->frame_size = ((gpr_uint32)*cur) << 24; p->frame_size = ((gpr_uint32)*cur) << 24;

@ -94,8 +94,8 @@ grpc_chttp2_parse_error grpc_chttp2_window_update_parser_parse(
} }
GPR_ASSERT(is_last); GPR_ASSERT(is_last);
if (transport_parsing->incoming_stream_id) { if (transport_parsing->incoming_stream_id != 0) {
if (stream_parsing) { if (stream_parsing != NULL) {
GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("update", transport_parsing, GRPC_CHTTP2_FLOWCTL_TRACE_STREAM("update", transport_parsing,
stream_parsing, outgoing_window_update, stream_parsing, outgoing_window_update,
p->amount); p->amount);

@ -622,7 +622,7 @@ static const gpr_uint8 inverse_base64[256] = {
static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md, static void on_hdr(grpc_chttp2_hpack_parser *p, grpc_mdelem *md,
int add_to_table) { int add_to_table) {
if (add_to_table) { if (add_to_table) {
grpc_mdelem_ref(md); GRPC_MDELEM_REF(md);
grpc_chttp2_hptbl_add(&p->table, md); grpc_chttp2_hptbl_add(&p->table, md);
} }
p->on_header(p->on_header_user_data, md); p->on_header(p->on_header_user_data, md);
@ -711,7 +711,7 @@ static int parse_stream_dep0(grpc_chttp2_hpack_parser *p, const gpr_uint8 *cur,
static int finish_indexed_field(grpc_chttp2_hpack_parser *p, static int finish_indexed_field(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
grpc_mdelem_ref(md); GRPC_MDELEM_REF(md);
on_hdr(p, md, 0); on_hdr(p, md, 0);
return parse_begin(p, cur, end); return parse_begin(p, cur, end);
} }
@ -740,7 +740,7 @@ static int finish_lithdr_incidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key), GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)), take_string(p, &p->value)),
1); 1);
return parse_begin(p, cur, end); return parse_begin(p, cur, end);
@ -793,7 +793,7 @@ static int finish_lithdr_notidx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key), GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)), take_string(p, &p->value)),
0); 0);
return parse_begin(p, cur, end); return parse_begin(p, cur, end);
@ -846,7 +846,7 @@ static int finish_lithdr_nvridx(grpc_chttp2_hpack_parser *p,
const gpr_uint8 *cur, const gpr_uint8 *end) { const gpr_uint8 *cur, const gpr_uint8 *end) {
grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index); grpc_mdelem *md = grpc_chttp2_hptbl_lookup(&p->table, p->index);
on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx, on_hdr(p, grpc_mdelem_from_metadata_strings(p->table.mdctx,
grpc_mdstr_ref(md->key), GRPC_MDSTR_REF(md->key),
take_string(p, &p->value)), take_string(p, &p->value)),
0); 0);
return parse_begin(p, cur, end); return parse_begin(p, cur, end);
@ -1329,17 +1329,14 @@ static int parse_value_string_with_literal_key(grpc_chttp2_hpack_parser *p,
/* PUBLIC INTERFACE */ /* PUBLIC INTERFACE */
static void on_header_not_set(void *user_data, grpc_mdelem *md) { static void on_header_not_set(void *user_data, grpc_mdelem *md) {
char *keyhex = char *keyhex = gpr_dump_slice(md->key->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
gpr_hexdump(grpc_mdstr_as_c_string(md->key),
GPR_SLICE_LENGTH(md->key->slice), GPR_HEXDUMP_PLAINTEXT);
char *valuehex = char *valuehex =
gpr_hexdump(grpc_mdstr_as_c_string(md->value), gpr_dump_slice(md->value->slice, GPR_DUMP_HEX | GPR_DUMP_ASCII);
GPR_SLICE_LENGTH(md->value->slice), GPR_HEXDUMP_PLAINTEXT);
gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex, gpr_log(GPR_ERROR, "on_header callback not set; key=%s value=%s", keyhex,
valuehex); valuehex);
gpr_free(keyhex); gpr_free(keyhex);
gpr_free(valuehex); gpr_free(valuehex);
grpc_mdelem_unref(md); GRPC_MDELEM_UNREF(md);
abort(); abort();
} }

@ -122,10 +122,10 @@ void grpc_chttp2_hptbl_init(grpc_chttp2_hptbl *tbl, grpc_mdctx *mdctx) {
void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) { void grpc_chttp2_hptbl_destroy(grpc_chttp2_hptbl *tbl) {
size_t i; size_t i;
for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) {
grpc_mdelem_unref(tbl->static_ents[i]); GRPC_MDELEM_UNREF(tbl->static_ents[i]);
} }
for (i = 0; i < tbl->num_ents; i++) { for (i = 0; i < tbl->num_ents; i++) {
grpc_mdelem_unref( GRPC_MDELEM_UNREF(
tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]); tbl->ents[(tbl->first_ent + i) % GRPC_CHTTP2_MAX_TABLE_COUNT]);
} }
} }
@ -155,7 +155,7 @@ static void evict1(grpc_chttp2_hptbl *tbl) {
GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD;
tbl->first_ent = (tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT; tbl->first_ent = (tbl->first_ent + 1) % GRPC_CHTTP2_MAX_TABLE_COUNT;
tbl->num_ents--; tbl->num_ents--;
grpc_mdelem_unref(first_ent); GRPC_MDELEM_UNREF(first_ent);
} }
void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) { void grpc_chttp2_hptbl_add(grpc_chttp2_hptbl *tbl, grpc_mdelem *md) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save